From 7be9c50ca0ceb18366c539c943fd14239f7c9868 Mon Sep 17 00:00:00 2001 From: BADBADBADBOY <15671628547@163.COM> Date: Sun, 28 Feb 2021 13:04:06 +0800 Subject: [PATCH 1/4] add version branch --- ...7\346\234\254\346\226\207\346\241\243.txt" | 0 config/det_DB_mobilev3.yaml | 87 - config/det_DB_mobilev3_common.yaml | 87 - config/det_DB_mobilev3_pytorch_qua.yaml | 88 - config/det_DB_resnet50.yaml | 87 - config/det_DB_resnet50_3_3.yaml | 87 - config/det_PAN_mobilev3.yaml | 82 - config/det_PAN_resnet18.yaml | 87 - config/det_PAN_resnet18_3_3.yaml | 87 - config/det_PSE_mobilev3.yaml | 78 - config/det_PSE_resnet50.yaml | 87 - config/det_PSE_resnet50_3_3.yaml | 87 - config/det_SAST_resnet50.yaml | 100 - .../det_SAST_resnet50_3_3_ori_dataload.yaml | 100 - config/det_SAST_resnet50_ori_dataload.yaml | 111 - config/rec_CRNN_mobilev3.yaml | 78 - config/rec_CRNN_ori.yaml | 93 - config/rec_CRNN_vgg16_bn.yaml | 78 - doc/example/det_test_list.txt | 9 - doc/example/det_train_list.txt | 9 - doc/example/label.txt | 3 - doc/example/rec_test_list.txt | 13 - doc/example/rec_train_list.txt | 10 - doc/md/ocr.jpg | Bin 197278 -> 0 bytes doc/md/onnx_to_tensorrt.md | 62 - doc/md/pytorch_to_onnx.md | 34 - ...55\347\273\203\346\226\207\346\241\243.md" | 62 - ...55\347\273\203\346\226\207\346\241\243.md" | 28 - ...41\345\236\213\345\211\252\346\236\235.md" | 34 - ...41\345\236\213\350\222\270\351\246\217.md" | 25 - doc/show/ocr1.jpg | Bin 237249 -> 0 bytes doc/show/ocr2.jpg | Bin 47777 -> 0 bytes finetune_prune_model.sh | 1 - infer.sh | 1 - onnx/onnx-simple.sh | 1 - ...7\346\234\254\346\226\207\346\241\243.txt" | 0 ptocr/__init__.py | 6 - ptocr/dataloader/DetLoad/DBProcess.py | 123 - ptocr/dataloader/DetLoad/MakeBorderMap.py | 114 - ptocr/dataloader/DetLoad/MakeSegMap.py | 171 - ptocr/dataloader/DetLoad/PANProcess.py | 121 - ptocr/dataloader/DetLoad/PSEProcess.py | 120 - ptocr/dataloader/DetLoad/SASTProcess.py | 504 -- ptocr/dataloader/DetLoad/SASTProcess_ori.py | 800 --- ptocr/dataloader/DetLoad/SASTProcess_ori1.py | 862 --- ptocr/dataloader/DetLoad/__init__.py | 6 - ptocr/dataloader/DetLoad/transform_img.py | 329 -- ptocr/dataloader/RecLoad/CRNNProcess.py | 129 - ptocr/dataloader/RecLoad/DataAgument.py | 428 -- ptocr/dataloader/RecLoad/__init__.py | 6 - ptocr/dataloader/__init__.py | 6 - ptocr/model/CommonFunction.py | 77 - ptocr/model/CommonFunction_Q.py | 48 - ptocr/model/__init__.py | 7 - ptocr/model/architectures/__init__.py | 6 - .../__pycache__/__init__.cpython-35.pyc | Bin 233 -> 0 bytes .../__pycache__/__init__.cpython-36.pyc | Bin 187 -> 0 bytes .../__pycache__/det_model.cpython-35.pyc | Bin 3746 -> 0 bytes .../__pycache__/det_model.cpython-36.pyc | Bin 3302 -> 0 bytes .../__pycache__/rec_model.cpython-36.pyc | Bin 1737 -> 0 bytes ptocr/model/architectures/det_model.py | 120 - ptocr/model/architectures/det_model_q.py | 85 - ptocr/model/architectures/rec_model.py | 43 - ptocr/model/backbone/__init__.py | 6 - .../__pycache__/__init__.cpython-35.pyc | Bin 228 -> 0 bytes .../__pycache__/__init__.cpython-36.pyc | Bin 182 -> 0 bytes .../__pycache__/det_mobilev3.cpython-35.pyc | Bin 8296 -> 0 bytes .../__pycache__/det_mobilev3.cpython-36.pyc | Bin 7813 -> 0 bytes .../__pycache__/det_resnet.cpython-35.pyc | Bin 9756 -> 0 bytes .../__pycache__/det_resnet.cpython-36.pyc | Bin 9459 -> 0 bytes .../__pycache__/det_resnet_3_3.cpython-36.pyc | Bin 6798 -> 0 bytes .../det_resnet_sast.cpython-35.pyc | Bin 9939 -> 0 bytes .../det_resnet_sast.cpython-36.pyc | Bin 9319 -> 0 bytes .../det_resnet_sast_3_3.cpython-36.pyc | Bin 6900 -> 0 bytes .../__pycache__/det_scnet.cpython-35.pyc | Bin 9087 -> 0 bytes .../__pycache__/rec_vgg.cpython-36.pyc | Bin 7165 -> 0 bytes ptocr/model/backbone/det_densenet.py | 242 - ptocr/model/backbone/det_mobilev3.py | 244 - .../backbone/det_mobilev3_pytorch_qua.py | 137 - ptocr/model/backbone/det_resnet.py | 368 -- ptocr/model/backbone/det_resnet_3_3.py | 233 - ptocr/model/backbone/det_resnet_sast.py | 362 -- ptocr/model/backbone/det_resnet_sast_3_3.py | 238 - ptocr/model/backbone/det_scnet.py | 294 -- ptocr/model/backbone/rec_crnn_backbone.py | 79 - ptocr/model/backbone/rec_vgg.py | 258 - ptocr/model/backbone/reg_mobilev3.py | 218 - ptocr/model/backbone/reg_resnet.py | 368 -- ptocr/model/head/__init__.py | 6 - .../head/__pycache__/__init__.cpython-35.pyc | Bin 224 -> 0 bytes .../head/__pycache__/__init__.cpython-36.pyc | Bin 178 -> 0 bytes .../__pycache__/det_DBHead.cpython-35.pyc | Bin 2057 -> 0 bytes .../__pycache__/det_DBHead.cpython-36.pyc | Bin 1770 -> 0 bytes .../det_FPEM_FFM_Head.cpython-35.pyc | Bin 4122 -> 0 bytes .../det_FPEM_FFM_Head.cpython-36.pyc | Bin 3585 -> 0 bytes .../__pycache__/det_FPNHead.cpython-35.pyc | Bin 2166 -> 0 bytes .../__pycache__/det_FPNHead.cpython-36.pyc | Bin 1819 -> 0 bytes .../__pycache__/det_SASTHead.cpython-35.pyc | Bin 6380 -> 0 bytes .../__pycache__/det_SASTHead.cpython-36.pyc | Bin 6155 -> 0 bytes .../__pycache__/rec_CRNNHead.cpython-36.pyc | Bin 3242 -> 0 bytes ptocr/model/head/det_DBHead.py | 54 - ptocr/model/head/det_DBHead_Qua.py | 72 - ptocr/model/head/det_FPEM_FFM_Head.py | 99 - ptocr/model/head/det_FPNHead.py | 59 - ptocr/model/head/det_SASTHead.py | 216 - ptocr/model/head/rec_CRNNHead.py | 107 - ptocr/model/loss/__init__.py | 6 - .../loss/__pycache__/__init__.cpython-35.pyc | Bin 224 -> 0 bytes .../loss/__pycache__/__init__.cpython-36.pyc | Bin 178 -> 0 bytes .../__pycache__/basical_loss.cpython-35.pyc | Bin 10424 -> 0 bytes .../__pycache__/basical_loss.cpython-36.pyc | Bin 9445 -> 0 bytes .../loss/__pycache__/ctc_loss.cpython-36.pyc | Bin 838 -> 0 bytes .../loss/__pycache__/db_loss.cpython-35.pyc | Bin 1506 -> 0 bytes .../loss/__pycache__/db_loss.cpython-36.pyc | Bin 1371 -> 0 bytes .../loss/__pycache__/pan_loss.cpython-35.pyc | Bin 2435 -> 0 bytes .../loss/__pycache__/pan_loss.cpython-36.pyc | Bin 2295 -> 0 bytes .../loss/__pycache__/pse_loss.cpython-35.pyc | Bin 2202 -> 0 bytes .../loss/__pycache__/pse_loss.cpython-36.pyc | Bin 1981 -> 0 bytes .../loss/__pycache__/sast_loss.cpython-35.pyc | Bin 3250 -> 0 bytes .../loss/__pycache__/sast_loss.cpython-36.pyc | Bin 3031 -> 0 bytes ptocr/model/loss/basical_loss.py | 275 - ptocr/model/loss/centerloss.py | 95 - ptocr/model/loss/ctc_loss.py | 19 - ptocr/model/loss/db_loss.py | 30 - ptocr/model/loss/pan_loss.py | 66 - ptocr/model/loss/pse_loss.py | 52 - ptocr/model/loss/sast_loss.py | 109 - ptocr/model/segout/__init__.py | 6 - .../__pycache__/__init__.cpython-35.pyc | Bin 226 -> 0 bytes .../__pycache__/__init__.cpython-36.pyc | Bin 180 -> 0 bytes .../__pycache__/det_DB_segout.cpython-35.pyc | Bin 3235 -> 0 bytes .../__pycache__/det_DB_segout.cpython-36.pyc | Bin 2929 -> 0 bytes .../__pycache__/det_PAN_segout.cpython-35.pyc | Bin 1143 -> 0 bytes .../__pycache__/det_PAN_segout.cpython-36.pyc | Bin 1045 -> 0 bytes .../__pycache__/det_PSE_segout.cpython-35.pyc | Bin 1091 -> 0 bytes .../__pycache__/det_PSE_segout.cpython-36.pyc | Bin 999 -> 0 bytes .../det_SAST_segout.cpython-35.pyc | Bin 3114 -> 0 bytes .../det_SAST_segout.cpython-36.pyc | Bin 3141 -> 0 bytes ptocr/model/segout/det_DB_segout.py | 90 - ptocr/model/segout/det_DB_segout_qua.py | 58 - ptocr/model/segout/det_PAN_segout.py | 23 - ptocr/model/segout/det_PSE_segout.py | 20 - ptocr/model/segout/det_SAST_segout.py | 91 - ptocr/optimizer.py | 48 - ptocr/postprocess/DBpostprocess.py | 219 - ptocr/postprocess/PANpostprocess.py | 135 - ptocr/postprocess/PSEpostprocess.py | 135 - ptocr/postprocess/SASTpostprocess.py | 294 -- ptocr/postprocess/__init__.py | 6 - ptocr/postprocess/dbprocess/Makefile | 15 - ptocr/postprocess/dbprocess/__init__.py | 32 - ptocr/postprocess/dbprocess/cppdbprocess.cpp | 369 -- ptocr/postprocess/dbprocess/cppdbprocess.so | Bin 299392 -> 0 bytes ptocr/postprocess/dbprocess/include/clipper.h | 423 -- .../dbprocess/include/clipper/clipper.cpp | 4622 ----------------- .../dbprocess/include/clipper/clipper.hpp | 404 -- .../dbprocess/include/postprocess_op.h | 91 - .../dbprocess/include/pybind11/attr.h | 492 -- .../dbprocess/include/pybind11/buffer_info.h | 108 - .../dbprocess/include/pybind11/cast.h | 2128 -------- .../dbprocess/include/pybind11/chrono.h | 162 - .../include/pybind11/class_support.h | 603 --- .../dbprocess/include/pybind11/common.h | 2 - .../dbprocess/include/pybind11/complex.h | 65 - .../dbprocess/include/pybind11/descr.h | 185 - .../dbprocess/include/pybind11/detail/class.h | 622 --- .../include/pybind11/detail/common.h | 807 --- .../dbprocess/include/pybind11/detail/descr.h | 100 - .../dbprocess/include/pybind11/detail/init.h | 335 -- .../include/pybind11/detail/internals.h | 291 -- .../include/pybind11/detail/typeid.h | 53 - .../dbprocess/include/pybind11/eigen.h | 607 --- .../dbprocess/include/pybind11/embed.h | 200 - .../dbprocess/include/pybind11/eval.h | 117 - .../dbprocess/include/pybind11/functional.h | 83 - .../dbprocess/include/pybind11/iostream.h | 200 - .../dbprocess/include/pybind11/numpy.h | 1610 ------ .../dbprocess/include/pybind11/operators.h | 168 - .../dbprocess/include/pybind11/options.h | 65 - .../dbprocess/include/pybind11/pybind11.h | 2094 -------- .../dbprocess/include/pybind11/pytypes.h | 1438 ----- .../dbprocess/include/pybind11/stl.h | 386 -- .../dbprocess/include/pybind11/stl_bind.h | 599 --- .../dbprocess/include/pybind11/typeid.h | 53 - ptocr/postprocess/lanms/.gitignore | 1 - ptocr/postprocess/lanms/.ycm_extra_conf.py | 140 - ptocr/postprocess/lanms/Makefile | 13 - ptocr/postprocess/lanms/__init__.py | 20 - ptocr/postprocess/lanms/__main__.py | 10 - ptocr/postprocess/lanms/adaptor.cpp | 61 - .../lanms/include/clipper/clipper.cpp | 4622 ----------------- .../lanms/include/clipper/clipper.hpp | 404 -- .../postprocess/lanms/include/pybind11/attr.h | 471 -- .../lanms/include/pybind11/buffer_info.h | 108 - .../postprocess/lanms/include/pybind11/cast.h | 2058 -------- .../lanms/include/pybind11/chrono.h | 162 - .../lanms/include/pybind11/class_support.h | 603 --- .../lanms/include/pybind11/common.h | 857 --- .../lanms/include/pybind11/complex.h | 61 - .../lanms/include/pybind11/descr.h | 185 - .../lanms/include/pybind11/eigen.h | 610 --- .../lanms/include/pybind11/embed.h | 194 - .../postprocess/lanms/include/pybind11/eval.h | 117 - .../lanms/include/pybind11/functional.h | 85 - .../lanms/include/pybind11/numpy.h | 1598 ------ .../lanms/include/pybind11/operators.h | 167 - .../lanms/include/pybind11/options.h | 65 - .../lanms/include/pybind11/pybind11.h | 1869 ------- .../lanms/include/pybind11/pytypes.h | 1318 ----- .../postprocess/lanms/include/pybind11/stl.h | 367 -- .../lanms/include/pybind11/stl_bind.h | 585 --- .../lanms/include/pybind11/typeid.h | 53 - ptocr/postprocess/lanms/lanms.h | 234 - ptocr/postprocess/locality_aware_nms.py | 199 - ptocr/postprocess/piexlmerge/Makefile | 15 - ptocr/postprocess/piexlmerge/__init__.py | 88 - .../piexlmerge/include/clipper/clipper.cpp | 4622 ----------------- .../piexlmerge/include/clipper/clipper.hpp | 404 -- .../piexlmerge/include/pybind11/attr.h | 492 -- .../piexlmerge/include/pybind11/buffer_info.h | 108 - .../piexlmerge/include/pybind11/cast.h | 2128 -------- .../piexlmerge/include/pybind11/chrono.h | 162 - .../include/pybind11/class_support.h | 603 --- .../piexlmerge/include/pybind11/common.h | 2 - .../piexlmerge/include/pybind11/complex.h | 65 - .../piexlmerge/include/pybind11/descr.h | 185 - .../include/pybind11/detail/class.h | 622 --- .../include/pybind11/detail/common.h | 807 --- .../include/pybind11/detail/descr.h | 100 - .../piexlmerge/include/pybind11/detail/init.h | 335 -- .../include/pybind11/detail/internals.h | 291 -- .../include/pybind11/detail/typeid.h | 53 - .../piexlmerge/include/pybind11/eigen.h | 607 --- .../piexlmerge/include/pybind11/embed.h | 200 - .../piexlmerge/include/pybind11/eval.h | 117 - .../piexlmerge/include/pybind11/functional.h | 83 - .../piexlmerge/include/pybind11/iostream.h | 200 - .../piexlmerge/include/pybind11/numpy.h | 1610 ------ .../piexlmerge/include/pybind11/operators.h | 168 - .../piexlmerge/include/pybind11/options.h | 65 - .../piexlmerge/include/pybind11/pybind11.h | 2094 -------- .../piexlmerge/include/pybind11/pytypes.h | 1438 ----- .../piexlmerge/include/pybind11/stl.h | 386 -- .../piexlmerge/include/pybind11/stl_bind.h | 599 --- .../piexlmerge/include/pybind11/typeid.h | 53 - ptocr/postprocess/piexlmerge/lanms.h | 234 - ptocr/postprocess/piexlmerge/pixelmerge.cpp | 310 -- ptocr/postprocess/piexlmerge/pixelmerge.so | Bin 291416 -> 0 bytes ptocr/utils/__init__.py | 6 - ptocr/utils/cal_iou_acc.py | 64 - ptocr/utils/gen_teacher_model.py | 58 - ptocr/utils/logger.py | 66 - ptocr/utils/metrics.py | 50 - ptocr/utils/prune_script.py | 572 -- ptocr/utils/transform_label.py | 128 - ptocr/utils/util_function.py | 185 - script/__init__.py | 6 - script/create_lmdb.py | 138 - script/get_key_label.py | 31 - script/get_train_list.py | 29 - script/onnx_to_tensorrt.py | 188 - script/pytorch_to_onnx.py | 144 - to_onnx.sh | 1 - to_tensorrt.sh | 1 - tools/__init__.py | 6 - tools/cal.py | 11 - tools/cal_rescall/__init__.py | 6 - .../__pycache__/__init__.cpython-35.pyc | Bin 225 -> 0 bytes .../__pycache__/__init__.cpython-36.pyc | Bin 179 -> 0 bytes .../__pycache__/cal_det.cpython-36.pyc | Bin 1666 -> 0 bytes .../__pycache__/cal_iou.cpython-36.pyc | Bin 4979 -> 0 bytes .../rrc_evaluation_funcs.cpython-35.pyc | Bin 14438 -> 0 bytes .../rrc_evaluation_funcs.cpython-36.pyc | Bin 13315 -> 0 bytes .../__pycache__/script.cpython-35.pyc | Bin 8636 -> 0 bytes .../__pycache__/script.cpython-36.pyc | Bin 7714 -> 0 bytes tools/cal_rescall/cal_det.py | 53 - tools/cal_rescall/cal_iou.py | 235 - tools/cal_rescall/rrc_evaluation_funcs.py | 414 -- tools/cal_rescall/script.py | 323 -- tools/det_infer.py | 192 - tools/det_sast.py | 238 - tools/det_train.py | 307 -- tools/det_train_qua.py | 333 -- tools/pruned/__init__.py | 0 tools/pruned/prune_model_all.py | 477 -- tools/pruned/prune_model_backbone.py | 365 -- tools/rec_infer.py | 82 - tools/rec_train.py | 336 -- 288 files changed, 72401 deletions(-) delete mode 100644 "checkpoint/\346\226\260\345\273\272\346\226\207\346\234\254\346\226\207\346\241\243.txt" delete mode 100644 config/det_DB_mobilev3.yaml delete mode 100644 config/det_DB_mobilev3_common.yaml delete mode 100644 config/det_DB_mobilev3_pytorch_qua.yaml delete mode 100644 config/det_DB_resnet50.yaml delete mode 100644 config/det_DB_resnet50_3_3.yaml delete mode 100644 config/det_PAN_mobilev3.yaml delete mode 100644 config/det_PAN_resnet18.yaml delete mode 100644 config/det_PAN_resnet18_3_3.yaml delete mode 100644 config/det_PSE_mobilev3.yaml delete mode 100644 config/det_PSE_resnet50.yaml delete mode 100644 config/det_PSE_resnet50_3_3.yaml delete mode 100644 config/det_SAST_resnet50.yaml delete mode 100644 config/det_SAST_resnet50_3_3_ori_dataload.yaml delete mode 100644 config/det_SAST_resnet50_ori_dataload.yaml delete mode 100644 config/rec_CRNN_mobilev3.yaml delete mode 100644 config/rec_CRNN_ori.yaml delete mode 100644 config/rec_CRNN_vgg16_bn.yaml delete mode 100644 doc/example/det_test_list.txt delete mode 100644 doc/example/det_train_list.txt delete mode 100644 doc/example/label.txt delete mode 100644 doc/example/rec_test_list.txt delete mode 100644 doc/example/rec_train_list.txt delete mode 100644 doc/md/ocr.jpg delete mode 100644 doc/md/onnx_to_tensorrt.md delete mode 100644 doc/md/pytorch_to_onnx.md delete mode 100644 "doc/md/\346\226\207\346\234\254\346\243\200\346\265\213\350\256\255\347\273\203\346\226\207\346\241\243.md" delete mode 100644 "doc/md/\346\226\207\346\234\254\350\257\206\345\210\253\350\256\255\347\273\203\346\226\207\346\241\243.md" delete mode 100644 "doc/md/\346\250\241\345\236\213\345\211\252\346\236\235.md" delete mode 100644 "doc/md/\346\250\241\345\236\213\350\222\270\351\246\217.md" delete mode 100644 doc/show/ocr1.jpg delete mode 100644 doc/show/ocr2.jpg delete mode 100644 finetune_prune_model.sh delete mode 100644 infer.sh delete mode 100644 onnx/onnx-simple.sh delete mode 100644 "pre_model/\346\226\260\345\273\272\346\226\207\346\234\254\346\226\207\346\241\243.txt" delete mode 100644 ptocr/__init__.py delete mode 100644 ptocr/dataloader/DetLoad/DBProcess.py delete mode 100644 ptocr/dataloader/DetLoad/MakeBorderMap.py delete mode 100644 ptocr/dataloader/DetLoad/MakeSegMap.py delete mode 100644 ptocr/dataloader/DetLoad/PANProcess.py delete mode 100644 ptocr/dataloader/DetLoad/PSEProcess.py delete mode 100644 ptocr/dataloader/DetLoad/SASTProcess.py delete mode 100644 ptocr/dataloader/DetLoad/SASTProcess_ori.py delete mode 100644 ptocr/dataloader/DetLoad/SASTProcess_ori1.py delete mode 100644 ptocr/dataloader/DetLoad/__init__.py delete mode 100644 ptocr/dataloader/DetLoad/transform_img.py delete mode 100644 ptocr/dataloader/RecLoad/CRNNProcess.py delete mode 100644 ptocr/dataloader/RecLoad/DataAgument.py delete mode 100644 ptocr/dataloader/RecLoad/__init__.py delete mode 100644 ptocr/dataloader/__init__.py delete mode 100644 ptocr/model/CommonFunction.py delete mode 100644 ptocr/model/CommonFunction_Q.py delete mode 100644 ptocr/model/__init__.py delete mode 100644 ptocr/model/architectures/__init__.py delete mode 100644 ptocr/model/architectures/__pycache__/__init__.cpython-35.pyc delete mode 100644 ptocr/model/architectures/__pycache__/__init__.cpython-36.pyc delete mode 100644 ptocr/model/architectures/__pycache__/det_model.cpython-35.pyc delete mode 100644 ptocr/model/architectures/__pycache__/det_model.cpython-36.pyc delete mode 100644 ptocr/model/architectures/__pycache__/rec_model.cpython-36.pyc delete mode 100644 ptocr/model/architectures/det_model.py delete mode 100644 ptocr/model/architectures/det_model_q.py delete mode 100644 ptocr/model/architectures/rec_model.py delete mode 100644 ptocr/model/backbone/__init__.py delete mode 100644 ptocr/model/backbone/__pycache__/__init__.cpython-35.pyc delete mode 100644 ptocr/model/backbone/__pycache__/__init__.cpython-36.pyc delete mode 100644 ptocr/model/backbone/__pycache__/det_mobilev3.cpython-35.pyc delete mode 100644 ptocr/model/backbone/__pycache__/det_mobilev3.cpython-36.pyc delete mode 100644 ptocr/model/backbone/__pycache__/det_resnet.cpython-35.pyc delete mode 100644 ptocr/model/backbone/__pycache__/det_resnet.cpython-36.pyc delete mode 100644 ptocr/model/backbone/__pycache__/det_resnet_3_3.cpython-36.pyc delete mode 100644 ptocr/model/backbone/__pycache__/det_resnet_sast.cpython-35.pyc delete mode 100644 ptocr/model/backbone/__pycache__/det_resnet_sast.cpython-36.pyc delete mode 100644 ptocr/model/backbone/__pycache__/det_resnet_sast_3_3.cpython-36.pyc delete mode 100644 ptocr/model/backbone/__pycache__/det_scnet.cpython-35.pyc delete mode 100644 ptocr/model/backbone/__pycache__/rec_vgg.cpython-36.pyc delete mode 100644 ptocr/model/backbone/det_densenet.py delete mode 100644 ptocr/model/backbone/det_mobilev3.py delete mode 100644 ptocr/model/backbone/det_mobilev3_pytorch_qua.py delete mode 100644 ptocr/model/backbone/det_resnet.py delete mode 100644 ptocr/model/backbone/det_resnet_3_3.py delete mode 100644 ptocr/model/backbone/det_resnet_sast.py delete mode 100644 ptocr/model/backbone/det_resnet_sast_3_3.py delete mode 100644 ptocr/model/backbone/det_scnet.py delete mode 100644 ptocr/model/backbone/rec_crnn_backbone.py delete mode 100644 ptocr/model/backbone/rec_vgg.py delete mode 100644 ptocr/model/backbone/reg_mobilev3.py delete mode 100644 ptocr/model/backbone/reg_resnet.py delete mode 100644 ptocr/model/head/__init__.py delete mode 100644 ptocr/model/head/__pycache__/__init__.cpython-35.pyc delete mode 100644 ptocr/model/head/__pycache__/__init__.cpython-36.pyc delete mode 100644 ptocr/model/head/__pycache__/det_DBHead.cpython-35.pyc delete mode 100644 ptocr/model/head/__pycache__/det_DBHead.cpython-36.pyc delete mode 100644 ptocr/model/head/__pycache__/det_FPEM_FFM_Head.cpython-35.pyc delete mode 100644 ptocr/model/head/__pycache__/det_FPEM_FFM_Head.cpython-36.pyc delete mode 100644 ptocr/model/head/__pycache__/det_FPNHead.cpython-35.pyc delete mode 100644 ptocr/model/head/__pycache__/det_FPNHead.cpython-36.pyc delete mode 100644 ptocr/model/head/__pycache__/det_SASTHead.cpython-35.pyc delete mode 100644 ptocr/model/head/__pycache__/det_SASTHead.cpython-36.pyc delete mode 100644 ptocr/model/head/__pycache__/rec_CRNNHead.cpython-36.pyc delete mode 100644 ptocr/model/head/det_DBHead.py delete mode 100644 ptocr/model/head/det_DBHead_Qua.py delete mode 100644 ptocr/model/head/det_FPEM_FFM_Head.py delete mode 100644 ptocr/model/head/det_FPNHead.py delete mode 100644 ptocr/model/head/det_SASTHead.py delete mode 100644 ptocr/model/head/rec_CRNNHead.py delete mode 100644 ptocr/model/loss/__init__.py delete mode 100644 ptocr/model/loss/__pycache__/__init__.cpython-35.pyc delete mode 100644 ptocr/model/loss/__pycache__/__init__.cpython-36.pyc delete mode 100644 ptocr/model/loss/__pycache__/basical_loss.cpython-35.pyc delete mode 100644 ptocr/model/loss/__pycache__/basical_loss.cpython-36.pyc delete mode 100644 ptocr/model/loss/__pycache__/ctc_loss.cpython-36.pyc delete mode 100644 ptocr/model/loss/__pycache__/db_loss.cpython-35.pyc delete mode 100644 ptocr/model/loss/__pycache__/db_loss.cpython-36.pyc delete mode 100644 ptocr/model/loss/__pycache__/pan_loss.cpython-35.pyc delete mode 100644 ptocr/model/loss/__pycache__/pan_loss.cpython-36.pyc delete mode 100644 ptocr/model/loss/__pycache__/pse_loss.cpython-35.pyc delete mode 100644 ptocr/model/loss/__pycache__/pse_loss.cpython-36.pyc delete mode 100644 ptocr/model/loss/__pycache__/sast_loss.cpython-35.pyc delete mode 100644 ptocr/model/loss/__pycache__/sast_loss.cpython-36.pyc delete mode 100644 ptocr/model/loss/basical_loss.py delete mode 100644 ptocr/model/loss/centerloss.py delete mode 100644 ptocr/model/loss/ctc_loss.py delete mode 100644 ptocr/model/loss/db_loss.py delete mode 100644 ptocr/model/loss/pan_loss.py delete mode 100644 ptocr/model/loss/pse_loss.py delete mode 100644 ptocr/model/loss/sast_loss.py delete mode 100644 ptocr/model/segout/__init__.py delete mode 100644 ptocr/model/segout/__pycache__/__init__.cpython-35.pyc delete mode 100644 ptocr/model/segout/__pycache__/__init__.cpython-36.pyc delete mode 100644 ptocr/model/segout/__pycache__/det_DB_segout.cpython-35.pyc delete mode 100644 ptocr/model/segout/__pycache__/det_DB_segout.cpython-36.pyc delete mode 100644 ptocr/model/segout/__pycache__/det_PAN_segout.cpython-35.pyc delete mode 100644 ptocr/model/segout/__pycache__/det_PAN_segout.cpython-36.pyc delete mode 100644 ptocr/model/segout/__pycache__/det_PSE_segout.cpython-35.pyc delete mode 100644 ptocr/model/segout/__pycache__/det_PSE_segout.cpython-36.pyc delete mode 100644 ptocr/model/segout/__pycache__/det_SAST_segout.cpython-35.pyc delete mode 100644 ptocr/model/segout/__pycache__/det_SAST_segout.cpython-36.pyc delete mode 100644 ptocr/model/segout/det_DB_segout.py delete mode 100644 ptocr/model/segout/det_DB_segout_qua.py delete mode 100644 ptocr/model/segout/det_PAN_segout.py delete mode 100644 ptocr/model/segout/det_PSE_segout.py delete mode 100644 ptocr/model/segout/det_SAST_segout.py delete mode 100644 ptocr/optimizer.py delete mode 100644 ptocr/postprocess/DBpostprocess.py delete mode 100644 ptocr/postprocess/PANpostprocess.py delete mode 100644 ptocr/postprocess/PSEpostprocess.py delete mode 100644 ptocr/postprocess/SASTpostprocess.py delete mode 100644 ptocr/postprocess/__init__.py delete mode 100644 ptocr/postprocess/dbprocess/Makefile delete mode 100644 ptocr/postprocess/dbprocess/__init__.py delete mode 100644 ptocr/postprocess/dbprocess/cppdbprocess.cpp delete mode 100644 ptocr/postprocess/dbprocess/cppdbprocess.so delete mode 100644 ptocr/postprocess/dbprocess/include/clipper.h delete mode 100644 ptocr/postprocess/dbprocess/include/clipper/clipper.cpp delete mode 100644 ptocr/postprocess/dbprocess/include/clipper/clipper.hpp delete mode 100644 ptocr/postprocess/dbprocess/include/postprocess_op.h delete mode 100644 ptocr/postprocess/dbprocess/include/pybind11/attr.h delete mode 100644 ptocr/postprocess/dbprocess/include/pybind11/buffer_info.h delete mode 100644 ptocr/postprocess/dbprocess/include/pybind11/cast.h delete mode 100644 ptocr/postprocess/dbprocess/include/pybind11/chrono.h delete mode 100644 ptocr/postprocess/dbprocess/include/pybind11/class_support.h delete mode 100644 ptocr/postprocess/dbprocess/include/pybind11/common.h delete mode 100644 ptocr/postprocess/dbprocess/include/pybind11/complex.h delete mode 100644 ptocr/postprocess/dbprocess/include/pybind11/descr.h delete mode 100644 ptocr/postprocess/dbprocess/include/pybind11/detail/class.h delete mode 100644 ptocr/postprocess/dbprocess/include/pybind11/detail/common.h delete mode 100644 ptocr/postprocess/dbprocess/include/pybind11/detail/descr.h delete mode 100644 ptocr/postprocess/dbprocess/include/pybind11/detail/init.h delete mode 100644 ptocr/postprocess/dbprocess/include/pybind11/detail/internals.h delete mode 100644 ptocr/postprocess/dbprocess/include/pybind11/detail/typeid.h delete mode 100644 ptocr/postprocess/dbprocess/include/pybind11/eigen.h delete mode 100644 ptocr/postprocess/dbprocess/include/pybind11/embed.h delete mode 100644 ptocr/postprocess/dbprocess/include/pybind11/eval.h delete mode 100644 ptocr/postprocess/dbprocess/include/pybind11/functional.h delete mode 100644 ptocr/postprocess/dbprocess/include/pybind11/iostream.h delete mode 100644 ptocr/postprocess/dbprocess/include/pybind11/numpy.h delete mode 100644 ptocr/postprocess/dbprocess/include/pybind11/operators.h delete mode 100644 ptocr/postprocess/dbprocess/include/pybind11/options.h delete mode 100644 ptocr/postprocess/dbprocess/include/pybind11/pybind11.h delete mode 100644 ptocr/postprocess/dbprocess/include/pybind11/pytypes.h delete mode 100644 ptocr/postprocess/dbprocess/include/pybind11/stl.h delete mode 100644 ptocr/postprocess/dbprocess/include/pybind11/stl_bind.h delete mode 100644 ptocr/postprocess/dbprocess/include/pybind11/typeid.h delete mode 100644 ptocr/postprocess/lanms/.gitignore delete mode 100644 ptocr/postprocess/lanms/.ycm_extra_conf.py delete mode 100644 ptocr/postprocess/lanms/Makefile delete mode 100644 ptocr/postprocess/lanms/__init__.py delete mode 100644 ptocr/postprocess/lanms/__main__.py delete mode 100644 ptocr/postprocess/lanms/adaptor.cpp delete mode 100644 ptocr/postprocess/lanms/include/clipper/clipper.cpp delete mode 100644 ptocr/postprocess/lanms/include/clipper/clipper.hpp delete mode 100644 ptocr/postprocess/lanms/include/pybind11/attr.h delete mode 100644 ptocr/postprocess/lanms/include/pybind11/buffer_info.h delete mode 100644 ptocr/postprocess/lanms/include/pybind11/cast.h delete mode 100644 ptocr/postprocess/lanms/include/pybind11/chrono.h delete mode 100644 ptocr/postprocess/lanms/include/pybind11/class_support.h delete mode 100644 ptocr/postprocess/lanms/include/pybind11/common.h delete mode 100644 ptocr/postprocess/lanms/include/pybind11/complex.h delete mode 100644 ptocr/postprocess/lanms/include/pybind11/descr.h delete mode 100644 ptocr/postprocess/lanms/include/pybind11/eigen.h delete mode 100644 ptocr/postprocess/lanms/include/pybind11/embed.h delete mode 100644 ptocr/postprocess/lanms/include/pybind11/eval.h delete mode 100644 ptocr/postprocess/lanms/include/pybind11/functional.h delete mode 100644 ptocr/postprocess/lanms/include/pybind11/numpy.h delete mode 100644 ptocr/postprocess/lanms/include/pybind11/operators.h delete mode 100644 ptocr/postprocess/lanms/include/pybind11/options.h delete mode 100644 ptocr/postprocess/lanms/include/pybind11/pybind11.h delete mode 100644 ptocr/postprocess/lanms/include/pybind11/pytypes.h delete mode 100644 ptocr/postprocess/lanms/include/pybind11/stl.h delete mode 100644 ptocr/postprocess/lanms/include/pybind11/stl_bind.h delete mode 100644 ptocr/postprocess/lanms/include/pybind11/typeid.h delete mode 100644 ptocr/postprocess/lanms/lanms.h delete mode 100644 ptocr/postprocess/locality_aware_nms.py delete mode 100644 ptocr/postprocess/piexlmerge/Makefile delete mode 100644 ptocr/postprocess/piexlmerge/__init__.py delete mode 100644 ptocr/postprocess/piexlmerge/include/clipper/clipper.cpp delete mode 100644 ptocr/postprocess/piexlmerge/include/clipper/clipper.hpp delete mode 100644 ptocr/postprocess/piexlmerge/include/pybind11/attr.h delete mode 100644 ptocr/postprocess/piexlmerge/include/pybind11/buffer_info.h delete mode 100644 ptocr/postprocess/piexlmerge/include/pybind11/cast.h delete mode 100644 ptocr/postprocess/piexlmerge/include/pybind11/chrono.h delete mode 100644 ptocr/postprocess/piexlmerge/include/pybind11/class_support.h delete mode 100644 ptocr/postprocess/piexlmerge/include/pybind11/common.h delete mode 100644 ptocr/postprocess/piexlmerge/include/pybind11/complex.h delete mode 100644 ptocr/postprocess/piexlmerge/include/pybind11/descr.h delete mode 100644 ptocr/postprocess/piexlmerge/include/pybind11/detail/class.h delete mode 100644 ptocr/postprocess/piexlmerge/include/pybind11/detail/common.h delete mode 100644 ptocr/postprocess/piexlmerge/include/pybind11/detail/descr.h delete mode 100644 ptocr/postprocess/piexlmerge/include/pybind11/detail/init.h delete mode 100644 ptocr/postprocess/piexlmerge/include/pybind11/detail/internals.h delete mode 100644 ptocr/postprocess/piexlmerge/include/pybind11/detail/typeid.h delete mode 100644 ptocr/postprocess/piexlmerge/include/pybind11/eigen.h delete mode 100644 ptocr/postprocess/piexlmerge/include/pybind11/embed.h delete mode 100644 ptocr/postprocess/piexlmerge/include/pybind11/eval.h delete mode 100644 ptocr/postprocess/piexlmerge/include/pybind11/functional.h delete mode 100644 ptocr/postprocess/piexlmerge/include/pybind11/iostream.h delete mode 100644 ptocr/postprocess/piexlmerge/include/pybind11/numpy.h delete mode 100644 ptocr/postprocess/piexlmerge/include/pybind11/operators.h delete mode 100644 ptocr/postprocess/piexlmerge/include/pybind11/options.h delete mode 100644 ptocr/postprocess/piexlmerge/include/pybind11/pybind11.h delete mode 100644 ptocr/postprocess/piexlmerge/include/pybind11/pytypes.h delete mode 100644 ptocr/postprocess/piexlmerge/include/pybind11/stl.h delete mode 100644 ptocr/postprocess/piexlmerge/include/pybind11/stl_bind.h delete mode 100644 ptocr/postprocess/piexlmerge/include/pybind11/typeid.h delete mode 100644 ptocr/postprocess/piexlmerge/lanms.h delete mode 100644 ptocr/postprocess/piexlmerge/pixelmerge.cpp delete mode 100644 ptocr/postprocess/piexlmerge/pixelmerge.so delete mode 100644 ptocr/utils/__init__.py delete mode 100644 ptocr/utils/cal_iou_acc.py delete mode 100644 ptocr/utils/gen_teacher_model.py delete mode 100644 ptocr/utils/logger.py delete mode 100644 ptocr/utils/metrics.py delete mode 100644 ptocr/utils/prune_script.py delete mode 100644 ptocr/utils/transform_label.py delete mode 100644 ptocr/utils/util_function.py delete mode 100644 script/__init__.py delete mode 100644 script/create_lmdb.py delete mode 100644 script/get_key_label.py delete mode 100644 script/get_train_list.py delete mode 100644 script/onnx_to_tensorrt.py delete mode 100644 script/pytorch_to_onnx.py delete mode 100644 to_onnx.sh delete mode 100644 to_tensorrt.sh delete mode 100644 tools/__init__.py delete mode 100644 tools/cal.py delete mode 100644 tools/cal_rescall/__init__.py delete mode 100644 tools/cal_rescall/__pycache__/__init__.cpython-35.pyc delete mode 100644 tools/cal_rescall/__pycache__/__init__.cpython-36.pyc delete mode 100644 tools/cal_rescall/__pycache__/cal_det.cpython-36.pyc delete mode 100644 tools/cal_rescall/__pycache__/cal_iou.cpython-36.pyc delete mode 100644 tools/cal_rescall/__pycache__/rrc_evaluation_funcs.cpython-35.pyc delete mode 100644 tools/cal_rescall/__pycache__/rrc_evaluation_funcs.cpython-36.pyc delete mode 100644 tools/cal_rescall/__pycache__/script.cpython-35.pyc delete mode 100644 tools/cal_rescall/__pycache__/script.cpython-36.pyc delete mode 100644 tools/cal_rescall/cal_det.py delete mode 100644 tools/cal_rescall/cal_iou.py delete mode 100644 tools/cal_rescall/rrc_evaluation_funcs.py delete mode 100644 tools/cal_rescall/script.py delete mode 100644 tools/det_infer.py delete mode 100644 tools/det_sast.py delete mode 100644 tools/det_train.py delete mode 100644 tools/det_train_qua.py delete mode 100644 tools/pruned/__init__.py delete mode 100644 tools/pruned/prune_model_all.py delete mode 100644 tools/pruned/prune_model_backbone.py delete mode 100644 tools/rec_infer.py delete mode 100644 tools/rec_train.py diff --git "a/checkpoint/\346\226\260\345\273\272\346\226\207\346\234\254\346\226\207\346\241\243.txt" "b/checkpoint/\346\226\260\345\273\272\346\226\207\346\234\254\346\226\207\346\241\243.txt" deleted file mode 100644 index e69de29..0000000 diff --git a/config/det_DB_mobilev3.yaml b/config/det_DB_mobilev3.yaml deleted file mode 100644 index b286185..0000000 --- a/config/det_DB_mobilev3.yaml +++ /dev/null @@ -1,87 +0,0 @@ -base: - gpu_id: '1' - algorithm: DB - pretrained: True - in_channels: [24, 40, 48, 96] - inner_channels: 96 - k: 50 - adaptive: True - crop_shape: [640,640] - shrink_ratio: 0.4 - n_epoch: 1200 - start_val: 400 - show_step: 20 - checkpoints: ./checkpoint - save_epoch: 100 - restore: True - restore_file : ./checkpoint/ag_DB_bb_mobilenet_v3_small_he_DB_Head_bs_16_ep_1200_mobile_slim_all/DB_best.pth.tar - -backbone: - function: ptocr.model.backbone.det_mobilev3,mobilenet_v3_small - -head: - function: ptocr.model.head.det_DBHead,DB_Head -# function: ptocr.model.head.det_FPEM_FFM_Head,FPEM_FFM_Head -# function: ptocr.model.head.det_FPNHead,FPN_Head - -segout: - function: ptocr.model.segout.det_DB_segout,SegDetector - -architectures: - model_function: ptocr.model.architectures.det_model,DetModel - loss_function: ptocr.model.architectures.det_model,DetLoss - -loss: - function: ptocr.model.loss.db_loss,DBLoss - l1_scale: 10 - bce_scale: 1 - -#optimizer: -# function: ptocr.optimizer,AdamDecay -# base_lr: 0.002 -# beta1: 0.9 -# beta2: 0.999 - -optimizer: - function: ptocr.optimizer,SGDDecay - base_lr: 0.002 - momentum: 0.99 - weight_decay: 0.00005 - -optimizer_decay: - function: ptocr.optimizer,adjust_learning_rate_poly - factor: 0.9 - -#optimizer_decay: -# function: ptocr.optimizer,adjust_learning_rate -# schedule: [1,2] -# gama: 0.1 - -trainload: - function: ptocr.dataloader.DetLoad.DBProcess,DBProcessTrain - train_file: /src/notebooks/detect_text/icdar2015/train_list.txt - num_workers: 10 - batch_size: 16 - -testload: - function: ptocr.dataloader.DetLoad.DBProcess,DBProcessTest - test_file: /src/notebooks/detect_text/icdar2015/test_list.txt - test_gt_path: /src/notebooks/detect_text/icdar2015/ch4_test_gts/ - test_size: 736 - stride: 32 - num_workers: 5 - batch_size: 4 - -postprocess: - function: ptocr.postprocess.DBpostprocess,DBPostProcess - is_poly: False - thresh: 0.5 - box_thresh: 0.6 - max_candidates: 1000 - unclip_ratio: 2 - min_size: 3 - -infer: - model_path: './checkpoint/ag_DB_bb_mobilenet_v3_small_he_DB_Head_bs_16_ep_1200/DB_best.pth.tar' - path: '/src/notebooks/detect_text/icdar2015/ch4_test_images' - save_path: './result' diff --git a/config/det_DB_mobilev3_common.yaml b/config/det_DB_mobilev3_common.yaml deleted file mode 100644 index 2de20ac..0000000 --- a/config/det_DB_mobilev3_common.yaml +++ /dev/null @@ -1,87 +0,0 @@ -base: - gpu_id: '2' - algorithm: DB - pretrained: True - in_channels: [24, 40, 48, 96] - inner_channels: 96 - k: 50 - adaptive: True - crop_shape: [640,640] - shrink_ratio: 0.4 - n_epoch: 400 - start_val: 500 - show_step: 20 - checkpoints: ./checkpoint - save_epoch: 1 - restore: False - restore_file : ./checkpoint/ag_DB_bb_mobilenet_v3_small_he_DB_Head_bs_16_ep_400/DB_35.pth.tar - -backbone: - function: ptocr.model.backbone.det_mobilev3,mobilenet_v3_small - -head: - function: ptocr.model.head.det_DBHead,DB_Head -# function: ptocr.model.head.det_FPEM_FFM_Head,FPEM_FFM_Head -# function: ptocr.model.head.det_FPNHead,FPN_Head - -segout: - function: ptocr.model.segout.det_DB_segout,SegDetector - -architectures: - model_function: ptocr.model.architectures.det_model,DetModel - loss_function: ptocr.model.architectures.det_model,DetLoss - -loss: - function: ptocr.model.loss.db_loss,DBLoss - l1_scale: 10 - bce_scale: 1 - -#optimizer: -# function: ptocr.optimizer,AdamDecay -# base_lr: 0.002 -# beta1: 0.9 -# beta2: 0.999 - -optimizer: - function: ptocr.optimizer,SGDDecay - base_lr: 0.002 - momentum: 0.99 - weight_decay: 0.00005 - -optimizer_decay: - function: ptocr.optimizer,adjust_learning_rate_poly - factor: 0.9 - -#optimizer_decay: -# function: ptocr.optimizer,adjust_learning_rate -# schedule: [1,2] -# gama: 0.1 - -trainload: - function: ptocr.dataloader.DetLoad.DBProcess,DBProcessTrain - train_file: /src/notebooks/chinese_recognize_data/detection/CommonData/train_list.txt - num_workers: 10 - batch_size: 16 - -testload: - function: ptocr.dataloader.DetLoad.DBProcess,DBProcessTest - test_file: /src/notebooks/detect_text/icdar2015/test_list.txt - test_gt_path: /src/notebooks/detect_text/icdar2015/ch4_test_gts/ - test_size: 736 - stride: 32 - num_workers: 5 - batch_size: 4 - -postprocess: - function: ptocr.postprocess.DBpostprocess,DBPostProcess - is_poly: False - thresh: 0.2 - box_thresh: 0.4 - max_candidates: 1000 - unclip_ratio: 2 - min_size: 3 - -infer: - model_path: './checkpoint/ag_DB_bb_mobilenet_v3_small_he_DB_Head_bs_16_ep_1200/DB_best.pth.tar' - path: '/src/notebooks/detect_text/icdar2015/ch4_test_images' - save_path: './result' diff --git a/config/det_DB_mobilev3_pytorch_qua.yaml b/config/det_DB_mobilev3_pytorch_qua.yaml deleted file mode 100644 index 9206e8f..0000000 --- a/config/det_DB_mobilev3_pytorch_qua.yaml +++ /dev/null @@ -1,88 +0,0 @@ -base: - gpu_id: '2' - algorithm: DB - backend: 'qnnpack' # fbgemm - pretrained: False - in_channels: [24, 40, 48, 96] - inner_channels: 96 - k: 50 - adaptive: True - crop_shape: [640,640] - shrink_ratio: 0.4 - n_epoch: 1200 - start_val: 400 - show_step: 20 - checkpoints: ./checkpoint - save_epoch: 100 - restore: False - restore_file : ./checkpoint/ag_DB_bb_mobilenet_v3_small_he_DB_Head_bs_16_ep_1200_mobile_slim_all/DB_best.pth.tar - -backbone: - function: ptocr.model.backbone.det_mobilev3_pytorch_qua,mobilenet_v3_small - -head: - function: ptocr.model.head.det_DBHead_Qua,DB_Head -# function: ptocr.model.head.det_FPEM_FFM_Head,FPEM_FFM_Head -# function: ptocr.model.head.det_FPNHead,FPN_Head - -segout: - function: ptocr.model.segout.det_DB_segout_qua,SegDetector - -architectures: - model_function: ptocr.model.architectures.det_model_q,DetModel - loss_function: ptocr.model.architectures.det_model_q,DetLoss - -loss: - function: ptocr.model.loss.db_loss,DBLoss - l1_scale: 10 - bce_scale: 1 - -#optimizer: -# function: ptocr.optimizer,AdamDecay -# base_lr: 0.002 -# beta1: 0.9 -# beta2: 0.999 - -optimizer: - function: ptocr.optimizer,SGDDecay - base_lr: 0.002 - momentum: 0.99 - weight_decay: 0.00005 - -optimizer_decay: - function: ptocr.optimizer,adjust_learning_rate_poly - factor: 0.9 - -#optimizer_decay: -# function: ptocr.optimizer,adjust_learning_rate -# schedule: [1,2] -# gama: 0.1 - -trainload: - function: ptocr.dataloader.DetLoad.DBProcess,DBProcessTrain - train_file: /src/notebooks/detect_text/icdar2015/train_list.txt - num_workers: 10 - batch_size: 16 - -testload: - function: ptocr.dataloader.DetLoad.DBProcess,DBProcessTest - test_file: /src/notebooks/detect_text/icdar2015/test_list.txt - test_gt_path: /src/notebooks/detect_text/icdar2015/ch4_test_gts/ - test_size: 736 - stride: 32 - num_workers: 5 - batch_size: 4 - -postprocess: - function: ptocr.postprocess.DBpostprocess,DBPostProcess - is_poly: False - thresh: 0.5 - box_thresh: 0.6 - max_candidates: 1000 - unclip_ratio: 2 - min_size: 3 - -infer: - model_path: './checkpoint/ag_DB_bb_mobilenet_v3_small_he_DB_Head_bs_16_ep_1200/DB_best.pth.tar' - path: '/src/notebooks/detect_text/icdar2015/ch4_test_images' - save_path: './result' diff --git a/config/det_DB_resnet50.yaml b/config/det_DB_resnet50.yaml deleted file mode 100644 index a2b425f..0000000 --- a/config/det_DB_resnet50.yaml +++ /dev/null @@ -1,87 +0,0 @@ -base: - gpu_id: '0' # 设置训练的gpu id,多卡训练设置为 '0,1,2' - algorithm: DB # 算法名称 - pretrained: True # 是否加载预训练 - in_channels: [256, 512, 1024, 2048] # - inner_channels: 256 # - k: 50 - adaptive: True - crop_shape: [640,640] #训练时crop图片的大小 - shrink_ratio: 0.4 # kernel向内收缩比率 - n_epoch: 1200 # 训练的epoch - start_val: 400 #开始验证的epoch,如果不想验证直接设置数值大于n_epoch - show_step: 20 #设置迭代多少次输出一次loss - checkpoints: ./checkpoint #保存模型地址 - save_epoch: 100 #设置每多少个epoch保存一次模型 - restore: False #是否恢复训练 - restore_file : ./DB.pth.tar #恢复训练所需加载模型的地址 - -backbone: - function: ptocr.model.backbone.det_resnet,resnet50 - -head: - function: ptocr.model.head.det_DBHead,DB_Head -# function: ptocr.model.head.det_FPEM_FFM_Head,FPEM_FFM_Head -# function: ptocr.model.head.det_FPNHead,FPN_Head - -segout: - function: ptocr.model.segout.det_DB_segout,SegDetector - -architectures: - model_function: ptocr.model.architectures.det_model,DetModel - loss_function: ptocr.model.architectures.det_model,DetLoss - -loss: - function: ptocr.model.loss.db_loss,DBLoss - l1_scale: 10 - bce_scale: 1 - -#optimizer: -# function: ptocr.optimizer,AdamDecay -# base_lr: 0.002 -# beta1: 0.9 -# beta2: 0.999 - -optimizer: - function: ptocr.optimizer,SGDDecay - base_lr: 0.002 - momentum: 0.99 - weight_decay: 0.0005 - -optimizer_decay: - function: ptocr.optimizer,adjust_learning_rate_poly - factor: 0.9 - -#optimizer_decay: -# function: ptocr.optimizer,adjust_learning_rate -# schedule: [1,2] -# gama: 0.1 - -trainload: - function: ptocr.dataloader.DetLoad.DBProcess,DBProcessTrain - train_file: /src/notebooks/detect_text/icdar2015/train_list.txt - num_workers: 10 - batch_size: 8 - -testload: - function: ptocr.dataloader.DetLoad.DBProcess,DBProcessTest - test_file: /src/notebooks/detect_text/icdar2015/test_list.txt - test_gt_path: /src/notebooks/detect_text/icdar2015/ch4_test_gts/ - test_size: 736 - stride: 32 - num_workers: 5 - batch_size: 4 - -postprocess: - function: ptocr.postprocess.DBpostprocess,DBPostProcess - is_poly: False #测试时,检测弯曲文本设置成 True,否则就是输出矩形框 - thresh: 0.5 - box_thresh: 0.6 - max_candidates: 1000 - unclip_ratio: 2 - min_size: 3 - -infer: - model_path: './checkpoint/ag_DB_bb_resnet50_he_DB_Head_bs_8_ep_1200_bk/DB_best.pth.tar' - path: '/src/notebooks/detect_text/icdar2015/ch4_test_images' - save_path: './result' diff --git a/config/det_DB_resnet50_3_3.yaml b/config/det_DB_resnet50_3_3.yaml deleted file mode 100644 index 8b117b5..0000000 --- a/config/det_DB_resnet50_3_3.yaml +++ /dev/null @@ -1,87 +0,0 @@ -base: - gpu_id: '0' - algorithm: DB - pretrained: True - in_channels: [256, 512, 1024, 2048] - inner_channels: 256 - k: 50 - adaptive: True - crop_shape: [640,640] - shrink_ratio: 0.4 - n_epoch: 1201 - start_val: 400 - show_step: 20 - checkpoints: ./checkpoint - save_epoch: 100 - restore: False - restore_file : ./DB.pth.tar - -backbone: - function: ptocr.model.backbone.det_resnet_3*3,resnet50 - -head: - function: ptocr.model.head.det_DBHead,DB_Head -# function: ptocr.model.head.det_FPEM_FFM_Head,FPEM_FFM_Head -# function: ptocr.model.head.det_FPNHead,FPN_Head - -segout: - function: ptocr.model.segout.det_DB_segout,SegDetector - -architectures: - model_function: ptocr.model.architectures.det_model,DetModel - loss_function: ptocr.model.architectures.det_model,DetLoss - -loss: - function: ptocr.model.loss.db_loss,DBLoss - l1_scale: 10 - bce_scale: 1 - -#optimizer: -# function: ptocr.optimizer,AdamDecay -# base_lr: 0.002 -# beta1: 0.9 -# beta2: 0.999 - -optimizer: - function: ptocr.optimizer,SGDDecay - base_lr: 0.002 - momentum: 0.99 - weight_decay: 0.0005 - -optimizer_decay: - function: ptocr.optimizer,adjust_learning_rate_poly - factor: 0.9 - -#optimizer_decay: -# function: ptocr.optimizer,adjust_learning_rate -# schedule: [1,2] -# gama: 0.1 - -trainload: - function: ptocr.dataloader.DetLoad.DBProcess,DBProcessTrain - train_file: /src/notebooks/detect_text/icdar2015/train_list.txt - num_workers: 10 - batch_size: 8 - -testload: - function: ptocr.dataloader.DetLoad.DBProcess,DBProcessTest - test_file: /src/notebooks/detect_text/icdar2015/test_list.txt - test_gt_path: /src/notebooks/detect_text/icdar2015/ch4_test_gts/ - test_size: 736 - stride: 32 - num_workers: 5 - batch_size: 4 - -postprocess: - function: ptocr.postprocess.DBpostprocess,DBPostProcess - is_poly: False - thresh: 0.5 - box_thresh: 0.6 - max_candidates: 1000 - unclip_ratio: 2 - min_size: 3 - -infer: - model_path: './checkpoint/ag_DB_bb_resnet50_he_DB_Head_bs_8_ep_1200_bk/DB_best.pth.tar' - path: '/src/notebooks/detect_text/icdar2015/ch4_test_images' - save_path: './result' diff --git a/config/det_PAN_mobilev3.yaml b/config/det_PAN_mobilev3.yaml deleted file mode 100644 index 186ef58..0000000 --- a/config/det_PAN_mobilev3.yaml +++ /dev/null @@ -1,82 +0,0 @@ -base: - gpu_id: '0' - algorithm: PAN - pretrained: False - in_channels: [24, 40, 48, 96] - inner_channels: 96 - classes: 6 - crop_shape: [640,640] - shrink_ratio: 0.4 - n_epoch: 600 - show_step: 20 - start_val: 300 - save_epoch: 100 - checkpoints: ./checkpoint - restore: False - restore_file: ./PAN.pth.tar - -backbone: - function: ptocr.model.backbone.det_mobilev3,mobilenet_v3_small - -head: -# function: ptocr.model.head.det_DBHead,DB_Head - function: ptocr.model.head.det_FPEM_FFM_Head,FPEM_FFM_Head -# function: ptocr.model.head.det_FPNHead,FPN_Head -segout: - function: ptocr.model.segout.det_PAN_segout,SegDetector - -architectures: - model_function: ptocr.model.architectures.det_model,DetModel - loss_function: ptocr.model.architectures.det_model,DetLoss - -loss: - function: ptocr.model.loss.pan_loss,PANLoss - kernel_rate: 0.5 - agg_dis_rate: 0.25 - -#optimizer: -# function: ptocr.optimizer,AdamDecay -# base_lr: 0.002 -# beta1: 0.9 -# beta2: 0.999 - -optimizer: - function: ptocr.optimizer,SGDDecay - base_lr: 0.001 - momentum: 0.99 - weight_decay: 0.00005 - -# optimizer_decay: -# function: ptocr.optimizer,adjust_learning_rate_poly -# factor: 0.9 - -optimizer_decay: - function: ptocr.optimizer,adjust_learning_rate - schedule: [200,400,500] - gama: 0.1 - - -trainload: - function: ptocr.dataloader.DetLoad.PANProcess,PANProcessTrain - train_file: /src/notebooks/detect_text/icdar2015/train_list.txt - num_workers: 10 - batch_size: 16 - -testload: - function: ptocr.dataloader.DetLoad.PANProcess,PANProcessTest - test_file: /src/notebooks/detect_text/icdar2015/test_list.txt - test_gt_path: /src/notebooks/detect_text/icdar2015/ch4_test_gts/ - test_size: 736 - stride: 32 - num_workers: 5 - batch_size: 2 - -postprocess: - function: ptocr.postprocess.PANpostprocess,PANPostProcess - is_poly: False - bin_th: 1 - scale: 1 - min_kernel_area: 8 - min_text_area: 50 - min_score: 0.93 - dis_thresh: 6 \ No newline at end of file diff --git a/config/det_PAN_resnet18.yaml b/config/det_PAN_resnet18.yaml deleted file mode 100644 index de766cd..0000000 --- a/config/det_PAN_resnet18.yaml +++ /dev/null @@ -1,87 +0,0 @@ -base: - gpu_id: '1' - algorithm: PAN - pretrained: True - in_channels: [64, 128, 256, 512] - inner_channels: 128 - classes: 6 - crop_shape: [640,640] - shrink_ratio: 0.5 - n_epoch: 600 - show_step: 20 - start_val: 300 - save_epoch: 50 - checkpoints: ./checkpoint - restore: False - restore_file: ./checkpoint/ag_PAN_bb_resnet18_he_FPEM_FFM_Head_bs_16_ep_600/PAN_200.pth.tar - -backbone: - function: ptocr.model.backbone.det_resnet,resnet18 - -head: -# function: ptocr.model.head.det_DBHead,DB_Head - function: ptocr.model.head.det_FPEM_FFM_Head,FPEM_FFM_Head -# function: ptocr.model.head.det_FPNHead,FPN_Head -segout: - function: ptocr.model.segout.det_PAN_segout,SegDetector - -architectures: - model_function: ptocr.model.architectures.det_model,DetModel - loss_function: ptocr.model.architectures.det_model,DetLoss - -loss: - function: ptocr.model.loss.pan_loss,PANLoss - kernel_rate: 0.5 - agg_dis_rate: 0.25 - -#optimizer: -# function: ptocr.optimizer,AdamDecay -# base_lr: 0.002 -# beta1: 0.9 -# beta2: 0.999 - -optimizer: - function: ptocr.optimizer,SGDDecay - base_lr: 0.001 - momentum: 0.99 - weight_decay: 0.00005 - -# optimizer_decay: -# function: ptocr.optimizer,adjust_learning_rate_poly -# factor: 0.9 - -optimizer_decay: - function: ptocr.optimizer,adjust_learning_rate - schedule: [200,400,500] - gama: 0.1 - - -trainload: - function: ptocr.dataloader.DetLoad.PANProcess,PANProcessTrain - train_file: /src/notebooks/detect_text/icdar2015/train_list.txt - num_workers: 10 - batch_size: 16 - -testload: - function: ptocr.dataloader.DetLoad.PANProcess,PANProcessTest - test_file: /src/notebooks/detect_text/icdar2015/test_list.txt - test_gt_path: /src/notebooks/detect_text/icdar2015/ch4_test_gts/ - test_size: 736 - stride: 32 - num_workers: 5 - batch_size: 2 - -postprocess: - function: ptocr.postprocess.PANpostprocess,PANPostProcess - is_poly: False - bin_th: 1 - scale: 1 - min_kernel_area: 4 - min_text_area: 300 - min_score: 0.90 - dis_thresh: 1 - -infer: - model_path: './checkpoint/ag_PAN_bb_resnet18_he_FPEM_FFM_Head_bs_16_ep_600/PAN_best.pth.tar' - path: '/src/notebooks/detect_text/icdar2015/ch4_test_images' - save_path: './result' \ No newline at end of file diff --git a/config/det_PAN_resnet18_3_3.yaml b/config/det_PAN_resnet18_3_3.yaml deleted file mode 100644 index 32d9ac0..0000000 --- a/config/det_PAN_resnet18_3_3.yaml +++ /dev/null @@ -1,87 +0,0 @@ -base: - gpu_id: '0' - algorithm: PAN - pretrained: True - in_channels: [64, 128, 256, 512] - inner_channels: 128 - classes: 6 - crop_shape: [640,640] - shrink_ratio: 0.5 - n_epoch: 601 - show_step: 20 - start_val: 200 - save_epoch: 100 - checkpoints: ./checkpoint - restore: True - restore_file: ./checkpoint/ag_PAN_bb_resnet18_he_FPEM_FFM_Head_bs_14_ep_601/PAN_best.pth.tar - -backbone: - function: ptocr.model.backbone.det_resnet_3*3,resnet18 - -head: -# function: ptocr.model.head.det_DBHead,DB_Head - function: ptocr.model.head.det_FPEM_FFM_Head,FPEM_FFM_Head -# function: ptocr.model.head.det_FPNHead,FPN_Head -segout: - function: ptocr.model.segout.det_PAN_segout,SegDetector - -architectures: - model_function: ptocr.model.architectures.det_model,DetModel - loss_function: ptocr.model.architectures.det_model,DetLoss - -loss: - function: ptocr.model.loss.pan_loss,PANLoss - kernel_rate: 0.5 - agg_dis_rate: 0.25 - -#optimizer: -# function: ptocr.optimizer,AdamDecay -# base_lr: 0.002 -# beta1: 0.9 -# beta2: 0.999 - -optimizer: - function: ptocr.optimizer,SGDDecay - base_lr: 0.001 - momentum: 0.99 - weight_decay: 0.00005 - -optimizer_decay: - function: ptocr.optimizer,adjust_learning_rate_poly - factor: 0.9 - -# optimizer_decay: -# function: ptocr.optimizer,adjust_learning_rate -# schedule: [200,400,500] -# gama: 0.1 - - -trainload: - function: ptocr.dataloader.DetLoad.PANProcess,PANProcessTrain - train_file: /src/notebooks/detect_text/icdar2015/train_list.txt - num_workers: 10 - batch_size: 14 - -testload: - function: ptocr.dataloader.DetLoad.PANProcess,PANProcessTest - test_file: /src/notebooks/detect_text/icdar2015/test_list.txt - test_gt_path: /src/notebooks/detect_text/icdar2015/ch4_test_gts/ - test_size: 736 - stride: 32 - num_workers: 5 - batch_size: 2 - -postprocess: - function: ptocr.postprocess.PANpostprocess,PANPostProcess - is_poly: False - bin_th: 1 - scale: 1 - min_kernel_area: 4 - min_text_area: 300 - min_score: 0.90 - dis_thresh: 1 - -infer: - model_path: './checkpoint/ag_PAN_bb_resnet18_he_FPEM_FFM_Head_bs_14_ep_601/PAN_best.pth.tar' - path: '/src/notebooks/detect_text/icdar2015/ch4_test_images' - save_path: './result' \ No newline at end of file diff --git a/config/det_PSE_mobilev3.yaml b/config/det_PSE_mobilev3.yaml deleted file mode 100644 index fb6979a..0000000 --- a/config/det_PSE_mobilev3.yaml +++ /dev/null @@ -1,78 +0,0 @@ -base: - gpu_id: '1' - algorithm: PSE - pretrained: True - in_channels: [24, 40, 48, 96] - inner_channels: 96 - classes: 7 - crop_shape: [640,640] - shrink_ratio: 0.4 - n_epoch: 1200 - show_step: 5 - checkpoints: ./checkpoint - restore: False - restore_file: ./PSE.pth.tar - -backbone: - function: ptocr.model.backbone.det_mobilev3,mobilenet_v3_small - -head: -# function: ptocr.model.head.det_DBHead,DB_Head -# function: ptocr.model.head.det_FPEM_FFM_Head,FPEM_FFM_Head - function: ptocr.model.head.det_FPNHead,FPN_Head - -segout: - function: ptocr.model.segout.det_PSE_segout,SegDetector - -architectures: - model_function: ptocr.model.architectures.det_model,DetModel - loss_function: ptocr.model.architectures.det_model,DetLoss - -loss: - function: ptocr.model.loss.pse_loss,PSELoss - text_tatio: 0.7 - -#optimizer: -# function: ptocr.optimizer,AdamDecay -# base_lr: 0.002 -# beta1: 0.9 -# beta2: 0.999 - -optimizer: - function: ptocr.optimizer,SGDDecay - base_lr: 0.002 - momentum: 0.99 - -optimizer_decay: - function: ptocr.optimizer,adjust_learning_rate_poly - factor: 0.9 - -trainload: - function: ptocr.dataloader.DetLoad.PSEProcess,PSEProcessTrain - train_file: /src/notebooks/detect_text/icdar2015/train_list.txt - num_workers: 0 - batch_size: 2 - -testload: - function: ptocr.dataloader.DetLoad.PSEProcess,PSEProcessTest - test_file: /src/notebooks/detect_text/icdar2015/test_list.txt - test_gt_path: /src/notebooks/detect_text/icdar2015/ch4_test_gts/ - test_size: 2240 - stride: 32 - num_workers: 5 - batch_size: 2 - -postprocess: - function: ptocr.postprocess.PSEpostprocess,PSEPostProcess - is_poly: True - binary_th: 1 - scale: 1 - min_kernel_area: 5 - min_text_area: 800 - min_score: 0.93 - -infer: - model_path: './checkpoint/ag_PSE_bb_mobilenet_v3_small_he_FPN_Head_bs_16_ep_1200/PSE_400.pth.tar' - path: '/src/notebooks/detect_text/icdar2015/ch4_test_images' - save_path: './result' - \ No newline at end of file diff --git a/config/det_PSE_resnet50.yaml b/config/det_PSE_resnet50.yaml deleted file mode 100644 index ad8575f..0000000 --- a/config/det_PSE_resnet50.yaml +++ /dev/null @@ -1,87 +0,0 @@ -base: - gpu_id: '0' - algorithm: PSE - pretrained: True - in_channels: [256, 512, 1024, 2048] - inner_channels: 256 - classes: 7 - crop_shape: [640,640] - shrink_ratio: 0.4 - n_epoch: 600 - show_step: 20 - start_val: 400 - save_epoch: 100 - checkpoints: ./checkpoint - restore: False - restore_file: ./checkpoint/ag_PSE_bb_resnet50_he_FPN_Head_bs_8_ep_600/PSE_400.pth.tar - -backbone: - function: ptocr.model.backbone.det_resnet,resnet50 - -head: -# function: ptocr.model.head.det_DBHead,DB_Head -# function: ptocr.model.head.det_FPEM_FFM_Head,FPEM_FFM_Head - function: ptocr.model.head.det_FPNHead,FPN_Head - -segout: - function: ptocr.model.segout.det_PSE_segout,SegDetector - -architectures: - model_function: ptocr.model.architectures.det_model,DetModel - loss_function: ptocr.model.architectures.det_model,DetLoss - -loss: - function: ptocr.model.loss.pse_loss,PSELoss - text_tatio: 0.7 - -#optimizer: -# function: ptocr.optimizer,AdamDecay -# base_lr: 0.002 -# beta1: 0.9 -# beta2: 0.999 - -optimizer: - function: ptocr.optimizer,SGDDecay - base_lr: 0.001 - momentum: 0.99 - weight_decay: 0.00005 - -# optimizer_decay: -# function: ptocr.optimizer,adjust_learning_rate_poly -# factor: 0.9 - -optimizer_decay: - function: ptocr.optimizer,adjust_learning_rate - schedule: [200,400,500] - gama: 0.1 - -trainload: - function: ptocr.dataloader.DetLoad.PSEProcess,PSEProcessTrain - train_file: /src/notebooks/detect_text/icdar2015/train_list.txt - num_workers: 12 - batch_size: 8 - -testload: - function: ptocr.dataloader.DetLoad.PSEProcess,PSEProcessTest - test_file: /src/notebooks/detect_text/icdar2015/test_list.txt - test_gt_path: /src/notebooks/detect_text/icdar2015/ch4_test_gts/ - test_size: 2240 - stride: 32 - num_workers: 5 - batch_size: 1 - -postprocess: - function: ptocr.postprocess.PSEpostprocess,PSEPostProcess - is_poly: False - binary_th: 1 - scale: 1 - min_kernel_area: 5 - min_text_area: 800 - min_score: 0.93 - -infer: - model_path: './checkpoint/ag_PSE_bb_resnet50_he_FPN_Head_bs_8_ep_600/PSE_best.pth.tar' - path: '/src/notebooks/detect_text/icdar2015/ch4_test_images' - save_path: './result' - - \ No newline at end of file diff --git a/config/det_PSE_resnet50_3_3.yaml b/config/det_PSE_resnet50_3_3.yaml deleted file mode 100644 index aca9e57..0000000 --- a/config/det_PSE_resnet50_3_3.yaml +++ /dev/null @@ -1,87 +0,0 @@ -base: - gpu_id: '0' - algorithm: PSE - pretrained: True - in_channels: [256, 512, 1024, 2048] - inner_channels: 256 - classes: 7 - crop_shape: [640,640] - shrink_ratio: 0.4 - n_epoch: 601 - show_step: 20 - start_val: 400 - save_epoch: 100 - checkpoints: ./checkpoint - restore: False - restore_file: ./checkpoint/ag_PSE_bb_resnet50_he_FPN_Head_bs_8_ep_601/PSE_best.pth.tar - -backbone: - function: ptocr.model.backbone.det_resnet_3*3,resnet50 - -head: -# function: ptocr.model.head.det_DBHead,DB_Head -# function: ptocr.model.head.det_FPEM_FFM_Head,FPEM_FFM_Head - function: ptocr.model.head.det_FPNHead,FPN_Head - -segout: - function: ptocr.model.segout.det_PSE_segout,SegDetector - -architectures: - model_function: ptocr.model.architectures.det_model,DetModel - loss_function: ptocr.model.architectures.det_model,DetLoss - -loss: - function: ptocr.model.loss.pse_loss,PSELoss - text_tatio: 0.7 - -#optimizer: -# function: ptocr.optimizer,AdamDecay -# base_lr: 0.002 -# beta1: 0.9 -# beta2: 0.999 - -optimizer: - function: ptocr.optimizer,SGDDecay - base_lr: 0.001 - momentum: 0.99 - weight_decay: 0.00005 - -# optimizer_decay: -# function: ptocr.optimizer,adjust_learning_rate_poly -# factor: 0.9 - -optimizer_decay: - function: ptocr.optimizer,adjust_learning_rate - schedule: [200,400,500] - gama: 0.1 - -trainload: - function: ptocr.dataloader.DetLoad.PSEProcess,PSEProcessTrain - train_file: /src/notebooks/detect_text/icdar2015/train_list.txt - num_workers: 12 - batch_size: 8 - -testload: - function: ptocr.dataloader.DetLoad.PSEProcess,PSEProcessTest - test_file: /src/notebooks/detect_text/icdar2015/test_list.txt - test_gt_path: /src/notebooks/detect_text/icdar2015/ch4_test_gts/ - test_size: 2240 - stride: 32 - num_workers: 5 - batch_size: 1 - -postprocess: - function: ptocr.postprocess.PSEpostprocess,PSEPostProcess - is_poly: False - binary_th: 1 - scale: 1 - min_kernel_area: 5 - min_text_area: 800 - min_score: 0.93 - -infer: - model_path: './checkpoint/ag_PSE_bb_resnet50_he_FPN_Head_bs_8_ep_600/PSE_best.pth.tar' - path: '/src/notebooks/detect_text/icdar2015/ch4_test_images' - save_path: './result' - - \ No newline at end of file diff --git a/config/det_SAST_resnet50.yaml b/config/det_SAST_resnet50.yaml deleted file mode 100644 index 8318077..0000000 --- a/config/det_SAST_resnet50.yaml +++ /dev/null @@ -1,100 +0,0 @@ -base: - gpu_id: '0' - algorithm: SAST - pretrained: True - with_attention: True - crop_shape: [512,512] - n_epoch: 900 - start_val: 600 - show_step: 20 - checkpoints: ./checkpoint - save_epoch: 100 - restore: False - restore_file : ./checkpoint/ag_SAST_bb_resnet50_he_SASTHead_bs_12_ep_2000/SAST_best.pth.tar - -backbone: - function: ptocr.model.backbone.det_resnet_sast,resnet50 - -head: - function: ptocr.model.head.det_SASTHead,SASTHead -# function: ptocr.model.head.det_FPEM_FFM_Head,FPEM_FFM_Head -# function: ptocr.model.head.det_FPNHead,FPN_Head - -segout: - function: ptocr.model.segout.det_SAST_segout,SegDetector - -architectures: - model_function: ptocr.model.architectures.det_model,DetModel - loss_function: ptocr.model.architectures.det_model,DetLoss - -loss: - function: ptocr.model.loss.sast_loss,SASTLoss - tvo_lw: 1.5 - tco_lw: 1.5 - score_lw: 1.0 - border_lw: 1.0 - -# optimizer: -# function: ptocr.optimizer,AdamDecay -# base_lr: 0.001 -# beta1: 0.9 -# beta2: 0.999 -# weight_decay: 0.00005 - -optimizer: - function: ptocr.optimizer,RMSPropDecay - base_lr: 0.001 - momentum: 0 - alpha: 0.95 - weight_decay: 0.00005 - - -# optimizer: -# function: ptocr.optimizer,SGDDecay -# weight_decay: 0.00005 -# base_lr: 0.005 -# momentum: 0.95 - -# optimizer_decay: -# function: ptocr.optimizer,adjust_learning_rate_poly -# factor: 0.9 - -optimizer_decay: - function: ptocr.optimizer,adjust_learning_rate - schedule: [300,600,800,850] - gama: 0.3 - - -trainload: - function: ptocr.dataloader.DetLoad.SASTProcess,SASTProcessTrain - train_file: /src/notebooks/detect_text/icdar2015/train_list.txt - num_workers: 12 - batch_size: 8 - min_crop_side_ratio: 0.3 - min_crop_size: 24 - min_text_size: 4 - - -testload: - function: ptocr.dataloader.DetLoad.SASTProcess_ori,SASTProcessTest - test_file: /src/notebooks/detect_text/icdar2015/test_list.txt - test_gt_path: /src/notebooks/detect_text/icdar2015/ch4_test_gts/ - test_size: 1536 - stride: 128 - num_workers: 5 - batch_size: 4 - -postprocess: - function: ptocr.postprocess.SASTpostprocess,SASTPostProcess - is_poly: False - score_thresh: 0.5 - nms_thresh: 0.2 - sample_pts_num: 2 - shrink_ratio_of_width: 0.3 - expand_scale: 1.0 - tcl_map_thresh: 0.7 - -infer: - model_path: './checkpoint/ag_SAST_bb_resnet50_he_SASTHead_bs_8_ep_1000/SAST_best.pth.tar' - path: '/src/notebooks/detect_text/icdar2015/ch4_test_images' - save_path: './result' diff --git a/config/det_SAST_resnet50_3_3_ori_dataload.yaml b/config/det_SAST_resnet50_3_3_ori_dataload.yaml deleted file mode 100644 index 89c4660..0000000 --- a/config/det_SAST_resnet50_3_3_ori_dataload.yaml +++ /dev/null @@ -1,100 +0,0 @@ -base: - gpu_id: '1' - algorithm: SAST - pretrained: True - with_attention: True - crop_shape: [512,512] - n_epoch: 901 - start_val: 500 - show_step: 20 - checkpoints: ./checkpoint - save_epoch: 100 - restore: False - restore_file : ./checkpoint/ag_SAST_bb_resnet50_he_SASTHead_bs_12_ep_2000/SAST_best.pth.tar - -backbone: - function: ptocr.model.backbone.det_resnet_sast_3*3,resnet50 - -head: - function: ptocr.model.head.det_SASTHead,SASTHead -# function: ptocr.model.head.det_FPEM_FFM_Head,FPEM_FFM_Head -# function: ptocr.model.head.det_FPNHead,FPN_Head - -segout: - function: ptocr.model.segout.det_SAST_segout,SegDetector - -architectures: - model_function: ptocr.model.architectures.det_model,DetModel - loss_function: ptocr.model.architectures.det_model,DetLoss - -loss: - function: ptocr.model.loss.sast_loss,SASTLoss - tvo_lw: 1.5 - tco_lw: 1.5 - score_lw: 1.0 - border_lw: 1.0 - -# optimizer: -# function: ptocr.optimizer,AdamDecay -# base_lr: 0.001 -# beta1: 0.9 -# beta2: 0.999 -# weight_decay: 0.00005 - -optimizer: - function: ptocr.optimizer,RMSPropDecay - base_lr: 0.001 - momentum: 0 - alpha: 0.95 - weight_decay: 0.00005 - - -# optimizer: -# function: ptocr.optimizer,SGDDecay -# weight_decay: 0.00005 -# base_lr: 0.005 -# momentum: 0.95 - -# optimizer_decay: -# function: ptocr.optimizer,adjust_learning_rate_poly -# factor: 0.9 - -optimizer_decay: - function: ptocr.optimizer,adjust_learning_rate - schedule: [300,600,800,850] - gama: 0.3 - - -trainload: - function: ptocr.dataloader.DetLoad.SASTProcess_ori,SASTProcessTrain - train_file: /src/notebooks/detect_text/icdar2015/train_list.txt - num_workers: 12 - batch_size: 8 - min_crop_side_ratio: 0.3 - min_crop_size: 24 - min_text_size: 4 - - -testload: - function: ptocr.dataloader.DetLoad.SASTProcess_ori,SASTProcessTest - test_file: /src/notebooks/detect_text/icdar2015/test_list.txt - test_gt_path: /src/notebooks/detect_text/icdar2015/ch4_test_gts/ - test_size: 1536 - stride: 128 - num_workers: 5 - batch_size: 4 - -postprocess: - function: ptocr.postprocess.SASTpostprocess,SASTPostProcess - is_poly: False - score_thresh: 0.5 - nms_thresh: 0.2 - sample_pts_num: 2 - shrink_ratio_of_width: 0.3 - expand_scale: 1.0 - tcl_map_thresh: 0.7 - -infer: - model_path: './checkpoint/ag_SAST_bb_resnet50_he_SASTHead_bs_8_ep_1000/SAST_best.pth.tar' - path: '/src/notebooks/detect_text/icdar2015/ch4_test_images' - save_path: './result' diff --git a/config/det_SAST_resnet50_ori_dataload.yaml b/config/det_SAST_resnet50_ori_dataload.yaml deleted file mode 100644 index a5e1505..0000000 --- a/config/det_SAST_resnet50_ori_dataload.yaml +++ /dev/null @@ -1,111 +0,0 @@ -base: - gpu_id: '2' - algorithm: SAST - pretrained: True - with_attention: True - crop_shape: [512,512] - n_epoch: 1000 - start_val: 600 - show_step: 20 - checkpoints: ./checkpoint - save_epoch: 100 - restore: False - restore_file : ./checkpoint/ag_SAST_bb_resnet50_he_SASTHead_bs_12_ep_2000/SAST_best.pth.tar - -backbone: - function: ptocr.model.backbone.det_resnet_sast,resnet50 - -head: - function: ptocr.model.head.det_SASTHead,SASTHead -# function: ptocr.model.head.det_FPEM_FFM_Head,FPEM_FFM_Head -# function: ptocr.model.head.det_FPNHead,FPN_Head - -segout: - function: ptocr.model.segout.det_SAST_segout,SegDetector - -architectures: - model_function: ptocr.model.architectures.det_model,DetModel - loss_function: ptocr.model.architectures.det_model,DetLoss - -loss: - function: ptocr.model.loss.sast_loss,SASTLoss - tvo_lw: 1.5 - tco_lw: 1.5 - score_lw: 1.0 - border_lw: 1.0 - -# optimizer: -# function: ptocr.optimizer,AdamDecay -# base_lr: 0.001 -# beta1: 0.9 -# beta2: 0.999 -# weight_decay: 0.00005 - -optimizer: - function: ptocr.optimizer,RMSPropDecay - base_lr: 0.001 - momentum: 0 - alpha: 0.95 - weight_decay: 0.00005 - - -# optimizer: -# function: ptocr.optimizer,SGDDecay -# weight_decay: 0.00005 -# base_lr: 0.005 -# momentum: 0.95 - -# optimizer_decay: -# function: ptocr.optimizer,adjust_learning_rate_poly -# factor: 0.9 - -optimizer_decay: - function: ptocr.optimizer,adjust_learning_rate - schedule: [300,600,800,850] - gama: 0.3 - - -# trainload: -# function: ptocr.dataloader.DetLoad.SASTProcess_ori,SASTProcessTrain -# train_file: /src/notebooks/detect_text/icdar2015/train_list.txt -# num_workers: 12 -# batch_size: 8 -# min_crop_side_ratio: 0.3 -# min_crop_size: 24 -# min_text_size: 4 - -trainload: - function: ptocr.dataloader.DetLoad.SASTProcess_ori1,SASTProcessTrain - data_dir: /src/notebooks/dataset_for_sast - train_file_target: icdar2015/train_label_json.txt - train_file_extre: [icdar17_mlt_latin/train_label_json.txt,coco_text_icdar_4pts/train_label_json.txt,icdar2013/train_label_json.txt] - train_file_ratio: 0.5 - num_workers: 12 - batch_size: 8 - min_crop_side_ratio: 0.3 - min_crop_size: 24 - min_text_size: 4 - -testload: - function: ptocr.dataloader.DetLoad.SASTProcess_ori,SASTProcessTest - test_file: /src/notebooks/detect_text/icdar2015/test_list.txt - test_gt_path: /src/notebooks/detect_text/icdar2015/ch4_test_gts/ - test_size: 1536 - stride: 128 - num_workers: 5 - batch_size: 4 - -postprocess: - function: ptocr.postprocess.SASTpostprocess,SASTPostProcess - is_poly: False - score_thresh: 0.5 - nms_thresh: 0.2 - sample_pts_num: 2 - shrink_ratio_of_width: 0.3 - expand_scale: 1.0 - tcl_map_thresh: 0.7 - -infer: - model_path: './checkpoint/ag_SAST_bb_resnet50_he_SASTHead_bs_8_ep_1000/SAST_best.pth.tar' - path: '/src/notebooks/detect_text/icdar2015/ch4_test_images' - save_path: './result' diff --git a/config/rec_CRNN_mobilev3.yaml b/config/rec_CRNN_mobilev3.yaml deleted file mode 100644 index 3ce7a17..0000000 --- a/config/rec_CRNN_mobilev3.yaml +++ /dev/null @@ -1,78 +0,0 @@ -base: - gpu_id: '0' - algorithm: CRNN - pretrained: True - inchannel: 96 - hiddenchannel: 48 - img_shape: [32,280] - is_gray: True - use_conv: False - use_attention: False - use_lstm: False - lstm_num: 2 - classes: 1000 - n_epoch: 20 - start_val: 1 - show_step: 20 - checkpoints: ./checkpoint - save_epoch: 1 - show_num: 10 - restore: False - restore_file : ./DB.pth.tar - -backbone: - function: ptocr.model.backbone.reg_mobilev3,mobilenet_v3_small - -head: - function: ptocr.model.head.rec_CRNNHead,CRNN_Head - -architectures: - model_function: ptocr.model.architectures.rec_model,RecModel - loss_function: ptocr.model.architectures.rec_model,RecLoss - -loss: - function: ptocr.model.loss.ctc_loss,CTCLoss - reduction: 'mean' - -optimizer: - function: ptocr.optimizer,AdamDecay - base_lr: 0.001 - beta1: 0.9 - beta2: 0.999 - weight_decay: 0.00005 - -# optimizer: -# function: ptocr.optimizer,SGDDecay -# base_lr: 0.002 -# momentum: 0.99 -# weight_decay: 0.00005 - -# optimizer_decay: -# function: ptocr.optimizer,adjust_learning_rate_poly -# factor: 0.9 - -optimizer_decay: - function: ptocr.optimizer,adjust_learning_rate - schedule: [5,8,11,14] - gama: 0.1 - -trainload: - function: ptocr.dataloader.RecLoad.CRNNProcess,CRNNProcessTrain - train_file: /src/notebooks/fangxuwei_96/crnn-master/train_data/data/train_file/train.txt - key_file: /src/notebooks/fangxuwei_96/crnn-master/train_data/data/train_file/key.txt - num_workers: 10 - batch_size: 256 - -testload: - function: ptocr.dataloader.RecLoad.CRNNProcess,CRNNProcessTest - test_file: /src/notebooks/fangxuwei_96/crnn-master/train_data/data/train_file/val.txt - num_workers: 5 - batch_size: 256 - -label_transform: - function: ptocr.utils.transform_label,strLabelConverter - -infer: - model_path: '' - path: '' - save_path: '' diff --git a/config/rec_CRNN_ori.yaml b/config/rec_CRNN_ori.yaml deleted file mode 100644 index c2264fe..0000000 --- a/config/rec_CRNN_ori.yaml +++ /dev/null @@ -1,93 +0,0 @@ -base: - gpu_id: '1' - algorithm: CRNN - pretrained: False - inchannel: 512 - hiddenchannel: 128 - img_shape: [32,200] - is_gray: True - use_conv: False - use_attention: False - use_lstm: False - lstm_num: 1 - classes: 1000 - n_epoch: 20 - start_val: 0 - show_step: 20 - checkpoints: ./checkpoint - save_epoch: 1 - show_num: 10 - restore: False - finetune: False - restore_file : ./checkpoint/ag_CRNN_bb_rec_crnn_backbone_he_CRNN_Head_bs_256_ep_20_test_center_3/CRNN_best_ori.pth.tar - -backbone: - function: ptocr.model.backbone.rec_crnn_backbone,rec_crnn_backbone - -head: - function: ptocr.model.head.rec_CRNNHead,CRNN_Head - -architectures: - model_function: ptocr.model.architectures.rec_model,RecModel - loss_function: ptocr.model.architectures.rec_model,RecLoss - -loss: - function: ptocr.model.loss.ctc_loss,CTCLoss - reduction: 'sum' - center_function: ptocr.model.loss.centerloss,CenterLoss - use_center: False - center_lr: 0.5 - label_score: 0.95 -# min_score: 0.01 - weight_center: 0.000001 - - -optimizer: - function: ptocr.optimizer,AdamDecay - base_lr: 0.001 - beta1: 0.9 - beta2: 0.999 - weight_decay: 0.00005 - -# optimizer: -# function: ptocr.optimizer,SGDDecay -# base_lr: 0.002 -# momentum: 0.99 -# weight_decay: 0.00005 - -# optimizer_decay: -# function: ptocr.optimizer,adjust_learning_rate_poly -# factor: 0.9 - -optimizer_decay: - function: ptocr.optimizer,adjust_learning_rate - schedule: [5,10,15] - gama: 0.1 - -optimizer_decay_center: - function: ptocr.optimizer,adjust_learning_rate_center - schedule: [6,10,15] - gama: 0.1 - -trainload: - function: ptocr.dataloader.RecLoad.CRNNProcess,CRNNProcessTrain - train_file: /src/notebooks/fangxuwei_96/crnn-master/train_data/data/train_center/train.txt - key_file: /src/notebooks/fangxuwei_96/crnn-master/train_data/data/train_center/key.txt - num_workers: 10 - batch_size: 256 - -testload: - function: ptocr.dataloader.RecLoad.CRNNProcess,CRNNProcessTest - test_file: /src/notebooks/fangxuwei_96/crnn-master/train_data/data/train_center/val.txt - num_workers: 5 - batch_size: 256 - - -label_transform: - function: ptocr.utils.transform_label,strLabelConverter - -infer: - model_path: './checkpoint/ag_CRNN_bb_rec_crnn_backbone_he_CRNN_Head_bs_256_ep_20_test_center_3/CRNN_best.pth.tar' -# model_path: './checkpoint/ag_CRNN_bb_rec_crnn_backbone_he_CRNN_Head_bs_256_ep_20_no_center/CRNN_best.pth.tar' - path: '/src/notebooks/fangxuwei_96/crnn-master/train_data/data/gen_data_sim/center_test/default' - save_path: '' diff --git a/config/rec_CRNN_vgg16_bn.yaml b/config/rec_CRNN_vgg16_bn.yaml deleted file mode 100644 index e2f4747..0000000 --- a/config/rec_CRNN_vgg16_bn.yaml +++ /dev/null @@ -1,78 +0,0 @@ -base: - gpu_id: '1' - algorithm: CRNN - pretrained: True - inchannel: 512 - hiddenchannel: 128 - img_shape: [32,100] - is_gray: True - use_conv: False - use_attention: False - use_lstm: False - lstm_num: 2 - classes: 1000 - n_epoch: 100 - start_val: 10 - show_step: 20 - checkpoints: ./checkpoint - save_epoch: 1 - show_num: 10 - restore: False - restore_file : ./DB.pth.tar - -backbone: - function: ptocr.model.backbone.rec_vgg,vgg16_bn - -head: - function: ptocr.model.head.rec_CRNNHead,CRNN_Head - -architectures: - model_function: ptocr.model.architectures.rec_model,RecModel - loss_function: ptocr.model.architectures.rec_model,RecLoss - -loss: - function: ptocr.model.loss.ctc_loss,CTCLoss - reduction: 'mean' - -optimizer: - function: ptocr.optimizer,AdamDecay - base_lr: 0.001 - beta1: 0.9 - beta2: 0.999 - weight_decay: 0.00005 - -# optimizer: -# function: ptocr.optimizer,SGDDecay -# base_lr: 0.002 -# momentum: 0.99 -# weight_decay: 0.00005 - -optimizer_decay: - function: ptocr.optimizer,adjust_learning_rate_poly - factor: 0.9 - -#optimizer_decay: -# function: ptocr.optimizer,adjust_learning_rate -# schedule: [1,2] -# gama: 0.1 - -trainload: - function: ptocr.dataloader.RecLoad.CRNNProcess,CRNNProcessTrain - train_file: /src/notebooks/detect_text/icdar2015/recognize/train_list.txt - key_file: /src/notebooks/detect_text/icdar2015/recognize/key.txt - num_workers: 10 - batch_size: 32 - -testload: - function: ptocr.dataloader.RecLoad.CRNNProcess,CRNNProcessTest - test_file: /src/notebooks/detect_text/icdar2015/recognize/test_list.txt - num_workers: 5 - batch_size: 32 - -label_transform: - function: ptocr.utils.transform_label,strLabelConverter - -infer: - model_path: '' - path: '' - save_path: '' diff --git a/doc/example/det_test_list.txt b/doc/example/det_test_list.txt deleted file mode 100644 index 3893474..0000000 --- a/doc/example/det_test_list.txt +++ /dev/null @@ -1,9 +0,0 @@ -/src/notebooks/detect_text/icdar2015/image/img_1000.jpg -/src/notebooks/detect_text/icdar2015/image/img_100.jpg -/src/notebooks/detect_text/icdar2015/image/img_101.jpg -/src/notebooks/detect_text/icdar2015/image/img_102.jpg -/src/notebooks/detect_text/icdar2015/image/img_103.jpg -/src/notebooks/detect_text/icdar2015/image/img_104.jpg -/src/notebooks/detect_text/icdar2015/image/img_105.jpg -/src/notebooks/detect_text/icdar2015/image/img_106.jpg -/src/notebooks/detect_text/icdar2015/image/img_107.jpg \ No newline at end of file diff --git a/doc/example/det_train_list.txt b/doc/example/det_train_list.txt deleted file mode 100644 index 1132395..0000000 --- a/doc/example/det_train_list.txt +++ /dev/null @@ -1,9 +0,0 @@ -/src/notebooks/detect_text/icdar2015/image/img_1000.jpg /src/notebooks/detect_text/icdar2015/label/gt_img_1000.txt -/src/notebooks/detect_text/icdar2015/image/img_100.jpg /src/notebooks/detect_text/icdar2015/label/gt_img_100.txt -/src/notebooks/detect_text/icdar2015/image/img_101.jpg /src/notebooks/detect_text/icdar2015/label/gt_img_101.txt -/src/notebooks/detect_text/icdar2015/image/img_102.jpg /src/notebooks/detect_text/icdar2015/label/gt_img_102.txt -/src/notebooks/detect_text/icdar2015/image/img_103.jpg /src/notebooks/detect_text/icdar2015/label/gt_img_103.txt -/src/notebooks/detect_text/icdar2015/image/img_104.jpg /src/notebooks/detect_text/icdar2015/label/gt_img_104.txt -/src/notebooks/detect_text/icdar2015/image/img_105.jpg /src/notebooks/detect_text/icdar2015/label/gt_img_105.txt -/src/notebooks/detect_text/icdar2015/image/img_106.jpg /src/notebooks/detect_text/icdar2015/label/gt_img_106.txt -/src/notebooks/detect_text/icdar2015/image/img_107.jpg /src/notebooks/detect_text/icdar2015/label/gt_img_107.txt \ No newline at end of file diff --git a/doc/example/label.txt b/doc/example/label.txt deleted file mode 100644 index 16a038d..0000000 --- a/doc/example/label.txt +++ /dev/null @@ -1,3 +0,0 @@ -367,87,426,86,433,140,375,141,### -381,212,431,217,434,240,384,236,text -386,261,447,265,450,287,389,283,text diff --git a/doc/example/rec_test_list.txt b/doc/example/rec_test_list.txt deleted file mode 100644 index fa73b1d..0000000 --- a/doc/example/rec_test_list.txt +++ /dev/null @@ -1,13 +0,0 @@ -/src/notebooks/detect_text/icdar2015/recognize/test_img/word_1.png JOINT -/src/notebooks/detect_text/icdar2015/recognize/test_img/word_2.png yourself -/src/notebooks/detect_text/icdar2015/recognize/test_img/word_3.png 154 -/src/notebooks/detect_text/icdar2015/recognize/test_img/word_4.png 197 -/src/notebooks/detect_text/icdar2015/recognize/test_img/word_5.png 727 -/src/notebooks/detect_text/icdar2015/recognize/test_img/word_6.png 198 -/src/notebooks/detect_text/icdar2015/recognize/test_img/word_7.png 20029 -/src/notebooks/detect_text/icdar2015/recognize/test_img/word_8.png Free -/src/notebooks/detect_text/icdar2015/recognize/test_img/word_9.png from -/src/notebooks/detect_text/icdar2015/recognize/test_img/word_10.png PAIN -/src/notebooks/detect_text/icdar2015/recognize/test_img/word_11.png BLOCK -/src/notebooks/detect_text/icdar2015/recognize/test_img/word_12.png 441B -/src/notebooks/detect_text/icdar2015/recognize/test_img/word_13.png STOREY \ No newline at end of file diff --git a/doc/example/rec_train_list.txt b/doc/example/rec_train_list.txt deleted file mode 100644 index ca6e8db..0000000 --- a/doc/example/rec_train_list.txt +++ /dev/null @@ -1,10 +0,0 @@ -/src/notebooks/detect_text/icdar2015/recognize/image/word_1.png Genaxis Theatre -/src/notebooks/detect_text/icdar2015/recognize/image/word_2.png [06] -/src/notebooks/detect_text/icdar2015/recognize/image/word_3.png 62-03 -/src/notebooks/detect_text/icdar2015/recognize/image/word_4.png Carpark -/src/notebooks/detect_text/icdar2015/recognize/image/word_5.png EXIT -/src/notebooks/detect_text/icdar2015/recognize/image/word_6.png I2R -/src/notebooks/detect_text/icdar2015/recognize/image/word_7.png fusionopolis -/src/notebooks/detect_text/icdar2015/recognize/image/word_8.png fusionopolis -/src/notebooks/detect_text/icdar2015/recognize/image/word_9.png Reserve -/src/notebooks/detect_text/icdar2015/recognize/image/word_10.png CAUTION \ No newline at end of file diff --git a/doc/md/ocr.jpg b/doc/md/ocr.jpg deleted file mode 100644 index 8cc1c92a5229c1d542c44fbd4e34953c36824df7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 197278 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

pBCzsxxr>@)Tt=y33p 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

Nj@&X@LwH9C*)fFWvbGQfv-RW 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

D&b&R=ZR_{T}~A-s~{>?DB! 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

VGMlncoAm0P=5XuW{aPHhUP-V~gh!~|Fyx8n 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 GCjJ8lk--=M diff --git a/doc/md/onnx_to_tensorrt.md b/doc/md/onnx_to_tensorrt.md deleted file mode 100644 index d8db60a..0000000 --- a/doc/md/onnx_to_tensorrt.md +++ /dev/null @@ -1,62 +0,0 @@ -### onnx转tensorrt -本项目使用tensorrt版本:TensorRT-7.0.0.11 - - - -#### 参数解释 -pytorch_to_onnx.py 文件参数 -|参数|含义| -|-|-| -|config|算法的配置文件| -|model_path|训练好的模型文件| -|img_path|测试的图片| -|save_path|onnx保存文件| -|batch_size|设置测试batch| -|max_size|设置最长边| -|algorithm|算法名称| -|add_padding|是否将短边padding到和长边一样| - - - -onnx_to_tensorrt.py 文件参数 -|参数|含义| -|-|-| -|onnx_path|生成的onnx文件| -|trt_engine_path|保存的engine文件路径| -|img_path|测试的图片| -|batch_size|设置测试batch| -|max_size|设置最长边| -|algorithm|算法名称| -|add_padding|是否将短边padding到和长边一样| - -#### 单张调用 -1. 生成onnx文件 - - -- DB算法调用 - ``` - python3 ./script/pytorch_to_onnx.py --config ./config/det_DB_mobilev3.yaml --model_path ./checkpoint/DB_best.pth.tar --img_path /src/notebooks/detect_text/icdar2015/ch4_test_images/img_10.jpg --save_path ./onnx/DB.onnx --batch_size 1 --max_size 1536 --algorithm DB --add_padding - ``` -2. simple onnx文件 - - ``` - sh onnx-simple.sh DB.onnx DB-simple.onnx - ``` - -3. 生成tensorrt engine -- DB算法调用 - ``` - CUDA_VISIBLE_DEVICES=2 python3 ./script/onnx_to_tensorrt.py --onnx_path ./onnx/DB-simple.onnx --trt_engine_path ./onnx/DB.engine --img_path /src/notebooks/detect_text/icdar2015/ch4_test_images/img_10.jpg --batch_size 1 --algorithm DB --max_size 1536 --add_padding - ``` - -4. infer 调用 - -``` -python3 ./tools/det_infer.py --config ./config/det_DB_mobilev3.yaml --img_path /src/notebooks/detect_text/icdar2015/ch4_test_images --result_save_path ./result --trt_path ./onnx/DB.engine --batch_size 1 --max_size 1536 --add_padding -``` - -#### batch 调用 - -操作同上,和单张调用一样,只是要把batch_size设置大于1 - -- 提示:其余算法类似 diff --git a/doc/md/pytorch_to_onnx.md b/doc/md/pytorch_to_onnx.md deleted file mode 100644 index d19e3d0..0000000 --- a/doc/md/pytorch_to_onnx.md +++ /dev/null @@ -1,34 +0,0 @@ -### pytorch 转onnx - -#### 1. 运行根目录to_onnx.sh文件: - -``` -sh to_onnx.sh -``` -里面有四个参数,需要对应修改,参数解释如下: -|参数|解释| -|-|-| -|config|对应算法的config文件| -|model_path|对应算法的模型文件| -|img_path|测试图片| -|save_path|保存onnx文件| -- 提示:这里onnx文件建议生成在项目中onnx文件夹下 -#### 2. 使用onnx文件夹下的 onnx-simple.sh对生成的onnx文件进行精简,运行: - -``` -sh onnx-simple.sh 生成的onnx文件 精简后的onnx文件 -``` -例如: - -``` -sh onnx-simple.sh DBnet.onnx DBnet-simple.onnx -``` -#### 3. onnx调用 -运行: - -``` -python3 ./tools/det_infer.py --config ./config/det_DB_mobilev3.yaml --model_path ./checkpoint/ag_DB_bb_mobilenet_v3_small_he_DB_Head_bs_16_ep_400/DB_64.pth.tar --img_path /src/notebooks/detect_text/icdar2015/ch4_test_images/img_10.jpg --result_save_path ./result --onnx_path ./onnx/DBnet-simple.onnx -``` -- 提示:这里如果加上--onnx_path就是onnx调用,否则是pytorch调用 - - diff --git "a/doc/md/\346\226\207\346\234\254\346\243\200\346\265\213\350\256\255\347\273\203\346\226\207\346\241\243.md" "b/doc/md/\346\226\207\346\234\254\346\243\200\346\265\213\350\256\255\347\273\203\346\226\207\346\241\243.md" deleted file mode 100644 index 931796e..0000000 --- "a/doc/md/\346\226\207\346\234\254\346\243\200\346\265\213\350\256\255\347\273\203\346\226\207\346\241\243.md" +++ /dev/null @@ -1,62 +0,0 @@ -## 训练文档 -*** - -### step 1 环境安装和预训练模型 -1. 编译c++后处理文件 - -``` -sh make.sh -``` -2. 下载预训练模型 -预训练模型地址:[下载链接](https://pan.baidu.com/s/1zONYFPsS3szaf5BHeQh5ZA)(code:fxw6) - -3. 下载icdar2015测试模型(不做测试可跳过这一步) -测试模型地址:[下载链接](https://pan.baidu.com/s/1zONYFPsS3szaf5BHeQh5ZA)(code:fxw6) - -4. 将下载下来的pre_model和checkpoint文件夹分别替换项目中的同名文件夹 -*** - -### step 2 准备训练所需文件 -1. 准备一个train_list.txt[示例](https://github.com/BADBADBADBOY/pytorchOCR/blob/master/doc/example/det_train_list.txt),格式是:图片文件绝对位置+图片文件标注txt文件绝对位置,用 \t 分隔. 参照下面修改成你自己的地址。 - -``` -python3 ./script/get_train_list.py --img_path /src/train/image --label_path /src/train/label --save_path /src/train -``` -运行后在/src/train会生成一个train_list.txt。 -- 提示:你的图片和label文件要同名。如果你要在训练时做验证要生成一个test_list.txt[示例](https://github.com/BADBADBADBOY/pytorchOCR/blob/master/doc/example/det_test_list.txt),在yaml中start_val可设置多少epoch后开始做验证。 -- 制作label文件说明:照着icdar2015的格式, x1,y1,x2,y2,x3,y3,x4,y4,label,其中不参与训练文本(例如模糊文本),label设置为###,代表不参与训练,除此之外表示参与训练,可以像我这样用text,或者别的也行。[label格式参照这里](https://github.com/BADBADBADBOY/pytorchOCR/blob/master/doc/example/label.txt) -*** -### step 3 模型训练 -#### 正常模型训练 - - -1. 修改./config中对应算法的yaml中参数,基本上只需修改数据路径即可。 -2. 运行下面命令 - -``` -python3 tools/det_train.py --config ./config/det_DB_mobilev3.yaml --log_str train_log --n_epoch 1200 --start_val 600 --base_lr 0.002 --gpu_id 2 -``` - -- 提示:在./config/det_DB_resnet50.yaml里有[参数解释](https://github.com/BADBADBADBOY/pytorchOCR/blob/master/config/det_DB_resnet50.yaml),其他的yaml文件都是类似的,可以参照里面的。log_str是为了多次训练的时候结果可以保存在不同文件夹 - -#### 断点恢复训练 -将yaml文件中base下的restore置为True,restore_file填上恢复训练的模型地址,运行: -``` -python3 tools/det_train.py --config ./config/det_DB_mobilev3.yaml --log_str train_log --n_epoch 1200 --start_val 600 --base_lr 0.002 --gpu_id 2 -``` - - -*** - -### step 4 模型测试 -1. 修改infer.sh中的参数 -2. 运行下面命令 - -``` -sh infer.sh -``` -- 提示:测试时infer.sh文件中的 --img_path 既可以是文件夹也可以是文件 - - - - diff --git "a/doc/md/\346\226\207\346\234\254\350\257\206\345\210\253\350\256\255\347\273\203\346\226\207\346\241\243.md" "b/doc/md/\346\226\207\346\234\254\350\257\206\345\210\253\350\256\255\347\273\203\346\226\207\346\241\243.md" deleted file mode 100644 index 6a9ca23..0000000 --- "a/doc/md/\346\226\207\346\234\254\350\257\206\345\210\253\350\256\255\347\273\203\346\226\207\346\241\243.md" +++ /dev/null @@ -1,28 +0,0 @@ -### 文本识别 -#### 数据准备 - -需要一个train_list.txt[示例](https://github.com/BADBADBADBOY/pytorchOCR/blob/master/doc/example/rec_train_list.txt) , 格式:图片绝对路径+\t+label。 具体可参照项目中data/example中例子。 -如果训练过程中需要做验证,需要制作相同的数据格式有一个test_list.txt[示例](https://github.com/BADBADBADBOY/pytorchOCR/blob/master/doc/example/rec_test_list.txt)。 - -#### 正常训练模型(以rec_CRNN_ori.yaml为例) -1. 将yaml中base下的restore,finetune置为False,loss下的use_center置为False,修改数据路径还有其他参数。 -2. 运行下面命令 - -``` -python3 ./tools/rec_train.py --config ./config/rec_CRNN_ori.yaml --log_str log - - -#### CenterLoss训练模型(以rec_CRNN_ori.yaml为例) -1. 将yaml中base下的restore,finetune置为True,loss下的use_center置为True,将正常训练得到的最优模型文件地址赋给base下的restore_file。 -2. 运行下面命令 - -``` -python3 ./tools/rec_train.py --config ./config/rec_CRNN_ori.yaml --log_str log -``` -#### 测试模型 -1. 将训练好的模型赋给yaml中infer下的model_path,图片地址赋给path -2. 运行下面命令 - -``` -python3 ./tools/rec_infer.py -``` \ No newline at end of file diff --git "a/doc/md/\346\250\241\345\236\213\345\211\252\346\236\235.md" "b/doc/md/\346\250\241\345\236\213\345\211\252\346\236\235.md" deleted file mode 100644 index f8b8174..0000000 --- "a/doc/md/\346\250\241\345\236\213\345\211\252\346\236\235.md" +++ /dev/null @@ -1,34 +0,0 @@ -### 模型剪枝 - - - -这里暂时支持对mobilev3 DBnet进行剪枝。尝试了对backbone和对整个模型两种方式压缩。 - -#### 参数解释 -prune_model_all.py 文件参数 -|参数|含义|额外说明| -|-|-|-| -|config|算法的配置文件|| -|cut_percent|剪枝比率|由于类似resnet的跨层连接,剪枝比率不完全等于这里设置的,可能偏小| -|base_num|保证剪完后的channel是base_num的倍数|除去剪完后为1的,其余是base_num的倍数| -|checkpoint|稀疏训练好的模型文件|| -|save_prune_model_path|剪完后保存的模型文件地址|这里会生成两个文件(其实可以合成一个保存)| -|img_file|测试的图片|| - -#### 如何操作 - -1. 稀疏训练 -``` -python3 tools/det_train.py --config ./config/det_DB_mobilev3.yaml --log_str train_pruned --sr_lr 0.00007 --n_epoch 1200 --start_val 600 --base_lr 0.001 --gpu_id 2 -``` - -2. 模型压缩 -``` -python3 tools/pruned/prune_model_all.py --config ./config/det_DB_mobilev3.yaml --base_num 2 --cut_percent 0.6 --checkpoint ./checkpoint/DB_best.pth.tar --save_prune_model_path ./checkpoint/pruned/ --img_file ./icdar2015/test/img_108.jpg -``` - -3. 剪枝后finetune -``` -python3 tools/det_train.py --config ./config/det_DB_mobilev3.yaml --log_str total_prune_finetune --pruned_model_dict_path ./checkpoint/pruned/pruned_dict.dict --prune_model_path ./checkpoint/pruned/pruned_dict.pth --prune_type total --n_epoch 200 --start_val 30 --base_lr 0.0008 --gpu_id 2 -``` - diff --git "a/doc/md/\346\250\241\345\236\213\350\222\270\351\246\217.md" "b/doc/md/\346\250\241\345\236\213\350\222\270\351\246\217.md" deleted file mode 100644 index 0b7943a..0000000 --- "a/doc/md/\346\250\241\345\236\213\350\222\270\351\246\217.md" +++ /dev/null @@ -1,25 +0,0 @@ -### 模型蒸馏 - -这里使用的是通过大模型的前项输出作为soft label和生成的label同时监督训练,这里目前只对DBnet做了尝试,其余算法应该类似, -其中soft label监督只对binary和thresh_binary,忽略了thresh,测试下来这种方式效果最好。 - - - -#### 如何训练(对压缩后模型进行蒸馏) - - -``` -python3 tools/det_train.py ---config ./config/det_DB_mobilev3.yaml ---log_str total_prune_20201015_distil3 ---pruned_model_dict_path ./checkpoint/ag_DB_bb_mobilenet_v3_small_he_DB_Head_bs_16_ep_1200_mobile_slim_all/pruned/pruned_dict.dict ---prune_model_path ./checkpoint/ag_DB_bb_mobilenet_v3_small_he_DB_Head_bs_16_ep_1200_mobile_slim_all/pruned/pruned_dict.pth ---prune_type total ---n_epoch 200 ---start_val 30 ---base_lr 0.0008 ---gpu_id 2 ---t_ratio 0.1 ---t_model_path ./checkpoint/ag_DB_bb_resnet50_he_DB_Head_bs_8_ep_1201/DB_best.pth.tar ---t_config ./config/det_DB_resnet50_3_3.yaml -``` diff --git a/doc/show/ocr1.jpg b/doc/show/ocr1.jpg deleted file mode 100644 index 397ff8cb29d0f13e8339626a535d2d9790385c1b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 237249 zcmbTdbx<5#^fovI0t5(>;GRH$puwF%f`tIV-7U!AZWAC#a3{dv9^Bn!@WI^&hrwN! zclWE`ezkw??&&^NUDfw?ou_X1?R%c*+~>LHH3056DOo815)u-?@Z|tJF9IY0uaJ=b zJO7sp<%qmz`(?MF|;?>IB&7A|GWP0A^&y% zuh+|og^q^)UyJ{5_S^x$Lq~r9Y7`miBj6Pt5;7jra}R(D06=>E677Eh|1U#&g^cnV z^(7<>%$EkWxG&)&BfokH_B9I1OKYE(`v4TY*Y7^Ci=n<(GD7?4fY0FB zIXSzyy19FJ2K){T3J&=b8XFg%keKv0IVC$MH!r`Su;^b^bxmzueM4hYS9ecuAGCj9 zaAI<5dS-TReqntBzPYu%v%9x{dUk$sd3Akrd-oqMBmnaN1?zv1{XcNwz2JKFG6pDU z|KUP<<@Vypcqp$wu%o^cQ$jOxc>j^Z4;^1TCabdZ4GpLA34yWWI0hjt*ZQZ^|DgS! z$o}5}^Z);a?04Lgr}zmZ_-;=QXcH?Rugzk)DsV*8iqN`##7z$dvi5C3|HIHHxmYRa(< zs;vFxdnRS<-J(kLre$ZBGc>_ynZvI(_|!>hi6YeA9k!#bl{GjsHk0yh>$u95y(&ULiL(}FzW%G+m!+8eqR1{y4sqR~n!PCRTfJej2&8=m z1U;O(#X4TAt8-L61Kf!#KePY>nj>zOG0ZBwL z-!=n2s1uVQbRQKqvo|c~hMYXClcq~cvS~z&#Z}Ip0m8*hYNy{5&%!ioJ!-#)*F>OW z07V$vs>>`94{ll=?B-N)dvR(Ph>r_*XZ+y~1Mkk1WHEH=z}jx=oSV7}@2o{WOw*q% z&5Y@F+rU1eCYbCu2TAg(w^}b+Xl^x#wZBq>9do8bF1VWd_AY5rSIAQ39NLs90nmIP zWBq;m2k%;?ZKRsOK7pCH`?j$Py>eP+lQuE77BMFM%z9PB?TUk?R-PF_$8;fLU`|D9 zl2@F5Y8K;ZxiPA#$*vIo`^u>{qLV?L<-*@S*`{Qnky1YU@oByEc@c^@d(odV7AASE zGy@f~99D|03D0$9CfZ|&m+(>7gMAY!X5ZxKpcCZCIDStRT|(?Zxns(D&b9q#x~6@v zk4=Xo_MoT`7oh{;L0A1N(y56@Y)Ml^JC0*t{)Qh24G>A)t z*3-x8#X?Y%nyi|-Bzw#`@}1h&lxbrFLq3-te8dct6jVnt-*z=*)CRZQ9J;#dVo;TSvA<#jwW<~vQ$}k_u3anD>zy)v8bOt zoiWL=W5pERS9j&fP&oFOs?I7|%{sy?z*m@F62XxRBgsLBiGn=5a56!aI$dBXhre`{gSrB;sp> zq>GRbW5Pq6zI9xGG}QP*^xqh|bJx#1B)d)sb<@Ujr~KHdFowgOu;EBb)#@PrV+fLJ zi%homdZE z&zPvPg-T0cZ_0wGk){JoP_PhJr7XNXi3^f&&VKCmdJ{I9Hn8GMiJ~Q4GPVCeDfBN- zpEmMmliQiAfLyWl5&oUo>?$8~yPFF?6D$*o2Z~D)rMxhVF(5qr8l{ zpmUSPN5<{MB+sCfuX!7xs;2x;NU-nEfOKm+{h4FXMGu}8sS8A@Qe7`AFFt+nA0P3P zOM)B7afQHd(@}k`0nL{08pf4&jrrzvO{KPW9DD40jDJioe+0;7_6TCD6&8p2O@Sg5 zUa+SvXq*U>9jq#!i)M!b)=ZxPjB%QE)fHzgDv;{yvK$P zV}_pUrQuKCyi;jXsXjxIEF-ua_vBj15~-1*r3<)r58H<9c`}m7{1b;T@M|l$7D+ZR zr(!?Fz=o1M};&%JLRT<&3+w zJlz;fDc8I(?ozLUp8@`v?h+mC*bm@!yWj=x>o6fTxV-qrweqQYmVGfvBZoOZsDsid z6cwYQ2*{XyKf9@MsCSwGwg4{*u1iO5yO&-pBllbxEJO%wzc-=@Df=3L_Ia!MJ=D&~ zzysW=@C^8wG1uPJ#(fGp{(ZUnM_c;OF}$Ivrh5DV*e*F;mY|rl*}p~d(Gq}0ZWs&b z^&&nlZ4n3}C^K2l&-};BIpVE&ueNmH%X!1?ahjvneCpQq3a#DY#dou*)TA`Zoe!uaSOk<{IC}^o(+ozdOEojG8 z%iy|_dSHk;{Jg16?J?jPV51mF`L_AA$Cu}1!CRyDCSv`-4#V<}G7UHJX_(J7$v#H( zdrM44yoEFM&Q=LAKTLfMuR6pjn30irsq#9)p8G;ZcnN!ArJ@r3& zaaGqK_HtJP9i?`GtK?*T&j8HzlZ2_p)7Rl2XFk^5YaBWhstsWysUjb&a>JXzfd#P> zEHP%Tg-iSxIAV~OS725zxKobp5J~p8>BPGkP*` zoxOBw6T4;75rkJB5XNr20b>QPyV+OaQ#z)7vqU`;3M^dX!WC+7(oP3){<{8p^T4FI ze$;AYw018j3tFK|QZj!h`}3G^LQy6CZ^>$C(^sAq=C<02a-*r5MOjKFufQ~P7nn)9 zr>A;U@THCqZx^Xg!c3MOUTx$T5!;A(mi}je^OH*rP2Q$$t+dWMg}`EuFMsT?qG_p@ zPsltdIf=>U7rjA~o~BcJ?N&P5X<`<<`65mQmBu#1!heD!_3&!lDY^)P3?aXt1)o$y z7tnsUeb~FcUVwbI1Ub(VVZ|iTf{b$jl=o6T2eGM&V}{bH_fq0!B1V$R_n1!p_GsVL zL(}tHS?RkJiRH4aF$?OG2$Q_^J^I6i{&l+3&;TsRHO!nginX{+ScjS;iq1?KhD^xA z9U!xKRyYwX@fjD^zns!D3CWchnVcmOVpiO1Uk3+V_^1rp zarV3$28|7KuxtqOR#0H_+1+nfeK=LcaUhNz<}>uL#C`*~pP_*DjEMYvNEkQ$*#+{; zgB;agLize+`L6%fi3rzLw#bqxK*HRxGQTw&%$ysldA(C z+p+x;wts7rIZ^RhFAWEQoGqL8ARo)Eh2m(r;5~qEa_#Vk#WED%_0VQrW6SNOKwPMt)QOJ z`Khvi=yw=Dn-luJLvx;m)qRq@j!c@b(|Nr?61T?CIjhd6dS2}c8qrq%e*S^Hp^Bs=JNV7~rs3 zaKL^Mzm@y0i}doXQziHcbRsp=z7jCi*bjanrMYjv`OF&;7akRq^bF_%?lR@M9cU?c zO{n1~?*40NiVFx&ek2~3B-l^4SCYgB{O_)eeE!2JMkeR265UaBkAB1@*D9N}#Z1P7 z0c<73WC474nI`YTxQ6=ED2PfSUfCAw=`tu0$!p@hpV*#K&3u2i^Qb&G;q+j|v_Yy! zf!`tl;k0GlFOSW8<=QzYW&`#muHo7HvtOQ@7X;J93w=f0#d!Jmd^VmZK2`*uGMMw? zie9A&1Q|ppN>a!ir&#`F))c=F>9!#5)e7HU95W#>v%w+{#}R`)1GN82tt*CS0!17U zqEREGB1*@yhoYa)WEa}m^~ox^Z$Cc+UiXhjKoJ84-jTl3>w?O;Y}Xb|R9RM!HVK1Y zkn@?&U{9DetSmrl#b7r=sPXcRlXV@3YiUn1v|$A(aL&o_w0Zf3?Rt;ut^Uu9lSpe+ zx_2X+eoYo#(y^S8Qt}oz-Mv*h^YL|22wu^RFGIC7&j7cXx(EBj>I0ms)Y}4k-~Bkh zLkBhHpagt`{ndo-QbJcXRg83ft9Nd_NL^yFN=TS&s#*h%my_$cEtUrJtBgG7N_P>= zTofHBSNNAVqfYzwKE+ZDC7q+1f}4QjN2}?PABJKR?dgcji%J23#+-{Q7E@|qsgJ(~Z zw!Zg_Yk`l#h5HtC<;Dde4}t_i1qC%65_Y!ZT%5A);E`w#Dp9NLjCGP^@OVcoEs5nP zpO`|C0^ndy`{ZFZu7rbE!9oqFRW(*dQr#S~SUT zMTw&20Trsi_?DkJ8-tYJU^g!0Qi@$Z0}j-m0b!(gHeE|mEGBPS^4E5dsVS$4s;8vW zgaZ`e)^ek2;Og7}YB)&@acF?%6iQN+)Z}6(iiIDIS$khCtF1|xdVQ2V+g6>}T{&=P zrm@v`YO!^q_ieM_HI0K>aqC#{P}ygnI;9M@U!+DukHW3{GZ`Q(?It+~(jks#004GY z;+%Adq%F%a#x}f^mhtQ0g20t6#8UzCyBk!ft5t53e`miKnu8Abn7bPV|QH=lj`cO z<&dR0Wb5;hI(HSVnhSNmVw9G~yIDznon)tJKk;YRZQduXxyD+ZrQfXVZG)>`-xUGC*XAPZ+4;l7{%?qlu5h^N@RvuN|fs9mmq7? zK;04(EF>x(zKgw!k|XV@<%*NMTvwxMl(!hkl*T#R`id6_-KQKl_Y{bF#1Fzc0 z;lCkN&5;)c#(R1+t&Pq1Hm}c69Lns;0*a5#o6@ot1lBG#_%a`=GN{q$(PP>GM%nC~ zJikXF@3yUVD&wCo!!1N}GgV z@jnADXQz$U@`^?1$%+LOv2LQ2PizY)o+1iY_OufkMf8?)^FQan(X6qhr_=~fkIMh8 zR<9fgMBF;n)DDk)rjI&)zbn#!FSV5oYB4#+u^RU8RF_)nDE##6f=YY;N_BCDp0r8O zkEL^b@{vv)5)+_xbv=vKBFA(@%&b`OZ6K^i8ET17X?57y*~d7Yo}d3Ak)Hy+553qq zZaO|UET4t}DU6+%*qqj(S3sD(jAcQ{yTraTN=bQcX_52QBsCw5uC~NQm}7-}z=81a z{M=Vne<;3T|AG@WKI~(JUl$unB+^x(`s8R#9Kdjgpm2qD@_Xt13eIUsu4-C5G5&&onw<%ZR0C&n7>e2Z}SNZ+9#V03V)c5;E>G|Wu7c>GVbqQq!Y(gB$Lgo9G=oaWmBW}5G*8Zwsi;33 zB;gnBe*Fv(sg1!;i5S)8C4Uv47zU=P>fe;T{zdyS-?%w?sV&bjH7klw>1mci`toC3@lXS!}&RibaU`!B>C{R1;UZGPl z$sbC>cW|qr0Xp%Yfr(FLPG2CzXVq#>S<~|3-G0s+r>w~8X@SxEh3&1Uxxn+2oiUG( zBo-L1#Uk&o6%??X4;Vpan?1VYC}eC%-Ndl~Ss0yozKeG9>zS=yVV4QZ@K;Hp$<%(Kn*h`1S%K`doypZ*C`0+rQ}IxS%vb-L zqnPvwTUsU`A44fVBhj>)A>Mf=7=1gk`QdSSk!!Iyzy(1*@+g5!|2;&3mvUny!MVz| z&?p-+@wQDCs(0CSj;6kzFYcvypLsaM?Hwq+!iQWOXsayrLXFu!#ybMgh~X2(dN?di zK1;sf9d4R{8pv7&Owp6Xjc_?KM@U2dg3Do2ym@p2>wV0eD}K5WJLrjghcdq)&^y_w z+P;$-o+tT1o&GO06E*F~c(0~RT1A%7)$g&R$D&m9flVbNkDnsnu64}hq-2YG#Mn4> zPK8G9I?1*}h7ulgH@l7YK#o+}yRNwKew{LVZ<}e4^tYGb7cC=T{{Eu~Q@yXw(VFS` zY}A2464i-AUko=av<-8qZ0RvWEtsFOu3+CObRA01_?Bk8V^53UO$2w^u2e-K6hVOA zketdYrAg^VU3;vAZ(~ab6 zVn1xhfP8VgHmW?JiYI@{WQxx8i|jTX!1xXFo$5ZTt#{5@m@~Zxi@#`@G6?vi)+xej z#LH0Elx@wjdU@qjHIp@|I(sxK@phz6)B<@PNM0vaiZkBrb^*%Ezml|vxzVa@PFXov z6zN&^r^vxv+YJ5!?P-4GEAZ1(wCl!b@p5Xn@ElDt;I*kZkig13H(IU^HiCmnjnEe^ zH?vULYjcS&$fuAuRs%AZ-j3$cTmriNqWX&G>$4zv=AIT0pLnj40=2hEG>*M6Hn=$D|XOa-RyL*kKr3d$uD#Z{)c^(P;XCIiHw0FvvfA=n^2;|!I_HiJ< z@$c~5ap$JjkBW)9&dUUAN|^Pjxu6`Gwc2DET5N-S)!X2M)w2?LYv?Q<)RDHXEf{8h zMgWd!3wXFF=WTh+K4Q&Ru~`2(macH?@q@1fVRuzL%QBv=h_h?pMwZuL69wKltD6f(qiG6otGmLE?5g$EEpi#hgBY?u4Axe#(Ujis{S!VG~^2&r^T*p(b{8S zi~1`GvfXjUbP`a|F_f;zC{>;FZbJ{pyRw2XwoP@t0@Ia#B5T_DCZdv0gXtOY{k*B= z;|@G2te`n)L*5mg3gsaN0Az+=6uRJqQ!>tMDY1o?bs_lhK4vOuTuNFa_g+btOXRGU zE=_6;IanriD0KlVvnP&wh)Aua?FY6$&xwi!>^d@aZJi#6u#&2!_Pz*-k>8!py7#m= z4+P@6^lCG3zblfQ;|vR*YLIhjsL=mOGTGH^VfV-zvJ<+_GV?%MBF7s$g#n-b?Na;0 zrJK%GG(zWBAvHM<*4u9hH$^9s%UF*o#{QMe;{5$X6V3VLq~Q!h@5yNOut;8dgr*Ik z?WIuA)|vgQvY0?@2qv3zSc%Zo+1O(Y<0D;C9B^FsTuqvR*T4KRiQbx#9S-%uX4=^k zVz(o|uV4D|L~EIWe3{!YZW8-3gkRWk8)8{(B@sW5;BEUnQPZg77ItXnkc< zi^$9HRRSTVpC7HC0k|tm1U3N}RTq~O?E36^H#{3J0u@o+Oob$K<=*(E~Altuf>0t5e5OGeL(+AP8#bLG29 zlzbx%T4I{0K0Cy?CH{e5&L>-%mr>vh)#~+0vOqvizsdJaEbl>vE9XPC{r<}|s;kVg znnyOXt7vWc9;VWczLh0B)nl*?+uOp&mAH`c3Le=3peTA7HWQyT{DznbAmH^BU+p``e&Dvs z8UwN!e`wRK26cPB6R917t{del~EM*zA`e%TC zOR@Bf&>teln7%R2sp&m#50VN^gm<`lF%AI}B(6su>Tvpvx=Fbe7ar5?p#QQ0LaBJ6 z*|~0DPZ+&L?i)B5527c*0of1U1b2GR0MxhxWq0t^(oL-q>B!~*%D#(rCiGHStZ65 zkp4k_Jx!v6^2BG75WTq`;uJ=IKcDZ}HBUDY=(P$~Ev;`-$93isKD_jBj5D z(Nz{&Hc4I;I2oA`PssZ393F-q?<9*-SNnC%9w{QkULsibCVqTTUhR@aSZ;##rfWdR zn*Pg|<>-43B}}FJ-<|>7J#p{8N$qMl{>IXTe4)wUf6Q#Rz_k07aiU;{d;{**AgX)9 zkhjp#(WBhU{yLc@&_pXoUPK%-Ao?p=YgF}o#7$k>tAL0lYG+|qYGopK(M=Tbo<~-< zh#Fq*!d;wqBLH$0uY}<%9}bR7uEagr5P5d3NqRC*9ng zc1a0q^}u+=%ygWQ`7CuCPc!C@oRx~Xh9zQQ!2cjmp4`Eqd-K!+o z;JUINk5uaixtUF z)Px}voA@(nv(fkt#)@% z)mbdtaay>lhN7@7%Nr^sKEy-{yn16Y(c-_+%kupy3vg!UjDa?YZp>Gn(Y(1G{QH1YgY_3{}ISI-{8N8Cguk8`N%uASI6$?%`Ay0VX)wX=C@5m z=3it%H;uEZW$|{C#VrP=wq;f1o5~WVQT_20<-qY&C;S+3t;{;g)Y52j_*5t?yO#n5VJ|MYKC|Pb|*nOi8@*`@pf3W06M;%^D;M$n+RJy znxOs?>W6JG?pI$^dAf$Cwe_;fRNWf|C{h+3S7Hy8hH{ z;V7$ZYd!3Jb%&{?F`K^R9Dg;WMByXXYl4K0dx9*qxI|OGi!wPWOp#`8C{TH5+5R%3--pB0gzQ*I!jJD7e z!87zk=;&Uj^QR^78O9MBUL0Zh%OerI4HUPImGp37pI|E*#s7^lBMdmiLAJ zrH*9svOm3fw;Iyyylx>fU@TryQB}b z23b0|xHvbPM6x%|!OPIvm@eQ?=vpUe>No87-yhq1i0pOtS7=WZ^5HVX=Or7V&j2~3 zrMt?wi;}Zj-zUlfU-R~Px>=J;*Oy$O3JAnt$o};|ZXMB0l_rq#K$_hAA&c|ua)j%N zD&jMLo>=0Y23zmPI#La{2ek55R{q=tq^wlC7sfbmzmLt~J9eW;Tg{0h=j7b9bK!E!Kxn~^Q;kCyYr1GR_8_3E+6*W7umEfb~|1)jU%3V~$U97k_ru08K3WWTu5 zp2jVt*2g+Yam@Gm)_l8S?D`i+_nFoBYmAzjy#gP_-X?hvW~|trTKanT5gJvXMQNFt zQt4l)E<4KOIuYMr#E}Q?`*CmMWNJu(YB^h%U)lk$MBU#7Sy)(zzpb?4h^*sY#bn7Gmo{l zBqCdZo=4rAgF6M=XuzF9z8@dMHZb%L^l_%lnR4Anr_)MvFCrZ>mPLTAvu7}f6)9e2 z?YZ{Os9z8-ly1wG^BA$3@$D(4#cpkcY$NzL+k@bS50S!b3-F(~QA|_@}vFcBQT`hTy09o`b2!@8PK}|_^>_ILPJAReqmbzvy#2dLB8RqiP6^w zB7uFA5o@OiZ34yN+1mfPzk#auifg0iOvZSysk4V ztdn$4FeQv(EXT#~d35zI`xxo&@N(*u0fXSgb1mt&!{R=9t}oJQdV8t+!SaN38GNKf zkwcDQt(EHM@0_>}7k-!(X!2F7ZC>tSMnO@JC@?4fNFZb+-9*Lno&Kkejl#Y|t z0G4u6_;7vj2&>5584%}uBFYk363ry&o?)sC#r{`n6SH zgOoT$B}1#&ozCclLHe7K5r>Mmd$Kd>odZSPOYRtGG2)s%M;czQX>J3Qd-F!HL^OkF zJ2r8FNP5%lK{Nfg4m!*_H*>kBQx)#O5jV?^J0y%A8(C9{+b4PNhwX$YXV2eg8S8}G z4^XboRuV(f$u;afd+lP1M-=N2Gl2y2X;>sTtXnv^`m4D@}0$HuICu%fVTgEcuBRKqmWCseg6$X-(WjTK(b%lAM9c{)qf+HND zrR*&)>3E&*=_uy=Cm*JXzVLca%Q#z74k>okS@~s99nVgYuEXsteT`d{KuM`nmr|x?eCRx$r>z+TjXkt5xc$C z5XzXWBT>~u2ho_hQQ?E+St4Xcrtvu9|e9mA04abQG77=)^+}h*;TND*j(;3T3 z@ktFM%nfdCSkA(y94yCIZOqPTDEdVbju;AxMa+-35jg`+F7=+LiNx?aV5x5axU;OO z+45!5QIlJge^T|V_jj>&z_zchlC|k{NcUtfAKe5WJ}P0hr#Qqw-gbvOKQJM1Zg>}a zPHu>vFeIM=Z_FnD(3}_qxFGaCz#e?QSNzKbhw(l9vj_=9_mZ`#yISy_ugz8f)4( z_bILNTK4YyGc@aIEJsBg#;$2Kh+a~WlN;k_Kn)lnv7s2E|G7R+ukwToO=n61x-C*% zIJC{hUMNH$8Bx(zN@X+K53XHB%$7fV3x2}eJF362kWR>uACHrlq3of}%<3nxRP%<1 zBUXz1^eKr#Ep*-@tfU5&uYK@FSfU1*`sO!;^;~Pfr<_e>I4Hi4K{#vKO~-+CR{|>m zWm>vAOfUy7yVw2n-Y9IMf}81=-9pW_1@>C?O9M={`y89+#{+b&8f$C;8b5BNPjPzb zF#qJ7wA3jHG8dEfoTdGn1A6bIwfdhMh()D3^N+gG)k-`OEfPO)mq|L&^+h6b$o^V5 zMoQDq-rlRT){#q}`Qt-)Yn^9+I7D!qo;N1`qwGHa5))}`M{sFB*qnH-QBHU63%u*Z zSukv9#H$}RNi@FQ^ao;T@K8EHxRwsH)N0M0(^346o<3<>93J__AbjGV{vTdfwp9>(*a>5a?pAF zxlpo#H1V3o@CsMNWS_t_dvsIjSU>dP(&~DIcSF9W^lC^)Sb8wjSviRTFGskFb24h8 z&2yG5Vm72!mh4#3N9ec7ukXKSrSQ0xhl^{iDe4bT6H>L!FrUh}`G0t2s2eC6bT!|W zXUmIAsk|Y31z|Ley5CJXI}T`SU5Rx8R)3LTi1L8u@9&z=xVXH-sfBH|i)Q+&{!?)* zoX=%sHIVSqOeY)4CGM2$M`48QOljbh8~D_#c31tbfs>G*YM4i!|A={jw#0{%SB|0` zs2Oy+@yf+lHYB-K^23}EU*AQR^eKZNdBtyz(waHbokC}tTOu*hkbYq_j;?w6y>MmB zSKc)a;;rEmAK!^2KLhywy}VB30;(%!E2+4BGS-1>*iKXT>JhvZjU^=mPb@T0xJ-f~ z)x5SPnvma>%mJ!S5myf{UR0X2{M^80SseV!SsQJQbwtdbr)M~i*sGZj(0YQQnz~7G z-(|stMda&oUh+HRnwicVZ4zABPs`n-XEZIt`igdYYGoJg0=rcw#5;V)dcrNFSQX}E ztx`V}i9@jNr;-Spxc`yUWwAT;$?PnK#`>gkl-Pc%{>51p0ar@et{CeZVb9yed#AzfeymCO3utZ)jRo2dd^;@Ql=9luw;CzHisV~Un$V0PBrBZ zoa%mmvi>;gXE=+;li5$IGy588KPD1wXZEBROv0k0aDo$HM9uW2DH`Bos=Yc{vzg}6 z)r(G#oDFClF?_I^&Y5YUSQ}V_#&Pj7r?1HXo}?Pr9^vipUz1wk5WJMye&R>N0Kg~O z@=r$nHob9vmzZ$eNOvjW3fA_uJ3c&$M-8^=NuC=)#z39B`~$(0qN#Ih7qP2DJnqF{ti7 zG11NFUQnCPi&wYy@S={@IV<1pD)ukJLjjSH(6_OV%`Ru0h*}?%+1=tL0XhLyM_e?UQW@4&$88PM5ILsduh$Bk{+o5Wd+KJ(rs z3crGM!)V{d-1RO^QR3=FBPg!}3jWXPpCP9d@WERJXVm?{Qp=~Pgja)k^UCal8wf^G zR77p3JMkN!R8PRGUa5_th(CN3_Xu$iFQVDrd3Xlg?dfbHFC;}HL)bhExH{_CJB041 zj#hsfZ0HpTJE(2{Afg5n@*it=YR+IbQXcQfrs(@eBAYCh+L@31&eOHX(AI>bn6>Rb zT4p8S{VfN0s)GW@O7;!NPU{n31jzLZpO7)3x^DPM8gCp2)x3OFqZ?VGY+e!NnT?c& z>&Va1tLdX@TfH6ZUgRpLR2)jAFA}K<^{(hhD-XX{T~r1YrjB;tSbaDDga5A(p6(>N z)$BKwBC9tamQZiV)QWz*xSA5#n?CwuF)A7Iy3gVGNq9ui*Zfw7DH6vs^59yOPq%`G zez8=S%hjxW%c7I&M2+o+W;UQrXpa=uYlk97zCEQ#xbJRVk^+nbRg1vMxa8bU84F5&cj^yqB>(!@|%Nr7kC)g*jFzznT z>>W$?H>^-x!}pT_4VZ=Mgk{d|8Gjwb)QC0koo-FU^%B;)LyXK5S&-bZ2s^ogG@pL1 zmvdPeT#D!$*-+dsRE;_&fgMG6t%kE}ysh)=KzEMXGOvgFxoxs;1M8Vt1WOagDi5ll zOL3C+rxxtWE;od*<|anHhgDNV;q9=XV;L;=%~ig{&%{%>>me5Ep*5?yh?+Gguq#Em zyF_1G<`5Z2_|*m9i{2c%-9|Xq%NtgEy?Ca5P?f0bbX^VGW-6>ycJb~XPFKeLc1^Ah z4k(?Kn{826Nla4XCB8)Eda7L(*9hP%mYVpwFd9XC3c4ToS&zy5yN@^RANoPG`#+*P za;HTO2~24&55`}cCX@6jt2)X_?JaR8-`whNAFNp1M0s(VJ_v>Fm-Df?nxYt0R83oyS0%Hhj) z++vT3-G)xfrLnG1-8DRY5qz&+!_OPd`~J;ff}x|+sOZa{3TsMr+k&Ou_DO>6*PZI(s<@t0 zZYgwJ^>DoU!6lq;E2pTz4j^X75YvniYTSRm2HmBk34_r50h<`WmVa8urW5Qu&pg>m zcHbeLc$_8YX0dkLr~M=niY-RWpWKgHf8@umlbLC(!>Z3)EJi`SRcopPQH9HfxS(?#I%$eTz}JP z*>;9^7Aa|~-Q#bm6>VC?=+FHOP90UjV}PT$TQ)$e{^KYE(s6>x7(s7aWYy- zP0fQ}J7hqd%hP>%q|kaIOd(r#v=0KIyB&F>o8b;kld|dDr;2isi;@+M3-?(GlfsVJ zZ=LcDxlYF0ble!qRC zEFs78r?sBgdre6vTt|nXb2N!3NiVyH_nb;k!;d8htv`}JXv(bk8EM{-wYxK+>y~3rq(fxJqt)I06FSwsfE86O+5^6mXecpwLu?(y2$Jg|lJ7BIo~QYG z*N^D9kSfz1!Z_u#RMJz6w2It0HaKLv=Nma8x)KXeOu5pd@1USu4UxErkk-PIM#SnC;W zo(zg524-0ym_Yamq?3KiZ{zX7iEn^J8Vy9W(x8W=X#cwt@m8rFM%DZXO^&Lw7t{UnFJGdXg% z*xw0}+M={HPE^VoLY^r{B3KXK!9j`I3DfzNK6!d00!+*pM`g{ZybP}u`Gh&6`OCu1 zcm=Q_3f7!^2EX+&C%u{0Ql9}N2aAUvP9EMt@uSnCTwK*+`Qkm(Ix@@%&Rq)wZE$72 zw7b3VERxQo8qlAY2B=o*qzTKzGuNKKn#Hd8lTm(g9NlXC3RVD1J z4B=QM1(QNA<^T4Eg`=@8ktuQsm?vkWI*RWNGL0HUONapqU5?;9TlclfF_smT1fr|a zTwKnelH*tVuAX*1OknPW4{ad}{k^9uJ(hC^^-Sm2VM>3sBZZ@jiCBZfA(;VVa3I4*n`i{?S2OKE zoU|R5&cSDQQr5Ib7dd~mSVU{;|5B-reFLj?S_K?CpF5O9$PCx5Cu#}R(EmcGh;q`d zl(RcU>*2Wr)r{*1AFh1oT$a6ZXHgVAYb0kl*40d@OO;9SWQwP+Z=T`059_IK7f?%= zT%KZ&BiE7A0g9?ZJX2C$>Ug?=7;RMGE{S5xsB-H&IC_m9n@R|^j(9saJl_`%38b}n zSvbr}pVC9Cy)jNAvS^pYtx3F4@bz;~@I{Hhxb=mW30jC@lmW>PdZKdRx=b3J0ibq~E) z;;GYiTTo2C669+sWa$8al=eC{p8=kY%QksiB@-yRO>1vk1FaVKjSvO03H8e+Muv{) z@FK*`%t+~ytNI(OxAndJNk|zeahc^^~5J@lZ3hFZ6 z9z3#ElCeXj7F^kb@L7}7HG-SoQfxj=4a_kd2C$N@3u0J3#e+55Y}vnLQFPQ(y~xr@ zFCl#HvfnvXGK$0-@GTskr%0K9mwV##hYpgA@-ye=F9d5EvpCgKs)vpcCiOiW+^QGV z0YjBH@nUVJ9lScNBs~4FoJ4c%>D>Qd%@`p`fVvWLWKkd0*+dmb+uYWln&*9{`3S_j zQ9H)m!^yYYoistz57Q<6ylFZ||3r_Gvky-2n256-7aqjjDi|KrXS5!=Z#KZPgb`gumTmHlE{96L zIT#hVS9j|v{>j|(=41kgp^x_DPBG#c-~u_agMDlQ`?cX_7_`<*mp=`;fJ;W~{>_h; z4TC=DcwPS-*u@6Hh!L6@6dz<@{w&ktk4fE$ok2&*x-(;yL+S;B{DKVL^QE&lnDF!) z6!=AXQ_4W9T_O6jB`UR#GFxw77~UHUVqz6bt`OQfW3DWZorzsinQH!^fD2@S;T)V^_HeL zS;x!-p78V)1KJm;A4VbL;L52Bp!RNd=)J_b{eQc0b4~o@WTY$w7e#SJe)dHFhhTpk^y8I%HXv!1;vtB>XMo=%l~`Beu%qQfL&4MTK^` zpV7OWAmmCcvx%8OZ(+96TH72u+%2Em@?*kU=hBUAb1!=7&NL_rO=NXISrvbGintEf zoM1+mx%8NimMH1Jon)6x+l)he9Th7OKNJOSfgiws&lC1Pv(hsH$UUcIgqHm0;6gJj z2ZQ0JcPOI$YT(~aq0vpEOUNmaJZDg}^yI?%IdZ=N;XQALkRh@A$O)%azI>VCeuwMI zS@N;r%pH1>zuELQM-*rzC6|Pm_{?IkQ{VVfI#~Rgsl7bF`){ABQ)tv12RdJ4(LVG& z)%L%I^CE^3Jiv9y4muI`Xh%*33_L$*<#y)OCnP1Yf&IR~k{Xni;b`! z92OI{>&W#Z$g}4N6#K;h%gRJK4@#QI0BgLN_AI$V$`_@#EyBYRME65jHX>Ld;o;HR zUXJ%qZCcFH=c48AKt!E6MXTWKpLF`48DcTDBtw5Am!F^oL)%P9)HF!JL$|X)_lnh~ z-fa&vcXi*|oQ=xIKa{srULn^ta_0m94Hl!~u;RYIv>)G34kt^kq_;9{KSGi(Ag4z< zUmk;*oSJ0dqZ0yDo<&#_OA8OF@0hpdXOAK?X2M{2B}rs&SCa5)f-IUv!Meydq=ToQ zM$%drv=^=KBzuw+Dod;#o0I>-k;J%_vRDg~)qi`&SPDRRiyghEp8-*SXq+05&mh>0 zRLBN>>7QmlA#K**+cqYah%y**PrU zh#)+=Sa#2mPOxJ|DGS5OdXM~*f^(88!4v<>L#0Ks0e&9S*Q||FeHNfngSr9ldwfxF z6JR^0#fWHLDDKRH7;2E`>;|m1Ep~JHkkM0zX5as6eiRM4;ZD^JTj!Io$o^Rb4eC2$SnLzOjs9&`(Ddd>$_XW7W(`p%tAUs=d-^(S@eFIFhjCmR^hYki8WrSlNrCihR*G~t? zhRz+_5SEei{{W8B^+?>$>rThd7QY;3&@SY7Zl2XP_#g}Z*Xxg>R zalZF>>}A8Ay@CBJ>1z)VUGKo0rx*bgFCT!ZCf4rcb#JstNIBh};*Vhj^)48R$saLF zz9c>>lJ?WWUM0Df>e@S4&-P@&5tW#n<#^8V&$VBVU%k-plS|fYq|&t&Rhmfd{z6f@ zWB&lIE9mpDU(W`{yt-sTou*bGSI!@`)}02W@s{Gw>wKwmq}q@HJF~h`!R&Y+l{ZE& zLVdjHoh`T9{8Mr*Vzxwz3>1}AoR6*!depkVfpxnJIPngAq6SQOW>R_QHRXQ|{tLwS zw^8c;An{z#jixAV^;xaLCO+xG9^aL9(*DkV3c8DXTWeP=-G0@r+|8e;GGe&u(}a4R zl;=`g8`0_FYkB z!uEI12mB+NGJ*2v?1;g;k<;7suN(2rlv-Df^$0DYOM89!jyRJFp1gLhoG&YtqH^L? z`EMhK@wS?c;kfrRu4ddyjGh4<^)!ee9g*Ee(pC8bJDy&$I|%^b}k42+`zLx4V>_4+XS70f^x6@hFWq+qD^{PAB4{{X>7Kj5SOB=9%HO<&=! z!)aSheFz9O&2l9lX?co_!xJ78Y2dN$wK=YBhRdjXdap!q%G;l4`0m43y7+J6xO8i% zw9R5o8tJTVt{^idysZoDcR)d5ka9Tb?TY->{ki-)*PpQuhV>5-cxKuguL@|oJ+wD> z8sp!`dvwv;$#Cft?b)#-0DkWSbb}{~_I(FQ@gKx*_$L>QZ2Up-TT}7BiFGYJPLk*v zlD4nmiHb`k7V7F$$_n|V_nd*hf~$e~SNl)=Zup)*46jrRWl z3~IZh-ty`T8V_xPymHaDLciYQ0FH6SKbfz4{gf2HhAxgW-`a$A?X^XE(vxsTQEEnR zi*CbD)nno>CsLj=2N=V*TEg)~qD=~tps+pr_3K?ujkkY^1e*!|)wvkYVIx)tiU^k8 zHVcdlZT0P3a=%llTE{)5D(YG;vgc1nNFF)Car2KWleCU-Mr!{6jC8hr7AfuaLln|R z`&LG9LC;We{OfDMe-17@eENr#cCcEhirq^PJjQI}KG>|g%^Ov3h1PfOW7}_c_9lQg zG0KI8KMkZ)hhsS-vhd6vLz6xwpU5hXu??gVpIq05t8d9Wer6{hi8bz8XN0a|k}2-5 zbotD17ciyRE&)^3an`&lAR5^?#_pqU9c!Zv9m1x^r2fuVx&_CICb98N<<*~t@-Ho(Almm2XnAn_KXPpRMV7G*@bXHMBlhMl6F0a1;@e zqt>ix-XMnW!a8firdsK?@vof^l{B+5!dR6@cl*Z~Ja9W=vb7Cv-@|%;h(EV&PPc#K zNM(-^#*Gz(LC2DWoB^C?>Z8)Wevh@4R^=@hKv8d1vB&t!#};~Ss+JmTOX52ZJjFJn zI-)}=@sNV$RD>V;PEhboRKF zI6GVWE5xtHIuDU}_OA&2qYA;}?Is-IW6^DqLHck%O8QsA^LZW}yt@jiExnD*Xh&c? zHR4Hnf7?~Bn!jk4Sw1do$^QUH)2&rUren|e*7#e>cW09=HaX7EuMe~ z-oDxK2C|wh{e<@~4YjqFzu=^jXU53IzlWyIbM>!_d=X@4@qLaPf9|druXBhbKj-UT zO!)7|5PVSh(+7jJi};sJ@eS>g$2fqrX6oBX+Ib@z2bCZmx#qX7lVYaqQ1~U|2|PjZ zPT#}dX@*;U7Q<1R$i|o=39k%{#Ga*^U&%##o}Fd(du8+oj(UpnPucw?d#{B8W{z9Y z@c#b*?NhEZF&fN>82a!y&q0COz0<>*4bO;Y`#qhzOEMvmlw;%rsXpKRYVdLD(mux} z_o(Vw@K1y5!n?<{oY4h^#_XZ`=1Eu9v|?QhoI@#ZT|oX4zo9yu&bEb;w+wI zAcM5>I6Z-{r~d%piC+mM_(l6sYr1njn31Q1t{OOPN!{~bTNY=@$t12%yLy_8vZIzJ zKBLZN-X3vXkJLISqQ2<-@F=2@vyMMn0ABQsobf^9o@pB=7$2QXwgCbXDKmpg$28n? zn&zF8_e(;PnlndHOlB&X>(5%xRxwdF{b?opzl}6WgULTZP+Z+-fKR<^7(3jx7TB{# zxef+<)Ch6>^G5Q+o`RcO!`;-~Ei4ooDH*n{GK9wBb4totPCAZhH*M^Fz@Xf18BoA< zIjLjRibmW3>z=<_a-ff|Omfu89|@1*Jhe6j9FJ_{)YdLa`V%J}WP$|+g7&7oe?H!R z_X9LVH_AR-AMaCL!ESly@~8>uQhhnctsZd)yrraTP;Na); zqp8i-z=zMzqVRpqIlfMxj!ib$uKl41!0YQxxs}n%pwAqS%CD2JLtaaJ*U)X$&mM4B z7~qPENdzmke)$`lBd=;=_50Zak`EiHvNBHP&h7n&U@J~u!1QXxy&6US(FgAEPhZBr zoga(e4!kR}`$2fpDN91Oz9PP0?SfbRCC`4mV!S-+PBD#-Ya~&XSsr00?CIgDnXV3=e#${o zC%c`?df>B~%-8-A_$J!aL8o|QPLk^*e2*6K*W8}f==T~@=?txWUUrqFl~|suk~((i zE1c8rjo*poL5P!XKnguN*UUQd*!q8E8)}MDd^hloww{Cj5&afTrx-S?z{gINmj;cZ z=+^>kZ39Sy$(1FEmIYmeFu@oe_0nqAQoF<@!9u(cG40y0JVz5n92Uv6?jXB+1_%09 z)N0AQ9Qc|sQMvSnxfbQg*@YmSS!+zth-m z*@%bCK7iLH<7v!39I*`FJi|F7JwXt(wDj#E zuC5jcA$ysee6gbLW9xu9tMGU;Qim)w>;6VZ`aAUezgp+7n&GKFT9ZloqszqRxI8?R zYgUY3oRT@1;PTm#zP&1F?YzUcJmWd7yPpPXv&*~tH&N-x`!j!qL8|DtnkSa`aLouj zD3T){;5y?V6O50}j6P|GsMR`k6Me3(O$_S+UZST?v}E5!B-{KeGb)4}9!UQH_1AloN1~$JWJc)E8P7Y&l3Z8O_Hccc zJ-yiE*Uo>n+~OCUw-Lf3Mevm~+btlFN3EcS&A_DULMcl*Tu0GwCPpBc2L9xv1cP5bD9W&rSv zIKcd?>Cf!7@F#$-nmN4NxhGL@s=@bG=(6ZktTE1LB@TE#eSlApW=J?kM=PAnfw`H zE47Y+@b=2;XCn(0%)yHdr-mUFFnRml=D9e7H9=3Gwis#vc^E zCExsb@o$f{O*%VAmN-MQ%_gy*m6f4#cETG96euKtjAJ9_Z}=!g*Pa0Vw!SFKdu0?_ zrOn(rtTwJpX?JNa%#W}fharyc{EqprNdEwWRrqWBSN7@9(#k2C*F@4UZ|@!4lPPG~ z5VGeP-oqSs&t7ZkulOpL>~g=fZ^DQ1_Joixk30uuB<43ve{9+&T-)cnukd#{#(A%o z!a{lH4>>!zU++Bq^=Ib)0E7NKYJU$dwci=|a`9~K^bHdF)n$2eZ!EK0I>@rb=beH@ zP)A4KitRt(yPh7?JZ<4mhF=k_be(2z3Tj>lT`Bbm>`Y46QP|BSE9O2A-F43!Nsp<; zcs{SA$>E=c{xR+sv`*U+n`k8tGTV#P?t00#hMV!HSrFRKc5Fx4)SYRT+N z*!%*vlQxktJD&jL9st0==f7I^583BX@j7^7@B0r<)8w~ZKr>ujxeK`CBZKYQyoLk* zhX-DR8%Og$m3;~OA(J1&$;Ll-CZyzU=t;-xU##7{9b9s-QeWl5N|*Mo#o5N8@e&w#s{~pVa}s=%w5T}{{S1?>bye(88-QD`*{tt z{NF=fQ5$TG5ssM29c$L^EK=6j?GZ?^!znPvaI5Dq3~-&wjCHRhjNy>6?zu+m?klea zQO+%p-*eD@4QWSG&@W6;DA_Z+%`1G0pbYfqy=rJ;FCBQ(!=4+x`zD`fsNSS=+^l)s z5p1(INaP@Gyw@|~LwkSl;#+G0JaT=s$vFTBKwH+-r z&k(JgG5B{*ahp>+#tFHQbY+bjp~&1%918PsJkG73Q%E&o_I@&##aA}krRRtIL#22s z31tc(wgy3JgKTPr#t*+8GtOz6PLXwKt|jynT$^@^G)pm!*OSY-L^5L=QBZd3SL<9a zj{Yn9R^`}`l)HQu4%awe%uOSXxoa4+H_9p<>r5H4k&Xx8zJ|&(102n-3_cQ&ibGR>T zKW^Ms1>X7Y;(-8>T*Z5Dv$))K~+o>3Z zu=pg0Zsxh)3FucEmE_vw)-t8G{{XYvG@zis`@{k>pL+B!4cdL0(G-qGLm#g-wtfrP8-Lo@Sa5u`fi%KNAN0oU$LgT|6?4NHDf>c5 z$^Zw0QG7L#t-oon7R)jGwA0Ba{hJ+0hyH{5R(N;sNgCsqEj4qfwYwJn5x-`^0>@GW z`!9uV(0}Oj`Pa_Bv>`G4X4E4*l(w~x>4-=8*T49JQ)A#gd4?DMC0eT?z~G%0IKTF4 z`SauCq5d`Nx%=6S4ts1+Kc#lzQ&K!SmPo?z*0MA&5!zfX?5083xoxCikUoaKvhWXt z55w;jX_{N!8}_mIk$ln5BqApYJT>QR0CWmpr%aRUUn%@IjDNyoF=fbIF6eSN=sSK@ z_D#l?(D>^9;y}tRylb!CLPtZhLbkU*sejJ2oA<6nJCr|X-wsPZj6NG!#};p8_;iSo zg~=}^^wT%fBbxgY;U9tI(0mVl7MBRT)#bN#xSmWZG=aWY)c!+?`M35)g>`S*Bf_w= zov{2ju_8r3G-2g+>-4X-(o0VT_(jPp6j4M~RYqOd^V^e?UniT_ny<=_(D-_kUK;Y) z`M>tC@n)g$^Y(1J*ED!tHSZQ%#iw3f?#%WONfLt`Bx7mdC}H28wdz0cZC`?~eiVPf zLAA{bQP4G;EB!;^jpnI7qp8OrYrN`uYNT+M;G%{(Jz0-VD}?d zD`|G$Vzx6~ys(YVL!aH-h{CC9Sz82+qdbqWzu=Dl00nRI4i1I3wxlO)Zi>wCrG0D+-co#URHgJbF=0bG1q4nDZ5J@6X{_^50ZiM7YgID*}vv zyF*RL+IsLR8Qi2qde)Jq?_sFBkz7h91cAUc9xy3Pg^xc|NLw|!<+0F}gbk0zgPd_n z$$_5TsVNKq$)o^)d-_q>@_PDIa<`Qq1oGLba!C(maV9Sl0(z0b^d6MhouqBOM|{)9 z4XQJ9xa9Lu$+bE_(xJ1P41c_RhqwO#UbKvEYw>DKSg6~_ z2d*h&8{40)I5(LmA$n(}8_@^^s{5KBHpqsq?1Y_yWsm48oRYMIIRmyTTgNhIXJql6)=90AOU2R1Q6oql8ZD}<F5F~-s0kk^AoTiG(@irfvAk@1mbbcp4{7k{o7IQ|>4hKR zS^oeMAb>5EupC>b%mZft<8SGT*09o~@ehT3Ho@Utki|dE&i??H2l{?>%=r6szAU^< z;ew#xaey&h)vmD9eoyi~KQ_||S+C`nK*3~H^A2(pmG-V|E?=>OE1nLNr-y@XG2SZsBZv6s`)TNZ0ri`GGsQFbs`^bz%-zKeviXrT zVX?_)9Ok=iD_Mucng*MxTZz`+RI`TmAt2h}WKh5y9A}#N@BRvzZ1&ze@Ka-Ho5S<3 zKPet;nsnll@<(Io4cswx{SycD`jw?J0>z@ zU+(%+%MOulpYi*tZ6EaFoc{piT1{1UX?jX8-CIWg0MYYRIACeP`NUB|rb(oJ7BmAS zvFtD_p9u^_dMd1|O78k49{&L0IP#1pFBOE2DtM{I%K9Y>Zq>DcWm8KIjT`EhCpq%I z&XD<_@s0c69dq|iagH;QD7n&M;OY8;vFHu*bM8NN{(Wobe}liZ1N?pXzws-og_LU_jyxRW&(s@zW{h0~1EJbRQf8ufcAB>@CB-g~>;Ac;EBW7MzfH)*^n)&|#_O)a`6EtO9=3P2D3lC7v5B`N; zKqY`LxJ?F^q6s)_2bo_Q15b200`dP!zH>%UAL=k zJBB^$tiG|jnq)DwKfM@5kc?zkAE9Y}T3ff;B#u42oaB8gsJ4a)u98sF&LZi!@t>u6 z6?Z-Q3h+jrhjS)}YREjZGkmf-xfuL?DY}=4fLIYrT--)*0R%>*_bhtT8qQ0|aL$u=ox$2E9dW%ZX39K*~Wl4VFBw=>_GhQL_JrZAtI=_|kMAH?=Pp`dn zP+HjKtE$xVuZwH_ouz%Ll>Y#u#3ecYCD;%0=kTx7&mQ~;*1iM$Gx$BH>lZfH!tcOZ zWY&_v?BSwl!vNVu!Q4o|CxE9s*XEbSxZ6_EB1q58Es#|Qra&NM^&D~auhAb5{6_d6 z;}6-J;l8EdJI@=~Yr0;Mr&;PcHlukarDYokHcYTc(nw(2BC#oi7;J>+kC)@BO0HQb zH)qiKjK@TMS^oe8Z_#frJWcUJ_f3oJ+KNSKVh__GR#o?MM4L_{wc>O0|o`dNzr7d!*}eMi%DIRE){X3~mfp7&)(*zu@8hMjPMw zCg+832bD9$eQqX>A_+#5L%SS+a=6{QpYGNN#Gm*tg@=y*AM08!i}0flt60sawZ@~Y zuaKrhDZlq&n6J&j1GgE^A0__)!A3veq}t!dZ-mi!IOx6<(X@!{q`17e(=4Ty@^?)d z_88rsby8PzfOzP0kU+1hp^wA4a>Z)MoTBzWBed_D#ueZVp-w#qAJV>v{f;7I;DDSE z*UsybPR0aa`gO&8<)@5V#s@h8j^GAzazC%>Uf26Pd^a9A@M8J8uCrqiyp*JNw*@20 zwnqc$Uad>Gqc}9q!${I(x$ty18)OM1%%JVS3?*!j*0_%x1&Kno;Q7G7$o#8gPxyUi zJ|9}#E&jW^=Lco`h26b|>}MT?aa#WXgc`!rc+@YXMv&x7DRYea_O2--tD)C;7f`p- zw5?J{v=f&=k;q>H*@Bb3qv}Uqgmukz9}+w_;$McJ0(7qy>Uu7n_8nVL`!%)fq`l0M z&u-|uBM-GusoV=3<#Ub=aUKx<&R+sw1F35;YRhzyh?2?fB9b#6L!2p40R(;Qox`<3 z@h9Rv*NVOz_*=oHQtrz`y?fT3mUuqUaC4PofI{#H>D$)4ntalz)7a*2^0G8OBWZEz z+Gm6Q%7_-?&RdBs<$dz$XZM3+=lI(Jx(p0;$gd!jkf7rSsLx)*kHWnXb$fkMZ9?@S zhSDj+#c5+~pSrV*4^zn}Jd%0lyx_T9XD!0@8TGGg0}e`d6t!sF(k9ezd^Y+;nzxsz zT&OJa0EC`7{7A0PQSe>Pwc;7hr!r|?6Y%B#0QP%d#60_h62B<~WPr|iz$~NG(|=~| zH^U9${{Rc$_}^7qUk9eF-)@4$q?fXY*UVKboTN^0ttLKE*w?Rq(OPeXm-b%KyhCHJ zPaeCe>*fnBF4ACQ0c9c9RmmYzG1QD_p{^`NR~p=je9K4A{x`DH=J7kp;iIj#jF74? z_Oww)a$mbRiQQBalZ>8(S3~1{KJQledGN;3wi3WEs{A0iBJ5qo=+UW?#wD^|3I=H2(k=b>C< z{{XLCLU<0_S5kL9z0PtZi?8Eeui&2!!=~6;THAuL#)FgmWb#R`3l&jX$J6E1>nCY! zbQ&I(O!2Foqj25cxz7l%m-|Qf^ZT(2QTPofRU`HOb?6=&(+W+zw(pya`u_l*=U#6H zv1{S4j{g7>ukN48wYI#nf=0n@yY$UYF~)zrA`ix%3Rjyo%_C|`=9i7NOM5>H+(mlq zu<9{fY3j+02_>}Lhkw>W;NB;m?y)ylej(k6<>KE|^iESlF zQC-oXxwmU5v$s9gQOWkNE%?PNUVLitM2)oLP-OW}U;zGLSG7TV$IVsPx9~>C`(NU4 zx8S4LOr#%nL0?+<&s0(43!@vH#jbd{*+Bl~q)YxEm3*Q5KdZOI%a={Lg2Z4R`>r4# z*1nAKR;DiXElT8kkLGyV^?*Dv3knbY2v$|^&0wT@7wm5s(*8ciBPG0#hZ-C)wlY|> zuGs#6IE3sfY@PEKp$7a9p2k7|}f7wyRd?wNEw9Qjeva`1T0EuO! z5!(x?A9K9hoXEbU<6-ybHTDPm6BkE@+xEA;nWQato({T?Vdo0hwnX#w0=`r5mx}EE zAKKhnUU`B^oZS}+K6AP72m4qf@D=nA{1NlU7d|fiv3yA^q1G?>MJ^gOb>&CfEb&_9 zP;VPfeq+xk6@jZCBkvD}U$q~AzA(B&p!lOqxVw~mwz5Bwb)QcvK&_vkHQ(f#{0H!# z{1ijM9wzdj@jr+4e-Fyo4RHo9Aw%oAKxG5{+WJS}hwY_(Z46)Vuf8rV$|~VB>x=Pl zh%!D=1}{%nuSB9nRCK9%ubg1_LWUMcYIBk^y9Z8byy z0dc0vZKYwxSjPEN_z)}Fz8n7E{{Rd=Aryu6rttOa5Wi>EBl|JQ^mCZc{q;k9!*iOir|M8^R!ehbaRt@1!!gSQf<$rb!-G%881aBT{{UXK9FF>wi9F4((W(|` z~gkC)v*8-k^4-DZtMkG4I7+H!3YIq;j{F%8}O~0a0$t5_8X`O*GzH0D2$N zp^%{at)6;gr7vo>X3-7XdU278Ya9w`eA1}TT3*unMAFs}%5jYT6a&AEao0Jd@_u~h z@Wm>Ovku)VIcA=t)1XL&N8cPCGHHsLJsMA9c&0tUW3=pDybSY6zjyq!{0DkEhRae$ zW;Ww*oRz za&R{iwNa98%)2%=LSZ}-7e6<8is1kNsobZ%Iy<5RGdDQE81GG2GJ}!4@qwIG^EltJ zq++dRGA<5E?f`N#igF=faJ_mTYHV_sfMm??V3ogxS0~KE0*G;sk+iO@y-urt;2k%B+5D;O7VM zuQL6uziVheXP*wd9vnJ1ihOZttJ!JNcxmKyx|m$;ASh5ulB@`38*mRabET<_`BCeU z$-3e)LHonoiu`T=0D_9xNB;l~{qZ^ml)%#JvC5>6m5Wd53PLR^^vv8FXGm=xNB!YX0G^YvG=~7-{Dn;gBGi+T>Yoay|TB3glNQ_ z&PQL%HPq^s)|WQ$FWFk{x!73>l^yGv@!hT0h3(DU2`o@a8DK{}k5GN_P9F2n(oQNT zyLeLIP4L&klA|-J^v z1}>$-Xja1^v&c=SuhPDTb>r-lNgXt>@^xBl@c#hVS-vAp@poRE#^3OnHH_LmA(K~@ zM~KQMP8WGUHs8=1`pVz^E5f%suzt&T5sUqwF&sbV<){7g?sJ@OWte>OJfBnjD?-J5 z&kA1^J#Ov)03VWS`MlDkX6Z?3@5IMBgN$(N{+`426J3a{hLXFvHl%gO{I{F@{uSb1 zwx5K)ApA%0PKn{K0>wPGy6wctth`bzJ0e63f#m9_NIAwc#(3hr#_G|p^c^{a4>1~R zWy5V77i@zkvCOTW*aE!&0L5Rl@54`q5xkn$jkF;dV;qp$%l4;`bU!S6hfh=2+|iRs zs!E$$#kBtNIDPV9ey=Uyi8(Z^N2|*LsELh_3C|OSQMPc12LC<(^%ufHsgwIqhAK z?3v-;j6NX!oxCIe00=*dyb0iq8hd96ELuXze|{EMQf{4w81aGaUq#Q$%`WGCwdGr% zTEl*IT`Jz~t>?9p>Lr>;ZU^t8W*I2ngc17Ec#=XjmFjld#MlF`?xU^);HI1LTf(>3 z_8$?o-wSx3OP1YU!urA=BJn2#2988k2v=@MJ$_$vUWcqFHg10V+JMK`YqAggbrgOd zTKSw>wl;_Tz4;tCyZ-=bVDJ6^0R1Hyk;yI8tI97q+n#Y=75jQdo*(#&;8r8|vgvcP zXB`B?{CZcv!x)BDkf?5X`d5H|!AhoP@kfB}BVF5ctBEkgjlfGDMnL1OdTQ@ReFVFl z--mo@Yoz&8NjLf=i@B0W&&qflHfyudyj5V23i+ZS=YHqpH}uVT?}aV?B;Dyyd0rmy zaz!d-7JJuYk5h#|@hc@uO4e9TAS6jzl~4(++@(!^R8vFWmS?kJaS zlt<4b{_%!?5soW2{t)}keh6llQ@k@gt+prJM{}HK+OhQi00(?li%5z80JJ;p0jaB`X; z3NzoA=bqK%{w3Xgx9A&--0#;U9=H zA=BZ<$`ap$>EDX|Q2m`TW()(~p$n9S?{{X>9z7^ekA@P5O z4fE`SQ_$6mwh5BtIWnS-pDyJC>z`VaT>YjsU)e|Ya{ZnBXW`vC@5Gv}oq2s}s_1iv zh68Ur43TBWQbR{4u^8a)%$UaKYpcE{ibIj@jE;GtgxJSFg(_F~ko{3$l;C)BO0yc$o%oJU))5aqble5WAMd&Df=B;eW&65qNxd+yP$pkb|2EdYPY%) z+d(PPzro8*c4sJBv!+z1vCy!ohZ%+8D;lc36#?7?>6!1i# zsf~{vGn`|!a$4_=H0!NA`B%|MLvJH#%LVqual1EWbtbuWEooi_e;Mm{`dy~4_B*R~ zS7;}cMzbgZiO&VPf!8&StOmUC#m1LturHj`S=mIbwI!ZFV?bNF?qwPG6lh)rhsAz7 z)E388x3-ehD<#B^jp(eS1E1l1Z{OkGIGzFd?ANASXucu6(rzyAw85y6W(zbd#g(Eh)8!zHj=Wcah1wM0 zZP*46RnB^Q*U>*1H9ryjL(sfWuj+aYj)0mZD{m#e*1VEj$qVJ9Ht{xOZMi#%8*)Gd z9M{iI%6$&i;3?$s$6s?@*e3Z}V{G)VgfAAC@N>l%<4(D`zkB;zVRB*p#k?ev{{TNw zasU7xq*qI?_zy|=NATND)V2Qr6uRr4DuHh8yf>kQmOEQk`J-#8xdGU7u>c?+T-8qw z`1;OI**i+`e(ksN>r!9blK%j^Sk+iA?^C&yf$dy`wiHs`oV3G8# z{{Rr`y0?cSxYHz-?CDqWrOZ&s%q`{Kx!$8YbIAuCNanb{tfMY%*`kWwoHe^x-$H)N zV>pi=A!D2$QQEis6`@<75!pvA+%IfBrF^I2>HgK@y?ODKYx#&j@FHv4pqhH0FHz`pU$fjz{{RV2s@ycI9hw3< z{P%H=pMbAu@xH7e)Ae!=(kwh~7+`*4zOwOe zj;y>HWUa%#-h|_Kve(IcUoLaccb0KAsIegT6jQxd2E{Nej$8V&^#w+ zYk%TD5^0)T7mwxHo2#$&zwRi@H%_=8TKMdIV(~9i^jxN=E)g9jz2aN16zT@*U=R>W z!n}yr{j7Ue)Zg$$eLGTr+lyAbO<6y&SHQQhKAGg;{iFLsPAfQnO`*m=+7-|1`Q6+jh8-U$S581yyQmEB@smvfIgtrvxwd)WN7wAaPh zV6PBhdhwH1W1kL>A!K#sUOaBkvFa<4YbLYayE|l&lTD8H;?gbX$7pYtdEg5BZgkJi zy_~jZxO_bQs(gL$%x%(qU4Cs>mbAVY}%ReI0HP_O}+qV!YSG+Usy(ETv@E&FzC{{RdB0N|uw5Ohs7Wxk8U-aOL+WiDZp z?9$3TeAv~K{{SJMudm?j7xvnWeC=?oFg*@Bir4U`j`d#zUHRG;uc_#B7W>wBFA|gf z>W~jX)KKFqS|iu7{T28h{tA8IO?by|;w?8@@TJ2Hypu!rn^pQr9$Oz&73}^A__yIt zijgB|9wX89f0K8YZFG$?{{Xz1+&>U2`OolI{1h+ZABG|ct2T||%T@VZFKzt0nTP#! zMae%ds%_BPHMP|G_>5DOmd8B*06O#Ug^4}+uG{kJ|M__M|OmHz;T^~=2r!d8<10HeVL zv`H*GA8FkqaOWT$zJ&T$yZ+2ywzZ#$ehFXA;@y77TJcYY(rM&dNQJ!Gowz@`Wkd4r zIofvmXV#Bsp5|WDT~DFnx6VqTZ%{cS{QYSX2@CBRZp4Kgdmr=OzdnCxzuQa1Ullbw zI1^sfd!(Z<8QXyu$IHlyu~SgZWZqBMhr_>FR2wHuOai<0{=c zQlw>6Gbug!9DQk62AFUF9PQ6qmdYbF&cu96AliqhPkLO9ZbYrhe6jxk1t0y2{6+EO zU+{;F`~`Cbr-*b}yq^%-$m|zc(<2QqiBMsSGoW9TgNJWQ_>1TiXPZv9jl-)*kw&VF%%gB#dUN&9!Y}wJp1a^*jGB*x{CA^k zvv@u@?ygf>y|!p?;#HlJIed}fcsKUP_!4z54z-TDZuKi| zcGmv@M7$Bnsn2dgDm0+{*g>}q&?Y$~)XP+G$zAAt)%zp<(ccU{Aw}aq6)vMTm9IpP z;%^>kcA8w9?yvq>l-o*Kl`Y;;Ht3jdCO~%uuY&&oYHcpV_JR1(bK-A@I<4o!tpe`K zD4#)!-Z_L52_-T&o1R#bG63TP@%sM&_MG?=@k_%$9;3Ff)VwF*d$})e<*=7q((Pg% zG*yfUZG6aqFC@5=AmD9WoF2a$_+rj|Z%{XPnq{_vZC#E2p9=i867D;ZMoA01q0bIW+k8OlXZJ;tE&7-*KQ&NKa=)gtDm4X%wGP=+D*QtaKG@A_?+!x z#3cS!WIFctHRH<@QpeWE z4Nr5Em-c7S=ZrME*NbM|g;1KS{HNcH^s4utvloND$s`kB_@ZSa1}Sw{d>*G6&o$kx zjFw^U0ZCF^5S_vGsvaPW+eWkh0B5=GWLI2|!!;D~5=@HJB9ZCe0lYtD;opLu8`AXn z=SvMb+}hiu@4eAcv4Yt4`qO?W$D6AO4+2Yb$EFQ0?Ee5Ko$$udF}Lj3Q>?ikcjn`t zu4%sz@4ne%VS^dr{^{odh#!S_LU(@g<4Gj!aQ^@u{AiyGJO`|Jo=Zm5qtLXwTbH$s z_B5l*g*x{8!oG5!{tAhvMvLV9KGdI&`D>~obB|;`we-)$uLx=<8qD%i%?kBzPxIm;SoRB%Z(RR?Uy> zukfnQA2og-cv+h~ES7=$b>}ti`+7W_mOT%vR_5lp^F^=x6$8cE--z_-X!IUe66Jk=-zNZC30&fUha%em64{yBbzxfLk?0JLBJe$VtWel^-QvG6;^ z*79vg{{XS4R74n9!lvvhjs`K>xj)!XOnX1r@8RpY^4@(qNx|eZ2e_Ay#B=!9s@&pBjlWjeD*ePuPFIs3k170LX~MeyI@qx?MhQ{k-w(m8IeG&>0Fl4L{k6#yUVYt@xh z;NZ`9*`+(0uxNtvHI;UZ4n}`LQ{SqKTNTIMwj<^|{{XI*ulj?JOm~zyAQFqb|}Wx_@A&`B#Gf z0Kr8z-1w*A&{35808v4%z6I<~qq>P;7_WY{k}GyDySd*}J}^a&<&8Zyacr(30~`DqAHq z{wTb@F2Ve?d=5=nn&Q^!WP*8iO59*6=kcs>va{cL5u+5`6tQlD*Zlg|H#?TB-rbFr zzq{4-1&ol;p%_xUbm};-1NgL?{vvBCh!e`XD}XVcd9Qr6)WjOeh(#}!7iuw6!Nqtl z#bLAJUbyPa2)ulC>JPnoIeqz>XP23O4Ks%Ey#D}e&@Kr%5k$$y7$9T{{f_;d?zEf# z0N97&#qPCn1*U~*;X4gN_Ic$I#{xo&A!1J*P;rBvfE?n#8hmv+TiZ>u_iJ+-5tGLM z08Dp1dROR&?JwdvygB~>1hM#K;Rx=M%T?F(T{UiW=3p)*u#P?K#ShJ}$>sBo;CHW6 z5ndGOImr18eNIJkZ`)7e=AZjwd?=qy@b84RX>EKkkKzQ^E)hiH1yZ|`KfIAUV}~H| z_eFf|;49w~e$jLMJos}2e{33Si!Hx#TuC!Lk`_Jut;A z8y!7mx6{|d+JBvEa(-oLmf}sY<7mR+v-5r<<`i)iDyp~N?nv=mIhvC$e$ihE{3j3W zukjB`k5SX^{Cn_~<+!)hq_>t!%bPjM9X>N2(egA5@sofH1I}y9zie-VvHr^6vaI*; zTIev}_>`SCYa4NMYPx#Mr|%=lEI}&C!Dc_}58=>$(x0|xg1kX_ugR%+W5)gh)O=H- zK9#B1c#BM1XVmSZU~OcD#!^orKvh6&kt1+$2*3XT1t0i%tjYTqczXW;#M+jltK6!! zz`mL>3fv#C6B%UcxCL?8fnJ1b*ZR~cMjq|=G;2nY`CnibD3L(hfB_&6pRYCaPwbPY zTS?#>JC=eso#&ZZ*?wjLo4tJNZcJ9FM&I>ex%v*Dm+N0g{>rkve+%r}0}+dv2|l5p z{{XFDpjDT-e&kYi(U!FBIyLxRqz@({8p&OA{Vp4;U=Dv)>?#w&$8?sYp@i{ax>r^KmmsQ7jn zE+k-(&t$g~y8^3%q~p}_TQ?sH?tkGw&>)ZPmp3z9!jdXC*{#LI(kr4UEW;jI83Pz5 zt9V!8UZvxY3t5qC9gU5<%Xu!Xbej^=IL7%Oar?t+^yk{Wo59{6hrrs$!s&In$A;lL zmBzDrZlY&4Qn+aq1Y_nUM#LV6AlHG4#`3>&y^d`zct^xLJ$u8xC-EkW;t4HhzR}~F z(NoMxW{MK~)D}{B8;QX=`Mm{sivIwqJParv-GKhJ>E9H*PvHGe#(pW6!|?cq{^s$X zN%fsxRa=Yc8G&+PLPSfP1q7Z4YV#X_s}mLe=LgjEucg4ZDk+hz&eOnpWS$|?)-5&x zc`R`jmBWRO5&h{KA1*<{HMv$4rFk7Z+&Sb+*wZ!>AF>CkJFp)v|c$>t2cCe+UVF z0sIcqtn~YP3Ge(Qx{c(Kz#%unYl(KvGk`ahSlgB-7#QnbVex;*Ryw8ShMlIXPo(K& z#TfvEl2783p~=8J*Nr;0Cx~?8w^LZoOh@>gsA}E{*Qd9R%v$R=QCJ|J%rvlwmwRj{ zgau?w;8jcQVrl#zqG`9yB)V>k14|T2+kDbk;4^xB*!*!@8gGI%j~f2Y^Xm4V9lnoA zwbt6+%TZVlk*M8yQJG>_BbHRz$M{q-`1eon=Y%v@(KKtz-xZ*C)O8#Ca$>QVNJ7N~ zeZr!nZaMjw`V4Cp)SN+In8(ueD~o>-xYTU*D?7W0#G(nU%uyn+`F7`$JNN0yt<5t_ zZChQvxR4(wNfJd7&NniH^B-Du*M@vs;+t!YN=UUWV_VgmW6wOLq)<=Z^zXHXkwoJR6**$V_;|-( zQ(L-Uiu9{1Hdyr=Yl$|4vf0*Vla4R}9sTQp_`UIa#-9+p8R5?eYxmY8!oD52x@|*R z8C(0SHIL8xD@oUD1Z5{V=rdhcg?=At-xahwDcebhMew*)l?}b8*{<~>I8eC5M;8Yf zJCAM+eCyG!5sZ- z&NNGJ5NRJ4d~0*7Xi!*7KA=|m1AdPsq`)XDxXQ2_y65n(uzzH)ffj!abn8^Nj^fhR zd&ju9f#fo{F2#@U3ElL`$gh^Xb9W+qX84;Upb!552?gwA4yWc!W}aPJnzK>Z$2*}? z6k_TXJVSmaV;XJ>Z!FXU{8-c=1m~wp`DXn2{vy>|ITBsUK_BlCUrS#~Wx2IupPKI4 zOnL>p&;H3@Gea?o_Ucc%4Duenr1!6-p}F$2JAc`_4?o5CN;eZGsiZ^)Zf;MuYt^8? zviMWsh`dMQRxquv_N9@qk|8JB66;YjWMJfGVpHrT=&3ZqMJ^M z_lh-5M)^E7;Y;>{+R8|!gx}pgvY26r1=v@1!rbTYan5U@{h53dr&)NHQj5U;7mMO= z#SKxj;(co7;jHw32|*(gV~XbGmZQEEYThLH zgKMf=d_#}oE~~8D+J9*29tyM7^!+AHRy5__S7{ZZjdRFk=j*}xoBj>L_|f2hj34k) zUmL;TuLWuz61DKpfG=&`=9Juhu+6FI(q>sO0?45iK*`({j&POpXN|w$lsYfN?-}Vn zH_^4NN5mS%qg`I>IyZtR_A$gqlUCWjKAs|;7Eq|ZXXF&R zQNIu`>*R{_pS((a&S;F3U zLN77=vnuWEIyF(b@TAUvgy+|cS9x({B$yss9essXlS6^q;m5_23x!uo9*L`!NhhLd7$5F*oeH9pV&N>$76{K;J zTb^}HxvzldBJni#@NJcTYyjJObgTBhGJ)gR`5icKN=uIiAtWP7yyt*;0_A<+8~&3Zq9ziaP^9}iw32Z&!) zw+Cv$sIS@=^FKdqaNWADMiPq`v7JQ8|UkL^3V0-J{Q$3klPVAatMGFzkVUkQJ~ zNA%4`J)SY}oyDw*etXRtUFwUMgU6EtT%K_7Vj6`v|h zSA9>_yN?2DR{Bh~tE%ddSxvZkab@G@lg~q*e@9C>mEU&s%wdzXU0 z;Hi4J3kQq5DW_`atUuE9w`+NRr4Hm{^v7yafSr${z83hYw7(j88nyhEHkUUZY?nsd zPcNLj#;vEae9}k`v9r1oF}MMqmFvH;pNYID@XPl909!3G?^L<|+H0#cyOwrHOlB;ob#OPI zIRb;69E?|od>i;>q3b$b=8XD|k!`C@DZ8|vQ?|p{ylSi$&0;r3>_VNqjARUq*CnKC zULf(0#AtL&y;8$azk7R&J9Fj95=av}6<8=JH(`;w3=VmsS}g_6?__?`Ux$hIFh46F z;>hoT`D68}`-?6z2Y#Jv!av}jKeeUr?Md*S8(nKpzyAP)pTnX{U0UnN5y!eW+JS+= z4p)*0?A7lj!sLtqMhNSQ%art~%IHxb5#yoy0u44lm=Vvg9<+^j6`h>7Gqg6=*73Bm z%PR#{RRoerAOTUueQSH+*!7EB>l>XSXvDEzMKF>nRFD*_E>z^4b)}+umgmr!A>A(4 z&+^42&9Tb;?l3ZXS2K6a9F{{Xi>o$&8X zpTd4K@x$x7d>Req#l8K`k!oZ~;+oFlFqa59Axyi$3Ro2%QS!%yK016v{{Vt={1nuG z;S|<m(lRhIGO;dpU>}uE;thP|@x%TO8Sw|ij|ju1 zYn~L)iMM~WxQ@}CFSleM#DK0sHUa0Jb6+|B(;x6tZxH^|UOCdWne@#%EBok-5-!z< zT7*7Y1GELs)BrIDjlG3(yhQEprgBQGcSqd1kL{hKd~DM^5ut1McDDMitEI)_DST_H z!RKDJ_`WQ*gvb8?9_i%AG9d@$Wsi&+@b8Xy_Fg#nm#kZOZJYZKP`n;p%zFg4q$mw0 zQiZ-=`lwE zAd(%(aoxG&Jl2KwuV(rkwXA+zWwIM1<|EfWpW|A$Fv}LTs9ZLX13j&~Qel?m-Hc4**hDHAA5;uRwx(%YWbyuu^6=ZzBjb<4yvxL2 zwI_l69q~#Z1Zp>0zx*Uq-dos51a?Hl=FU}P%V*WcHR!%BzqZjlH)DH$a|NB9je(lx z;$wt`MZ~#gBk-?({3h;eQ>!Ywb(mXYB>2 z_zE8k>6)Fstu2Dbs7ZRuA_>`5u&N53r1i~t7yK00#@Z*u&k6V|#9kQIA=WgrE&YkR zww6SX%?z!zxEzg~em?ce{@6Y_)05*@!~J7Sywmk>>`x8o@=lS@zA+?9jAM2j=Z@U^ z*Rw|k+3D5By`p{VtXMU!qc|#ma^*4yp&t74_SN8b$iSbwVk?_6Dn$5nKq~2E6EbwX^b~ z)}zS6#8pl(2=n5|{Kb6!b52xfs-MdxFUS4~=Vz4@!K_SorzpzZ?CtuLq=SEkESHS^ zu6?=x0CEh`4Xnvy9PXHcX$r>2NwS$ zK@rQjP+JNQ8;&^7dW^nK)mQiB`Wxg8{{XaKe``O`^KaUd;}3~HXTOD(zAe$LG>t<| zvA(f3Ed}^EGr^0^?c(X@_drdoCvlk0zDEX~$N-$%L z5*b)$p}?<*{{U@Y+tWw*zwplMz?w&fb*(mU?G(Dyu62MQo+B3V<@q@vmgP8PQg)Df17A0Zf9;f4_`f60$42><6aL@-0Mbt^mer%VXjNIZoN_vIULpHu zA2-C`4qJo13-tNDnji45qP08AXP5VKo$gDJ2SJ+o`}X0|zR|Dv07D|~`#tZ0Y{(<73^kTNvI1mMi@#SQx~TsotTwzVAxg((YDcI1Anvg8v1`yckfgWEf=T`w@vVMOF@o+YPoVB=jq!JhTf|qI zEv~CI=(5l)-onQ>GD@~l7Iz4Nn26LnyKPJWSn-GhsM=D)2iyFq`%ZP$%z?%?yK!M!OsWlReluz0Kq~$P2nE_zJqh| z2gGsP>QhLTcGni`_DwF|aYcoOGD8nTo=$q#%GOh@=C=A)pSt?sppY~Pl!`ORX6KIG z_~3V~%|pT$Hku{B_S8_=Su7FV!EtjoLx%$!LITWl$4)B`slk5ln#Yo-D_I`z<8Rw8 z> z9qZh_2y6Z#(f$&~x_yqHExSW=wdF6n1inTG*1RuolM3K}1{?4d^hd!~fA~`Npp%8M zoq+TtzH8a5bdofu@ajPf-Ys8-+FsZ2ZcCg!T8f$X=B)8F0=Smj0 z@)#q6SxA#-GCS?sM$iT_Ivje}SqH>@H{wsl-wHR6Z~RMlZ47gZne45^w{l12lC2qj z2rdC;#(3%Jk??l%_fnqP@(3>Du+a41u|mw5Mv>W?RtKQnf%NTHuDn@cqx@UZ?lmV# z>@76=nC{X)nVn^gl%{alIAS*pW7fWMq@f%`rx=`z;yORt55*q~Blroa>0SWRE^TL) zXx>}Pn{wAP5XW(8nC>8+*vB5#^L4;tq#ieQz|L#Zek5p?)99MUqoqeRhJYsWH0ycC z@0uI}uG|h8NF$DiHRjST@XgcE`ewe9E%&J0x1&9yz#kCc@rQx*O&`R%=Bwp+dqo;` zxVG3Ng56~-sCN!j7IDWtJ!`1=XYkwM9RC0kygL_(_04hoH#NcYAc|O{Nn2|e!^jsX zrSKR5oD+{x;X7lSOhkC2pAq3${pIP$t}92uHupX^*Yy7Y4ry9)Nvqvtf=CGxugqm^ z^aE)m`U70@qN%qy^&D@|`xEy5{k(2GPvPrNfFBJ!F`{U{4*V~36SHa7+wXOKK}KFk z*c`@z#@vIs!u-a(R^#D){{V!i`)`gfw7ml7#8U|qNAvvKO zC$rZYFAnJb3)5RgpTrOL&1c5irfp=tWAl}W6TT=EVL=KDG3rHmbsCz?sZ=cyl-LPe;Dczc(cXH;fou1i|Ns(p8A^H zl6Wa1IL3YWIW?oLXubi`{ut|8&9B69s_UA~l#gemy@`hJcdU!a0Q1!L70l}zc9-!Z z!g6XhCOfaODUIVR<-7p_B?w+e9FNkeU3gmB9}W19{@h0#wsz5mJxqvK1L>Uhr$$`U zS1MR}MmJ}gTA_jkQa)X~NbX7OJ8*IBUqpV+Ujh$`{{Uwwd{b-jQs=?ilj`>oCY^C) z-`UKOmED{Ul3@wcm7IEaH!JVBuN;NoT92K z7ILM6fOr_>*RpCl#-;E##l9WZd}pb6S5L8?^q&#jQs^v!H;7YB8j;nmn(8|zcaAy! z(QK}$TdI7-DLngE>34;6$h5o7E;!a3SS<4$t(~Mo2q02(wPPJRb^ieCSLe5eyd87m z--`bL5*cQO&dt0@dmG+dAKf8KyEk#3=W4zH>PCB3nN>(Sl5s=g2u)FsGdaCd-fOE^ zq_#<>Yg<*ilp<~@qG`@p^ju>c`q#|bY=-XFa3yy(;yp5RgI`qqTKI#f=(?_@;lBkj zmqmur?ez=kn-6@ZJDHY1!-i5rbm@xuUrPCOz>)l*uTkrq{{S&xQHR>-__=O(-vZ`) zJxQirE`3%10Jf~&+6ZRyyvfyMAsdcY3`zQRuUsD-G&%kWYkEDDtp%o~9D0TT*^uo|{SiDAV+d>n#?; zSijX$X!PALMZb+>fn)nUq}NjBEOPF0fPxRLd6VKq|F3( z0@BVg610Q>&fcxT<+IwnkNyeYe`%q9%i8X_CCqDKbK<>P@9hm4lXQM!0rG%bk}yCA z?{w!i*lB+nw9g3YR@RbR+-P1H)|!1<*Hzc|X^=%~qjdQ5KyQ(V2!L~+L0=n)l;t-b zi2WNV!%)NF)k$yM@{ihM_LaTy&&O*Igj!tfqWDivxLcna>hP;eG}i1q$!%iW$|shq z<}l~y$9&h-Kk!3u2l!&o{t8|3lV0#%oFBuwr-fmSZ@jpzdL5IeyHU~g#M9wsh( z!~xt$Bji|qHTVO^UlH}M68J95Qq(VRbmh`+4Wo+-oqJH0Vv)mdyC?Ukv4JKrfKMH3 z>wow+AK*rt;O~k)F7br1>h^l(vu~@~#ba}}K z)5+$(wN5@~25T>EgsUjZ_DAOIx_c{pr*QY}QQTTIT}I_Rao(>yVaU#U_B9j2=3>gg zpGN2_=oLneZd7JGQJ49Yf=0|^f+`s_q#*H*Ffm)L0<@cm<fKPX63R{XSF9MOQt^ zak4OEvUH4{hpsW#H3a%}gpb`J1an)>7J84SX}@P(-qlIW&z3K9m%7rh*-Vd7RVUKf zi)9G)IIgPiPm)$UNGptheQKnZOur*5bmVrXw0CARg6MJ@hKMHHh2E#-_Z6Fcq3nN^ zJ&sLw)9GNI3l4e?)mgM-gO~ipTB{`VIo=&hQ_nRo3vPh0V!7i8D=y>1@6WryV3eM=sn_G(VQmj|z9d?clZ}=zT z&xpPm$|msVh_ziE;~D<|Mz|{({{Vm-ujyD9+5^idASom8tQ}$tn@F&$BJ;}wn(C*? zC1ZxAIYU;Ds{9lG00lSkW`h(___ILKJX354#PJhjbB?(<1sL(@0ncAwa`;*Q00kQO zb>baX(()~0@4>p%47(!L?LTx*LC2hhmtn>~hQBo8)TWGeBzM8VH6(hhXNFw#%8}{b zpRrF<2QG~Mioa&>_$l4Dg>>uBw0K8G*Yxdj?X?TdPs4Z0gu8zcjD`+R&B>8^*VA7N z{{U`Jg`XCr5n1?%XgAEBV_Iub{gB3^lb0o<$mjUEujNO=UOv=33#qN{g{IAaS4iNA%Fph>M%`m#y5J3RU6dwXg)vc z)*4;J_u9;w!FMwtn&JGn`I`?R86=)aHI3qLiW=9AEaVpL7L^X7gk1=(7^I_)j(P)u z!2am;uOsm-{C7Gu7J7WlsS=phcF5j9I0M_QXb%{{dvR@Yg6m)bn?_FT z(D4P$vO=a)J45Ay$rmyaoCO5*>-h@Lmr-k}ZQ>9#F-;=oKQn7b7(Li~dQ#|@7gx5D z3yW8p?$L_x3I4f9KX-w^=RVbur|WTEc!oP=welytL*>mhR6K5NXZ#L=ZfOZ zC^%5L?&Mlxen-1pokzm@gqCm;Rt{$X0@6Q2)E+ACui^RH7P`87Nb!%Aur~~Pde=GQ zU2-wvJ!au9oT2a^o4!#g(R%khkQVzQ^7PoGP)mMPV+%^n$ZyB^hBQ2QOB zd8Uxa#C)4rjdhINRQ)PZBEltZ8$ya z+I^SFOPK-^5ElEmC$HgOq#h);^6fO%G6>^}$=*h1!lH@gVoB%#9G_ud27cbYD$=!& z+OtU4G<{mjTC=dzwDWO3n=qftk|~{;Hz6y(XkIk$-Hr_Yd##(2_y<+GXHgg@zf2`d3)KzyHng_XZa?CJ)1j7YXj)Tle$dBuK)@=vJevA%Xp~f5hf6-Xg!#}f!jl4_yUs?Ev;#Rn9w2N(b8h)+f8?{NiL?B{eiWOp{<#r#JjUYp=w16}GeJ9xWa)4tBSl=kReL%d|XTMFP4 zmn;Tq&+JE{SpL)BwLiqGss12p`cAKFX&P8Jhb`Aj{?pT9V1{j=I6yHO2u^wH*0z7( zsCHf+pTgb=YxVIf+$NoI9)oG7q-&z#M)noX4PIF z3J(i-z5>za?jx+JR%n!}1G~vA7!~QloN-@D^}~C+oqDR*k?9`=R=cm+{PBkOpwDTr|DlKe#Un* z{6zl%f{c86@ibVMNU(-I78%qMjNV`>pq}AlIsX8Aiu)4Lg{&c1ebggq74sR-EHN}@ zms@|}OLLPQcw#Wvo$Wqbe~nuI04p9=%3`?-lB~!G+%kIOALr7%Z}#ufq|)@?hMKFg zpSR0=p%fg2UoZLMz1zl9C7+0xp!<>X0QLU>>*BmS{t9Py+CRacgzh(Pj_&PA?uvFX zuKG&q$oeU}YR+3hMYFK8o@Ut@KuOQt-JAxaP0-Tm-sXAy!!*V@CW=qGihjItUUT7Z zjy@mNE))A-`&!~SV-c)SX%{Z6c=@ede7~bR`N)!f}i?IP^b- zc}V!9Kih8t{66vg$Qxy=E|_sLpJg?Hc)(;kFaC}1zuIX1=?v)I#ve;W{ zgHCx}Sb_V~9GLnAuSnKP!6mW5Brqp$PzU)H;hrkBI#-Ex8>Rrn*OFo4lXf!4t_KJ3 z?O#EKNv4mN%xz=Vbx(*oKB3^7uMPMI!&jabxYVvpw<~vj_S?xJPzZ?J1X1Mz3vh~l zQJnEf;zfT6=#pGdB-f+KYaw#-mX0|Qv+|eaQ*XD@x^MU=@56mJ;%~)9vGG2WHI9*a z`|FKM$YDH|*68y~3}=9%U7!}u>c;VZf~`Iz-D{S*U8sWaU&|~K5N2p%F2u6|*<&P} z1B`UVc=hYTjw_d^R(Si#MRhWM8hF=P)xWkO(=^y_;7egHp*(S}VOKi_7~|%4ausq3 z$F+36Fw->+6T;sS?6plY`%IE=4U5R*yF!y}`(=b*Fz5~d>HX^Z6T?3VBlrvOPsCmu z@IQpp$67VDoLBxP)eQMdtAXZua!AYbAV7{$2c~n*E6qMUc$>t382%La{{UOoC(EklE(59sKJ~wu6 zvwiW<*on1^R0etFYf;Uhxkul<-! zyhksYY0K~VgKl5z_5QWdYo81}Jz=0@?AJeQ^3d;gC*~?LI^+KU)m#sVye(y?L?naG zjl+$=%675z#(upkpOsrvX^;<{6R}&rZPU2k*<6=*wU$=jY7smep9sVI|x_ko9K(fAvPqnm<9#ROQjx`7e8$zJ`>*vX&+z-lF?aA$) zwcPlc;#crarS_|dWw^Pxf zMS1sw{1d8pqf7q)Lz3DU?!kXH@DgPJ^}q+IKc#Y4UM0?}V`Xk6QF80{gmLo;dK4qR zE7N`l-!-3$u5>Hyu`RW*xZmb)2wnc};lh0mahl@xPB6LOgM?=;S@kh|TcT>70PxYq zpQmZxZ;>Lp)NLd$6RzxTb#*N^;O(0}0zUldvC@s-jp86rvX8_60EiR*&psa3bojg+-W7M9m2PLA zJH}BdR$_J(UVlpY5wi`cKr_a1*Kj?5OxNg-z?+M&5B~tcD?BxS48q#!^n0|lZJFNk zUARC;eDJ=%H5t85T2VSO2r1T+Wlx0~G?o{3em!f;i2nfLBD;C)boNy#1XlQgWDYWS zwqzOO=SW4!%DfyRaaIR_dfODzYV?`>KZS@FNL}-{ls@y%co3| zJZxKgdArU*?g_`?Uq)-_JTc+hd9EE&D;XDS4sbabQQp2c5c%a~eu;;sp#yhLy0*}# zOPi?+MG*=zypw~FEAvaooRC3)(Bo3tG>DImo(Ckv?!&;a42B#_V@qjh2DhANk7xWdUMFPPwTy6byGDIB^Zje;aQeqX;hyKQ{>-{n$?(mF ztnmK;p&t@oD4)#RmWxeBBOQ7+bM9%@7VLZpY{&~p`cH$SU8fn}b0aoBsy?5tWc`;s zKc^?cty^2TYqr0%xYS~@j7r{96J&v(W_IoX9&!AIUboXc%?i>T$6#*-+2ML^Smk`@ z@vI%SxzP)w?qAsJ!^`2{fOa=p+>fYuFT_40)Rm)Fc#el~XJkY2kX{*Ca9bl`9Pn!s z{tAuz1-FjA7Fu|xz{_yA-V`n`v_o?=&|O=x1*U~vn`^1;I;lL@s(!=}eW&(B(d=H> zU0YvYYAtXjOm|Zc zmNNp9+nj*PqbE7f!p*mcYA5M%p-y-u3a;87FaH1p>HVK9wC{uw{9*AHmHz+}O+IVQ zPs6UO^TBOv0@|yDZxb*PkQ_7{tfeeb5ho|E58x=UR^I!Iz7~*Q2|yPgt)o2 zxhSJMx9)Si@(EGT09WOXk^574%fr92k?==`A-?f_+}ecNbP1yAgrK z(}9dveq9-TdYgUEGZi?0XV#|$YcIU~T(|h`0!XQSpz4;VIA)2~U?*d2ZS8cvGmP8j?49XjaYk7pKJ;wl_rAM-o(#K3Ol%%z1 zSXXyiNn#T(Bc~#;lj6UJt|bfh$t~TE-IKu4;Ql$S+j&y-{{W-S4a(y=jl+KEE$^7wJ*AN*K=G+HD3X(_VROGi<3C_&>6T91r&h|xV z(4uVwMDc=ikyJ12{J91LW36ha=aJ5N_pVpse~V9tZS7-$*X?q?KymWztBY?*7)_+L zEKR0v8OGnBsxPNBt(}~y>Oid@3Tq9oX#Rc5s$DY}-Z+8UqqkqBRMe$yK4mUIk2xne zCW~2FY-7r;a}M`OXy!j4BRR!m>RM=aa=1~95scSjLAnYXINrQtsI4JV z+Zav?S~Je|3lA;Z2&31E%evB%bA|amKDF8F7HPODk8zsBp4DNIf$5JW?nI zA6r=Er?Fsg+juzOb*)PsTIWsBRtP~=59WEG{J@O%5X)5ZZkK2kp-O>sUvv+{l=_>Lq(inr2WFC1?F0QJxP z7~|VL4N{DGR-3yz?NConX~s5sgmrcj-etPBW3f=i2uuw7Qz6tY^y~Qq5;#^>$UNZn z;;YLcicqV;UjG1{ODx76ME?NOR0WAOO<4`^V*L81qR~qvmq&HvZ5tK@~6m09dqBHKjZYTr$1mD z4-ub*9x8ti>bfK`zP_lomyk2KD{i~nEB)=!~cenTdd#~foBFN*XnH(ZKsW?eZXmu{VJwV$7v_{q(4 zz9I24U0z$e`Rf>vjAtcAPb7XKzA@q-W9?&wO>S}ae+Il?BmtoDtWd)V3gR6WBaHPq zB$`b};4YJ*^sd9= zd@`MLPxCh!645t~%Ea@E<^KR=Emk|9iC!qT0o1mc1;AGww`24*yW)$6xbb$T<(%x8 zr_9;F9S5+k$iA%cWe<36v1pfZ>Cw)i?d~mJ8IZ0+M$$^G2*DV~t$cIfZ}=sA3#?5q zh`d%toNi>2-p@$4Uol}W#xT-8aB+da{A=u8Wrfa{V9GMFE5Yb@D{uU*JsT1K34s?zh(~){3QLEt!MCFha?ud zPP&(Ryv;k@+&oVNk#Ay2&Iw)z6~KHy{{VuSc*Eh>!|fBpwmM&iE-kb>IP4&}k~xf$ zA}$vsI3IT$0ggH3*WYUw?j?j8N^yh;2xv9G!oL{t0?N8#5+Luq#bp1z9wPBep88SoP&GFYB`(jd>aVCNsfFMRxqr<8c)ULoKT*Kjr@b(vcJk z<@kE+f;qZ?aogmYvULp#)5Ja>zS4CYcy$doQi-9ux19qd$ao-+>)N)Vk9UUd_#g4} z2?y8Zm;7ruF-gOE|!&F_vQK>xfD}am;V4yXZjOIWodJv%cIJux^&iZSnO2E ziZ_VHcITXdj`{g|*Ogy>#99`u`zrW*z|!g$3*tWrSj`TL;wS@K+*{1@Ng&P+?=6eq zu5rdd2E7x-J}c6^6QDMstK7ACiL#Ev|mE<$pJQLf@yy1|H=Ldp%dRMPbq|*0wN3$qFy-EK7VNcoa%l2IOVxAqh zx*DdVe?RtyuYMG$zMYgVKsg~-1PuC|^{;rjZ#N7voDw~|e_G@I7{k6foj~2VmK}XL zuF@Dhm&j=Gj=ueC!_B7UDpHpG@jQIyDlo%FKlAn0TxO z00kY@*Tmloz8^#rWY%}rF*hG6%wQ5f3in?h>G9a=W<{6Hd7E^Kzja4y`PcUIn;r@9 zei=6%N^Fu&GE@22uR-Z^+o_{Dp9ae@wy@hUAGrvMdM+?4vU_>t(po7la>~G{3)h_U z?Ocb$g9Xl)Wf6hpYjx@dHuvjYxAv#m&z2%*Q-TKGmE__oNy_&^mQ zm1!`ccJ}`OIH)f*yMb)>!9v1v8FG2&H8q>=lsH)8h;VW_HB#zW+{+}4=%5zJ8OAyP z02=3aXzS5*GZj_j4&!uEHWf(YKjc@>-xO_G{CTW2U>TC-ht2#z9+mX|jcccPufjJn z+(7?s2nzPQwIJ$Zxfz&Vl z(|r()Sgdja=s?`3xcZ*e<=zqaji>kv#l9fFx$w{S^oH_Pg6Wi$w8O9@{G~%<8E)9) z8Lv>)qd#npX!rS>m@voR=lS}66>Ii=`1#}gE5cqRmfyzy3Gna3{{RhMCY^I_u4y*X zeW&d*IcUUjMrW9XS1frwv(0;}STv80%`Y8Ft?@$kUxx4Eom)-3(*FRqr}F$c9hKaV zd8o#pEbhz65ptkNKp9pTIpcr!R@Pa*G3axf*sW&NEuI#D$g{L7JVyddj2;+u9Zh=| z#_JD=9xiLI5@?!_!_Nm<=@wST-sWH|ueGZeA>&J1W4cJ=P+0AgX)M^rE6%@bFNU`N z0JGk+rdsJM_POtv$Ee>#0Jzj{BmV$c;eh?xBLaYb7Bkws8t|u5Hc_*GGs>XtH6|Ym zbxVKQzxJf@pM(5MscW-o!$!W+;Y(@cTW>5YGz}t#+7ys+oQ$_lYt4QYO1>oc3Hw3c zzwYIW!B9r1k?d4KtZA4`dvTm&pQ)|J@T@*FwU+NslT-UcskGFz_%}Ni`zQQJ(X|~a(?X5B6&x02N}HWRN7i)bXOx0lKPOimxW#(;dQs(T zbE^@Gl;g+JPx1!y81K06Ffh&q(wOL{5*O80Jc^0l*j~Z8$U9RrwR@` zob&isx7c{z%S!#8BDE1}5?!{Eu(1}hc|>3-JF$*==zEI#97JC#Ov)N+cb+R-cJR!t zB7Dbb4UFT8@=q288h#&@b~~_eab1qJ@gwazT$#~zm5tZS++fH6axgzC^N$hu!rJQ8 z1-H9aLxNp4;Gh1za;Ryh=9BnOW$-79F7z*jUL4hKZ?6rWi)L-$w9{?U6FRhq+Yj+9yJgT-jO3^EO0?BVwlnjtdNo)ff29YbU~+4-eamfpUrx;y?!~ z7?J_?&N}wZVQPAi*QIH1BDRX%V+kA(e5t`5ae_(5Jl8&5kj*(_BzcNn@z?xM_-%RN zpMiQ_xvAWXokLf9c8)7(zjI>??qm}`KRc$z03H_^h)jdwnSMeW#qq9qhY&4riGDoMx-#yzNS>!#=aez)a;=4}<{4MbAm+<>U)%5H8 z*43`ylwH9b#s0%AWGN>Y3%G(n$3iR2_S~tYZ-)2Xj9(sjBx;(>bHH3`zG#}|LHSBH zFxUfw$>Z9hm&0Bfm*NJU{iSoL==w|%AGi5ryXr2l@`T3&AoHG^YkJnp5Zr1PGfXXD zD{*x$@}6PihGEV)#s)h5Y0am%#LYr0ojgMG-%lgkY4+CBT+Je>+;A715yAfe>(q0~ zO3dg|lwIO?8n?y|3Tc|Bhh^~HlED|5D#~s$&W=}N7Gs^m3~`@YnlN9AuVe=i5JpZHA4ds8qzxqO&-u8_2bG8E=%ZZ|`x^zexNCYinWu00idv zBjNKK&vCCnmruBiMGQ|Y*(3e&ALn12ciY!bk~|hZRu|A@`&a0P!#{}L8`i&Pp9H|S za+o|B;af{9nIwVZcqVZ2+~5!iTzM*|9f|9l;hm?+7^C0exyBQhJLr1y_>&x+hd*2I z-^MAWMvbc4Ql-_JMNu=l%#xz^tWhKOo=H*EE^5@QT2&+Vme^XeW{4eoCuWf3`{eih z0ZGou>zs3+&vjqd^}c>|(dja1wSVtsOC+XdnA-e`w2fS)IOJ26jlReei|BINjxO=Ps1_0@0Hc^<-t- zU1=uL|D*Yxhwyaqdd*!jV4I;r(ANAXl3UiObiW}uKQN+C55_fVZ}r)V2Dr*to!e5E}g&*1<32_H}$-rH^)}3Dzf@ZsUi!&SyhEk zqEP+a4z`mvmv&G|;p4a-c>xl6!8Y8cLhLXX;eYak-kwD58^SgC+u;(;PTHjU(cFrg zDJ50LgpyOy;Fq}DB~JdPta;h?rN4LkkJTkSe;4gtw@^l;e3FsKv8V0``NwxG((l!! zLQnc1*sqX_%NTvvm#C9-Rze=kW=gPiiqt4Rn)Yj6qBE@GaO3suBy`W|IV+~JdC~F5 z)+d^Q4@>YSag(568#p+g zCDHYEsVz(F9O5XDrCa7>-ND{~uJYio@dMX%R&UeF*aGo zY08ygsoH!Hj#2%ARlLRrBgHtat~~KPGvGd5$@H3r zkMzXB(O<*Yt%gQWNf{%dTNIS1oA5S5Zpf2zFtE1aFcHo+fmeatx3UOLIzT3h2u>Cm z+=eQSGhNzlazCNhpx{&E{c!D0>^j^`v`$H128RH{`D8? zsQ-}oCtl4@#dwWFPpV%Yx#xgqG3o)!k~%FInU~9X9CZJcBrq@gpNCdwjWPY=zn>D8 z&wY<#0zr9}ln}SlL2Wo`xv53t^LD|6Ci=*gQ}XWX1$t5$`Xc{K6X1{#(K7bpC# zIwJC$1jr0^8xg6}4=0rFsinMc0p|jTSU8kjE+PJJe<|&iQ|N#JI-us##I2#<6JRqy zyzDY<#n#s^G?5oK+8$aPomIF(yW}JDQhnCmwFU6S#0@f7(u{J7Ix+ZTw~iR0;wYlRw>Ydda-ugkLRr>y5+)3i_nF^24cZ|Vmx z68vR*2r^oZmBqiae(w4SEfVDH)jfV818 z2#j0lfIIb$t9J>hz|}nlrZy;QGrd+Fl7GJWl~yNLLXFLUe|-u!aJR8rc2dmtYfjsn zr^!akRcae1mXEN?8CA|RNt<6L?n7r_d>zgJl=up$7dXe#XEh7GtH^~Gr`f&ZcKxhL z_g+1m?$ZeUURtsorQ|v{vX$5ez^J+>kWzyAJ#9c$iX#9`008=d_)`p zF!Eu=as{K6A8uD`>g!J70YMo{<^dM9**=fnbNms=p(hB-=_OpS^cpto2PQ_M6~MTx zr{fhhBPT>HKz**eI=KB0Pl_<-XdI!?gTlDUL0tlL%q&fYW*feTum2%ui{CyjAGDv zplOV5?97%bZ+lY;P^uxaYeQk(d!pv3Q%K$}?@j0!HkwGIu)@fsRt|^&*7JTxYiqrU z+-e!e8+5szwK|9Ea87;XIe7+~e~z$boJ~Df{${odq=Y=dgIc~w=Gyl8U3i%+*5Zr10b(L|*y&#? ztQcjGCTQ5cKbFU(f^u&%aruEzQxe-U&z6}#GWyL8TK)5fD_YZk<#aPX{1-orVthE5BqmC zH$=Q;<$7%r;xygB7@5y5qNuQvPrFN7S&EB&eArNIZzWxLLn=u$i26ett@qLCat{=D z|39+*vlR>fKDaD)GZjmB9z9*@K8&W5Q_U@y7w|Y5H8lPlcl=ajv}aSdJ|YVxZJi+% z#C=kz3>JW}GcuWU{ClNHv0*dkyTTjnFi~ou>wd2I(3Jawu{S;evX0B|&9A$cTij5R zaLpGY-xRBEd}(B=D5Yu4K%xFCXpMuC0N!v%ah*TKF<+|(IJvcpV@`;7#gp9KTn{S6 zDt$Sqq8|EDNTFI#)m2`j+3KvR{qOV|M&QNz>06sq$p>xbwwrQtl(dBvxN@Dk4vWd& zkxn5i5s6(O5CcPjC`ea)!+&IC#G5#ymn5P9=~*fZWfEvPwolcVJd3knQ=d^)UA?<8 z@iMyQPDG@VFv^Ft(2x_pxvr1H??6U@ls;NK6*ZvJT|>^klixYi-+xkpvVTcR2=ZJ_ zPxVeFb%fFvXll;^#y(+3a}~GqoC^sn?eW()RK_g(bAMa)3*6zjdui?9Z5wek3LKNZ zhGHGg<2wiig~UOuLnkl_E;wFAJ}kS$+n8La)ZV1O$?yHihzCr4ev71%8AAG&q^3eX ztce3~Zd$HJ+S8j^%UxNG9a1}2_P+<%M(9lpb2;XkZDtjHsx$-hcp9sG#zbL-vH$Q^ zR;;FGe>vEm@=`54-`XJt|4(SuT4rjO7w?Ur41bBdXPgYNWp`ZPNu=;h1fOQC@qWKn z*fB&JlFt4+oXP1*sfFe|d+c_X=zPwxNrIO3(XAE(U&}Q`JAW9LMmV39%R?w{rSVT}{|olPSYLy(iDo03DqTSJ!3 zv{b>oV8eOc$paC0p4%12QXd6fQr6f9YARu@G*uxcAAJ1Pyeat-aTl)C{G~e4XBA5R zgB`r);WK1&^FpdD`Fb}4XoLY;Y?ZNwDeaP~wMP;Qn=0PODT8UG=zd8k(J7L8_)`v% z_3xZ9_{-AUqtYR6yG(mS>vd=}srn2S`a{hJ?p;}Xld%0!#A-WeYS;0=wwaJ6eX9t! zTE+7_5Auccsr+XaF>?H#zoAuAz~C95_J|U45MCNj`uqm;4k@zdg6Vn`kBJdT z;1h&HkjBvR|GLKwUJ@~O1NAM0Z zwFzg#o4;!H1XoU^4PLE$v}Hc&f`3ve)?ke^e5%FYC-B6Z!-u}dU$~j#bR^VRT3T2Y}Spc-eSf|%fIZcb#!vT)dbMGQvxZQZAs zb$E8nshjh=-}L&Jz!*QY+uXxU3Wbc#5u=iNfHhr9|bzyS?029=4?p1(~j%$H{^?spi!T-2gB`$8V!`bCD? z0u2wq`1g|9^c&NDj}!(}U8|(5S*LRuITEL+XclHWQB>Y~xFP-N1}i{-gAp#m{kMfv zR*vBfvtOU$4*V?j?E3C^A07V{I1^!nfOj8;7v-kNLplrxP~*_9SMa;D+l_VRU{u1v zf?tjF)?9rV^V@myuVkIJo41!SFn63kS^>oAIKw%UXD`DE|1P?L$jY{|Rq4GUe3G;> z)(xTy2_V3zqkM*3^+06b-KsJ<=G?TiRLT6l;s-fZ^`u7G=}ADvq^Ko8Jw`R|!%yRi zpFQf69~&A?1jM>1C2d7OBmGpN<(tnFBZZ7j9tT1kH%FaEtBeeWt`S#y;^nqI$ot&) znm;=b$z^dyz)&BQTg0!W-z6>wf~~H-E9_I%V>JUCW_Ng&?ska&xqeN0Jn>{AhY=Xv z<%B$bjCc=$tJ{%F=0HC)I^R?Z@KpglOTMR!N3B zZ5xzU01RZ-A~L4Hy#`sjmS+MfA}juSbNh`5M8q>6M|8qua39cLq=yuEbVPSbobQc~ zIoFk@V_$pd`W=f-qCX-i->QlM+nKZ@(c?f^{36Gv>o&h>Q^D?k-BMwPjx|0CPl z2kJ-S!o0bO_r>B@>YX{%J9*PqteS5BXvc_u?glKCRN-x#zG(*|<9%;6&`=gny=@1m zSx%FVv61BWT`i5;q~>bK&Hm=V5&IH)%4}IITW~n0ACJi^^gI7ke2=}mifk`0ZPsi- zKK|mGQ7*KVc!r)y-412F*P=V| z=qD+p34hNWPHpN<61N>SC>8wGgn7o=XnzD{bvL$}$x!hLTZc}Ra@%cIjlh&fnlkTz zYVQruBn-q89!IssmhbzL><6M^ArcPmlshy{%?yc!8491=KrTjuh`S-p5J7M+01>c; z6qJ)b<=Vqq95Qu@V6BCofqQ|I(kBDG)BFRbW%l;aCUs+gzGWVxiPTW8?aptJBZj=Z-x>d>mtOCSA7e?xbb=O+Jx16E9mV7yj`-GWFo0mdT;laU%Rdz_2St+}pr{YZAQJmaqlYXGklFSX?LB#sE34y}yZL=#ar zvGKWC9sM9a{@eGrz_zDj&O5WlJ67aGi7wuf*2pRNo!a`)FZgp?pqB8WR`@^aX z44Y>8rRCpc6V_&Ryb=d~ftTxLlEva(R}zW4t6%KM11J)UkQun>-V3;pNbLYm zLsNFUYU$?loe^IYw2Ny?W*;^2X6d^`Ky7=>7>r)4Su9>f<^}^5MAIRi9NsIFrESMqpK+|lr@ zqD*cZaxqN4E4L<%9+YUS8P^JBw8jqfoKE9!y1Rdn6ic%X*M2|r)yzG8#cfyi$R2Pu ze6si530q4k9 zs$AYB52vCL;O+k@3;(jS87=<6Aq=;MhLodSot3n7XR4%m^a4qteU%6A$H;`rb_Rla z$IcPa;;^kYeUHhaM^YEz5xH~tn|@Gg5>HmP_a5pS@d2``@^p-G=i~brs`*?^k7o4( z5s%@~K%VeC;j#K5h0JHcb8h$5UnhpWQx_0E@e%M$UTVY4c-8!wYI-eFE)Xo~(c|Xj zA-{qw33*2NiGPDZYxrE#N&J4)!fomi<=KAY_n)>GN*DelP3FY>r`k!7b#Pv=9|aHURPqrq*l@PDl-JOeHc?t>^5AGL`kW4ql^p<3&*QF+-o|NVu}yD` zyV!qIw|HtBt4ii_IhgY5jznMRf3(`uyvK9x+t zYSrg3&91R;!VPfJXmOT+lU^cq4F(b8Ge!&TmRizQZOz%}yeCi{no$4G7?k zq^=D0V-~ju16X6e70W&vgW`;^;o`R-e=s}erY`7=)o?v zoglIbt_=cy4{2bb-6mDVFQk{_-CXDc6&A%@91{NUc#y9nJLTa14_mGpxPdKfN)`3W z9)wn)P5ur1Ypg}XL4Sc@c(z{alqj&VAgrp=(Aae?0Ds2u92}LbwZ*TTcGW~WoqX(O z1R9pWb}nz&Rt#oq6^|qiK^nadj~DG5IWF`^85Eeaa6-_}D2IL+Dx!x7KrRK(i&F&z zSur^qQfZG5c|xVg>KIffn41;Xe!z-=D~VP$_ON}DdE=%e78DaA0XZf;D0pwc!woT#bG2tNGK?t`wV6zR3@Lj<7tEMXn z>tGgSp6>jr8mAQ6_-+=I8Wt80QT9cPCn4$^%RTR2L3_;<9;3Y`gkK7rOht5Y?3|wN zSTbHyft`YFt4vcjh8v$MJd%i_pTi#uZ;Q$w9IRXJb#^JKT|P6clomG6x?6T^Igs?~ z+TGEThfrT}hG2D6<^8x%U4+TOY=YJ{=d~}U%RE=JCR*zZU4BjGHp~3kv zK(=;pm2%SBGOJS~r+FNG`mm_2d26&jM^gesg@FNW_I#@n;tQFUFUq?|Xn^yXzv0wu zls``nY}CCjW(3oat^G`otiuJ-UTv*7B%Lnv1k4XyT=_7YnT_~6)v_Zu;%|Ldvw8=7 zWx;*&{>VE&T?{6yW+dLr7=N(F1AGUHv*b6fh>>#Y|M=9BK9T#J2f=xHg3kEj1Es^i zgyYKBv}Joxd3B6m_}j)$6J!*$0w2yvBp2ryypoWR{~bP%ld}oWXq;`z-gpteVhdA* zn&Jt#2w$pl^brp(FiUz$2jTa{{LcK%o-}^U{p*VgM`UWolMY9({;jDA;v}0@<8tqT z@{wun)ah+C-MVH(EPB4zDC0J_=QH)+HEo&W8p)%qYWxeNa3&#B72%P*9FT9?TpeQ= zK;CZu{ULWmay$dn^n`Kl9^Vf<2=n=8K#^<^q}XPtZR>N%3>Un4ZO7{UvKp5eNiJOn zv8F$$PZg%S)$H7$$lr_L6oJ}{CmQ+_?_> z>XmK&HzU_4h!5WBo|*+`ipzDdsG^#iuB8bA@orx<4OFaY3;^A7D-6>;chcpj{k6YxH;|5B70*U-B0nE5V-}hfEz4H1eMCh%F7~HtIv*gh2z0 z0LWxx*2hZpG-fgJXLT!T=TEtV4Xs_EeUtKJ*Iy;=pr-w%V73DejM-Z(D3;kbR9`o1 z=g_V6a>=Hyl>y}P7+-$UGZ1@4toyM$zosvf`6)R+vx+Ur`b{WlD~EC{{~*8CFNIG0 z5C%gFQ3J4;ktBRj$6Gzifvl~x@R^3hx&i2o5P)EK!`yy7mx9y5>0Wl&Gu{0Ruh*lJ zL*aoWzrXV&&<*ej;jYL@ zT(D!EP_8C~w`)ir^}$$8)JLA#sx^`FlUp7cIS}8|;c70YmeD^Z0F5tmw9V$x8Lr_R+c2RLLEs) zb(|5pZ!GeZ3xQL$9v4GT=@eLxupV54m?TAN6E$7Dvc1C zZ?udXs)R!Q2hOUR=jTd>MHW>bvdl z1+!(*tog+OIBQ_D39XM!0f>60gKf-0b7^qZnAwxkJE`!#HPmXR9Z00*X9m{F)NdEh z>mV9hI=A>t>IT-#4J{{z5BznNoe95yPNd2#x2X_r-=Xs~p8#0pL5j?WyI_H#$MI^p zWuH_!xes6<(y916*X&x|#cOeg<;%NBR-Lp(rn*Bf%Gb@7@4iBd+N$X{w;MgsV69cY zs$Sbxft8U+Re8vmV&b@bJW%GZPaQ8;)kZE2LCP}0*fe46K^)YiJ_#niLSd*H12O3G zJ%~MlGPipKjB34ol2)2Fy4JtA|B~=@de48xiQ`1-q)Bi#FKrddr2n$ z(EalrQ~_44p9@NjtKzvq*^0!QK*1t2SNFbir0>3eBjSHB+I1LO{YJou~vjdoN>;w?I>2!^ps zHzsiXZRw<3mqQ7|@9ld2ZCkw8@PuT7-*kOQ(pzReGkom)iPD`IZBKpHjyl)ma_=MfVhnwnFLOY2+pDxTBV zWSuj{1ZkQD1c`p?zc7lN13j$H4mj_ZHwvXQ-AazZ#b$}vKy0N3s%RiFYs_G=tvYa@Mc4L#_$pF4T|V_k4kdNWA; zivacvpG9JQ2`)RD7D_zsv}AFVcQ7K=re3{}3er>gNU`s*3Y zn)!iVucNI-w>c%~>RcM*uwR3@*2W@9mfG!Sh?r$e>QJ%7jF_vK%i%B^^UIfCL(t@M zo{7Sz6mK3~Shjri&JHG_WQN5j|9O+JQkiHc+0SV+tq{i^Y0$M8jL=3Z2H>lK<*v@3 zg7V&^a14^9G1&q^tnV63dXog5CQ1j`o`+y=!(!WYi|6-TORbXj!3L4Hhd_xTO&%fD z)OrdMtUIRQK>3g5V_F6u!v64vm?w?4zgi>h4bG7qoXpsaZuz{9P-;9M3ELv#^UIv@ z^Xh0=AnKrwOA_QpZMrs?Wt{bII|~{_I+joV*7{MxtWk;O-?g@+{f{SKZ2>89)V;i- zgdgy`Auq6$MFB}@XiNi--qPTU`WIkIxyFT{0~-s^f4><`%hp5z=#r#s6H{D1T0Eiz zmr%By=T>zkv=9}$G$<=kcnkSuTtij2i8?;Qk`(N@Nr`>bgdyQ(Z!169Hk7HG4NzZj zS~Bal`xkHXYQw)2HwyEYX@I9`{bHFE%SWb z^@KZu+&JRvt#yhWt2Y6^J*gv$A`?Sm6iXJojBQCMAcFAbQQUIvQ54iMpLy2VD+#BD zH!E3C3wEfO?GAV1U{`)Z&&c^HnefGri{qh_$W8FG2na| zf_}EXqX`^MRGc0Owa_9$dd}p8+p3S-q)r9G4lFIMoxmx}c&>kUq{;b^x-2{~Q8v&t zKO{0dIQg%&{1&1k5OLrdln$hNc7ZmC?)A7J%Rdclb%;k6YWpdNyP>|rnc;TTO}c<1 zJ{7jok)_s9A@nAOM(k-(BNM~W6xLFkqynI4^z(9PW1 zyi`yYN1Hd7X0qxa1lB0wOM%|G`W3g5u~*UDGRo1ltheRse8HtbHC4c&_QwQGQK5~> zukDTNU+1<<@xpl-YibdCy+fYp1Fpe@FJ%8K%UPDI*d|G~Ffsw%E)ujrIf3#H_g{fJ zv9;4K>G#{98fkEslXqR?Kbs7F;=e}80hzHUe5$>m6b3wl)PUy#aumQ~mje7^x}AJH z6nx+1V%@093^IveIQ?sT9AZ&=>mp&aINv#9l#F+|GGL&W@eV2g*5A5W=peZ;Z)u@8 zt_9Fw5u~P0woGKm)-K~8?R_6-Diu8)Ci6>jIkfJ8_{I_UF%;B&4nt=bB^k;~ec=jG z+dr@V6JJPi#37NN89Etn=vKS?$Hpee4vQ}b^*8!oS*8`nh!r9xr1y*u3)4q;(maa4 zl~GgQFQfGNTh%%^u72qzXRO%L8IRf%))B~(k-WWorM!}KasAQrIYi7SbBAVF?O#4QYpV+#q121RkZXx!{^e~3b;@)69?}=U=zI>nRheUH zIskq!U#FVBCHQ>3Mk#P4kTGM6%24j0>l&fC-{8)<8x$S&qb`paXq_lv@a2OF>iOtD zDcnS9RIcBpMPiZ@Ys>QP(mmw*Kc4@^be(LKj7>axgL2L;U<%mB@J8M9gclE0)c@$_ zDb?gnClg@4o-X`Z|B>kiM5`00`E+p(tPKBbyX1J@r=|$4rVTmV&wD6sXVQYH97$_k zpvz{}`qi`gap-qmuLCgBXPHX*u4RY~Zf^x~%LJY*wU?S{VEnDghTJtJWhjylV%tm# zO&3J+um5zjO?f$;`OE>UNNO!^Qb!pstlD{UuBs{KJ!g$H|M~vMt{gWubyI1LNK4_# z4Pd%I?s`}$o!PD=(GdD_LgMzf+TW{%jq!J0Ya|rJDAeRB;E8^JMS{moOs1GtzLN7- zuM?Pr(wnJ@eO93Y>lllO{*iXbjPdkZwefdpx@5&uI(lFT4rSq#qNG( zQ_dgD1+dMVIS)2{xsZC{#i`}(E#VjYNM!d@YA1QLioB7_gT|NVP39v!uudo&sZ4gc zZwSN*ZWI@?oXb>fKCi4y>q+7CimnI_=rp*jF#75b)aG*q1|bH+x&|hxeO4=iOIneoiqJ zpGty0sYWA4yp0W0!z1K|T3+8fXyb3<(NPlKBaIW#T4Y!Qg3=17g^mhCk1+%snLdSD z?+U()j|2Og)9}A>O)0Da1o}VeJOr=v=sh#z$+XIh)E?LveX%wF0HL8wiR;n8Bz=-j zidw!CLR}Cbi&hOQ=qcX`%Fh)kM=Kb-hsvM=b80oI7JgRqcnHVI>g%@4^ET?flm>gh zT_NSjKIbXn3a?3!&K@YEn@BzIHqP&P{Xr;R?PF_V%EwPPUpBVzhU5a={K~n{VJ$}_ zOBD;2U(-Sg?cQs}joZ>ZZZGpOd$RWLw7--4Tm5_6Q-5p8=YJ>zVf&(irHhvHT@H`B zJSoTgpL`%@C{#Omc%S%EC{Le?_qxYea^l2S^ACV`tl}Qiehz0JuX3weunY8f`Kdi& zo9yfq7T0fMuUFa%iHzuanI_x;N`Xi~yTqTlM+EuRt-+$;5?I+tms75f@F_*0XTXHX zu1tAGI?vi>kF}kg94x(I>@ElvKx)(y7e(ip#n?!osGm?$_RmsGt&0zbf_DL^IwI|p zHGWK69}1FstsL^E33FIb+EJgi1ap5__lD(3FYK1~pXRMi7i6z89j>M*V7^NplFHm% zJzb#<-xj03T1@HZ`atB5f`-su(ck#BbGuo51r-67n4UAK83XFzT<9r>(E8 zaDC067xX4qQQWe^mQaZ~C%)%EaXBLI7XF%)KcA?XpThxP_3kO{XRyS5Df}kc**5$8 ztGqIU`UUHTeBRCYPuOPLQhg#cEtH3GHDCN;a9+deynpJdqV8`@h(d`<;I5THGw>JD zcftZ%xMvzayS%x8Ydi?X&`WsYl4fe_Q?Q-q+2i7G1zk;f?&?*@gVd({4k zx^3z5?g+eZFyvD!+xd?Sx!KNmo=T{|8)4l$^Jz*j7v17)+epr>_&5E@yfqI3gTBVf zz4%PoBet$^GBC*KY4tMX?zor(Cjaxy4ynasVt+i(T$w!5?|_1WU&+&W=`ohxbZbET z^HtB#GsCO*rW=Go%}kk*o2mR|17ua3c90bb;3 z(+}2;SjjPZfpDh#s;mo)YM_3vo05(T**3CtuMU)9sBzJzRyh5y^Ro zgaE|Bc*zjy-B>O1(62?tbjmT?h(od{IZ=buc-Zla^!68$3tG)t-hbu49%6*704=)8 z4$~?y8IinF@>e>tC+rM!110jv5kQSj93-EsN}P8FEvz*!+pH0zn5Elqe|vuB@?8BJbX6Lu!23SepO++f#`t zfImjRAp;OLce0QdNWQcSV^C4o19Pd!g;k^nATDdq{lZ`FCnD5)TaG4b#|-$`+K9hY z`mzig{Z1Z$zgyVbs3(=w;B2o#xhCqu?K$}iHhDA5-6Va+SjFYcqlpmv9oJjp+TpZyiY9+=Mpg9>H(6khP|Tr2!&Ae4kiJV;7$a(jPH zAyW%AU_GlqiqL-bGGC*-_ybt8V#kYNo1*K1F?oQgL%h~cwR2aEA1Mo#ssb?~O=tcMp*g8H!I-9gCaKczgscpwrYtK1{PdFb9M*H}Xc5Hq~MqMTr}z9k2Ac#9-ybzRyHEW~c2c*6R7)wUWB zl~GS`9$y3Q%FpDZ4CyHEBVEg+MN{oF-t1Zd zm*3-eb(rekL{x^bV&Zxsyc6v-rH&bFb%c)f z=JXjl@W<3gs$!-0pKkdvI$vF!-^6velBllYF+Mw|O3?ShRnMws(w7B9Y+gUsdMY<; zhb)WFIF!>}Uoj{@0a(v1f4?Y^DXLMA;5jaWN?D}4s=wRp10J5dwxinV;sGa-Woz?fZaF;)&ApacBBY-Gn`C98{4V~}JdbNQ zW)zqfn(OQQQ6DcLL6oVB5=S}t#mi{XJ#hR;|1nM--(Nlk$@VjUJic!tv);6 zr(7woW8xvcVb{;_Br9wpsT1NqGDZuhRzOtYq)#^A2j;BcsP8jOcUi&`r?-|d@7z&z z{k(KI1OJs!v+f|#UQ5}@6y{&1AfeEtV7q&_k>7R>%n0VPk-L<{wr|cz70k%b3(@b zi8R;>Q--Eei&wy?!eDSOxN?Z^+iPrVM9NcA!`Riuas+@$x{?cB8s$(Q-n(oy|93rR z{pOmvZk{XFDVDms6(vo8m~b(LlWT<0nOO#7Ux6DER-BMok}>a0JPH74 zpxI}q`|H!3H#ImLBOpQEp5$T_j24dvGM@sXTSvH@p?xvRh!$Q$ zpyJjTW8qK1tyKl(j#(g-sLz)l(fhCmtGWI#%Tiu=+6ktB1N7~@*kUqCUH}W|4R!B= zbJpWvs}`oAEWH;G>qYH*e=KSqtFkxhsE?&DtEftlHkX+fcR2_84x9tT`L%{RcpcKU zzKIF?Wl4GkCF*!v^8Cc>H)3Huo7Gcd_lC^w^m5m^YHeB!)?zz9q!jrpgbHtyn&C+0 z)&vs`6_g1BraV^!e=U<520eNG3GTM~^ zkikuTV#9uJp_0<45%V@24?5IvLfvdYnbSm4<`2s3K>sUb+YKte|Ks1;ZTZEfgsLc?bML!3LUQI^A#S_e&h`BMjzfirHPFpP6yr1Jdu)>RT$%UQa8Uj$qhw2SH9+qe zJ>z2<<(a*E@2FotR;Ae+rQWdYSOq`dmu_lbh!Y+h{*l}KQ89m_^~1B7fqY9b$%bK3 zS-r%gZmC0;(8R-sT>X?TdylXYNP;QRauaTg+riGSC6JaMt^oI*rQ5_QO#4p@Wt#p3 zsrts_&Vrgpcy1bYJK8IKy&SD#@Wi3TGHN`e2M0I7CXr?qp_y~Xg9U+v&zCiA8}W>p zHRoh@*R*3#eia<#!WR&)HY6j5Ye@D7lHobJ*Xm|HAkpe3UKY(G*aM+LBu@4*UCN>_ zw3qhVXzgk4iw}+vgFXh(k+SR!E5-Tyh}#7Sr|#7=1$8&Y3i^;AjZ{HvC^#9S9z4HbMEYS0anqND&(Y)+EK{*>ftuOnYnZH22+_A;aBtQC0N*u%j=hiCV__H7*>g}%T8kZkF2{Db85w8s){vRR&RpX zCAS2i^t+CU=B)Q^fsu}^$*MF$o(tTG@a35XJii2q=uob?&^No-O>=ZH)83A%Eqm!y zm2@mI?17DgAz^BOwC7j-62Z z-8Et^iN3NP0A|?v6u<6wif%VAg9oJ(jiEb&xjiXfWrm>3$N!P-PzDuwEbSZ{FT7zL zsmDq7Nm6Uvx?lBTWhGJh+sUu`CbNyIg}mjS;}2Mgv{g#u*CHxVpmj1cA*D;Ga3EZB z?P0Zd9yK>|ygU2bGgH7Dy`uL=3swdO0zX8)sw z{U*f4Rt_dkfce;iJ~h@3emX*;FN5uRWH0ex)R|Ln7T@Pz^nXOi2_`4WMq zA7P94^`PP>PPEuBPQOTQyEep8KNK7tb+NM@IxGr|j!|4xT5p6iH+tE$MgyNx;KGhh ztJVgVX}P+6ZB@exDo&j)EA!ab4s z?hs+E6{>vFlPE5wV>Sy~zk@WywinA54=@jGN(eFCynQ0E)+E4rx_hx5V(9>}xCM1$ zj9y|P$Br8?u{R3_7ey%2G|}d#W_{sF0AYe0TX3)-%M!*WDYXxgT)pV}F^6f^?dAEz`WJqGs$Pm=DdL+>8-) zqakZlsp2Bi_O0O*t*{!R`I5VaE)TNQZJ1F>;z7*pV~e+w#}Ji{I0vJtBRCZRC4AG~@42>l^B1&3)k9oQ=w+X~Fnl7nlELdFUcCyrKpl4c);O;f$avrCwBCF@ z>!qRmAK7XRQ=b?L583gf@pShecNjXr2v{)tAPvAa;){ji_0?sYne|Ar&{-bHm$+Bs0QJs#D;XXol0X|5*+ zuxdx=ihnu1>%i6iYeVD@X7;7C5<&eEtlhdD^MK!@n5 z4lDkj@ftsO^4JkmKE7I99b7LPA8e`vHWSf)9P~Om0c!x$lYE{K*_n1F^tEvV`?F;! zP@PjOO0$mt^=F|U_M;Zw%gLkQ%3#DwcSaTQz zd8Q9-Y2H$L4|;_Iuei$gE&=5&;cmAx=CZ(E&z%Ict3KuZkQD-T1XZQ2c7WF$_RC=b zekeLus!*VAsO9RR!{@lPNZKxYx1zvO{>_C7dG5`2-d*sb=;>ZtP4=wX;fw(RT_#7n zgaev?Y+If?>28I^!lmn-&_|2`vU(ZnStoBp^~gP`if{vSss5_`iuu_QvByPKEkFIn zQ!-jwyjK`M!yoSn0i~wWIq3%J2I;QRG1!PPVC?@s?-#zoaq#fmX^_I}AXU0#Fle0s@;wT}}Eko(7vf!gaq5u zqcf@9$ON5Ue*ey+$bv9-`iH0$}NOFgKH{nOdgS;$o@qNel%S*Ub}*!LgF zy*3Boc>vSujuA=jPPZ#Nia}QI=dV1QcRS|d2<02HIX=FllnFUUOO~{kx%@My7UJMN zcCwZB>?Kd$0?Tmw_i{)>dQ)S{34}C0#={D>_cht|j zp`_RhT-B6<5tixsShEdscpXN8*Eji-@f=H^?qy0E0;ko65D0@qak$X0QjFz}hs&>vy$c)2eiQHJD-17!uhLnDDig(>Ke_ zA%SN;j`V`+c^s!4$n^4t!ygQ7#F;SDsEgDuzR2n#4~NE&3Vk{8RB=%{Dgg@%2&5*S zs|$E|1Zu$Xbb#lgl2c7n#MPnbRca@Azkf-8_ZIFcLcB-8(j(1oG=ku4b8CxRsX0vd z$no>s{d)R^@#YCq$+rRr$=pk2O)B6!;yk)i2gC$7 zApaA)LhY+dwZlnK^Y6fS!M>`sscnjS@Xn$&q59n$8u1a`5dKXB8{n;>a0smy)LeL> z^`!JrEv4{1g{gX%!!uQ}f(ThFRC_7xrkOM6EoodD1~10b7}ufS#rR?EU^edd=QG1~ z@JQJ%jHL!a!&p1x4y$ff^nYO5?N{4GN7^W2DIUsBE8v-sa%BB+1kvQZCJNcB|Ka`4 z|AL<2i$dUESJ!WtJrOAX(y7?lHK!H!VsP~&xhEv70ji(D@&Y8xp~&xHdK~e?!PAAK zckameTatAI0(m656J*F)&N!pHsOtHZyr3mW;(FG&T}ye=XN7EZ{fE}wga3kcO(H>VQl~|-jqJ+zK z@5dJm0v~WJ)U#>@s@rFhbKh(%FwhKV&pLw^N z4ll~peB+f-HiZQC6H;WUpG1=I``ipASKJ2spL)}U2_8>dKgDda(y7-a}o5?g^W zm;Yr}FBkIWN&3E$jIwliGi^Zm%AWqNDQ9@W0!uYoXuq#%n)lK3=zL~7^9LRG5rOzB z+z=-3C|>C?LHysZ83lDaY+32o*GAs*))ul8y&{*-8pRO9`{ zlMoIa<(KR|dA~Q2ozOc3s@(omLmLCGB4(5=<41eoK@Dq<1x<|^46NL5CI-W8*k z=M+~t!ymw90SE#kxKm0F2>{8JavijV(Y0QSEwu_O!aU|r6&$F0TlwcSr^X#dhKZY1 zogueO%5y`gEu5K5zhJN&OHGy;B}MG&;}4Dc1ed0&to$KOI19|X372f0Xb(4OTyQ+q z^!i{DQ@dN=+`bh#GFHJ9xQ4AEsy&^Dt@gk5?B#AuiV`+y7Vzk`GP|PJkmkInyE%AfWV$E=t7z1RdAQW9~re##Aju;LTr4Of~ z=%fNow>?vmcC5l%p=RPF>RKBDxZ1Lx@HV7mE8qKCc=3Ikgo-B62u&Uf&;n|e*zvU- z4K@X%oPld;vg$^zd zR0mg)b%oRN*G>4^L#G7Yxm??jK|?7b2VM;`?O3;%7%)=Z$@&loDa(bkD9LL+0nesU(UO2 z*Tv|zHKAmKIi{pEu-%V`36ytW{@im9Q=z>-`&TEkDe+G+L4eBItmcIAN73Z04{`+Q ziaU!em2B%e#uigmi?SnRFw(SXNF0C~HtqXxW$3b&|`O9VWtqLH&g7&b9|-p!+qojY$u0651yqz@one zsJY9g0fGbKU0_Z@1-(4y%k8kU(Yy1e?RMWy_f~q14yoR1zDaUxstwt_`JT$Ch4@#z z-wx#wE9LOxMMVSiMQt*5Bcij+f4!BGYovWwJ_|{pH&cs!gXggRZeu401ZLD;CMgT) zULUVyg?!L1Zx6~@TKE%YR#s9m_(kScRqw=ccSt#Lq0wu9rHUuMOOvc0H|X7?sx-{2 z3dP;u9S#=8c>F(TeBNUfHNJ@d14&-X&dS?8nBFZYc1Fxk^lz5duC@l(^*IMtXn{;;n{Nar!Wy%w-Xv1r=}}cOzB)stTnJN#2o;^<59Y zCqws3MnROCwQlp|?x0BEj#KEE`0*$z8|kuoG%w!npPo0I*Akzf&uNke=B9Fu(;SB5 z!iWY&?b33=pjtFx`t5?2by2%QA6C5_{@LimopXkN!5^@Zgoog2uiYw$hZfc@zZ7Gt zoo}gPY!1uSWt@6ukkaVI?32G2A_b^A46rqh2?A{%MA=~6mm)M_io;UIbYO%2_Ui_= zD83}x!5#i8YKHy*_Onf#-q{S{LD;GG)#LWOs*uZtw=Gp4Gkmi%5YwL;%74CgY=7&-BU&f!Sh_Nrj&RIPCnxjWj0-MTP1exmi%n8 zLb=%nF_Ir{&>ogU!_qHjRxR^Sf(`Jpe%S9rUo%YBv!oi8x&+|a{$wmS1H<^`ajw_J z%^Fv7yCEFY&621=D3@uHSqX*U!->^&Wp^^+BU>LqriWeyiqF82oZQ(!cMjgIWKqYV zp=p}1ANxbeK4sCIk^6jOrs(bOEFt}{`!!k!AA(Bll~MZ<=w%3Mmg2^z_RjDNBfw+F zgC&n5XqB4nuo|pH9X%8U&;EX2`mgH_cXqqZ%1E1rZx?G?=L(0J_%&Jxd*|+RN){PV zUWntqhwpF5kx^4^TV1u|y{-|$ZR$~vlWxfDlRSgPQ>?~7y81D4J_DzV&$t3Ba6ZJQ zgSUH!}F?j@vqzuwpV9B55Bs&A=Wl3dMqtP zBBSt7QTE7!wi6Uv2c}dK+7019miUi^5#akuFPLe~JQ%HKpg4hrA@X%xoFCHVzNDNx z?Qey7`bC!_nkh-Ad|wqM-|9{K_?c)num4Dx6-T`BQ)O~^<5JZPf#nw+D$NfLbe-Eu zcPVp63-aQ`mn)a=bY==O~2%DC&i zzEQV4rv3KXXB8k}Xq;K->SrD1wb&oOOQE^ZBX2_%6J5Pq?WQEvsIrJHsj?~fkHj+1 z3?y!wqP7khBcHM-f?J)9MmkJE)Y!q!DSzFn+?BTg5u!(t2o1`YY zoqlX#LPzbQdWR%hc!EY%wx0{p(PE^Z$Tfa;+u!(492v-)us`iMgs(1ka%^6GaAZ+(h&Cgd zsuU$LpZ{g|v{c5}FMG@n;&xkRhSD&*8W3NtD*B{H=Ic7275b0~CQFL8 zv2D!tFW#PM)M#A-OOh)J)Mj2m;5^*3a)v4+iSXV4iB3}`3;%%6JpH;0=wT$ zlmH-0;8@@X-(GUmwR5BQm-~{M=xKN73Cv%hQUj6YRm`RsSp)69q6-d6!+pkku32cV zg3$5LE+m#Ty6oPf$C7Oa0`Come+>ZSR;doJH3u0#SYiSn-F8@M9-)!|e=z}UpKP7C z6`;{D+SBhYBSfj-w*c|G!$P-PS}GqWD5jp=Kf1r<^BA0?4#6Wor*qs#P=}L-e9BnM4o5KmT&%F zDlZ zyeB<}1N`Hf^*vUtVqx^Dd)hizhK;RFebPsmc2VTut`t+^7Y3lFkKhPj^XP&;JBA_3)!n4K}t37X?rEpaoadj;#`ynS+z} z+42oR@j9r@jViMruhSAuzupxX3j8w~@&oA{16>9+>c>HgN&h2B(6+j31>BF{671F% z3T+$k_S31FYlrcBOBW-E?wWxoBl2GZjpl(ectv;|k=@KC-8!)v?Py|U77+FJ1^d^5 z{jUK#YSvayv-Q8(>F8ddR2>Ydt%1j=Ykm3MbU6=q8$!|#?t=76Zi8z!tCX)~8w5vj z&E#bGXbt(9B7Cw=%-);D=s(h)-2ruIUlveR^Pb4dz1rOE`isc3vfY}XoLz^SQF(mu zu($OG+W`@aAEWv)XJE<&iPYzc6J5I_@7&1&W4{vn7`Ifd@`&S5A2;)vqRW_%{11VM zzJDp6sY?Jd%F<0sDu|vfkmGiq6ANTBfG2=CxYX#5`g+Xp*f)I8n$vIPz+ldWHm}~U>X3h1jt6sU9`Y(-qwg|MT$C4KxaP<`6mtugPkhlO>{l$(mk+Pr&hXuz=lJ#N0%s;)A(coE5e@??Q`6a2lrvt4ztvl3>%ZD{+mt%~nL-JtqknZ=;`m4Dkyix5I>WI<5L*ZYQLS(Pit(wuu zi6)ew{fK~qzu)Si-iW`DmuSLrO{5)aw0fSwYlPj6JhMFToalQ<6i^a~xSejIp!8GJ zK#v_>@lnwec$GVYgL2GG7O(SCX9Pm*Yv6-7vqR8+6TMGMw9V7B(%yNP1p8u3Ab}j5 zx{UkYPWN7Ibh@5`l#q@9HXO7 zlH>?KPvFk0!jIC_#&hbE9P{XeN1~Ig6;=19@Ma~8PMq=+HGQSk=oi%&WNcbHgoS{a zdf&rmSdd{VN0UkB7(k|lI)jCJZt7D$^-Dg7Ju7l$C*7S+`+FHDC!A1ic9rWS=yUy& zGWs7?+N+Q1y%$G@!Jg!E+jU=6YEj!hC`o1&o^g{k5k=f$=NAB6}=_lXb>~&K&BhlCU>i@0%Q^sCYaLxe>hZVt;hq zatuR>gH+A5*~WjkGK=7ln&_?D4{$=UaI)l02>EP$5cn>_D=JVt+gmTWxk9eQ3kp}F z#Io+D$Mu&(qF*{cf86Y?M&FdgEhA6TgYM*vr$X?mG`-qVI8f4hKo7SI|>EPT^D1&EXE{dt5tSfP}BOK2tPO524J zB1rSTTC9>wc86(`jGHEC1|-~L2EU1?T?0oRxcXW+Pqb%?BpqpGXeDr;aNi$ndLS#0 zARS#K^`r5+6@ZKmk=RXb9Lf3xF?u%{teCg|)70g%Y%1-Crw4uH_viNS7hc9Sevutz z*jizvjR*$)>a<>Mab7TFHO%3ea53D4Ie1r5#onitf8I&eRjEqrdhDVpVLShAI49wo z4_tqu6%?}AD)A5Ic;ykpDK;rAS*ViJX@7hjd~s0vIYH-0*!}#?MyBt40ICA)O0BCo z6KwL!az}sfe4gXLOEUi#%-!1Ug+ayE@73VUOpyBp!)7bbKM!|-^>OqWn-*Gh<4v96 zIUAa?zwRjeXPBz@uH~;Q>t*xZY;jCoL;;oq9{m^-5qmp>YFa;__79p_H0S+C5%3NE z+PKHX%l!J%hTizOO;4w?GK+cR(uxH|^pN4M#35G#um4@c7c%fzit^Vd|8Y@F0Uoaqe*CfQ<6JcCfk<`trMNc0qgM@UE4HZ0`{}y5o1; zS21rq>m_XcyvO59@}kyimeE={0vpuLH2!0m`SHE-^wK#_yLkp*g>3PDh3#KWm{^<( zmi7P&g`z+dHFaUtx|}9mMY$5kjG{74`j>fq;X5w5Jz&T zjH)vm()5$Bi@>YjSAd|8{mPuY8CEG_`C$u|HyeA$-S_GA-lO{Ch{rd+ie}#q*tepA zoUeJrNcN}qc5@2U1vyS?*!MfcAJBg+7Oe_X%L_rk+EtVuBA7z@(#zr0xgu;AZbK6% zf3g-5mUWqT&-Sh8A(5SOx?SViW8N#-Q?x&o}jbmR*EBWZO(@@Fpbj|Tm4 zn05Bq=XvF111!r~_p0{%8YR3AKy&}felsu&6yDiBzIMsN-nN_Y0)ZlRJQ35igjWTy z58e4)?r0=ewsexLHMEnoHWZ4#b`d)imRizxl#ep^=Tj`%<-&l#HY*ZOs1fn#);A22 z3oreBbosU7)xdAar*klZKO%ZxhE^MHp1Mg1R;rU9QLhgSp0PEY4trmc$4bVVE>EFq z#t%PyH;;-tmKBq@f8>hhB6q(H{qn)AI)u9^ywB?K_)h6siq}8#H=5xyB&$Zr(Z#<@ zeaC`p6VGOPtP(yF)USUj$gkB)%J>G!8^y@4NY=K~@4XNhcc5Z;F(PjdWDDj!{t_SK zhp+SrU`)r}r4YPuX-E3)>>i`ttjgY?fQqbZ5{nHcMyC(;+S zq4mC-ApFS&ycv;zrVl#PkZ;$dJ zIa;v#Hjf66#zt7ccDc(GY;~n=ZlB1~OT4RvYLOc_J0yTh@nml%eNIzO1RIEB!eYVoHLv8r>M;lQcF)>l*}1&9E6uL z1Fj2EM1JCuTn8jspE%PnM&#K0*y)3f+Dww3lE^3R2o)2ZB%N+i$PZ6=B#w|6eu5G? zWIRuhCQ_^;HLNjdJ*lC#Ci};q{%+cRCB2j>J;Do=rwhYuODIc4wEW{KdT(+UtAW4h z17Qp3(hpzVXgP0CiX@xH0+6LXE&GoI|1_PJ4j9GdL#BH@j}dTHiZ(>wbP5>meXEQT z#7YooGlS3hYkp&}&a({7k-bBAIv}EyG_$AHnWe5>w3EwV9biLMGJ5{|-RKtou{_Rm z%JK?ljD5B#ZdbT?FQ;(0tg47G-}Y9C(Ip{R7K8hbyvEgbE)Q*OZ_{5JAt9PDhr0xw%e$wIuf=NTW%j?-h2Lxb%T zWv^Yhh?If_t)H&m&JBHPw0Oxj%bZKLa%~Nep4S*IYJjHGk+l(;tdf`BndrHXKoX3P zVS?JRJ~h%VoT_*ZwmbfTCI6V?l{auALPdEl>dYM?CeG z1C7^<&`ceUG`m>#Y=Can{HQ_BCHDr{nYj8IyNBqVOKZ4$krPM|9c@NRRL(hi)tE0^ zFUyEO?4C)1Vb>SbEe~&ry~})X~`mrca>b98gCgjmHYRPaE8}khg0_hZDTi8=c;EJ!;uV^Y*&voa2^ZNCh^Bgb8G!lm5)Fp0Nv%Nlt z$APhLLi+3OQo?@j&4^|ulyK`mxrF{lGTkQHN(CwCFy+2P5_lQU#dni>rtfJ{$ksN| z0Bb>dcu(6K-`CEds?oTI5BuDX-1rv$Be7tH@9`f~8sM$`QPE*ll@FF9VQb6*~N6D2RL*3f!6 z_`J0r0o?~8oA6&%wOnV&fzdA^4*;>64QG!v3_Z7UfwGxIXFrEHF&cBf7oDYZIh`JS zlQ8oRqu2vU)~Q0s9@WwlBjP$eS9uyP_OC)#x&g7;RJI^gv(j0th~#~WRzLIlM1(?u z%cz7$)TM9?*m3xNb@!6s0ntppZ)oh}ZWZ@wI~l_s0%J%^N!d2fvBshivgB8B&ofQ@ z?@!o%d?QzNRTA&=*AC-I$ zyeW5CUtV+1t`T6_D6AyQw(4B zz*baBy6n^S8fC?`OY?OO)0G2`9pG%m>WSWi^z6gL5m!4#Y0-wofyO4%%K9fBT`(q} zm77k9%DfpiJQwxah7{((3*36&2z7w@>LvCRaRVY{$ltoqb-(rvhRG-N#YL4c4@Bgqkra$Zpx=R7+BU}c(xraa4#t;BUv!jS93!qN38*IFh-?-J*nOBo zFvp)Y``68iV!{Gp7N7#ios}s2HF3JJ62^^`4zc!40VPXYdIDpQR_->3f9=Y&!AdIB z>MKsgIpVJrR0a~0KGND3Gtu>W2DmiNTT_=V2wAz$So|1>3nN`k$SSd4LEQ{=8)P#x zSN2(`RNcLc>nFP~8kjqAhL?ZKu`A4e@=w@}pd`r@rcb5Q62K;&pjIGhq+PB4%R^8l zaj_L;j3K`QCldBN=m^gv^=e%Lw!SF~*iku4ktI$@#yX*PRt>Bw7eiL>e|bJw_fwYO z=;^j2@n1Xn%VSF@EElX2Mni8a$zQY4;nO;V9Y| zfu3l0M2oz~V+Rj`wRd&M8HODqSXWwk?8vLPpW;C!AdaJZ*DNOWYYS3h{8Pgv3gaxP zC!Y%(Q)U=F3Yaghdg@BMHV90LpYDe1R>d3|U1`7%v>)WHdfoVbH|grrp46et`X|F+ zDFrl>IX4w0gTgt;Idpxu8-hfaVI#nwfmgCzBEw`%63N0?sUkRJh&Y~q?Ev=QB)|S; zvRRQbIWx}u%H#syp!z^mU;_tn&a9+~ga}=3EoI@-B7VUfvP6AY|)&=G2 z@=NcojrL`KIiqi~zHIsE-2Sr9W~82NjvZiU18lazjkv`#PUY(ZdA+yAV6Ykb0!=WX4q0UXCWE zif{U@)A$cclSMMgh3O)zG!5-#*HXUqW0H~rw8=NUACa+X_7(mNx|JBpaXav%=#XP$ z_Q?A6PnXZvNNHH7>{8{eht5(%ftTq!j59=D+y0i?T8&ZD{_9dfz=Z11 z@`Chg#&1OD`9eQZm7*3(KOLDxke@~$l^)#Bi+`>6gsnB4K$P5JRq5xFHDT0{rn)&C zfcW&Ys$s0OO0ujnYLfp4gNDrck%fJRzg@xd?bx?9fKw}EyusSi4x*biixU}#3(idf z|9JEEgx#ty;wa>+;`G-m7^OUlwSqiolU_T=>5^r8E}oXE;j{9M6+CfdrTC!tH5QX-87`pJ$h+rLwl?YDU$OUQ1=apvDq9?rhOi!^dIJ zFTwrmu}9)Pmj{taGnKNJsl2VfJ)$`u7^$V1j=g{4HB{Fh-nZW`c^uhwZO8^^+V>Y3 zD07h{4AaMm#pNa3;i?HV3FgkffXjb@@Zus=b@G$jOw}^Ssk(51O##>*Vda zOu?@we5~+42_UY{pyEBI4|lfGh73fWv)wi%jh0uvJu%{`zc2S$Y%j6CA;zEk*|S3Xxmr6vww2&YZ%j{-){vu3py3a<-taT6n3X-RGU8s-qn=`G~&E z28A8g-U9~aPXy5t0<Nz{wGO-mnHhja2Y9 z^Jra+aq9`acp6FOe0o>T2YqBYcqP2JL*%?JCUWkY`UpB?6xkb>lQnx4eYr@~6`+tA zt#O34n{P8&(FF@tnwu_KLPIYi=KB^pStFo@KaV~4T;}_ZFAW?=AIQ2@%F5=ABp$!( z-mZJ{dE+aKftPw8c)Vr1yN%5U>gO|@UBD>pR?e~Xz&UEe(579mkGux*+6$U(=PqkU zkY!lhTc&M=zdlLg^Qm88WifNU?f!$eWLs?z$8qMkc|ibhiHx^oZX4&Wi`09N zxk>->FOn109NWV>88|7p`DbIpF5#%mM?d&}?U)+@t=z>L@NMI}Ti)CLOl)?_ASAU@ zb^L*7-CdgXCU2XY*=GYpq>i?lzTuvxT*?FENnh&6+S0EldegkJExL*rPlRF3fnMaA zJFTSnfHMxi!ycDGZB{+nBM)JUcb{*_#~nk96YA(&?~S7^=a1(~%jxM2(gHSMtO-HH zZwvI}k_*reL^mCNqlkg0In`X(uC4FQNf&Ll2)8^@qU`%g^UTNcduG`3{PgP7R*R0M z7tY)fvSIWlpiV&y(VHge)amjU zn-sSqWo{Opol1KAHS+VzmYmp{MCxl#{QBDO?e+IhG=qi$&>l~=7^Art-;`e1YUn2p z@s*$!g4(=`p|p^J_I=K<`^qSz!p#XC<>R>JyDW{R{c@yK0Un_aWk9H@+*5J$&SF zwmfWqH7$y8YYl3DuAe@zuRVqY%quy~F;-Ii5jLTPorKkx4vnK&`^x92`zQpJA8iLtXM zPQ;vg+}@P~auD)m#=EKW>>!6&^m${}oSuM4+_Roc&s6Gj&zbr9EN}Da>WgMY3AwV! zsQe_2$CVWkPnN!_G(*Qmf_oUMc}6Ry7}zY;v@xBle$i`Ckl7?5098nkws!L5$uu<7 z*j}_ykNlyT5S#MQ*7j^vbE8?S*eS4PQ8;ID$=s=UoTo|W6VE&ilh?*coHyFyyxnTv z!4%`)Y4MWyR6$zK>4AX$j%iB09;Ecz=BOorI(sS5Pz_D7&JT)$(X*LM1Pj}&?o;l3 z{^R7=4fCU+*`_>>;38)(?mP)n8;VU6OiA$myJ3Pq?>tGm(8~SbT}P&niQ9+wwARlk zaTDAzeno4sCDcq^mj#jT(TLYf)a-^XRPUb(9EdZ|(%-=EAG&PPMh2E%g1WM9SkuOQ#>@2|`&6YuJJua8FJh-_H$Fd+rW0vE#~-m;?IROd9N z3@y^Cp}&;JG~bU#olmMDlfDz0Sm_SSr}YEF97hKTOsz1*ej~U$>!ntUvO&uvaIviu zwNy!Kc=$`Zcj(ETUW;aq=8eGZZmUl~gOIa&hAx&ix&}f}7+H3e@+0Y>(1Q?aV}L>x0KJtxPz zRAj|y$P$7|5*~@D;O(xM5~yxr4WgElJ+xnXba#QoUS5liT;cs0+KQI&8aoodcB%z> zrfs@q{32;!IQaOdVjs`tDoGzS09m9Y15xYJxRUN%Kf0$@yttFrnAMV!m5(gxvqa}U zQt1%!tf63DltqH36nL!j7YC**1;0g5Q1&-ndoCJzTptI{RHezv89F3q>UbAt7^)35 zqH|fDDr+O=iG%%AtZb8W&*%bxJ4g1Nv*Oy?xBJpKLy;q~6`em1;~4$y-HJ)h`foIO z%qO8VS|t~USPD*lYlvxVKcxAOM035G5&(~B2fdyGLwa=^6LuNcoOq1yk)nYyG|sA0 zLGeMwo@rZ0krInGQ{=%}z+38Pf^UyCRNfB=F&NlKt%pfddG4BTWw|di{Cmq5tpKDO zjJb0JI1*M#K$d4sIrvLq28he(^FLN|POL-cBH^tn;)^V#d<~rr3fU=tU06%gJGFhi zwt`}(mk%KJ_J6q&zyeVIzOM0D%?-_~oL`u%oM4T(=jnR&>BlHVxxV&?be87o?JAD- zd`BYNiX{iSc<${EtD0b4d?IQebjO)f9_t@@*c1Enx)NfWeJ|>DvYgJ|&yNgjFaggM z_;(_8unDHE9UdQYE9^?Ub+WXY|NEgSC9}z9s`j2m$HRVpmPGt6-1%|_$8jB@z=plT z-N(YCJXD)<@v$fdT#mm@kPXxeUeJBB+--C&=VN)uTY%Y@3^+euH`okvt@QmFcK_Neb#SiJa)q}wL7Z6}P7)!GA${V#0T}&WskcfBz4}cg-rSGK_n8SLr){f;e}sXy zipZH9uWH6)Mx_WY#C4HX3aqkbpRe;MTI5vJ#ANXoA7W|nq0I&BwZL_nZ+JPeTZyCI zvd}I*FxyrH7H;IO7*xP{TkHEp6Or;)!3D6jkq@fA)sQU9(fNWBrVO}&5M6fw-EKP7 z;+tfCLmkp4fRgmW;RFe&S7AG|EItS_K#VG6l>K|~AyF5MA+_QoC}p-C6qUf?0jIm>0(SMt*$VLX&?z{+JOs_swf+ro)uw^HoVwJ#Vsq}a zyp9Q$7dxDD^W<0e9=%NZkEGwFO{6MTwsOXeWgUz$Uy{*4=~6l8KD+*f&L!s%cW(hc zE%Bo`rzXH_o{c$hRW~cBR}SyAXSs9jf(A6yIPJn<4vcEfmjxWPB3$;FrW<&ql>#ps zjBBgz4foPmfF!1pH*t{>09#x0gBA`d(ZTaeP%h|??bg=9I(0Vb{XN5~W6!Lf1@I+H z`95y5&cX#pFBrP4lAhAK-15-o3~i|mq*oTELHhUHb$bThhc_RA3nz&K8 zp_=)1j4!`(XW3G}UflN3r^%3-O%yENKCs?n)tvD4pCtCT`CL_GVotO_l%jnbi>wFR zwge4dH%iD*MPBoDZt?Qr>n_lQTx2NW#iQD>(nZk~?ze@PWZlq|t0&IcM(7F5)=p-5 z&RUar@)5li0jMaheN54@U$LQ=Qp~djt#Cx?MgTDz*M*x6m7w#3h|z7UZy)#mBbfzP zbLN{rpxdi7`=;<-{R)P2o!MJTPs|~_2=#tg9h9fouVM4r^9-C4q7fVvwcEz2U{g^W zU_{~d8A|4`kQea7xkVMB|8K}A#sP{EH&({vpsLe6uWRKk-l{py4M>El{OD&7PeEJd z88VR)C^5K=_f<}{8cB1bmnz{GsxHUKwcE|Q9lEjJ-F4U8xtnU|w$K8E`3*4ZwE{+} z=sM?0Ay};SP47@`bO>O&Yy?jl&V6P`i5^_8b?CRk7=-~l433{I<`Kl{2AkazZXwyr zvS36Zq;kC*R`bsEY|J2i-#lisqHir3)E8oaBPTGj(pKRIreTMBiR^(#fJ`IuyOn;z z{YzT{qmhhD(5Hv8S3g0Rvg3`Glmvx#uyaT<@$dY_BvCT>L#=YHy2NCBJ%>$X9E_Rg4eBE zv~~VSpTW6TB^vYZEm#Hj@>?Ek5l0AA=XKZlt-KW z5xvSnFU~#34y>T>E0y#8wN_9gamCR}x|Nv+MmTdytgI5K>bw)jahyxOc;d1Z4QQOK~b zL=te=Dna0zDig}9Njkh7__w4I;+QS}>F+2@XNbW@>rLa4s(ZbIH`WCfKdSLzkal>B zyqeUCHF9%tr&fuHS=He%l_XNU?I+7E-o-ivb9&jlfh&ZYvn6@MR|}hWSkk)mwLNFX;4G%Pwry-^d`~ZIApHBdbG^PH6h6W_V0h@#0>JQVB8W zU1jjCDv^HoU@O$-5~pU@pIeG}L*?+J^a=s3%^&s|;-UU2A99d+MiI79-Yt^j3nQAC z$0r&7mLI(n4fiCyt$47>u4<8aG4+59c z_p)z_Dhr*5-P!5^xKJ0wlmUWrZER;YN47?3+QD&Og#gV$tHnBEya`TGt*4w{JK2>E zA^&)jBNx7*wrN3M;A@loKwHg*wgaV{v+t^Fp`^ed`yWZ#ce$AiLeO<~v;5Udh&`K| z8+Cb+t!Gwm5A zSp)7-$UY zW5cDfZ@2o0M=va$Rzb7M?VKF{Uez|cE9ezc1drBISoTQ-lVmVl*&u^Y%z!3=@>4J? z!Aa>BTy@%d5>{3ls2>7Nx++sYLYgzl;V{ z2=Hr$`9b)4(xi?Z=MxIn%xr6qw;~?9t00q<-IF~}*^TNn9LG%CJ=j0TI&y_7M5Y2NV0{P>3gVqnz}R@@*| zz|3&QtSQ)i0#b`E>E}+o1ph)*7~2+Ij2wP8QeH#9%AGjtoTQXkKob+GAsz=cYok%i zH!$S|pf0z-6~iP#uVmKmgH#u?k+T4L(&XO35&k&|7DGi!OOG`ddf@L;_x)Bd5z1zr zpV}1Scw8UjKTr$?*2BB%v6^W?{q;$@!!a8`jw|3bg4CD6>U(U7cW7YzUzonBRA3x3 z_(laqn9Pwm5-lzc>xi8;-mKMfT?*K;V_2f*vDuZaR#;fuZb#nO5A<4g4wTAJ>`nW8 zlI;JzmqQ1FQ~`XlN0{a1zE|MS)lNcs$}CEKQXq5An*Lz1>`p^1+q+St?hH2HKHusQ z^MatqTR_UGZW7EOH2(%iH%9Ybko&HsTi}WQreZpMPQM>3{O^Ax5rNvFo@>I?0|#^M zRq;`)SMNj#BAD*1!Ykp(Qx(JZd4cubiGn;=nFqo@Hpw4)_F8o$>$KD~d_&EuJ?+~} zpaHWRvX3X#33jyVSiLUWwCv4(t)kOEuN1VXn5tAi zy&)-P5B;TfqxI)Mp=oXC!<3f;goB7m!U ztT_W5j8%nyxXkXPZv1gu)rjmOW=p{eWx+VpAs1|vE;uxw?kv`Fb}S8WEgc49c*$;o-rPN%fx4)ZeQ=FgJw-rXxCD%y7c4I`ed>ffv>2M%P+6)Y0Yn!Kr736Jko!Xo z=AOYYQmzE~t&BVl>Gox=n8UqpN%>nfbdu@!ne9H%$JGY1dnr0AlJ$7b?WWE1OvhUX zaSUmRnd>lXxW&QKoLFSf`c|=T2xe2M>%B>q^RWTH(`Pkq>cvqSUp>4_{>Za`dnVh3 zCnQKbtq={fs>L$R2_2l4STN2ze$UHmsuHjnvd*mFMg*^CrP$vm#+(Uqutn^LNK1N2jy*qlOYh z*uukPR&7?%E{Gn+JMJrqR-7OPa{z>R>Pbk9T?tG`E^xtqpViwd8B6 z1stN*Ma;R(2;Hn4y56`Ab{f^dz?ee<+nU9LE>ppRC<1UI_%n7;bjGb&A5#%_(KL)E zJkNUlU*BTk<2>7bS1p;^>D3U4qT~>oK;;tITc+}%O3Q1m9=_B{kB3(8anJv_z|6Im zBi4@MstxehMTonQptn=O(rZm@(LTdUA0mP@#OV}PIm=c&_^6& zahd*KFtr_S|Ek}^F6dfs_G@L|G2%xi^@7ct+ukFu0t9oG@s`IEh80OJS_@1My2XS{ zdD#zp?);CVtBz~xecLD^A|Q&=EeI;z%@hShM3nA{AT=6k*c7B=0umxH6cNeM-6;}7 zYIKc;!A1@i-}n69zqhl`KIc5=exCcfujoW6)5`yJDK~qQP?oRW36KK$Vqv$`Lau(# zBfJ$20T!tC(UW(cDp%_tQ8|9GUo)L&KijoHaJSutz%Ek+02Qm`s%nYeu;bXQYJ8En z9(%+y87eW_Hz8&XlmS4&-vap`v6lf*$Cl6ck{h{pq5Km+8>A)D7%K$+Mp9Mm9wsm( z=Ux>BT4fi5_-G8W-yPgb7gPkuMxWb7t)6!eh&g)|Cf%{Gz>ITGDY@42=;$BIWm(Gk za!LRw@wNU~w_3Jvi}aDl0?&S4+rJ8Z^-K`qz1WsOMyNdjxIni;t^EO6G5R@9C`;@t z^>Jxm(rlbCi#z6JJb#up{|CBTbq4ZleAS+v3*gg80&(TwinH%bZz%*Nqm$n!=XPhny{hLY1sOy0r2iZGzsM%l1 zE{80&ziFSb^qhIJ!dkn0g!u-@)Ldp;hf+IvhVyki?Qct>Os%TT3H9qybvnG8}Hdr-Y!PR=?(&Nxx7YhnJQx&$#(5s9VnKoB?m zlwThRM8C{cl1N!F+dTZif8N8uO`V#QcEld%H+$-pHmT>@g0fbvuM{66S2jJUzsXj6-~{W<0pr|Jw#GF*nrXUJnH| zH&9p>DS);YXVj{=y7=wqEYR!Tpa{AIhvm$HxBWRcGV8nsRWSS-lQJJ`9Q=<_@_o=b zKyz&nNl^*Z>AoiCrx<@L# ziYyM_YxtA%+SiKzGH=Z8D#Ud?yC>6guKvW^Iw3;p3P=Bo5Hmfz>kb!+v!v6*{Vws2 zfIw##gWqN@1Tnt1J|Pc*Kz!MA1MbR-Vzu7L(1{d2zvvyporNGYP8*j(#ac2}&>w1> z2SXZxc%(Uz#(MK8^1ZZ7Gw-11asQnc`2ic)?bFC zo*?sn;%k?>4Wi8~^%vnQ@d%bV%YM#XRzeee59K>5rD{?2tix7%K)F16S+R@f_G&*i zW7(zF>V7Q^Wog3Q1G||ZXErt3^PTze=;xtl9t}GNEGtKUrF_2DvEA4;Hbxu$13U%5 zl?xeNFnSk6&DF}%;egWlK#;{GR<1tshmGn`(ldLQ6t8YP^f6`=+0g!`IY>>K$U=G; zH#5X0L-3f|Q~6~zoc1K;((a%W@8kww-Ntv%Wy$HY)$#&Q2G(RNf_n`_$$dh|f7L>h z<@G3IX>f>{LGk~rrJISo1lmp*gS?GoS5x9rbSYL|^41Pb%oz5H>a#kND_;lZ(0L1g zl#n?+#znSt19L{l^SoDcIsb)Eb!#Y7x@=jayD6%8a$EDNoD~n@>1c?_d&~X2&VT2kxMwUX0qht#!1Q24T9ilU-N_{=LV!c<>0BW zfc-eozIIT3pk=|=_e&D)vGoo6E}zy(!kizpxED_M`3e4Xg})zNkONEoaY_#G@lKAr z7^Ga2Eg+z^fyG|FGX=Y~UlWhwG$#o(*0pB5NRq3Q7C(*QH@@~e@0vdY4Ye}TNf=Jl z^axOc3bjwJeG!9y{*US{7al?klCz4?DzccXs@#rFc*8VA@jKydz12nlW4^t36QnC4 zMJF_nfEh15m&e!def%aWbi7A3+e7-|Z*L6LQf9 z^4SzS_CC5JW5k4+$GxK08%nrfV`UUU3|oH+tke}QG$XOn<-SIju03GBInpLcuxL91 zICPU$2afd(braWPqUI4MR ziiqp)=cX%svb3gFRW=>~Y|t%}j+{!6Y9FTyfB+osz2kUxD%kcReAA%Cm`agP&gUL90Qy%YrjnfhK8C~R4CWCNY=`~z`eL>1 z1o*zGZI0P#0KP6K#i4#Ozbp8Xml3d^BF&`Yyqpjp+U28Qowj*a2QyCE4TcSMb#?50 zUaXG*iSA=i4%D5Xfq##PTV!n;igqk=o<9~GdZn`o{sqGsPHUGD87*JLH$qWRrutN$ z{d~}4a7I(X*)*G0!%I%CX)DTk+Dll@yL5>DkLqg_Jl}Vq=Dv)j3=r)?t*Vm!!+y=~ zXi*cpL_EnEpvW+B3_-husI;4$U0FAYeRY&@HSy#2zUVr9x_HYR;5|QSd3<$WzD!1! zEw=!B4}%v6n}B=jLDxub@XvK;>Gf7Rewydy+@bC!vdvcxo{|jEPFai^*NmP1WNB^Y z(t^j@n#f=GDJcBz@(8jyKIifkn|#&hHM;N-&{UfNA%f`?6k8h~R`}k`Fc17Bg&m40 z3sb~t5{W9C%ZUT-fJ!8Brp+oEK;^X1WvVK==e#EJ#HFtnf;812xSFrC3Mzl0nBfHE z%m6y(B&Ctd0=AcGkg*uiig_^yj9U3Wvd%8JtVxxX{N3-Hhqt%8?=}9gRXdmHBbIYT zqJRG>)2zaUt=a(gtq9K_F;FJU+WM?XwYu6MLHCOW4RA1z)WeHiYIGc1b-Oi6ciulP ze_@Z)F(Z2J>ESlkuUd+RL=GsgNX`65^{2fI>IEHPU1_04=9f=R%F%CnAcB>Blt?sT z=fr!vEb0A?>fDa|sd%>vm?)88hER?w zt5(l`!?vY)f}?K@d^N=!V+&h0eglg1k!5^1O;kPQYFq$bh3ivkR%=rv1vI}uc}+}b z2WEL;WC_DchPU4;JD(qP_W>>YT1$iew!I)nogfG!OVLL;OL!SvSv_xigBlEBc`?^@ z);_0khY-o2+w7m=-0a14mq`kEpN7nMRWTFTPF*EAs1bBD01}j6g|kFir=!(zoR2_S zCG8B=ik(Zb{`uYySPD?!{qua_t&o3$d2d(NqM3RA1Ft6WcM>Xvx=;aUm)+-M**?=S_R{9p#V%mHZd}P!G8Ng@N{^%5{+HH6 zQ%D_B{k%YD(<)cBI(%}usn#ak|9bD*Us4IWJg9@To0(Qa&aZUUphO9ck-j)skzMNmUwI5vb&w=^v(l&f)-ao^at;8zQk7*V@t0Z zayKx4>*x2@U)kLS!eOz!*3}>1)mnY2ahcBksgKAE{x~t_6Hxhn?`>eaZPbxgV{IIL ztL~SesO>00cEOJa@O2whxaTV3W!=Y*x@BT#z4J0k2gmZ!tKWTZBn5pu<LZpj$ zU#oMRQc#*s9R${GT4;*f8@rtMP1o)0WR<_33ZwQPRk0|xT@6DktJTb^k|6u_a*WW8 z;p7fl(rIZ$Fa`o9wkP8o)P1=v!N&*xQRzBu3`cz|C%q$}i!1^iSsSNLcr64OJ!bsOln4}?NLx+eTZi5EpS*B)*3gk$>H08Eqh=zr^|IyfiBa z?S&DILLL5L1#@?8&^66L4u_w{T#|btV?;O}paINs3Q^e~BYzOnUfmXpbUn-9EYA-T z|2rHY#g#2oT9TVX6ZAFVo|@W^^zHoTU&yuBzqstLj-8kA*1tC=y~1Cb|H(1|DBq|U680UaEhpm8QVi>Tkw68hdOtGB@K^iuY+Kl{LeZP+T^!P`;f zoLVZ@LpVH}V1S%;9vQhP*$oQMhcY-LdB>jlIh(*7LC1gJlu?kNI z)Fp7BeuB+w`8ez2eom64scz>;zG`!yVwMe?&fv1~(dBl!T-)xDNxDr`C&`YtfLGlu z#X(m1P+M1PeHRHaY=BHZXWiJ{40XM2%fM$eP=najsY+sY{1npB@SAmuio4Zm3>g`JR1m0PNm_t=U6*K8sav7RrIcr_Ds52**j^0=we%gWjMpgtVULR z%xdnP&((GxG!{wdqAk{|1Nvp9QkU*4yM-<&%VXZM9dM~PF?-Mn{z%_fO|*KWEXu2Q z_rnbhM5Io>OF*#AT83%+b`c#z-&1Ru`d8Yj-lk&tTDU*e5cdp5;bn7DcSLFf6d)eG zc$?w#H2}#c^yvYKmL<+#(Q8A_Zz5%NV^P$~d8q8+i+gF6>-Trqc!JMX+g=l*LQyf$ zd*0nSnuf|K9RO=nEpo-4HhWf;2*o}6ym=*r8Z1fDMR@iw`qET+ZZMPvhi<6jTcr++Yx z+H4dW%ivBnm3W;^LgIx>pv%?@LCY?pfP4VKk?6GBS~v)M1F*?bFt-4gL4DI>XN1O2Bh zgd=_vqK)8Zi9%WFS?PM8J3>Fc^U<+wKsq(oj2?Jml-j&0ysBNe0VBgOM-^!O^~ z|5h#+myc3u**EGaU!W61HE~02Y!mM2zs^{RmVvT(l{R~)dr-ShC8QO-p#kR(k&j+kUo{(Z|{>P9Y@4Ku?n5MT+vOkA41(XkV zgRYCI@!);Njqp=nz2t0EUvgqx0>F*#cAdOhO}XDW{(7i!J65z63NCKamRjk#i20Ad zb2#i4UK4G#+AyQ~;9r#{IMocA@qID|G$imQ^)}@1{(?C%**{eSKI!azEIo`IJ-hhYh;|=8E_6NuPy~BJfrrDa1MbR;Nj=P~;FnV+P0u#G*Bh^Tnes!j@ zAdinL?TjQHT-b0yu1v=GGJU2%`sSeb;nhq1f4TUN7F)*9#z&=Tg7h_;LTi|M z`E5{DG@Ku&*rWc=qb2rznUz*!Rb!1Gc(HBxU&8`*RP7wykX|W?Y#s@bN~=4RIdSN z7Ux!*nNW1c@Z4vn7#o@A!7jxYdJG7xPx4ns3V!y6_<0v}{bO^Vo1oEsm2{O>(ahwUa#0qv=s$f&3FK3I1c&%!dW#1YX z91v#3XR=PT&(t{z+zoQCuQS8<12#mxM5$1A&xE>*_UM{n4YU=nNL^h`ZTmEwFUrtN z+)oahP}0ZZ4+OBVsxULD5^V!To)X-NF9_A$6z&?LaP*G_dOh=jg)fh&l!3?Odtk9r z5M0JXZ=Aps0i;!}c*b+Sto-ppY zmhYl3@QUFrG!Wd_A-#ZoKw+1eQe`Xu) zr!OUK@dbnC~Wa4 z9yapQ$RL;zU+{5;#R*SHh@U9I)7_9(>9BoPiax4nGsWSe< z2%A|djNi{4@7g(zxuf?7aMU4WWwU=lXT`M8H^k*CJRl(KQp8AmonG|EfXkM`o=w4P zNsjRluN2rTs=;r_Es@yA!E#LH?PVB&UWXUSMtO}?4_h=&lU*_5M$HifFcaKw=v z?8K(x(Vw4Y((307%vfQaRuu6-PYg76MKW52S!dN9I3$|k-je6`^`c(!(@$AN(;)AzxW&5YfN%|>I329|>Ee0N^hho|y z%FZ*!mkXEFy>~69n#x8}cGYuGGZNQpAd=}-?pJOGzR>|vc@OsXP&r@H%KhnW9Ju#O#&p77QJC{RzEa|`iXiU!J8l=+rf`QD{*9y%m|Wi01%`ooEj zNN&}s{mtbydRIc?zcgjexnP+xR6PZrM?`^*4hC43F@8VB)Va(3%UGKc02i#&mSn{n zPoxQpe=$+!$47WkTY1H2&TchD&afvxJix9pwKoirPY~RLGXKM_v@~ngkFS`R)!TY= zBKf3_PmtZHxsd)@e!>s<@*EHN86kw`U&FuS!7}Y2_g`Nv0){?*lRgm48@YtxKpGHg ziAIYguP{k)L%cZ-9xMLZbZ1{3ie3FuoNz~%{S!6$gJ^e;ofEg(+vJmfD3`u!AEzC& zem=&)h*v;F86-^n*u_afCWA8@YtQ^^%shjGWBJCj-Kk2@ax}n@!Pk>mReMz;*U}A_ z^l|B%*sCLcS@TM5|BQ=eQm)Y$z`EKMH10tcY$bu9HT5T*mS?f>7&YnNSH9?nhR6F*q(ynjIpkBx`(i8Y}-v6Azqt~2E!Xg1>CWBK6l`BpkXEd zR`%q+TV**PRS!+&-i;QQT|Ss;?m;*ZQgJ%3-ERB+c-p8wM?5*dN2ZVIS z-x`xnfqA?Lp`c!fyfNXDUgj+7?L}i$UtCVV_&F+}tr=Z!NrEO(3z@!F zcs~Dd^CQ}eQakWvd-!(fFmV78O!=!4{}{lrZsT5H9R0+3N9*=K=`vFT3um>hqkeel zBZa@i{N4K{C905EFVOKrLj1CAja>Vj=yETmyGjod$j7%gc#WHU2R+Bjlu($^5>l9^ z>M*wXNhhgU$h3dkx-tIjUmKVt{FOi^KI*DlVj=1BrA1NZ8bv@0vVc%EGK4Ao09G9; zid)t32k({nhj{6joG-M0i#uGJ{ngWtM_Kbr$v`C5E{*Emx9cZeZ)JPd#7tmuI1hJO z6cbH&fAzi(jAwHDK$rKum5IfL5u_*{Hj8`_?XTbCu1vML?k)}cO7S}fo(PTTczY$t zIkAT+Yn=PW&c5`8+19)CAMOA@z>{jB(c0E>y1ONt^+;fbl+p$`D27+LwA&FbP=jyg z1u*4kK8&p`BZbmmlnK$??`wa7T?mF?sVEfiu1Sj$)h2+G$lNss%UDN!`r&T&o`v{>EMM5&agv!)gQ zQSGOOJcMCYP`pUdZ_7|tpn{oIK-yz89*&2n6JNF$y1gEt`GP$94E1B{h!0|01+4cE zucg@B-M`9<|5KI#1?GALV@P|we)?AdhWbUZe=Is@?PKC;)dUYxacWnS2D$l1| zrCvH=#dH0f33iY8na1U}UO{(`)n=FYWcR~BL`ASd8SyGX!O!!-@xpVk6nu5=GU!;o z8z>LYmLW*CuY>n3j@1>m?1J~}fWx5_`=0o%J5!0lo2o@_IoO5Sw>Z`2OLIyDcn zXSPzG{XKN$WvOozXOw& zu%Jq0RBgfgBQ@z&PTJaI_3BlUQVV-F=FhzoqV#)w zvo}d6Mfc#?%Zm@_Z;S3r_hhnTN$r-^_IWymPp4egG>GQ=hEFqwEvRp3hgCgvmo=`c z{XFPu&&YmVkL5h(R37hlf514HFNCwg%Vzjn82EYAZN`xtucLw6539YyX<6U}ggSAL zF4dfr8NwM-c%Ky}o(7mDNjS8M7=sw&%J-hn?n>!^|9s~uEpP;i=T1-NU zTy2>qZ+XgXvauct2Kn?^eLpLYKefvZYN7eLV@AW~k-?q8^k6RPA=A-tfE~sGrOLcN zg6g!A%Mo%p(yiO^tRJ}ZqQRbd?%B6?X2w7fY#JT~CL>4!-(A}{h(Z~hNqO#$aUrJJ zk<&xhX2$o?Re?r^fS*D{Z|c$?E7-ILj^69T+;i=vsj9*zX(u{fxQgqPqA?IggsC}XaEMDh`0lDExY}E6e(9iHdNJ%ka=vEUn4EI=C&kAgW*m% zUOSUNgY8Y?_QB!pmqPF5frHo-|3xKX(fdD3QG)i{pP6{JS96LAyzThO`A)X>Nd|Gj zGNGl>L=`rkEQR_=i)Z@tDffPm^%a{c}@isQ($Q>0F_fgr{sI)d0Z_X3N$)zQ9U z>!SAI1^4DX@52*$ys}1`y}Nop0LWFZH`u>;$`%aN<#lZ%U%{kH;^a|SMtgxFo;L3I z;PUKXNYP?Jwt{Q@?G&yv>ElHL-+k_rvvQ*zFeg^54deo2$odQLK5x!yU>_M&|6#h* za6RvK3(FNCR{2^hzC9kwO?rSg>sGh(^w1Cr%(v3#p*h!pm}mZDH5~3wAFR3jj5rb~ zD1Z9JYsB@yerIPj$mT~_D$kHvdd0jIif4jNx^&dKXD-FTZDq3QrjDlcVi`f=tvqo; zgqkP=zysi8<&s1K2-irt9m@5uMWLxBci{P{DtRRzjd5G;`q;OVp0Evxn$ZGB26gie$LV)k$(*oJq%WjO=vvTh^MHZ6kWP*|=PyMQ20P4pB*d>}vXWTI=S@uMRco7vQ^( zJ2E|#MRTuhG~=XF(z4WsLRa$|U;3Jz*G8rp*HArh7*R~ZG?0!=Ni;Obu7kv2aAJ$% zX$cRcGq|9w`InZTjNWzKD{ZcHR3`I{uWqOmDGwVqhz$^e20J71VYgR_3!+X9=vOO zH}qP}P9{g!C>Njt`;i-rFle}+{_Vb5)~_q=QMQbqc6hmPfk*B%Ysv1cyI!L?M+&3Q z5>yI>ve6GPDE2KV2l<_Muq?r!uP(6uY1E@z5|Y@}kpm0bW7MYYe^hv;PENn}xE2sy-BA{#{{~Vn zp*&m|o9)*dunHA7(2gHPVr*Vc){J$nPGnkb7|CQ)Q?!CalGfv#lF!7N|ID*xO9H(` zL9k~+3w8c>8(VS`0WBqm3gC0LZ{R!I9d1AeogPHcDlN6c2icI)@LkKH*3OB8?kE@Of zpcZ+E{+-1X@u||caU+4h;#SvsSbp3}EHevyc$dR=A;*#U;o~T8YrABqnPIzV+;lW+p^sIo%9%0#Wj>^^MiHY=HE+NG zH5SQJeSD@wk~Oj=Nj1bx%gz_(E<9fAPo9ojB=}swB0?FO%JYQaSi|R~M47{?o6kK2 zQ!ZJ&!pE%e?Tcm2%>!}iQb?ECj`bjm`0pRD= zrBCh+=d_mfL=8Ai3#PwR5@PJ|wa#WeyV%2Xeci~iioLEQ^dW6tIvYxxS6&b*V9$-% zhB-I3ep-3wV*Y_P55*$m?k0F3IYYfF@`D`Hho0=c1JFibQeE^LlfQ~d6)FV zMYT^#!=(RpZ>Sj_yvkdMw_nYzUd^$4V`I=G%QZ7bL(f^+PrdzFl&QCAzj~F2-^KnF zg2?+Iak^56JgEPHAt689OB!pqBii(4>Arxm1Mk&4W*W@lksD`DPi-b+X7{bQVhSU} ztsm{+DqdMQ(29O|%z-*8V~Vl-lq*N!=gKG}s>( z=wYr2Pa^l^w(Pu2P8b0mf#aW*xP&Cxkw?6>^UyJHbvb-l^xGhhqDgZ@?Ck8OUh1%g zyF^dq@Sh|T#~aBXS~3=npysPT$;A?CQ~pX!;pvonxP+#Rm=^W#fiRQ0o4N%6w0~=u zgPB1()+*orLw9mh+Xy)Ycy=DLBtq;+AB&=jtk|2WWCi7`dY6xs| zg5eqiK`t)bSEp#=P1!203O^DAwa|@*mNsCLPL9q?`~+Nd>?i*OjNr6E@Q$0ry=nBrgihm?NB8=?ly$|f(7s1egIRdX6(rrz5SX+YvH3>9V=n3Ia2Ox{+X`~;yl4hIhyl}p2ZS44>)Ftw z=UMNHpZ`^>HPL-))oFJY@Fr?F^b$V)R2{9sykOW}5ZfoRy5_yI?rFW#x9El-p)dIJ7@&-fcwVB`NqsZTl~DI(zl zt&0=I(7-#|Id@d1@(YH`=4@PK&Ew_Y6g~L-TYx}ppjG3oJ)Qi8dA?hTm-^)C;#?lg zw0BPAafQ{kcYfusnz9=O!8G#iPyZ~uO?7b1%UGy~xGSkAg^4EMGm<>ka2u&B3ocJja3oGhZfZ(j&0B4d_i?II7RQHyj$`E4 zFU5)nZvK-@cz4Km2QKN z)z+4!mq6UMim6O}xkK6vWow%bRgzBBXfYv64BJ`jWr>?Jg6Qu_q7^sL4aRPtL~<`} z<~DGY7wRokJ8|daWXUmg=a}{Ja!g7Z?Nyj>!`GsNr26z5-?U{iCJ?j80Oc*hk!|mP ztlu!pdE68bPq5A|p4*h3Hh#!*u2UTN7xUy_O||!p;>FuxFIgtb`D51-Z&qb+^R?UH&3-A*RlFaBKkm46VeX(C*rx=a%^6a)8E!(5%T&KVP z&f{^mBw#tL95;4v=L+ZEo;zSsB5Of^xLSUOV*f7lMnE1a2ojPrUlGt zQ`?dH8Q#GsMI{d==5&l4XX4pGysahNDk7gka@77{>WAKLQZ9;y&@ErL);+fKkE&1c z9|%UL>r*1DvYr7DKZJvW$O*CtWamg^!>(Y&)gAsW&b)}9Q^kC~1Xw(xM*y0tIvC^(XMAZP^?UD%fRuOAn4|xot4dg8Z)WT8J^cM~sCpnz(h`A^JJ~_q_6in_0T;J1`AWvN zNo|X@#It*xCZpYutS~viEHk8YL`@do{p^U!Y&_I3HfdV+H)twfGPrilC38jyH3|TW z35(!vP{Kvfeb$jv(KH?}Q|nE>GJg=~3eZ`WoYE#RmX4xeju@V6AKH)pAB0dY%Np|yx~ zo}j)sL`RTktVob!gH!rR!8&q-oZc!i)np7SMYsDI(0Xk>IYOGKv!zh(VH0bhNS|wqrpki=# zyUA^uo2Tn09hnjOYR8n0Hc|e*%=fYr-Eoai&$~%vNW3^N#V@{cB6C8)Sqth+3BhTL5l}-pZN!AwX^kRTtj-Rg%A>*W7%bUWwSYz~mRZwNUH2Laiy2DtdggNv8{U z;Gowv^bMQ-rc&O7jV7Uw*cOgeB5SAy0;#3yT@n8JW;Wk@^22^?nnRN!HUlESk>Hx{ zaYb$#$l5C`6GCYOn>rIAJ+mLekCqiG@{n@{ zW64LO4hwxi2VMvOkr@@70e$q4kALOrsSpQY<6~_pgpa@DVKYn1Cb)Mq?}FYA>3R7t z@dH4fJJY7^ZQzoQ%0o31o-x><=#QT_h~M7=j<&Y`oX_P`)z!3XQWul->Rujf8coi%@rhxv)uUe-NmS9q)(0ANWp5o3X^IW*@i7{rgVTY?uUw z0KB)d*Ul9(r2Qx>j{PfOe#)QV+kp}KO&{RWp7I&5etO50ioAtg?rLknL&}Q)7WHJs zT)IM5d85N%0>+U9Q#r>_=qeqv4&wJ}CQXjqJWk>p)7nl^6)Q|zIvDF|EoWVpgtJ1{ zB*^mBd@{lv=ZXG3cms7ZY<~R zI87aJH09=(+zg>I)U&h^IMVm>DLEs`mT>R#Gb4~6&(9vjXsSKQaZSpNK z>eg21S6qBIY!)cA0O{!Jb_C?oGKs}Y9H7YY6xJ8Nc?3p1Yo_Lv$qPMY zq{Rk^h3SS)7#C5L?^a;$$$^^rySMb#kLewbKf6&EE<@)2qk6ObV+yKl8|b+F)V;-X zrB&zYXQ}08tLh%e_8oz|SVDD%^!5#3?8*-$>* z`--J`+igqh(b@aSzbtY>TW~iPW<=yeY;hBdym+nC96Gqe?aUu=&%Y5#ncHLEXv5LZ zTVzl|%(0qJ@>lzB98X7b;&e#Bv^{8p7m`eQ#dXbU32C?oAH`K>kj zQL(OI7WY~$%}Yr=?u?%Ha1i5r$0m2x$K$qBQ=`vp<-TMm4>0`9Ro=D9=;je#(kAGJ zrgy8nt4*)Z;85g>*RoTn5V_g zy&OZd%>Vc=>U#3r2hhXqZvEB~F%x;7>l##F&CEVC*b@~F(zb#AwJK|{<%_Q5U~0e7 zXjVNi$#b6CH(@!YSH+r_pEet!TwkLsd92te>|pe%BrUotyW@!3$roAaq(o4lfb`bK$@q11c|w zf@RoHuvx!5o^XuZ7xl zo}yweQU|#d=s(eCCk2*UoPZSAC?Wbi%#3!R~`!z4r3Os^Yy9KgE(N?}qNJzCe88U5Xx}sN-9+c^{6G z^nV13Vqw=q`3GDI&3DqVo=yR(eKSGqc0c`-1F}~|3{@h1-=pqZ$m}N^A?*5)5r~OVyg`fK% z>esDlJCO=S@`p@OSGLxTkn^(P7_W;_aJsPjYiAL~C$zE46cx3|5qTma$io zmh5ud0@QPG<6&jz`6H{UrNxD0fb>%!n3Y<5vOR1Us>!f;ebS^2^GN$~KkrHL85Q-> z4W(TG3{R>AMbz?1ANH|PQD-y$YSr+A@aaHA*)*5}nYytnj7m(5Th-7tj(6$9JTq_+ zG3_wX_VG}@Jk0=D7bvf|FPNVF;(|PTgo-%xb(y7U&)iUI`FW$DYKZAkKDuz-HqG7Z zk{D=mdHv|HPQ4$Rc2v96a-zN`Q*)~jRkP#Rm;*!k`XI;F+b zQvY-E8DrgR0JG6J@mHQY(rids)HSzy;K<;xG z*nWUdiEDhYd-L{1wdkD7(=@);XSW%c)TOatMZ(}&7Nt6E+LU4#!A|9&1RSESU%Z^+ zX$lP&t1(~IW)}ov*}#f{d2}t{(su(o4R+Z?^!JEw&2C4Pf7OZ=lyeL@QYeehM&>dL z67x9v4=&ND;L$u`kU11q2jAaAk)TXWxvPPw#!q|0e9#JqU$N%JK|WW`D`c45+;>E~ zY8z~A-irE}e*Kvym?Wh$ve#+sgcbr!TcK3A1Q1=zCHx)2$Ux0pNnmK*xCHudV@uAL znB>RjaU~x9qMe5w>fSbKt}d0!COs+!DYu%PAb7=<0uuB_J>E2bd4X3O|I{5~$Gv-Z z!MvC}WBCFLuagRd9`6nB)`r2<3|y z+-{Er*OwLcA_j|o<-Z^dR-!Ezw=k zNO>*`*3kFSuPVM&tH@T|ikN=GdC0`6Oy?z&$KXvqiaQ3~+*uTCjeB%DD<{gl9F>-I z!@D2({BViIAb>qZnH2T)&nx3&kx}DE#ZCFL%k^EY2B#kg$e#p*=eM*%iu5bK@))^6 z25-_)Rak#kNsSx%rkU|{(odCR=IN9ov*keJbM6urHNH0*(HfJL_$M8w&`UtF4^84; zfZ46Nf|cglMhyoa?puE`c3{_`iE^Qdds&nVy>GFSg-0GdP?v&%!A6u(!c^kYQ=i%} zeH}+Dt9RaK;ut~dl;_vmmt#pH?yG|jNPh0W!30NH{-&zR&)vR08;a)Py5afEEcUrl zg?IolN>O|5C3ibePCrg8au%DdRbwAWIlYEzgFM8%Qkd16cp&=I%=_FYJg=mDdAd*i z(Y9sTU1mb^zilW-l?Gex4x8qw;M1f( zmj!s91N>v*eak0)@!wiC%mu7syj?_cZbb`1vYj*tEMvlTX9Auw1?zp@^Jk^%dYX=@ zE?gehI$s%-DSpCN>PqifIP6D%ZIgYr zg!Bjp@gRm~B*=X9&v(iLX9zc%B-pC-_0aVH{-fSmUW_+ic-FWfSWk_lxg)VX(@j@5wl@c5sY%ijbmAwNh^$FGZMQh6D>$@b*0pJpk`YAJ zIJSpzko5HF@%Z@Bi{+ZK(2YFD#wDit#opp;a@%EP9_1az4PQTW@?14IJS*}D^$iJG zU2r_F`84*fsxCp!D_#26*q!XuNjQj7yo?MmdSc#YVn<;gs|C~SUU<|7{F+}jKzpIyN^2@A5VINojQp&Y?}C_Z{<6-e4}c1m-|^}=TN>) zi{|qVz4xD-;XglC5Ao6WK}XcfKFL=oe7_eA-ur-l}WIxous32pzevQb}WK2%*Nk;Jw8FaVs88D7{2kxGHrw9_B)DarWEQ? zZ7|au*aFYiB@XE*$qNK`Yp~*Z8{L1;OXN7-T9^=XZlI+WFjIqFLo%0N_H*_1nc;g9 zF)Z*{_3z#h6Z_(?0z7q3#%rg6=1GyPn~%9=*WTP3znb^2#l&+({k3Ga_E?uRbJPB{ zdEcecNvrjnkf!kEh||LDxorF01gHt5o0p>&pF_qbeBMp94MV7eT3XYKr9OW~B_;Sv z5z22iHL16=Z2;k=(QO>Aw$~yQ;m&%p@Vv@6O7p2M{}+I$#iV=PCO#Y#@a1#yo%x`H zaTX*|n&ecA^XzXB8CR3!i!(B*r=Z}vQ`DFLtvmQ1y?ys^S({c<$~yPaNPRfWaS1?3 zZjmHQE!um;Jv%`+_x`=Btip@`u$;3|&=!=S>IgXKa++WmQ>bVOA3BDEH2S`zC68a1chP+*V)xsIFXI0wx(dIhzBdel zA|aqiNlZdeDFNvih!P@9xO#=4W(zyHc6eHGtD5NQ6w-RRWY(V?euNTelLV>-F3ei ziOhF&uTSR9AT;nrD!OTYDYzj!12X&M6v!7)HLwSv#XbSCYOa-8y}^4suTsadQA;MT z7B)A0l5T2U;@i}ydY#iq@{X=a40>ioyp!7~1;!YTg0_h!LC+c!cfmDYDFCcBr0iMX zS9hd=WsL96MLKxnWNcDzK%B+;Yr_-1Z-54o$XyWv8~@XG0^|Cc2j9L5W_ec;;{MZ- zhafQ9aWyT%k$96YMHejGc6%@}3?}#H1p3Vr%6K9cGaf=^6foWSK0W|MlPAZ)~l9*`D3>tzN)L#kUzs|xY(9~yQ>(?T$ zs|{_hS9r*Y_CU2CHt)C$Pi&F;iGj@y3cF96&fRfuC#z+yoF?8wKY1&3Fm6$opnYbg zy`Uvach1D94tCDN9VAQUT$vH{CQ*nw{Ofhst3i(ufC@)Q$A91MwBnS1aRni05>&Q| z{6N8wGl@%7NdwSagvIPJUGxDV(E*hK9Ca8iK#h&slsYggw??g4Y3*Ozrt0srg9D@B5Jo#9*D zGGENP<7t|N48h&f`HIwOZ4`Hk=_;$XZ!E0a9wnR_0f)pg{7TWdHU&2JKrVimWU`!s zrGvgI1)xO6;`d#nh^!X?&7aJ<6upoN1`;&^7K`XM2m{zN+LWpFUFRt>?wTbmWU=}A z9Yphxz}L}*L4nw6ZCTeY#Qlzx&)y~rWR)Jq0PJvDzh95MjmV~_s$aUr4L*e5v6w;)-1?eCVQW0aKn`^j5bBO(SA zJd3E^>9SY=j_6Qoffs-8gtoCwEm7h!)vUG=J?$cGa*jO<>!Z>1&Z7%V9~~&R3|zO( zr75l!{5fXP0Z(!x3I`tHUlqL0_cZYfRqoYkw)u}lOyHEi+w_O>)%ze0#AlSj} zmMjMT7X1hUR`w=G>^`Vw(bvqqYTZY4EVhn>$O=j|jqN<==; zd7MJ-c-w2#l6R{Hyi%GeH%IUkq%+CU508Tv*uj)b01)1(`KNG@(Tb*k^9#1-5-VEE z3+&I437%TAq(eH8slNU6;|y)e87X;x7&DFn@9*QWS7i0O4j#qqQz~<5({O|rD$hkl zL7H9Dx0dXJT!CF^(knWs(DvPKzO{X-oj5tVYS6w?kydPqw_3gG z`g0G6`)xe>%0l!bKyxK=F-s%Ae_in8&`9*uP@)OIkm76tFIUB_cF@%mSk8bs?;l2>mHy9 z44qwu1WKV6+uwktSQhGJtHqXhdvnzXi|9o^_}w##yCeF8Tv*9J^s^mqUE%Qg8{0t&`oBGaKqyUywL=jdy)xRqxPYwp7Th zclZ0bP?1vmNE;Zt{mB-?9Iq&(VjV18{4Pit`rCL>{Dxi3Iy6lEEoHA839>{{(mV?? zPl0ou()iQ;wZ&(NIZcD`b|&>qcQ`+zXGJ)9jxjMjsJs$ zD0j&KyX$NX!k)O+`{wH;q|kE4i#pGUx2He=S;y3v1+B25brvrSCM z;)UHR|0Jv09^&jU)_rKvP%`=h5EY^XUaoghq?{% zfJHm3zHvsj{`S<3fmE((1P)z+S6vg(YZOslR`jLPaE94#*vpVD?!iHaBD%Bk%qQ%7 z%+yf!V@(FG#m=Xn656_qNU!+uf9#*Wfm{wxXqiAB_#*@@I*KyEo@AQB!sbER`Mbe@rope~^EQE)r zO;(riWUAcAVv;z0|L(w?2Kl>?E*cjyg-etVqxewIhmpjN4@fL`H^o4Tlg$(Q1Xh|X zfWTK;7|PMH)mYLW5t$n7RZ)X}U!S(oRTj?IB^2UMl5M27x~U0|_2KYoE2POj>;GaT zUD505Ag}wo|NgkVOPo3_3%#nE0_Bh7Iz{y2H6$jzIp=xPL_6_K&^#KWYeDNQ;R{g9 z76-$?wRymlBM6>t6b&1LvkECn6V$Eln(RoKnhUtP-mT5uv~!d%_iP}cczxL;G6z}x z$W)sDCo@2n264L3lH0f^GSg;vT?+$!u%dzS?WDm#6L}LgSFOS_%eM=X!ex@Cl1-xj ziO#g;)BcljY@Gg#tBjx9Jpqo}>zbN&U&WhK-|ED`wfUSt*!|GuC8`NFfaY*+lMhmy zs?D-s3T6`EP~OjcBJyfb?xtSDGXt_CBmN@T5SqGE`5{K}%c$UEF%+{&KS@g0xAMJJ zWQ3P%R2{PqC+B$SDb&B5=H%6_<3}DOLg~CXj=d=Qotk~0)$6je*`wHRgus<|%DO?x zq#W@C^tz>)%n}R<;vpUux?8x<7VT(?$U~($IjSykTkNuRQSl$Q$ z4fuG;aWGsv1+(bRPGl%raqCmJPU6 z{HzaKfZVnc$fS_>uvI6$`72I@_v1Ko-VP~wZDsvz^1mDJHWCW%0+%C4n|$#Bk8?gj za>Bthpw+Mt~=p}`p2k)|hnarS^TvN}d zJT4>S-f1akinhM|X+aWXmzX}&HWA^IS~zxjSyMg|Ymx?yrYbo!o_fd`j=&t~2Fcm9 z@ic4zt*wCWI&snA=khan)F$p!KC>FL7)`La^J7KhSGPML2l&zNPW|Uo?!bdrydm18 zfB0jL#r1cs`O<)pK~Bis@vl|$NeRucJCbgt^8Kud>n6EeU6(ia?dTsclP8{QL6

zZ4Wm#V!E(TYXX7?X>HYcZ4_{sM|z$y?sK_?PkJ1+g?Hxi)mCY1{he1Oha}IP`m$Lf z@z|pTXyt_KY*(9JFOBkf%X>fF0Mtzk%%6WkD8p)SgNZDkH1pIeL8PXlZ?RMGX3+A} z-H(?o+?KGPpL^K-}?84nSU$Znvn;)Vg}b>->b_T(q(1+u~xU|HITaNA80DQ zUcPY?NAFpjN46;f&UsZ)FQ?_7tRU#;%Rd?J9vL@RiE0ITXs(Xv|Dtir|4G&w;Cmp2 zF*3vpNTy@OI21=;CK@)HDFnW*pF$Ya2Wprz8_>iGTEB3J3sbs?QW+wm;q(9rGiLc| z1tf~75Vof=v8D;$#~_(9d9|7MJxEkE0L1|5KXsm%t$11o|GVrT^P^IwO6(y$$c2%U z9#9psOM$OfJ(?f^FVkX~exGEhxwCC8u<-lw(k3V)R@`Bm#7+7lA6MYax4YemYEnpT z%dL+->4Y*pjDN{AL=RSd*qk8y4)uK*@A(>o`h#G?y98!<2M!*br(Fgarf9e;%!gDq zDC)TV&N&HhKz$1`#!~IV$=cX!|(uGc@FOi=7kx^!$izFHHo?pq73x@1Mwgv*2WmCiU!W1yy1ynK}|9#~5Vt zp;tTi#b7f@DkowCm4x3vGVwgwKDwn)G9j!$rMpEyIW76ad5<6A0KNWDDLc|-J#LPvNu`lW62)g=>xzE zL29@XxydyimwdG*&BV0DBhsX)kT^+BGmZcw?)l*ItN`sS`H_^+W zE+Wq_96}OLnN8UEkKSFv35-Sm$j z<`Avl4p!0hu6_*p!gS5N@^gOQqg{mDm5jhjjBKKp)&m~w5q zvsYE|OIO`)qwL4jVRQE=WpwGv)&4mR14ij0@+#hp-9x#yPd^FYf3lv(gIX@cyRZ4d zK=?WZGovwQc{K+r4bZG>P8z?1ZeE8j-C*|Ffxg$ogkv~t=;yvD3bAMQiqggD<;h-v zm)mpqgUQr^fuslzjwrOkm3R5?v!G$^)$nqQ{+odb=#j=z+QiyU#wM*z4f$X9km6=+AUdxGZv~(A(J;BsWqLz#fF1(lf4S9!;4toWz%;Gi z7g_KYNL19P*t+}cX>;XlHccRkb(M~77SCoL6Z~RodDh}}m|s_x|u~3s2$@hyKEqr0)V|$X4N~LcXZr*OGETV>ix@|OW5jpHAYKY9*6#3OQ+N+ z`@?^vTimv;4eK$d0-d?>2{F#9=DpK!HhE^up(`Eq+)^f`OOY2#rp3K8Ilnu|j#IxX zP%?;By-P|R-zbe_z)*C@8+AT-kBqvvBf-JsxYpv6<-zFncEQ;0*6rDcoU~f*K1QUg zHDR9YTD*Jgs8KCLbR2L-Uo0HfpWF|lNFV-H=K59ff|=feM@x6CF|{CX?Hr} z>xKglGHAv2FHT!b$Kl8B>mQ!6m5o;-#ECX(JZJ^Pah(f98(#+E(R#9B8VVtSHObEm z-X|!{CQg2VaL6F#u|@~VfW>7`&)P{Z=q>}KwNZ0H;}Gcr>+}{B=6w;T-Sc#XUe30d zaT~Js>yt}5$m<@9sJzr0qRdt^n#LG^h5J%5)^gjWJgc*fv_98^bLo@p%a2{jD>N;I z3Eh?&LZP9i1M!asbJd4!DkZEc;}Ab&0DmCA{4OwyqWJvY<=N?cWZrKldQEq?gP|dF z7k(C2o-sF)-q+W_=rY{0zyy$Tm%|z7*gO(R(2v)1C4?TsUlUfbppL}*1Umczz&WG# z>(vStF$fFPjO4u)<7a$JRh3omD9p`I8aWQeOGFdUcmcKUv~>iz(|6l3r+G_(iH?4m zWjS_FU5y?yL{@t>B#KYtthw;X#%{oyaHl#m0YU#idz0S5wfWlr2MQj!{HQF4b@&QA z*#n$cWkB&k3|AoY>*Dn|!?^v*Wyf^-h$X_C#u0_jfU>m}?x3{{NcO{^_<>&6qmPAe z^?k|39L~z+eg2(#oFAI60RO0>0{K~yi6AjkA7Pdwu9c;jYe!XlVH8W?oO%F{1koam zeD<7Gq4wJe?|pSHQRP5Iua3iK5h?dD2&0SMobQfh*yxqnQZ)F{l$d0p1WP&|(@#2WN)Bd*#=w=iKkDGM;Es5`aOdFn&MZN3QdE!= zq$gg%#kt)Hofx23euwy(?=8gY>6RV%J2C~ptJ)c|g3VUaeoT-)%JR2WV0N@3wg!i< zZd%9zk6de(;CZn9NyP5qmBj^Oz-p$dNBN`*GlB&KN7!QlsK~sWm1fo__3akTXm;H7 z04E|MptyrW^$Fq0iu3CyhJMc8gWm|shR zH6D(GVQShKG22}nijGVMQ>WjMT;3c8EQv$*ZLRo1|HN6_)%Z&+U;OV2Xwq2OW(h1m z6U97hY;-t#jK&KtWe4zg=kdhn+UN7~Jam(Ma*0De!V6*Vv{+-2sJO5YEaaAiH09AaJ0dTtjG{S1P_emPn2PLT|mN( z789-gc`7&YCehP{US>`N+8!GQkYRAXQS4Hv&CS8tv$+p7v=Y1@i8S=mVcKY z0Y~uz0i5Fx6uCBxwu`B}>_mf3g{S;mJ`w?tF+5Bpnz|T&Z)8C8sD>Ac^Q}yKSadg8 zDxXA5b^j#u3XP+lk|fT?@(X6xN7xuFf}_h8BY04o$S~{c%plFR(Qv>($>$prZlO!6 z-t;3Aw#QW6F7#V+pvP}WVK89A>GZU{CMxuhQ(O2H`cTm&is2X8DJUX9y8C|3;(Dj2 zWVsKb+U+G?1$*^|t+9o~r&-ec( zw;Rp5!spLZ*k;IH+BZ~)#bjsKN%ipM-H#^T_KUgW@JCUu?}-OavZ}%Gk;#PqMY@mn zZGO&F9OQtm{5cc4x~@Pas=jg3xt?KmX_5c<|8h%A{_4mt%r0-BdZ(c+%T#{ZGg~4V)l@Uk#G4l_c0_t&}v9F@rtK;7aJf1B` z!}YJnv@lo>v)HUnjh3|Ak#52!O|x%_{zt+fL?0(*{V1IDBI&K)Jstlm(V({~jR^=W zpBk?>=SQl5z_n7?kgq)o7mW{wF%wmmnQF3-dX@Romkco@_~5%KD@VP9j7>~vJFIHf zFILHo13$^i-gZ!`+nR2*nh)cdT5rHigS=u>;+Jy)pZV;thqsR{6m3^8S<2i3RDd_Jo)*MjPWGxOWt3go$lC6#MK8;T!fPy;Yb0HsTtn#&Dv~o$Vu@&)t)gU`V@Ysf;LE zwtz53r9%bBylA3Qw|*Uq*H3e7izyg%0JL)e_EOh$wVjB8nWUWGq0 z1tgtwzj5qkDE<3oAdy$Lc4f`+$yM<4_TVENM%BoP-DH&iJZ?SCA4Tq4Qj$6lQ#=? z;OvgPiCgjr;okTE{sbRRpUQ))nE%d=w2} z5LU8Dv9)F3^09s%MIc9Yv8B^cS0(Vh495%4%+H9fqFGp-S6{l_TYx@W`SK<|<_PRC zx**aCXKs6>wpu3s0(+-+Igfp!tb5Ase(d0jZ7yEhII{VQk8nGyh-1(J#kW#*b2Uu9#Py?zE1-w zQm9;RY`+cJB6#AX`WJX*hI`PL$0ro!@yDUwYpW#!tWcK~gjO*RYL`RyhKH;lp?ZD{Mg7NB_5zL~fY zhxk-4*v46RBHy6zQA(?MNjK99QL2#gWqD_S+b-7W_DkqLrxii z3St^>J!B}mqVt;%)iT&FzVrnt_G1Vsy!nwLtpF=7jj z7nP2CMR5ALrt5L_oDJo!);bDIQIMG@h0v^kn*jT`Sz^oFHJJ39WVXZ;X`x($_cCWgE_NCmYq7xPGmsJL%10 zx#^73X;|_TvN7{c*^(y-At~_LNioo*TC^5t;C8f2DLld^+2QLv(0CT1x7hW-%6dNY($w~Yvq+d4OK)? zgn?XBBrCb?f;3WvRCT;^m@oc6k|-vqJ62pNbX|ix^NcaQd^Pv%K=vp~(3$vX$EnFV z+sQg3=E4?wb}-rMxBV9Zn-=PVMHXWQvqe22kp#3hE zLtl|CqwUe8Aa`9-epT&55@W%ql5{^A*NJ@fF}z%d z2_z^)T@Ve_x**qxBGW8#PlwwaJB1AVC5Cw z`?5}*nPo3BCX#Xqgf+JnoNhHZJ3pj0@Eno28MxfY1rKee%^)~tYSm53iNomzD0hrn zbiCiMqVHOCI(k03>^=Xc*2!5)@*jzUz#{B6tf%chw)c;*B9|X(Z8Usc__q;<>2Q0R4g6GXK#ALmm3P;oEWn~_YbY&l*WXft<@tIl2z#S=}+hYM{c z8M5<_Ne>k|hpf}cOUB5lL#D4WK=@T_XsemANw z$u99cEvvJ?RLh*KR~EnlGEc3znW`(f5$QhWrjanr{%b#DyR|E7W^q4)+hZ z!?bikf)4VN$6>60E_#zQD*I?iIa;y)Pb> zrs)R~?ECI9-e`WcB6WSD-^PUx>|`Q}X|zbK^%L}5J&J6OO*LHQW%zGj^$Lo4hR;4| zUd`tisVx5Mz#XWsIIO8Gt_4c47Jc_T1V3e;|9r)2U<6uUD$0Pi_x|b*O?Dw2f)*1aCr&igN zCx7Onh-r1M7wq2ho=J$2s#6GsCOdsgx~dG9H7~S&gWTixL9Uj!?Dga>_tIfdsftU5 zy>yo~?y;qNk9u!ox1EVorPJNE?w4|Ye-s+ZDHW);+)W=?iq1G63fF&z4O4#}+7fMu z0QrZE*1@SR0dTiF*bFC&IwUxk!0ib@H}CxuR5ARXs~V_*3=`y)d0gJe>omx)UiG?> zmjxNX-Rvq-l`S^iD32@`N?`Lom#9A?Hwn_5wDpq2`!&(Si~BEA%x$lo_}n}^fkauT zLP;#MX66IVIy1|=E(J9XKVV~*$nK+PP%V&z2N;V`D=XSJJJ1&1DGLX-{3@uz=7rE- zk<0L;Y<1GJQCEQRGu8hJMPO7}i~EzZ<;SKkzn+NCa`XmbO56b!5#6*)5w<-nNYk04 ziztp6_;BgHal6Em`o)B1#^T8nSD7~3N!Qk3pt`&DEgp^0gM1!cY9ae2rJ(I?AtFsE zsTb0Pq-olSw)G<&DSSo;kLIj&=m`Os(wLW)9uTHA@tPXCVOThgSG~zY^48`4Bk@HMZ(0>vRgnwiLBUL?H%yi9%O>*$ z-=|dSwoS0ywH;hA_0RRaGElj02k?hvytjze{*vt0(pL}Vn5|daJ24l%IJ0K)AU^Ev zxbF|_^Ig6A0&l!ixfa-^i#7%0kc4~oP`$=S6W-32<2?a%wXc-`WJ4X9-fsO9UMJ6p zl^px%pRO8-CDqH0CgsOanyfPJ^eb+wN_e2u7U3+eOrSf&(WakVFJU~D(QO}B3fGE> zGssl%N=1Oo1l&o^#KbNAt+sw>-20C0MNcc$Ae(8$r7IRZn?dY$%F7v>>{O5^vtbn` z_-`gQ5ihiqaFp=i=E_yI3+U~*K;pKEb$N+u&un*)(1%(L390e~ov==^zdeRfbgu{o_GaMGd`} zsD^h-3i^oyZG+gJ?>5(^fHpD5e4V+{UssePXb>XkfK3EN$eW!nFqxHp9R1Y3@_jz= z1EOa5I4MQ)TVcH!g5enJfOOYAg;i??XdU zBFL<+zTejoMT{^;RQO-1g(?avH-9S?JR_mI7Hy1A+{xT61gc6meZ#Km17wWnb?h4R z{&ZO!Jb&3(QjHuWsa}b|-$o|ES4u8T{?9~ieAHo8qw<*i{9+c-hnQ(Az%Td#Y&CKs z{jSp-u^%P)HFvipsDSth6#_t7PG0T%NAvgCzVe@^m`D7@CVq`sEi5#!o$P*e2F-Q2 zk~GyU*C;VjInm&c5>e;f=9WIUy#RuI_~SQ;C}2H(J3N4nPG$PdH{!ozUF-iye*8!B z8rV&bUp3=9^Jdd}apeeKhyLC7p_P|q!PF~-L?C@5%+D`u=1q!&4?-kQ@+1$0|5t`h znE$r=?lfAHNG$`Jm%sDfN<_fZ_5HIH;O;AX;ePSp7&ND;R@Zi&Dj{UL^*k+@zdQ4# zTc5sbym3_p1==5J2R!yi-{sMM#q{%JjW^K;n~R4Rz~A8<>&P7SNAZCQFs06olrIbGp-VWo;1uH zuOww=CeRGap_2c$y%4k4#w-Tz0mnTEAdUT5(4=~HOHL;0SR%Z17VZozs?5D5ZFse} zkoGBBk&8O{+QZnnG)b7#)p7_vY{HyDk%59!6ka zUR)X7Y?FJw-`+}OUTAC?r}SdNFk`<;%HCDS1u7QSV=0#235eyVgD|!nFblbN`X#ddzDi4tIMSCX|&I-;pzwR2W=oLjVqQfZA+M zTF&KEnHxLR?a$2#UhzIw+u!tFgHsU~VL~Z?12P@!Q8UbY0!`uH^lU3wDU!aZO|*?+ z*jcaZ6raxXi3jH`(fyR-evP-8?1HSo7am-$fzGOen`T5OGaiy*H`O+cR~dgdwDrQ? z-~Uw<1GMIgDfoI^BtGiS5Yp4T^n+|;nvu=0U?@lZJ^zh3BIBu5SEE@MyaE(rr;@=v z&1o5Ye?bF)rc>~!QADLvdDfM$Vy?egM~MlTvvL5jy=V0bue~OK#`BLL1V4#8o>%|a z@LqCuAtg-Ib^WVR%Lz3~D+Z8`o4=!lfyW@f-Np4CaM*!5zOg3I&9u2(mr8&c@VVQa z4-m25vGInx+b`ngTI%_%??3%!K$1<1P7k;Qvf_3=db>upQ8ulVjUXKU>~8OGwRvA7 zkG@Y6xG;5VAYT96d!JoiUgTWR(v5{)Ot0{N;2qDkE7SA*cQkjTXWXK3CA+H?r?py? zpgJO>rL=y^T_;cXE(!V{Nlii~Rhaced0WVhsKno>T;k|uGbpN^#HF8NAYr#xAz`lT z6YNE+nbyQOOqw{RW9OgF$*QqV<+IW0&AomsLl8hPmCm8VG{0i#<~%H%w$~lHbEGt; zQZzMA$$UM!;zug8-`TO>(+cW!TR22U3wP`E3N=Oxdg4)IV2ZQ;HV`2Q|0(qD<`jWT z9whETt;JvmDgvb4yAEQVfept;J#57f5hq8M2_QQ^eA$KNHo6_7^^N>LlFGV}_eN>U z3wrHp3*(TSFERj$({Mg9%2rrUHBBu=fJg3z-RQ>~H~mBi44#&4vACC4x084(wR8{l zsc!9bTKOp=_sL{fVWZicO?B{icOz%bS=}uh|?zELL+m=jv z|NCgHQZVz5&t?qO2h;eR&6;!_HdKR}>N76ePd)xUvgf6J?at^Z zabeT#t{mDiuarTW1Q;xX++m{44dZ)22cRiNy;;c5MdvQpV}=T z(q(o`!S^JtssFY!)GqOBVUVHL)m7fP=mXb)i3TqdNf%}krfo8oJuArB2Pq%#K~~-x z1(DU!gI!nocl~QQVk4bOe!Ry;lg^uIaB+dn!>$!Zmz)kSmsfeE_G9XZmJ=k6 z94elpPNzq+J?k&AYQ1GIi{e-PT;vssYBzJ1Gf}TimeTnDBpZ@D`;fDrF)~)1Ubx!) zv~hUy7-5C~mxZAGAZ`tNmDzEuhSs2&Bb)rVGin|tx(KikS;o|`B3&%tK>en^%Qt^( z3DyFdnDbYz-yHPX{vuYdr;G8KJ@_s%f)4gE@vzv30(?4GvMTngFg`~yNlQfA70*A6 zxz5j+`d(L5$hb2KpUw>y?Gieb7}}vkBq}~6VQBBXgoK5UDlv^8{#;`IXNTxFJ7KlZAYEuN>SqL{@97eOKLr^ln= zJ_BuRmBy(wL}S56aw^%hpRNVQ%*@i{zD&M+@QUk&$`h&rFWyU|#F(Fp^NPJR>D5~K z-y2&RH2Tx#1vK?peGXSnrmh8yI+wYc9=PmL9zB>>Qc4A}sTEqAGS3X9KWJEYd&1yC z1^d?RE%l%*8&?_2T>tj_t$5loztdmEIt4PSM}1wBhg&lee0qV;-cPh`wd|@LF3Ob zERF!i8if%c7_fRZ=CIM$fA#aeY8FkhxxdTaz11Gnyiy-uv;ES3(1CG14{%4TsX^}e z<*T*axLiK6(vY~F*WL)tLRY3lq6e!()L6^$_8OKcYeRXNIgLJ! zS0a<)r^p3tk{587fJ_6R`_kN7*T|GRV!<0qW_BOY4+)org+Hp6jSo2>3C+zlSEZ{=t0y40^-7vJ z$}EfC$0Gd_4Y0RbSG%VKH=QL1mVKi6j|&%gJ<3T*6K;2G1Pg4*orO4wAAEi}$n#QP zPVq+F;s%fai$#{h9h~L5K7G3#Af!0_2?7H| zRwtCHYnLf%VqVj`4hp@fC!@CmD^5!f0CI!Z>nZ!14!)TBb)!E55Qbe@ zWH%o66e>Ga)v1uv8XdU~`LaT?FZFbrRF&$~_Q+E2${RL^v#Mk6Qs6Y4a3z3O(R92x zS$S1`UnO?y9Gw>>^s-dP!D?MXYyXklTQ=Xl_cKes37S!m@bKZy5ue{4`XWk-srMf| z&n#S@bG&putoPH1xfM3P(aCg?*q?;X`w?eT=-WuA>ug007yC&RA9Ke~w~83X%!8^I zO=H`i(UZ}9AJ)mWlD#w+c(SY_aHo_inf8@$`<=F%Rza`va%)FSbEDq(74J4h3bZCg z8YH|SbRksgPs*Fnl#1tiL*9Mqo%NhnX$Rbd-SBYGO!apxV@qj*$&U;_3ifcKmN^y~ zRWQ!yNSt-aONtuV=0C&zHid_uX`Y4;*1(@``tsD>NZQMEtF5_RT$x&Krtv&{*mk3@ z)2n?+x04sO)80)t2VEJd#DpuOqPog>v?ou?xNke(i8brI7ji#n{gx^}EAPfO;{^sm zK6{osP&-g^nZQwf(o_QbI=y9iut}Wh8SqWPIenpO!f% zk6J{2I~n2U7(UPTcYSEEqz%Z<+#$#~xC)ntNtaEGn-s%^MQcJwi(g|ERM)O7p~18h zCdHvyBa&nF%Rvl4j1C!Urn!u#=RcA;PTJ-)9+o8MZ_am;qgZ$_BLY!(nYQh8WnAge zJ1`!sYThr1OzZ)(bfk#L=2J27(z#2*XDjFMVcd2Qq6kUxoqOW=BvgyV`6L!0U{oM?t z6HU8VR38k(rZ~Dn`L>0^sMkM`{bCtakS=@jC|J!Qkg1IJN_otUjxVn%3szdU@l%7l z?_J=Aeq5fen3P8dT_?vY-I;^-8Xj)3SMwE5Z{7HK=y_H?KP+uR>S>v>-*iKLZ!3F! zP@jLwLE+|mAVtn*;(KHK*M-J+v!i{=XGLg<6OfS0GphY}@kt36!)8;p)Czn9k1dUu zhO%E~T+j95QOqjpos}8hrJYWFLE#>G;qq^^ACYV%$ZWNWuYnI96$fe5j^)S;(M7s? zmGD+OI%m-6kz4#$ouc5$EkG%(T(oc@XGdKhO**E>)8AdLJmHDP?>6*N6sd97ZsNs} zbEfL3x-?(h@xx5EW)rek+$moSN^;#JUJZ&{*&XD~1wFgERfyI5J6{8J5~h)QRIEfc zRKm;_DQEi&IflkRAAr{Y?egm&kN-%}c<*1&E;)=|cf&pqN7_KgFiF8emNH;!vLSj^ zP=1?eaH$0VYF{|e7@c;&QD3SPdQ7mamtk`yR9f{}4kv~BfuWsn@-R!{Md4<*eU8uiIM<_!(YnF=&)wU7+MUDOpEcMFej zh;*&=OWr@gd5xZMw*k;tn7dUpkq>$q)IF~N>8yV}c)hPIdT>rHd z8~nI``+zx%`9uO}2Rd}QiRkMjAgkQw19TQ8C>@MHMSIy(K4BPCszuvgp_Ny zN5-bX756mCB$O0h>h?_IToY>z5>8eOE2y>xwq_ox$;W1lo#C7Sk`5?MOXV+!^&_*7 z9j0(-h0fru+J+GjtE-76Gd+Pjz#~k5Y^W9QB{Ik;NNO;^50s$A&t7k_>S+~k_^T0^ z_%$CudaotlDVm6gFuM7@fA8~AZOYS+b>*wylyC1J0UTW1@cqq~r8OD~R+l5>Rs z+-1(_-Rn=TQH1A-oo^22y|et-6gtu=PWuSZ9l9{5gDU}oPF3o+B7{fTA9i@bFzEQ5 zz{cq8ps9t(x>figpiMBHE_p)S5r0gqJ%e}NwyVaB_6pH`;mLeOmUq)-9Uv9jOU%Xj zjBWl6(nx_V8X^{~Fp$I>i^p$Fg&O!1-YAKjm|lpl+BYW7`C3i@^NBC>klx_9s7nSb zbJ*CIBEbHKyn4)~EB@-UOP=MG&!VEhD)KfIF$b`j@KtYny(UEDlV6bHJ&jjVcQmKa z5AvyY5W$3}O?QbC%a1N1MF{fgJPMtTpVf%51^7V6>#{2feC`t?|LPfG4ZYdQQgozI zjH05SrHK-mDaIg@O+nLm6m&|4DFKqJH=)S6ly#;|v#m;$GFua2T*|e~!$wLSO-+#D zSsR^;+1}4&?i%!fnFxNjY=zkp83CagS#2I@zYz2HNFWx>3>A%US&^>h0hn`0GQ37f z4x1%5zM^MN$R{ONQ5QPXZr>k|+E>ZXCc}Z)%~eiM-syt%CH4&CS3UdVPt~uOS6g@f zZv}VP5pNECzcL?e@bB56Zh+gOKCp%&02ZSTq&MyAVdRpZ~9dt;Xbm%w}5@O zi7tJVm0zp(<*r`pFfyq+!x%@tQ3AUG|~Xo+XS8kLE!E_u4J7_ux5KJqX^P zHZEkj`mFq1>#jiy1gf)7N(!1tfW9VDZTeGKGj1W^Ds`t-2Nv|q6}H57I|fX3*v z>G)nf50adkHkDq`s(*6g^7K|0brL%BLJpTS%{@h=CUCjY!0j=FnR2&VTN)SJC>CTH z+%*e%0KxMocFKj6G(N9kQ`)QYvf8n_A7neNYZcZ`>wMDqy|($z=n|iMw{3_Kq(9T% zaLZRQ4lmB?I_J4^cItP0ioTDYQ}4tpN4XIV4w^Vc;7;wI>)LrZR63=8i^+xnEvcU6 z6HNNc7EJMeR)M3h*?cft&zgybMc%9Kv`vsXs?JX+KLxjx$u6$>+fRFSy;r4a!M!}l zdi;MJorPah|J%mli;4mQigXMKQBt~@h>`-L(xo&^K)N>s=@=oPfJ{k|k{I3HNKQIN z=f;Qu+kDRN`2z;8ZD;3v?(4p;_mvI_LhzU^eIn*ouzszSjVe#BB$&UEB@J1Dgm+1I zQ()qA&gGpEN@VqXhz+Z1EG_yBIjL};JVXj0 zO-f(Mi`Y8&N@6N0cC4jCRZAo0c!JW zkB#t8bh7>76y>yZP~>qyLArq2-tly>uy$Ypv1hc@hTlFoTQmO`l=07ER*35Rh?Y-G zvh?N$sX@#*^DDfxSon#KPIS1w@9*0ED5WIeyc-|l5@M`B$&GRsv7MUO+_6oVFm3pnq9^A$!P;xc{?*j|)Y(r?ZbQEThr*}G zj@L)MFt*bYEs=x$IFWj!{MCFNtFkP`4pFFAS|&_cWqyB*f$reZ@p$FrETGZaR!I}i z*L^uMHCp+|%)>(xOQHIljb8xS+n~4EGk_rr1{lNuwQ9}2+{@=5nM&IkZoLI;U4`)@ z+@rQ=n<{tp9ud8*ezBFZLMO;CE=y+1GWu8{bC6gP{Ol6m&3*>&Hn4II%$W6|)>Sf7 z@{e`mP39BX_=pn{V;_}APuh0CDjyVk6k53CfU#tgZzE;vbA;Sanu>jk73ZGn=`RY9 zWgb^B?jal|OHYEV0Tzd)=);fj^H*K>uDvsSlJuW38JUKS&|Y!P>5I&|#CXITtYy!6 zXsOilLD|!wAfa%8-(MSBJ2ULnkYW&~#iJFRF;7dRhE0%(o%XjBxLN<7bF`$ z&I{1^o+{bE8G~P7in=O9UH!TnEyMaLgmoULJ932AN?(lWyCHE8($=mJamzJyF~J6q z6(X!>LYq@6y7XBxu!z%Ue0A#^O40Y>a{QLaNJt_x&77MC3f29v={drF+e|7ZpX_zE z@A)24`~a0Xjdv`Rg53+2F*PACG0V{a@1-=rW^>VNdc~{2`oQE8`qfdoAwih0_Kj8n zyZ+Zr!4%1C&kq4Rmiw+g`PSyU^xzB+XPyy;wA;w3!`I{s zK1zh2jGT?9)GfRS=w?zb87F}AvMg(p^pZ>9U)n?vTUMqkitsASo2vmfOCPJspS-#u z&nZ3|R(AV-&Ni-kB)pWN(Qi6>dS5}JBFU@OQ59sO5Ox2(H>VgXoogCuXO08M3=9JHV;xkw}hsJkXFu57<%5Kr~W|+UR}e@!jhi?%(Wj%tu_F zawiqm$wNPINUNe+e}HYbsB@&;CnFsQ!-iXX`tyAQ$T!K0!oq zx6Edz7H76zsKUZiop&h8t5OBr`Wm@a=t_8=Jelex1IDO z6bk7N?xl-k2)4`Qe0tQMXf8)UJi29{#IUVz1qn3x`7L?N#=@?K4{wd-LASSpu#s`O z%Q`;H(ZaS!i3_!~1=BjFQTCj?99<7CaZEDCs$K-_!3WiDWYJrpAnW!Td;Qn9tm0;u zcPq2??|Cw%KBK>$rcbU_67+XWp(4|^Z78FqC9oj-&2BsRh11q`IPnxQ*z!X9;iQ{R z@urrdWhy}==VmLv)7XRUyu_!bVOya^@l+@J?D#VMXjfSE(do^YM0>kLd7dFgZby#f zu~iG%_S3rrt3OY!r2^oiJMfbVEBx2MD*t6|?+!a$l|SWmx~=)+;u0a9&pSo;(8bgE zA+&o}i`bbQCJ+ym7a^DyJh^nCidz*SO1&mnPppI+fgwLi>1Sq(&| zF0~%w-(e7)FdEW-kjomTm;PRW_j!o2A(Qk%Y7RN}EW*5Gh!?$mtB(xDT(e@lNWKbI<8DfhTtm7@Lhy9e^d0(L-U zdgm;M9Wzq&(Yr*1$FCu0ZWOHW()UM(gLzoMIw>s|nIWF)tE5L-(@8K}GtCO=8J^I6 z)NcK)N^1opp}%G69!lcgZRG-hOt)$0-^eto*V?8ZUl%T1=_4q8&;Wlg2fy@%>=k%r zYijT=AD2)(D!hJ?1=S);qSTw-HcD7YXw|E{Y!7!@wO7bFe*>*j(rpYUek_@Y};`+K@a;4-b29XIIYwWh@XVmT7E20hWuLI!soVM4$VQx!pj^kq&1!m4j{#GS}OvLj5Af0e@gW z(&!#XvApx$OnDMfW1ju!D7A*hNBMc6Z0DlNvJpO{cwYZe^dc*(@vrLZa-)zwf~2E6 zUGxFp6z`MTW7l-y19e_*E;>+zr_&}dx7|n=YkpkjafQ^Ht*jx;whtUih6y;MCX`+~rTfWv?wI9~)(q=o&@^)W_M@tOEM*C@1`}ncU$!!rIyaXzXP$Ta(#;w~7|MHgBC7~!mTdVY7*Bp2a_l8jmc~ z^Bhw28dm<1iIIN)s+=V@8Tz@4s=76InnPs&k(rh81(o4@y_TxV4kI$)ocXH{;6r>g zE7-)=c9fxexM~2y0Oahm(R1tMkKP7jRvJ47kXFw6$#xWfbxliEazE363YU1i7mBck zEa#?AL5D7SOwpMoc(YqtSZR3Bo*(;olZp=V%9Pd^g{C@cc zv)EVpU&C%mQ{GhP_$!M_M^#Mb+5AhaOkOef-+_FKl3zKhg5VsXqc*&h%_~5#g0m(R z7?T*0XXHS`V`17dRvJTCV#O5372u=HFo+l_-KVT4158IR>KbwU)fw)hvYbl>W7iV% z;o4U)(rsqlHjDv`)l&;d@Ipco_4N;w1)GjHLi8*QnYE5J3EB7^n;FpEVB7!f0{p5z zgwX24%0hN9%Vt4fzJ7gX|5!!-^>I12a;zqB|CU*jCKjIX)}6Dx zc^?YE$^fHlH>wjRNetyUfWK}!{~<}RQ0hjQ#6nKicjJ$p^Tc3?o=U&p!S+dR%Htb}(H z15~cL&1B>#w_M|oCoY=+T;E6kE--%ylb9_9Q|7$&UDkbW$VUPFOz@8fiQlk&Yh3YA zlS5ITd+*~ZX|9ivMlOvRu!ij2AdHrxMWsj{ge~5GFJ23QEj33z-*u-X4UVsZF}iK| zEP@p_@$&)Fr;T#i{H&=NtdSf=>uzwJq2@q4%Bc{vA7aKT#bSR2N842*e^ zg^W5I!;JiJEh(NKazaljy)OcPBtjNyoVc#e9$?*ap4>HUDRR&Hltu^i*IJs);~-P3 zgLN7}MB#dAEi+KRPN4ghyCM2sSTrrF_!HeOWOEGvV0u#EV0^_+uIJ>~oIa3#CHx}x zQlCT>6{z(3ii4myO)nnp#6+2MW&W-LI1}3_+g?$hHQ?Q0>D_G8l>^F**)$CJnXkZk z-_bcaQ^@8pBx2=Qw9VF&IrZC$HZ0B$Jb+A`BjGM;l?dnx6Yq0or;b(3zj^>j zGc$l-?lo?s6-!~?TIQgx2vU|O-VPbhofA#H(g=2$`~nx6Xt7~!v~*eP zIl>!HOU&wRyDm0LwaayH(!hf5a6$hLk%elv&2gL*h27F#P_a~>+b%w*X`87BeX6t3 zj{y8eXL-{M&zv~Ux%BdwZa@>PqA*N%=nfpQ)@m6KlwWl&IkaziHrZal2OGo{`nTI* zw`-x$Z3dVleydkAFb)#gtIUF`CJpMLtSfJ{{?PMAa5|60!^TY9Zf#XOV_duZbH&^P zX6rVs7HJuNSBw6(gxD$wlHHePo(J0<#rd{Z$LR)XHIrqdV_pMSK*g)T@kS-5x&z^{ zJl9iwp|f5*RfS=Ft1VF~Z|-fJToI|JxR$aQPX>mdJ=in-RMt*fOs_`a8%URJ*B#8oiwY#*$# zC+9vU&y?vOS*>rJe>a(uF1t_&ABY%u6{MyLpp4M`p9jagAiu07?Ht#cjX{4%$P3&` z9Pn>~-7y<8YkgNPx8@T+4U&_5E;UMbRQ!6y66lKuny!K#QOGIUWE2r=1hC}%maiH=A`$rGB^Brj7V`8#vLUz#(#GHjBv*?dmGzkKNOaLm8qN7Bp3P>JR85% z!Va~ck^XDISN@oL_G(!Z3yu;^qlsS=7k-$iCe9a0iHe^HmT$caT}05aR1AdV>O0rh z-6lmEBZ@0{fzDkK_iiw?ilR9_{AI&5(vk-t{7WoY$T_o$92zjBjxBP-+96XXH%P<8 zBGNTWIYUT8!N-r_AZi}+i;5kKzx`LETSg``TfJ>ucglj*-pS4u*7js4ZK@`0adQ}d zb$J=mWQy6yleymBb!W;g81#qg3^C7hqk3jPa{t$&NQ z8WWtEUin_cN5lc6`2Iq3Fp-RP{-h#nB#*AN31Rqo5JFSxqrSFuq|4;|j%!;_>5pXf zB!`hqo=ZuonrG~0kY;lcTy7<2`ZlWTwz+zaX4|SjOReteix>U-tqpRZF{FbPK_q1t zJ0r%q$MH7CruGww^(`bp{kEIWpx3^$rGKQNQ2)otqnk0)^CIVwK^`Lk5v8b1mOpo9-{YBWp}{qnpeHms+#7!>Hrx{ zNgiYfQBg{8#XU=E^Z-AlUDU&uIjc zVKmXqTxeptSBL8nn-@b2JLpS3kmkD^D>?napK*!s44;-8{Am3BDIL9~uDv77S2!=t zM)XH*^)aPf=X8_%J!i9wPkqrMZmp<1cHAk0hkMdWiPB-+j zTV}bmFkNS|Et50m)~xtsA9p%py1Bnplj(!weMjF{0@2xmzvlvJzV%Jbz}xj_qk0gO zByrYUY#gkUufxH^=}E@xkXg;mh$`k3sQVD(Hi#O^=LBZE>)b3Bc-}d~Co9gv$zLma zi)xl-KalNEDnw#)6w3#3pk7e;R*dLkT`MnqP*%n#_~zjfcH5VW_p=sev%9}Ia>|{> zRmrZ$`42Q;43AR83VOEk)`EpV`z=bVk#9B!xo#dRV9f&$cBWL=EIA^X0zY#Z3#?0Y z$^0WL4~}j;neUbG8YSrRH$Q*>n4QG0-~pJd{=6j>0cZ?|yGLw@^IfRsg)lQAepT!; z$owCfmc}3$$-IvP4#kmydX;Fo&6XP3LvG|a&r9yiQyea8mzUf!7G2C)C74)^eH%=J z2h1B?&^t5h%~;NC^y``T`!VU%F#;E@BfJziNxO%J9B)0WqOMGLtnOh)zZUtsn_#8k zQNj8*i26G9txNATNqeO|s?^OCi3&gaURxaL{ z`@V*4dEZL$iuanVugE(MvJ0xF=Sw1cr4CjiDl4Lf9Hs==3)H!R^L_P&53QXZq++*|J0GZS{+#TJ{LP&)zS{k>5J*jm-XV6CZI!LNX&IYlCb z-=!TZ{GN1o+SgXZWwr4TBqn!YnBg21l@H^oI!V8gLRyFM1-N&HxXI73XYh)!CO*r! zPmXR0e2{hakbjwf>in)|-g_C>WLo_e|FJGzcIyxxbtG04as5~A_0l=_>^DAq^+{Yy z-}a`0#q)Pl|8|L_Bb}-j)JrY?%6>K{`&j4eF;>R%O9@xym={&P(>WvKO3=;QRmk^h znBI)+|FW=8R{T0qIe-#ZmSEd=mSJ{p;GJ_>|11?>YU=sNb_0}LsWr7fg*lQz<6X#G za>oGEeOWgL{x8R~6aqzUW^;fnZ)^A`cbS;y&eXlweY^Grj<|nTZe4Qx&80d;aJy9q z(WAp(6UYBsqkEiuqjl*6t$0sx2cMwZX1x$Hc*f9xprQP~{I2ij@+-;b;Q5o&l=XcH z{vQ__#Y1){N7hoie%H*9#a%4roZ$~CxVIK^`f%mLG#2J^;uD*d{r-r-X{DzkoMC{; zxU&O3_#^sS>H!KNTx#4~r7Q=L;SbU(t3Z@RgKoW4dY4ai4rC{;r@P z`M~M_X`AB(w~Aw^W353#FjZK zXTDf#VWITZzVtroL?H~WyRoIWy13czuKJF>aQg33-|?r66m@onTT?muUY4vr&U(5p z(8JCTpUdr!z&_6F_mr_5Qf-aM-;PI!luRkPejym-{$y|N5{!p8=St3VpgEOG23zS( zVNStsCd#hC8TzYjG);u!)%)uETu$g%@V+Ph)#}l^jp6P%s(w5D#Wj_`peW$>)+m&} z^&qEp|fI3(5gn4rIa&*iXa3voAX|zA@S4$iLKQBTfO4nzMtTPI;5M}{|3tk{1_?O zt=@)z5aa47msQ<7S*#<(pN#};Ru&d!{e0@HC9LIfFXfrV<`WJMJC1maWDaS1Kri0b zm!YLbAldah{xh&$yECtQJ^wX-O_JtKnW6gNk?=9KWlqQxloOJEe&AKWeNnpjmGv{l zM$%uF%?9^5jg|BYuQg|a{wjjTKLvKnKL>;~SD4d%So83^4oBJ8qT5#do!E76>*(`F zS$J<{k(ugSY5L!~F2?6CqBtcaXw|*4XOiO`x$F+T^d$5R>iRp^Ec^Il2zr6ji+BCY z-OGf(5|YL5XXh}4#}oAgbMj>@>esW;9A5h&3Upo4v;K~&x<@gs6ni8QxJHVOvVm0X}^Byj>Hy*Ah_bQ_7)j!|SS@KiDS%1uJZHzRW``tet+0k_;e&w7|x#W5T zlME#bUm}<)ar02uSmTpclWW8K{*ms4oF|taroMiy7Cm05-#^vgQl(W;GFPj5eNBAK@=%KA-&kscSNaIf{F&KG~e+ zjpG){&N1PFgv#D7!{^A^#4|O1F4tGhDI#1>BkqZ%6?1-F_)Y;o{707h%AZZP4y#*o zv+h03u>RW{KuTCqL?ft)Hy>4_j;yVW6cfF+xSsQ5Nkb1t^`{ckQt(SJ=2{p*QuiLF z-!P6*@n!h&=f!wzj>3#}LJr!TDe13TOKFh~qE-Bo2q_Y&aym3f9FXJn_1#*=m8%bF z&u32`maW~EeU@`*F4qbq4mZCUH&N8>rO?XlLwHx$<9vcD?k!P2{q^SL;tM@YdkG)$ zW2oG8fgMsCzK?fI_)_|MC6to0*Z&uZKJLXN;f_KlA8EEG>AA;!?mi{E%BRYBZg_$B z%ap&=27MdHZM##)TI**dpFw;Wdf#jTWud_O!bvuCb{;WV;FCi3GjRWXtyiZ93fB{8 z-_|Zh>tC|6991rgrt_lDJab-YN|e7hfMZGYO1jy{t3(Si!)XOb=0*s-+DiJ1Pj=hC zr+qWv9>bB*+O*M()B19HR58cO<(o&jf6e?A9`VJwPWPThIzIq6&1r%jlKCE)SuxiF zlNK%ED*=Jr^(%Zz4`1xeo5Jl`1z8%Dae&(f2Ng!+RGOR}l^4d%`+jOuyXxo2h2QQL z^Qx0O?=eO3)ndM_)cRY3j~ zXLi%lbhlg6#G;X!37g-4Nd~zkP(4h1z0K}RhAs#bFT~YeP@Qj*xBeWeXQs!7y}(pmZdYiIbPHBQ>TRPsOm$Ax z0s3!*X*+UFJrH>Ea%K`$-FHAzfzHi!7y$SW?R>GN2ao&ykv$~}H4zg{rD1>5$8XVQ zx+h*!Q*t$R-2^z7+Gq$djZNdlo8&_TEfQ1(UE*R)H@pvQv?=R9h{v#LCjBFmv_>G9 zUHd-4Tt+O(j-MA+#Y-%z*i|%_4m!8hWb9x3dKczm=4smR=aI$(CT1{V1rC}D66nsl zu`jI5D$;&N`W=P-1^kkb)azxsL0suBN7}+yqwExP2kympvrxw3b)2k46UlGTivZVa zONDE)00`)TIfHB6yGmL*{9K;i?FM{w zpMkQhD@yJ0_@!I;l7h!%s{Hd+@KuxYLt|dj6(go?e<1i0)Q6y0qzOpB9QIea!V@7U zo|CExtzv{%6B&W*+dgkzC*FD>-wQrkEno0$u2mW?(gMF820NETVG)G8hz>v7)FS5r zX91-R`?M%9=FWw2PaF9uY~MO&iDgRVt%wh`QAeMFpz$2Gjld7aXuGOUf>e?{_@1SW z{wWcH8CX;L-`I9W&4u(W(l0!h<&7^5FSD}5jNI@jysr|qb23MlfgM|O%Z_b&&@iL@ zHsoljIUIMRxvdva);a;#;kEBm+?GpjAAc%0(Um6lT|kisjdnU7SFe}cxC@aV_*q#f z4wwWN0^>7}t*LenN=O|b<`XRZMA=1;QqiP5;A|8!n%x$FQx=2}zaq$IEkZInB>eiLjnMi=eT>tgb}Dym>vGTpGQlV=+e9i8<9unfW(bg zhIaVsu6`4*gQ0}Ft=D^7lstorb5t1CF4Dmcaq^-B3M~4$XM_2CLm1a=sHg|tcq?QP zaME?&FE@XrNE)|iVIgg0--adylTEgET|FU$jC;k5V{Y=Kv;zs_OAA#)qW7J|#e zUnnV~-h!V(IOOu6!zJh+iE^6U6A|M;mnMlih3y4P?jE^NMuDYf!_3IoH2%5*7by zKo@7-+_krk4G&d`*FDjOg%b)A2hIq@GUv-}>l(^9o(qkm^C^vC+Zq%*CEE8Sa7B9U(N%t3kz!&IBwvi znjArcZGzGb&s2m>8U17Z$0iFQr?3u_dSa21$~G{gLbfx#9P363N&^7uw$BcFG50Il z9?`9&zdBBu1derngr~LMtTj`GuP8AIrPrZQmxmM1cLbK~6W~RYr#;XJ0t*rcwrfk2 z3B|e>%UL_%v|+EU5weK_9H5LZxSd4sH`_FKp)AL`*BXCel@ZGXJ}51|OKgs(0YHH= zR!y_6vfO?sE)^O+YFLd3Mit_JtvHjU@eIGhKiaR|5}Bg}LPWl&8dt%$sKASKNHs+C2Yu4MpJJ2+I@`D)V|msDCkaB=h5#edwTec;C6zF~ zfc$7PO>%h({?F?<3&yCkvnn&ZYms>!GR0J7*Hc3+DbQ9L3n$!7$7b{(=uN%OYzxh` zdp=U%$7!R%7~YB2`RVdy108C}9_;truU{JXJaD0MA~}#PT&&KX((vVr-0I85*oSJh zm*X3j^A7}qQ^XY)d; z?I;C=ulrA=)hc(#{_b*hm{2)=vM+m|18^*J8zZ`$-jS9aKy6vZzVGSIo{-WOzf1&u zi>SBqNNhdr3s}vc$%|(CWK~j9E8%r_Yx$@2WN6xK*Oy>^^K?Jz1@e!51+D~b-6ifi zrFCZockaNy<};6y4ygaqUiSvkOSB5r&e2!x1Uf(Wir_Qph|j4X6)L&|D}M=FBf!Il z+t%$r{UfVKTxzSR6B~myfWmEVykAGH{^pQFn5m6gil}2}cMF}OCv3%V7$0|5)!K~b z^jVP_7q0O)>g5zK&ub7~V@c#6Opb58wJY?6d9IN({&LYjCKy3aN8gDWA_y+I#6Kb( ziW@2MmoDPHtg-qTt2%Uy!hV=EhSJ3Mg$(^R_HVdr(aAN~KJ6=(r-GS`W+^`A62bhE zs)a7)IeaaD`0EnvbWH3r#cJvG2W38J4(uh~s(=wcQbs$@_#EBcRVXj>$$d^X`8{!W zHDBlJIdAn(8_l`zwBVMRW=PLWV?`|VfikMw(gmeDbBg||W_|;r(L2SU#xm^O8UL2c zD?Hp16-Cg%=ABtKO%NV_R~vUHQtOX>V0f56%h>m^JNlVSG5%W8 ziq(g=%A3a`8o$mp!D@t~&xefzO5J0;G{mCcy$s#bp48s(r`&&OSWqvp>X2U*0CQqX z2pi-P7+hRmVyIL8mC+s3t-@1>M(meKiPc@wFkXx@O2-?K0ud=n@zkyFC;_Gz1h(@K z3yTKCbeSwH8143TEoiU(?9Q`rgGmQg+#L5aemXn8Z_C4^vyxF3v|D^zmwUH1<=;J0YP|E0kWbv(c@ai6G zoY3CEJzumF%aEt?t>;5P8?Oq0FnrqLXn%XGI!z&{x;C{vkh!~`Ple}CsdRK%xe-Jo zM`?h%#O<0~2l@x=7RP3juF-3$;qKs13H3^}LScATY*Z(h`6MQz%j_WMhxE@yVVXkf z0;@wC_pI`uKx13Us$S-)Efm6i5?29|^08#?Epoh|1qW zm$Duj0`m@lBX&&K7U2M3Or6hjIKArbNC&U;5g^|&BfrIjx`bp{QM{L0=l_c#^+g$vT}XL73PtP zHBM-!CAmx{3d%I6;rM>~96t@>kE4zc(7)WuNy4guvX5IF1uu3@A;%$iH*1`!#*7pJ?rT z_<{8r=jF+sk!Z7W!47H-^gwIfXLxk|=TnV0M{h(=S!#65OCV09;d5<9JBe&GA$H66 z#jKwc`mP>tp2FBJWV_?SCHqnoqXf!@c3Z^sg7&WC%3W>hW5%Z= z+J=Y1=#)o-t1B9Zf)efMq4Xh9P{U^ zQ8;r7ZNnLb&Y9UCgdYJq~O-|CTbp7(6Y0ngRM+bw$t;t||9J&NYe01JDf zC#6|KLC+cdYwr!un=C!PlEL82?%&5Bo}hu-^eCk157@!{3Ql`5!8p=9r&m`zc3qO1IVwuX1F711?4r2ZWCFNb_{6$lO%rK@ z;!(HOSh=fa4h}_M2>=nsTMvvC4aeW*%-z4O z7A>XTSC$O#u(hH;@rPbp@g1?f_t|pf_4I>~rVLJ52WnU8nxnoG<%V|r<} z@I7qvmW|-O?AlJ2@cQIz(cBL%@p2yj< zv;rn|8(R%IV1J;pdvfw?>~Y3s2Yb~AzyYKHw>sTH&=BqfVcFY#Z@v4`S}d9&j9bX^ zxAq8i73TM7?^7Y1Ruq)T^`^YXq*~J5X|DU~=#=k#&(?G6Er=&i?y zJ*&X?{P+Y01KlVVi4wY`bIv`9LsnO5*&NX4fG8>rYICN>Kxb?Z@9pYEPfE*Q4;!lL zl2ZBpeqPVDr9LGYy6OB>pP35pVhgi`D5^1VCq&N8*Cf}bv2xzDHXFi3B)86pJ}@x? z|86(^>&Hp~{#o|0)tT3DF$H?+tYfke==~6Kw&v=BZ6>Cu$Bsu@*`IP4ZFQo;)~#-F zNOzI_2Wq}|+6$)IJpGa%c@`A9ks~wPzbj;I(o~Bv+s2v9S057hi<@&||2`Hw7Gz2%2x4|xbm z^A^AFT?Nc-FBR$}r&gYX0_z1G2BFWE^Li6cR4y3fZHJK$52T3h&nJDb59&BjnIXSL z?|<5GO#Q90=<-NO?HxJ@=<42`VSA<^5|5&nVoHXe>cmLmvyC~6lj<&_f74u{jSg>( zor1pkLOFtrF`WC@@5U+qHuektY+waNI4nUL4R?}XTJX&rAnRzpYHpL2?LEb4 z29JuVdG+S7>4Mr7ge!G8|Hf$RNO^b<-KBWU$1w994|8`&<@58D5KKXn)qPRF`CoOg zPzbT5B*3#5W6lFgYFp+_PTnIi{@H1SBA9a7cGV-7lS*k9qc~vzLgKhlm-CH_{2N^^ zB&M!o2E^EGO$%RZlURP<6c&yK?@NCxb&vJBx-Erv9rF+$yR z5DmIxTPctNjckOG<8^v+SZ5AIy>Hv(?;0w3nyzL%nYLax{n-56W2m?NADOIGC~ZK6{M6@w&%TZm>YoIE4*r_v1RwaJ zEfoP4sF(daA5OH1@MC>Gi+oYdVbvzI^NKMFgmI=y17?s!{3Q597f86A>=YOS)&XQ> zvipzV`!!Y7NF7BdF2*;89UZ|bK`EN@d?-NCz&H_F9SQfmdh6vWSCQu||Hyt8W?1#U zK~zm0dqpnYuSHe@Isg+Bo!D7CW~Q+Jp7r+c!JCIlH6^g$h5%U>W)F&H3b1T6j`bcI z$t&jzDFU>bl4Xo>)ANR?RrQ|@LO!eWPD6Ar$&{cJ$E9?50g=9W73N^a-($nIXR-sy z@ikt(`rEfXI*KA6|c4zA=;O*r)>*SOE zk{$|vG<)k{nK!*mfb4-{WPT#9LqS#_H)^BMUwJ6Y4K~!4iGZWJ)em?{zcY0a>9281 zLQmZiMVMsUC5q`fi8M5PA}K|xVAuv9@Z_-L^-FWh$5$6+o-VaMpB*6Fi``XX$Pa;^ zH4$^Y7w(GH#JG)%PlevxJ1M8^LV@O%EBiVR)SS_3fM~9K%`=Z~?*dh^-3Ul$QE_-A zW0jKH3igBeq{tn@B}(N!(%_q%>{Z@zb*CPQ6JPuu&+Qzz;1&|5M^4hfRZMXzjOZU| zz-p@dN_r>C>8!ze@$hK~PM}A-5!5}#5Hcg9kk-K+C_M5%bV2XuPVgM4Ll!2{=i`_! z_f`c1kp)hh+hoSRvUm@r{Z`U=h2#*Fb{^&g?SL_zbY%Z^c^#_E%ufacD+6|_L*RPO zl?a{}=%W8#0Lv<(AQ5q*3c&9fS zOz+C=Sc;|vBWBxfB2xz!oEkYsHC$6lpOFv+x+@2F%mHn$<}7V@NPzS4(z~703YCi9 z@g3ls_PYROc&csqGnttBe(CQQ#Zo+CRZ{aSr{|*7KN3S)OS#Xuj^r4_r>s z_;1N7A^M$_3pe|>Up)Co#v_E|MNh{}LKXafuy25!uSY&6|9gw%Gm^GECt0UxY><3{ z+LxC-gYPQ`#)Ww=GgJ119^*s`;io-8!IKBXDiWQQC%7h9xy$w{8^&wJ2@J?{Da!h1 z=Gf^NbU$jpP{}K7n35EAiK-?7(cir*CCluaBWcrL`EW_=RAY~gN=M@>%w$!-#I{m! zH5dYC&^Y(2X@@?e+p}~h-0h^3>?H|Q4bYYPYrNzP`q57^%mD0f$`hoKEAu_QF26a) z&+)Wzo3bY(5I_cg$jWdQKKb2o$a=dmt$g`BNt}e7XD0Q3U8@J~Ce^c0nLB=0%nL8t zaJ1Nn>`|?4{@T$0RfYn8GpyOid`6gj( z{nFL|F)Dsx#7tnU;;+ZJhn6#M8cp7Re=>I1Um+My+&xuZFxRJohn=1PqSV-bAM!7W zld(ioyuCkVkNzjNbcxKwsQmL$nf&n_lH)&OGFB-Zz7+gvRUPJ<5iuaXJZ1z}30`Cys@Jh`&w#kgsOnv81$d71} z;YFQ?n+@}|mHo!gT(J3y$>lJkiJ_mQ@mf!sfwL{>w{S)HALGrC-Y;8MP@DV?d;e{7 zjz_*W;56H`7CZMGhyxr7dzT{9U%6y7 z##g?KFIjR7!q~3f16LUzP_G@`+*#uP(;VRF@#7}&BR^^K4VxT`+%S6fQcug6B#bIC zwX#~+^;{Gw=*D&Cs;Br4R_^q|GJr_NehJpN3qjv1@1o!rwU2Zz+2HLjx4On2?`Za0ON zpqA&FBYaPl%W59VWJp+u^sKv6&C=3+?d}dy41uKA)`%}aJO@7E;=Rc0{#NW+McA)i zNSAmJfB0Pg#T&_;qV{pFAw=KMVTSO!sC-rgKAZWMQ9j{m(uc*Hd~Ew2y~DW5QLOy8 z`J~JEt)02LR$kFyEjq=I6E#aM|H%HlEq!EjrP#0Nh;TPs#>ZzVJ%2CXcczy$Su4z4 zo~=_W7)5Nk0Nwrq`MN3ss1D$h&%rTQZmOI`eTbVDJnE4_nwiF+M@gltS}H}Hduafp znpz8M-7}sjjG#o=AvN1`Inv$Dnmg0bl=Z`fer)MSgYc9t zmK6YeEzP?>)0o&I@72WH6HG4Daz5Kkox8N#*c@6j*E1pN>OZ8#hT|~d$nZ4rt$0h! zYLzo3NX+nJ7)cE!_-{y4sA(ySg{kwoFmykF%a%N4c;#R(R48X0dXr#+9|s;xY0X^s zFiRtCj{domD?Zmgyp4{^U}3>Y`0rU^F+yjKn4|VA$DIDwqKdIQiI#d>i@`Dtn)@pDL6RWv!jef^>H9j#t+AI=`dJ|pxMU6<_bhK&pz z;`%2qZk^(B&!DttZ@6M(Dankt@P0$_Iu%O)$oTx__}>rS3A!>{zSNex?br9%33QJj zu-ZVyM-qrV8d@Bk$d6q7Aj z3dt;o;&$J)Q%_}~pLpDwK_O|X4UO0Tj zYTI_WLW4`T77flXSkn%o3?)cB0S|4%bqcH8E zx^Y<53%|44vnqW%6NyG~Q-(ycQPuNHl$tG!E;?8uub=7M==ej4!0@m3Y`TdCt|A+D zl?48E>;b~2Dm+b2P)`p=Lv>-c`$ksQ{-ZZeOE@sw&hdl@*$Gj5nsf7M58rs<&9`1n zlohMiV#2!08{IH*YF@Y}pkP`xVg_0ggJ1s{#%!j7qoMbmYSU|hZ>@F8J+BTK6Sc&L z5nnC{Q7(rLay^{v4kz6)uRw+w#`b+7o`rc$(Vng#Xrzt~=;k2pe<;(hWtk#}lVvmb zgtD`_dfVO*p08Mf)#@8$=8B?ijK6HEPm%LpJE!14xSVkD&TjkTx&*Tj*MJ^nR06?z zjh$L6SOm7QFxvV~$DW^eRBIRfEkt*9c{bb#zHj!vm8pL6z_DJFNAdn$iAUEk7CDr- zJ!?BsC#?Ep`7XOTs@&b@+iUgYS2F|ajp}bcb2J#Bz}_K}dzME4hw~1eYD{|)*y_Pw zWWB^vhI7Ddtk`GO<`rvM7}bzb!+I=t+It`7@DEtL?0h)a7qd_k`TSX|syo{k#`X>} zJ6kerUz-cT2zcKA+y?z!u6Ypd5g};}gy{G~>H}Ea%6SLQyWc_?uXNVL*mjmU>#|-2 z?R~FFaT)!K z({kRGYvc2TmsRP`+zGsi@0(Qqg9|5mYLo$6ar#poMOSmpG|~65{3{oqzQ?~;`GmDP z)g9x?=u|CXv8(JwElAE6Nq69LABobXf<}Z>dMp3C!8#P0H=jDwxXj`||C~=d0~?S+ z#58ntNcTbldywlx50mo~9>m0a#6F-1^UdavQSd-9W=%X8l>Kue28Z1R45mQQWDSUV zmqg=Atuv{aEILw`r94y^AX*eLr@6D#!ro!cb||c5z6yC)uww)qjXK*h1`yi1s26s% zb7lu{zN+k`K&CUs4*xu(-`MEi2<;iDKzV&qf3f{9Iw=jdpYKZ}jFo9s)h72W4(h_2 z)e3@jrhY`~+K8Ab^Nvh)g_=7OY7FIG(%XLL^srF+Y?qc^R3xXJUe+Y}v5vTIBFN0Q z?z;=?Y}q^W$HxR=x>kE4%T^EEZmV24G*nNQl_TkY?=nqfryYOr^oZy1-Ls;PuheQW zkvR>;yHH_#+K-*U!nBZ;ES;aoZx2?F-X9*JO$qZ& z|Hsi)$2IwWZB!H$1QF?&5~3g=-OQpyK%{d@N_WGCAl)Dx6G^4JL2@+GFuElM8!-lp z@B94TzuAX9yYKrs_c_-HVI%6rn1pzP{)$s3baW z^ve#v%XHAnwNfvRGG`sXe_*2)a+s+;a$V&|!o8lImzp1yo5~Ju_n1Bk2pT2G+qY7} zY<^|u4I!W6!d8iVO0O3a!aq;l`fc0z%=Wq4?TwaLjlIWd3dXrjkM0NB|3kF6pCFd(ib=4-)*IQQ3|NB zdFyhVjP#I((BeqI2-Ycr+{-U8E&C_eR{n$E@{~#bUZt7V$?RFTH(6h24=nUJl|_Vn zxoWfi67}vDs^%Ga$lsXrt9YaNv|g`NhUeP|Bvv zaQWdqxBiWb95OBi__`_Y*Td1qZ$grMq!Ujk*a8K?K6a{NJ=%2DZ3`hRJHU255U_^* zxS-#K8T#gs>)mthU#T&|U8i@hD=$C0uRm*wFUnTKW{YZ%%3 zDKy{f3l;Cbc;%+&AK{o-A(6o|@b+d9H<90{rSxZJQ?l{0F+e_+7n7?Q=wfQ_PV(7~ z*EpXgyh58bug^;?7nY@3Zd=8@;-uTl6>%l}YSBw?NMrmk>~15990L10zes(?1@`Qr z@+9(C&B(D@Z16uVoZf9}ZVZKaQGI*LBu^%XrQe>((iml#)UwyNU|d5)RXQ>4NxH4> z%TOmf^K1{j;@EAB=WlW!e3dg#fl~~m|GfwV2-`K4WThm8R>K!AfBFB&TvI59zY+w2 zPW`(RNLb@6oN`XLy>6l}va<2mc=AF&^@5EBo4h_U>F&r7E#yO&vzMlZQ@1vqj(hLg{%R zIAI2)G2})6yD}2y%CBR7`52v00lXmx@Jy($fpHuEi7ZCKV0!##2PeD%I(%{D#;A!Y zEZF3Tc?=%Wj|IN#3yOju2|u%MmqF<_C`p0{ zUQJ+}z|BgeBOtxlGu$^D>(raP)DOlbo1`msRJ(U_e6Pap$E9HdlJWk4;p95FV|C$z zCAJ9G+6=?9%>nqw^sMvZ-1^RsN_@HhpU0=n{ZSR8{0TWVja}tViAG zzQbij@~Koui%&q1LRkL}t@?^H<8q1j94;9qP{>$5GsAB?IU2MR)B`#xp}u|`w<22) z>hYE|lQ!41BhpTIdL^sj%@XFXXIIZ935}B^Cn569T(R`^~@lZ#@-z zHWAFUsaoc~er{nhaZ-3mYO0WX3#s>#K~H|nw>Vks^*X*)xxthkpQe@QBJ*hoRv0*r zf1ZlbB+wlLqvs?!&**{lE8o;3irudeTEa*0CtUc3zhf)8cbF2HMH6^r4hctVlvd(Y zz-#b1FH{q|WUos?mV2uUA51Ru3?s9iEN3Fu37t3u-xG&OVvo^%lO)W!^NrWsUHB1! z4|JDvyAagZFCLvp$-aT*Bh4F)*I-y9{ONj1R}Hb0L~ZgJHrXaPTj4%F^cy}!j~+)| zI%8qT@ZR|Yu?in{ui;1Fp=LM07lfZ9%#nZcJyrq7GLLYq?Oz~- z`t!!H*+OVZH%HnEjHa;&ilZYJ<#WP855U|@_U11+=%jO`AT<6>Oy5mw-sdzVu#3*8LWa<7)o|&H^EjZJpTTR?-479Lfrb z&zVxv&JM|Gq`iyTt4C>{%E!FzT)5OskaP6B*?2g3FSc1Pi}c`WaliECk0r!na(s6O z38(5N^dFfOEJT05AdBDk@gwR3&(;&xP^gVh>qnn9nOWZvCCer`4)h1KZ}v?e zs9Gp!ny~V2g-mp!ZR;4P-xxm`xw(COQKoR45fa;3r&}y&CS7zx&MgV{3X+*Cx>{(* z@vM$l!4YT6_~ViW2ONz(<*rZ}2r3birJfwt{&y>#F6O@Z7(he2E`HgCfpRhS!WqbV z?Dp@)w$t4B-`Zr@5jW3d0R11GcwJ)fOaK?&4JWAk5o7XaDyV6g;am3ID+_E)wWH|T zT#RY|xQv0I1k8gqqXi#tcd3Szo*C^#Iittipa#VZ<7a$2G}{m-p#Q+Gj8ojekv@!k zO=EviQ)}@b8Ao9Tj8>$kGj#s)f^_$0v638*e3t>zIEP>L3;fIAtrfY`cwK-N@zy43 zM>I$p`QpVj4tP#rDBjjE2ePgoUXiNVxo*!~wajYw-FaFcaq))4JIV^mx-;hU=&Nb7 zL-{TEn`E*0W5GaSAfzv19_qnZ1&(%tcl?cysnndtnY6bDLVaZ;mkA(PGDxxY~DB z9PCq-^m#lhJi3c>spRg{Q1NwZAgD}VxS83!S$?+H=nY>uF=+^Ksd}F{$o5m0L&<%A z1z=PuteUpwYPn?!92BTWvge8`fj;!wV9H~)Hy$z?iB(mk7wult6Zbq>WJYl0%a^9s zVJb=-7sSOI(p;DRGQIx&^)FB>ZM!Sl`3vu+#^i4bvAwS{S?v0GaPeU1O>Zg|@6=xP)L8>{vmNMjgds#k|%&flbm5^7ROQTWL*rONi zC5_SHPFWbKdt70oBx|WG#eFpkXm^*Tg6`oAXp@QGH*YlI9cL_8`eHwRQejF6NxUd^ z@4E-5_W886W$PRV-oqYTO`AKPw}7x0TLDfA!=l|VAYZw9Ck#bqa^!frigz0=7{8v< zBqxV`yvo7T?%R5B7f6vW=F3tfkG zGZveaWg;TpwO@Q+9F6^%&}YofK5)ai$o`K43(g*b9uE(l3-92;X?KTcb67UO(aGi6 zYtJ6t`CxIcFHn(ePldUzBempswCw&h=2XhnA*J>*JCleMEz)X(R?KUNE zO%*x>y5Ngp%TC>jOb;I4#(XE z?sF4Hu?)NaCzx99u!;iTdv)J$u5{H0{4pmZb}X_v(ww@{>g!&}&Ka4$Aivz7cXCyw zpmtbBJ==d|{L!twZ-7{Mu@1i_r(N_@4@vJ;%R?LtyEn2Dj$*u>CP)&s2bUh!{EVfTA)<~L_#T#TN%)h15kYYPM(3~QOdlpBR z93MI+2@F+3w_kPbRu#;Yg|^p)-c5R;a$5#!qR&;Qu^ku*`e0k`HSE`y|5~l7n@IES zGH#+*B6+Ool*9K0#iL)FoE_PJ&+29!3sWUxCpfwD@2g6k^#!!MteL6MJnoinaa*LqZKv`0MzaHy2@e*qTCqXoEy6bDan0~$r&B1c9-)if695mu!} z`_+a<;~cnw&y;~Rdr@>tgjJ&7nTRL;{GYF3cRjaH@gMU>Fs-9S5_igbZ3e2KU-w;_ol8CSE7mjA8Cbg3LN-KI=_)(V2*xdNDG6!lEurq|Bwu_3Pf%98bB1gM?}CH~ExaI!Dmcz@&U?e~*gs zJ_?7{=p%EN`!p@`uoTy;NwMVkXUtotKPj1H17=s6=(97c+c=766%=qNintC+#IHtd zU+DHMr^HKGbus2eb*+I?`0e6qWd7WJbg>wOK{G^O?xnXYc4A%`Av zF?>kX#6p^)-5WxoAb|{chKVgd?u`$-U>SMwMDjQ-F>xwxK|O>>5B zK^}deX2RWFp%bcr`To}@8p2b6%;g0rNOZ5oxqJRev}S!;@7#qFtMLY9eNOezV7s=U zn+|n2v-rB3;6DkD*d*01e)#E?{`_hc?;iw8=)(MA@`ibVe|({M%i{Pk7oZ<8|P~7X>&w6kBbyD7ZaOJeSkfN7tsJnLjM9cw#i%YG=|kLaL4OT z{Vi3&PEkov;CXilQhfnC#_1X@9xFr|_5SRFv^NL^ZXY!r8O8c#0?Z~CmzWHpZX*qBn zu;sF)wo?M_J>r}1TFQ8;YB;NPv>cs_=Yh9-=x1vGZBR}i z18s9prt&@ikB%=-9o5vDCdB1m%N9qd%q})4j{P-tq1o24XcF$wckz^Frnj5y-q@fd ze)PRD?9HUFJ;(Z$0*yG(i7B;wA3GETqZT%+;3$gCzE?C#q=B@dZ6Y54jcQ}P+$BZs zXiz#M)!kGxH<_s84j!L!sDyokJ1$<|_?gGQUViU;`64P=@z= zy_$L)QYzQLP0d^=&gdvs0)8{6z*|xu`|Y{DP~!Gm(IEF>n>cWHliIx8pj?AeTwq>? zfujTMKYx9*4wlAdT%1Wl84yO#|lY@%E&nj5U+blK=#goB}N@g{hE)AcJ4_Q3%47rnir%Iz4%t}%S zO%49^<|rKw9Xd0U#akf3(G`d^3fYW$(>upq?pK;=2UrJ%txu3xjXyL#5Sz30&32QDPR=pC1SuPtGEQx`>DD`Y5KtHE|o~>=P>vyl-ZM%k1{v*6r|`B)WHVf=U*pb}d(>vh63@uEIg_34uM%xrmGurmAEVta_#HXX&;DDHqa(@m zp%+h=T3h0z$f2HBjQ^W=?Ar#e8YP1LAQ$+f8qsy1O2JaLPK zux>zGiac)OUCm{t#;@np29~AD(hkr!XO$0uWX#YsOK6d?CnSU;-^$y;_upNMtGgEh zb~EQ-+>?u5A}_@V`Z%&;>nV6q-x*z*0U!~%e&bj$DtwJ`<&XFxBG~8JMCoV`|Hn*O z;iPvh$qKEw#3BGJylbYYk6$Opj{2Y+=n2M2zlCbOIt{t@BP9AnO`o~tld z$$}6ly)wVec})lfqaeJlv)&c zU`KtO%Ls?HH!nm%S}qwn*OHG~IuAQH@lV(ke=UyW%j?h*RV&pnV&y<~2EKL-HR!8o zcDKm>kBllF(2}~|R!h3QULvp?F6UYlTXR|U=4tejYCM`yx&U@imlgwG_o2{HAvtpO zGps!jCRIAcL?X_8D4Y2-;!B*@F5GH6B z=|P%VO`JJeA2Kls=}e0 zRwc`~v=^WEfKohWgRg!|qmkwr(*Xi-P@`m|!@y;ICB?J%ptiR!L4TtN%fy26oAC(v z1OBJivV1m^s5NnOdyiBIKqY>Y8t3axYZ-5X-by7{+zi_W4=O?fp-V^$TS%QJZeUsb zHv`$H_zrO4^ZJ#aXZ+%@*10?^gh1Clqrge}V`L(tGG9L{l)SyxZ;${k-Zn)9f9n7; z?cg9EYh=LTEP+P+2L-O7;4|!lJ%IHD3wF269p;oRfZ83)M|wIWsp9`;qU7EnQ1$31 z82#%*kwZC}bmtsNvd>Prw1; z{ku#WpPPz<2?UOQ35G zNEUUf*dEVlVdHvyH_wUuHRUVRyBXo?=B)A{{aWW+q)FKI3b&ukHksqDq&@4+Gx`$> z!o%oyrd-&DPW7hK%(Vs?y4^8=YlqcD-Mc>(V2HN+M}I2N_F?uL%%fJA)L9--3M{U} zRDTBk{pch9Hq&uBo2lb?B6Sm*dghVm4&xSpoCeDcIV*_zO!meE*1k@N@qfW}IXBVS za7I}iz&NqpW8Kr6I&~T3memBhx)%VzdgMW^46F6qsD>k_vuC#v_{SRHcUO(1yZ7bb z3q8hZZ*hL#W7dv8h+TH9Xp4P6^ z5pR#9l9Dne25sg6!2AKp^Q@S_AUN&>UvCcAteIC?I6Dc3Z$o&XBaNK^G+S>I;v_3r z&8(1JMZX+EqLdP5F{iyM#cO_{hTjdaOQs^`E4Iz2|6^DKGU30W0o8bA5DJcaQrWZM z!kky#9FZX0#IN)p*$4~%4K}vXc=hi7LO=Ip<;?fE&enSXJ8Z6h^}(k7IF6?=Bt4sH ztdNP@#g!67Ol^AyVH`QrcvTfZHimISt2g>vST))j2a&vPSIY`|<@I&m-&?Moj;G@ok1- zBIOu`fM9j=F~)>~xf9)RdP3aB^MxSrGnX?MET{&MDgS|F=4wBsaAM-{xuCxiMdKOa zXdQog2Ur1)!+|5?u`a9}8bF$2pmD_5Al`vvEdmk&b-3z8fImx#%k+{&tR`#0YoGjf zRFK7SxS!O?w6`?o$%aCc0MWEi60^^*WV=?|3&DIRUsjdPeSng=WqB6?PXoZzs5Fsqd$_prkuU-q#FEJAnA$kR;i5y@w%iZbFjW z8y9Y>m~#L&nCpgA=f-gpD+yHlg~=Z$os#2ljheH1bqn!5`G8XYlhAzr8|jsU#mycWx0VLPt{CuF!QazDL@pFE!I3E& z;P50nJ-S!6zZ{wq+4AoM5$uY;9hr^yBfZiuQ1bf*WmEl8MqFVo1*%JHH=lNpn1>!O;|(rAw8xiCfs)Y_f_OhL+8G?L7;*`#!Pb<#^}iv8j}zwfiRP zRXyA8mF0Kb}b={x%1+=zi;!W5J_S{vn9dzuL&Zg0d>4p%}ebCoQ2PN*} z$m2l*1=4S-a}Az&38|B)tdTNb&NI1Q9WW3+9-gDh5i?vkJ8pu0<#)S=HE7e)jZ3xK8Sq@|1|~l+R(@ zl`q{9vz2s0uJdD2?Qa7^)&-PD(Tx!sE@1D%$ICJN|B)3L&B=XUdeL(;%)k!_5}~LF z^hIQt4{7*44L+d$qU@Onmn;-3RTg>mHO45Eg`2P_3xYwP!bk{|ud8MjgJBHMlTiVc4k=+9S_;2sqK5?Qxzm#cK}e zT&V}Q!T9gxQ_SY4f@??S4_&{meE%*`lJZnAVtCrXS@-A;Z(Is}=22VB*RjJXIZg09 zhL2ygSq=dAXcMixX2}UdS*hEvv zqMG$UpO^9@n4pL^XY$fUZLclY&lvcVa94-U#fCj^n?M<3#hA-Yz>P}tgRtH?4b|!r zvhKLdQ1;!oZC2}T_UeHr)o1vp^^`Zi?tbOubrH$cWG{*8-`VV5wfn}2EQ_VJabUwD zY2;&F$Mt{BF`#zzLQIUwK^BY8y&}VJ-|muk^S`Lfs`x%yVuA?Dn{Qb=16pxP|5k40 zA~gNGgwp74`O;##NY)*$vuTGcG4K~wa$)Qz)b_=9ib{ssGs^KIh~L0G2^uSK?-5)- z%^=dzJiR1#sg(b^$|EcHQRp0L)X1h6Mu}@QsLtxyT3D`m1xj(@5KE`++>zJu*q7$) zqy^rjQQtO7yhZ?cHuKq*@ja(IWU&p+V>&=q@u%}05krjY;92q6?aAeB?x@e)RZG(n zj#f3cUI0|z%cuZ-B@czVYSw*sMMh75Kuh95^^8qaKmMbm?C@&ww+JIOhBs{V0om71 zEjjAfk*TGnc7UaZLWY&ylf;4-Jw8;fa79)ZSwI4@0~3mcqX8%RigxjsC&(T9XK%Gj zhb3k``;CRp_8>eRMvoR)BW^}8Eg_Ozd_mIz{P;R;&0SGEFSg~g(;e2S#?M_CO( zu8?>@RJQbCJ+bU&VVOOZgqPWS*XLSA({yU_O+Pr=H5JbBqIc59Ey&c@DW7zL7BV|}S!C&*w4_2b$*mhR>OwgLaddYBh!tX3a*b}pd7K8Upd{-;TwQ$t~mandCN=SZ^dN;Q%>-6{f6!D zF>Dm{XLPvnoY-a$Y1n-q>rZ9JWu|o_e;iNpnQnj&iTtV4b(|Y-lQs-HJg5!6rYwn~ zvibc>09)ef{6VcELS54e7XWCV(?kFQk=yMN z(<=tLmeXuYfe{VhfOWsGF;^a3g?^g7#4oEW1jX3h>elHEv~GSr3NsR3 zE8Dvf-e8=24FSv`?GMs9>M_pqUVqpM^LP2!0Yl@!vK-a#A)%jKyVwu5vaPi5Z^dQz zYvq(TqaSWwJo=hgb+3sct_e{ME)3)a_`X@_v>wR9u-e2CL3)!pKplkSv~Ts#W6x_$ zdOf23Wr*mGLd?+vDuRjZ`Ej}FHFs2D2(cwa@c*E-jz=pww zPF%j4aPv}^e?#60zcsF{v0PEDh_#3A`!ZW$v`cMeSr}R5>r(xMA|6kB8a@}7^-b-^ zZpWMer zwk;{KZT=?V? zz{&&Au&>J>$B!~>*(7?7!rzMJ%JM0x>)XMHGwvNnSX?d{T#UZpM# z<1TbMr*T60*ZHNVa4%nW@^eUE%?T7A)c6=J&(18c=-wWOR&e7ZSX1j)JA@5$D^D)wQ!{KAYhm-lnIvsh5UbHZ3Wb zQJC9D)pC*U3(%9X8tvHLIbYI`pUfj6o74q9Pw!@~KhS;_r(qzm5y?dBV{V&oUl&QWvuF+(QYvTOCA>BID zEy*`AXNVO3*Sy8RW5TP)ZGGF^OrvnzA#%JV<6 z92@uT;L+QH+poG~Y?$}V9A*|Kq*M!H+x6du&8>T8Nw2aKH$|;{E?-d+VPkxiJrEX? z06`2JBeraqCbMdl7d_6fo_#UEel@4IUIe0p-q*CTbwV%i#aJ{hkL`dYU;^DuIs841 zxqP!fCVmF1*)J}nUa!3#D*1vuJHFhl)9*t5&Q#8`*DlJXq&TxX0~Y4O z-yhM7Uw9K(z|UZ~r>n6(|B+=n`l+JefD(MiHDYO314bkEOFqYYH%pH4kh*LyHdhGX ztkhLN4(sVB*j)oYZtv#`u+!89+hZO&lDk{5eqXdnU-$xdz=7~Odt~=!kr&AOG)IHi z$n?9wHBJQq()jm2)DG*MGaF32`@#of_GjMf6=BZeaHP@b_cre|FSo1fRK}etQ1bwZ zW=H-_nouW7#}m&*PjeSx{2I-HnG(gSwz7tK+4T4V4b$FW(Xv@zuu`7A@0}YzMjRH1 z_FJ8?e$H4xTE)00OPfoL@o((v;mRYD8pPbE>sv#MNpJNn#rQaqd4lEvL_FXXx)Q)1 z$SeJy-P(KHHqFSJgFrnl!t_=(mQ!ng)tYmt6mA<-o9Dc8_I31$kp2W+|E zal0!a(%pyUwrDP0$9d8ih9Sp|S|(sA@$&8^gmil_U=~&n&395#CnX#TBksZw^jKch zYJNp6+NO`T#p%txGSs>8Hxm-soQA^CZ)*d7JS9jF)@3iYD!Qqc>|c9*XNBjZENRd9 zGV?4R$61t*g)eOON;K`^A^cZO;5|L&v69dO!5jj~%QgRx8|)l3tcS&(2WDwYZCelkOVl z!>mb@f=b>FsjKqRs;r2WZugRuu}bjRkR*E)gtNd zi!lRpE63uO!y zh`8?8lm`GP5&2rSEvQi`bcgPHQ|1GTs?UTzXcQFbXSWZ!AqSyet08_HcMy9u&2DIewJ0D@=1);gdGuU_&!R6lO#E3uQ>C){WQwM)bY) zKMrVci!Bt^rT%2iQ=N0(Mf%bSK$*)S*9er8AP8=}xWgbYRAI^IA8i6mf_^S_I5S#W zxC7-8zpxtTwW;;jNuO=_vm3=kxmm|sj9?gSeU8Ku_OgJoT`Ndl=^t-jVEiV(b{WoW zE|_v5ShKqEGcejru-}0(eJKH`b^wLc*!5)D#74fSM`ok#?SEvx#qP7EtW%eujFy$8 zIS$IG69vWkLl@ayHW@+z-tsO{0eE8V*B;}cB&o^BuBkR9OsM?#{&WCzQiodXy%^kJ zZKqRypZIG(_3sM=d4^Y*T=L-ewDic-HY%PP(Au7wM)B1j@TuXp1;{pkH=Pq9BGL_r_B95ENCgN z=5|*51lW8aYs!BHNS-#6TbHXWEjjStmCYT-c zsulQx7b?M@;ddiAIV>}CEA5utf+ZsXcrwzL6_$s+9T(1(^tK%0fb`K2!db|3vUbWk zc1C%|Kkx1HaTZW=fh|-_#5`lOz%=IrRI|7$r_j}3fnvShMQ7NjciHl{DaTXr%mR#m5^$T5)qETI-~ zM>7G9)f3+EVxQBgG9(t-Y4ZOHwRSO}&1P@E>BhJsGV>ExzMf%K6V`Xci>FHD3!$q} zx_u_^=RVu|blr;GurIQ=-(~y#Vr!$-v|tM%iS~Fp{`9;eTK}7wk8;n2bnpEh`DChq zzDeus%|*v<8j~>guhLiB;lPmdQz!C~jC1BL`eJ|PMx1{^Kv(C6mCr3r&1IvB*nrwX zZT#$|_%Dp%w5iOz!&|7JC+K^f%?POe0;Mn1Jx;%b1NAE0MejxtHxmuMbAfwBL}weJ51=bBbfMgxTOl@U zj&uWc(zz&E*RT$627a?REC zNYe3^@1=QzRWzV+1^`L#zVrxdkBWTxmT-vrL$vdLlnd9+B!{LS%(mpn8!Mz<*_opI zY6Ekw8b`k(Uh-`<3?UM3_-$swLXEBR!uw4|AIfmyWCfQv(uMl z`GHE=SyQqRS<$zocYb30HS`gDtQ~M#S4yk2*Rs7p8a900WJ+4GIAsg?t)yIRR=)|k zdp;lre_;R1<(N&xj_$ezGlDnjNL+|+&shSeFvaydgkv5(91pD*KiaVkSN(I?rh%35 z*u(nY>BS&gp27>F=filJuQ+B>95)vGQ&%R94YnF$hnI?PUhY+e1XK7Wt78NzBgmUI@4OBuPuGagYswW;2g?vYL-|yQB#GS5(C8 zx|`5IXRR1&+a6Md!b&U z=+2|$?6XbwQJ*(u)!pt^M66-`moJr<*3`agA{#WWLt65?p4K;^#l zhctmF%)JiO3Krw^h>wHpVw3D*jB$wJR3DAWhe50qKcub_FZDMX3G|gOle;?`ipuRy zM#U$>q@U#a227#`xF1lkOCiZB7rsvlZ8TqJ?R!sXer_Oqc$>hFStU_EavDdiRtA=* zjj8!49nxfNbHilLPLxL>Z$`#8@!!WE65-;iXPuxPnMHN)=fcDFT0sMRul8S7)NbfU zTTVN$$^egN9XV4~%>6>4<0oLCnYjVR8NE~;BBok*F*76Wf8&yUQm?G`Rv4Zx-@Wb{ z#6(Nv-?IWicgGNG!snyTA3I{L0y)=2s(YAebujS|yPfml<+nXtOnM{z%~WNSN^UP) z?u=K+o>k20`y8RP?bqV7$T!7@a47*OK}<>&=?&)STAz(pL0Rn6;t^;F0fgSEUdMCi z0IGvg*o{DW`k%EEKd1Cmu(yQe@+fc0-(ebOn?r!>BzwyRg<`JCE0XP5-*~TM3e5^9 zleyjdyM}+sB{w~wR@VLs(e$C~l>tS|(Xvh0aqu@ZwM~7zrSPuJwJ|L|ZLw2h<^<7q z&&^S-|0<^*TP_rt{C=`{YWC`A+;X<>8vmGxCevs@|8>b;we%~8Q~rFG`=$5BIml3# z-?Q1`)5zUi$Jh90p!Wk5A(S{twW*_mX+aPEFzZxG*GKn-y|}j5jT8L*=8~6R1W6-g z-4skLV|L-rV;uv;9FKAvZDJNd*w#6JKdB6RsJHj&$rIupm+fNQJ^zf}pTCUW?eE4X zt<7@QM*s6g1S^Qy`E~Xhr%kSBsl+h~4?UTlzTWmJeN{@~4^(`4U%dd=inw?mKmbW*aeE%7SORu%PdGavRH$O_eqvi!lmokaTtw-TJ-_~0q zY5t!?^|2MfXH zY_~c2`Lwv{I3?}^hQhqzm14l8C(iq0>s(!qp8977>-yRVx?%>3`?X)gkYw!sXO%f! zv}0~wK}+!W8y$dDQE)QOY^q0r9Nj#1#{NdNzCA;bYEJU?XSsN0TPbGBQx5r{^U7rI zPk&xOUK~S`@k>!xWj_l#evMp!rTe>aafS>Pa^kQx=LZqa<%Nwl0R=HJPVCI-HI@OcG9f z5JUB%UvsvlTHD3;79&;hF_+AE7t2ciPlO#rJ*-bhOjz9wK44v4zgYgJ`1}b*wDcHc~3oN6%K8V;-?L+bk53$>wTXBE;F!&~n4EEy<1Pf|;wa z(3fywQ?3VZwxC&GjrP!5uSYO*auROZ#*6H85!Kw~w9ifxSob zXn~}2WG?l3b-yrtDAL)b=TzLb)_ULL z77hwR&MX~Bp6lC2Ln>#YibuV~+}3!!ad*qZueXe34PQzj_&N!?KC6*puHZwO4K)Un zEK{|Q?*ZY@#wYFm>*uRjcuytucpdv})o#-V;iyePkF9nfw8DT+(X+-Gr`1O=7yyqg zjPIK1>dmOj=C+yVfD$k9K;`~(2B;7_9ogxP!FzsU)z_t#)cRrlor+*vBDvk_tpL-; zTbfmLjT}i_|8r5h{2xel*&!o@%2=C(+~Zna`1J=rm;^p!A%4J(e?q49I6pL2<~_Ea zvU=4ojf7S3*NV2-nJEjJw2$d8U3!x!EsAP!jNSTWzY=Emi@_@&3$8q`H#y@t6*1Lw zq{j>QmIfJ_kA}#AI@x{#U>Q{=BFad^Ew}OrE`c#M=ZpBmUGN(-V5i7EW>XQHyRia{ z;I@W3SA^?MFf7IMvv{|JyZ(i1_yU}z4ZItmi~H=8EoR1(n7$d3r78>b&0z0vc+y>b z(IuGPf%|ii;I@vr0`}jB-LuNZ4!%n_2u|>zB;NG3FTS8p1ln2+;evHph@Hq8=V{=O z$ifC660t7ivfOvUqZ69^&&y(?qv8{+>+bPrWj{>>!>>tWmDK~_j2%?gLPZUHSDvA% zh;KQb#inm&*F6vfMy~Dx=a0@4QcaqM@_fzc3@mcG!45U&Kdy}7`yAd=dm15rU})NY zi^=Acv(;S|Z=apvj4m4w{~zB<#ybVGGP$jv(ffzJ9jfL^RCilNE6SUzIV1gzLwGG) zT(&&-mCygD;f*#tpmW4-XC5VgAGoxOgMKDB-m}`5I|U@pXc*oPbpTsYwMgU(T@S z9ub*tcJPrT(95ji^|3BBPn%r}&KAc9`6SN(n`QWFt@>US?&}6?-6d&!efC-e>AbH6 z{#P6t32crcD+*S_k>W;oU7gQ)V+`imdc{H@5Wk-6=6{QD$wUuNt7apKA2>_mV%N%csIzk!Ay=@Zh7dkk` zvB#lMwu~|2)q8U6_z?m(myS!Nk;EEwM3*$oalVINT_H##_5;cFYQDK`nI3hAP$SYv z<_ioXN)e6x6L0{IL|O33ledu!i2P^vSh48=VhQ*QzZGSjPp-`;zs)bYB&Cjr#IGKe zU@QjB`AAO&`b+*urJk)!mbR`i;D*wFVe-JlodRUg^+Iedw4&?*>Oz z{^guZ|8W%U-NN$&tALdsygnX4W8{_1-`sc5es9$ znJ>}vfBwmRS6+flm{5v>gru!H;I!6QLlMVkQWEZ6mlCs0jXv(ai6J#5bBXI5qEpma zcyl?qVI9@QV5_376kx`a}cLO52jQ4=`0rt@~Yi)@hee`tSTL8=&J0Ef>*|d zZ^CZE4Y6#}|FLuyeoemb+Xq1rK@g-<1x4v@n92t!Y3Ud#B_Lgc35ZBeKtQBaT7*eA zqZ>xY=x*4EF<_g&`}@3}|6s4(*L`2-d7Q_2yni7SAwG6##0Qi2H%$`HemoDd8DPJh z+BCSxxBZUlubTfyVlzLeq_B?1){V9Mxj+lFC}SO$*(-}}>jOHeXJ*6baUnn5ahwgg^_C zEn+;VM~c03qU5BEUpK}$0@+g+@}l#%==MCGXk>j^v2s_`UT#t(@SHGQLQGAxt>MOW z;g%jQ(*n$0+pAojuqkeaRxyYWE_3a!u4dD8HT-7WH=CM9H&ZT>k$N)WKVcW=zqxNI zOF;Z^No(%p!^$-!fhJ~YZUN2O@=H$(NEDqeio;XrsXfSj%Ksw72aGX6xq5p2Ram*TPhE=|*?`7nQ z(|d*&B@o+5BF_Sji}rqCk0;}w6qMBGnF10GdWpWK!SyHuH@7uJH-aXx3Oic`tcIQ< zK1~?>&C!akv(bN)C~0RxdR6-Vrq(JvHiu&zU%N@SOSw%*Vo{?k?x#(+bI7DiLb$v5&`pY9dXz5ZbUQaEJ6i68i>qY@M+9khjEH5mT9iMd}nR7tdJ; zcri3)*6SvP-U+U^CKb@PaU%Irk&L@n%;8dHFKLzM&1&Lr{HRWgN&QFiAwpbW2qNOe zjTaKR*X2N%M8*Fm;k73>DGPl2f^pV3?G@kVe0_YGQ(VcQ08bHxwk-VugugYZd2rFS z!v}whN-Cye;ht%-d_W0km9b^wVdO}2&Al7`LB>OOa+(ts<)?;U(()gCG_m@4PsLYC zk}VM~w3Z6JiSMpYSa?D(wZl6CE`q~0%4roEnsXPlL>v-8PxE|MDR&Z=@-^lv|QidX+I5J1V@5Hf9XifJZTc0apHP1 zXgCVB6JnxefVQg;sa0W@;%V zr24|?*cWjqdva=Zb`HJM7aJ`(e32_#P}zf@c&-)`G( z2})&ucMu`>U0i{ik zr5eWjQjE8}i_-+4ki8?v2L_66SV)FY>x0mr8>UI_8x&sqN%3JuJGuAsDq3-VrTyQi z!x>YJ^-S@db=%f{==-`>y1nBT(jA2(X3CCz=?bV;!+>*;7$wK}f_Hzn}Y<$}h_S%!eB1TgTlg zb%m!F{YhA33G2&OMpV~S}wl%n#orZP67p_iM(FTlEc_teNdUzi(wfF?$)F%!031^gjgu z(*uC3DEj8T?~M&PFm%pBsxE;}vc35wCdpxSBowg!*)HJwTU7JCXS6zUf00v(MSUfMSKmEL9H~<&2S<+Zr%meED=>XcVSP> zK}iFOx#IG&%X(dWAPU5e|p6SJx*N8sW_ZybFzJMU0beY!bYt*aG(_h1-JPGpRUH?ac$~YdnR+g ztmEuI(Jnqne|+h0v`Jj6(BkXSVYk)Ck%kkSf3;%Z_O>2y*fMhmeuX;ZMqqjLFk4J@ zsDs!@D`yQw2scOAbRE~z7Bp8j-{5fZXLkCOo4??op{*nBbVvYu0 z`f2zL4KEv>uQ%#1a{*Tvyd{XQ)02GfFP=A{psK#9y0t3j!GlJNT!DUL*pL?=zXk=V zR}^3sbj6h~cPWc&16RmumTG>6X2Dc-#l3i+$cHRDLBoN3@%Ct^PY`JNTxf1rqjbJs z%tob_R?!Uwvy=<3R7LJA5T|BN&8cK*s}pU;MSZcM9!$mt2ju>`nIDIrSvi#d+}!)j zCap%te~x?Y`7AKxB!NZUR~A8FM?89%Z{ZSulJ)GBu!6J&&do`= z6eIFqUCr|gaZU5b_VHh$Dy~dgiXzUxRx>C1mKun*gdyCltFs{(?60YtNE{+B3>;Pb2jXEd+XBd?#~`}+WoYaf7BbNOs2Bpwp?v$ z<+^O2@>Sy}g-iQ`TVNZg=Ki_vEe~AKZ!lnMrAnRf-4NB57MdA)PyWqY{KUP#$3LD+ zV3H^pn9>EU0El7De4$<|wixFBYUBHhsRG)~LOd~n0A_Se zY54e)yA+rj_v)z~6N+a@^M$n7!*?7K^dZ+6GlK;41m*bco~bw6{hTAw*ysQS%14IaYBs&v{3|=7QC>MKwyz~^dPUua=u-Y4#xmTB} zS7ohS3V`#t(av>1wileWR$~!84FtF5Ud6f?odrIvpU}v$eq!LQ#MhoARMZ2I33Imp zOMcYlq+t1v1OtS+_le904*_gJKG+KXa!ZoOUp1ec&=58P)L0DSj2_zYr0t`3ng$-hNrp!Y}uH?rXE|soDfd|&1aP1JN=;WCKfQ#Pp z5z5v9HKP&t8S3mDFj!L|lowB$1!;iy!tYvgQ-{r<+_H@8nZuz{C-eg5q={QeGBXGQ3u?Lr5#z^jDvP zfjy4)C+DB5mr)3#NbdbJ(`=I&8Yr#;Y(kv-4#gty_ahBY$&ME(#GE`&mt@V&JNSN6^cee>w{qKl-=!w`Hj(6OEVzXdRO zE2#z8e&gP}rLY327Yp#M+aK)MwE{_0vB6bTwsmp5j2Bq7!@2-6e4!R4oy4*mH<2Hq zt}NfigSOOV=jbecZZR8#SOtGyM(a>J11C`LTFYk0AfT~MC9&l;&{h}xBVow^uTD2F zpAP>wc%%|guK2vEu2{83{M`sBo^Ja+pXKw~)F2$O{abymcit!3=j zh{M0Xv~sI_cQb1-^HZ{iYsU}pinoV`3(Y-kbOvjwOpA*Wfu_s?^8 zeq%C4PPm8K-qfby_^)#%HcShrZXDFH__Z}jW0MVOba$}t$E%y)mw;pNlUQ?=TRvZ& zw=%4=5PoWUAL&zz5CvqseXzs|KsaHRb*=%`a`>`KQ5k?9a)$kEICb|9+nA12zJy85 z!_sO++l6Rv5h_Tgpmy0bdQ{Uj`TOSi*@QO3gd-zx3Z`@Daub>mGFyb)S zFKkqdQw7U8PNMkB@}<}QX5X(?RYJr8`7+)4TKyxf{*CtvpDL+ACM9i)hTl(__`my2 zfK-%kEfeZipb?K{AGxzlgFRJZD%Y~wTVW&I zv@tvhPafkFmv>FMo_vZ4>q7*30uI1ZO!4-6fmRvdiu0y%=jo&0Kiu+uTgv~ zM(B2IMT_ zoQQXp-(7)v7nvYmuvg#SFN2>Ue!wMe`k5<7TZ?FFh#}BMnFdS3;~eW3X}VnM94ql> z@IS!A4EY&wErS=z4ZycQ(({?1R)y36C;W83VS2D49xNQ`>4am`zshJPe!AGQGz4i3(f?}2;1-)AhSIe4gkUop=W z^AM2Yjpe2qCKS9y!jlvDG%HecBW5j_QK#}^mrTYF{%)7cmI(+!O)8`8D{VanAMjBk z6wk<~JnuoBTC-u5?vBc?195{KK&&rYY6S4*2Hl+$31Mt5Fg9b}&VlhhA3my=o$3$M@x1}EKF={vX1ou-G{JL1GIe` zn=Qx}yu`%$nGS`~!3_7jt*j;4S`r^wlzYcv58r-W{dFObW(PEkRnnxRz2@>Y$WU%- z{@l3D9@wK}FN#XXtwEso%tFD$*3NTfUMHpN>%bt+#!c|K--luO+0NOa1c-}PBa%r8P!vf8sX#K$G}dP0Tnhh_!g zVj`RTq`dJ(8NJKccu*>=@hO`Y1Ph{2C~L{ND>y=)0E z@w+C)-+R3HAo@kzv>`7PlC`T4z>hhNI(*V{PA)nu06 zG){XNvngY_5h^$A3#R=HqL=iecqaXmL0sf*LWcfW_#Ug)4f%WO<=!qYFC#f`ne2nv zss42Q3h93qilU-U6&(K~Kl#z|A|&tjz*@$}5y$t?myVw1=K*T<do4gSvXs zcD?@K>TTS|hcQA~XI!a5jRFH7PZqdpzK>2g=SuIUX^C_zK}v5rOy)bh^XlgaGdgHx zi4MAD+Ln|_A=k#Z4s~!*77C1zo$sg6v(Yu|x!t>O-LzV=RTJ0$aPBQ>W#^ZW^FvGA zzK`jC8z{i=FF6V<&+C6w{xebIKrJ|>Lr=}U+{zLxz-z+$<0O+{o+AqqKa z&<{$>rEZ+GkzF+OZn;VbR=N97Z_vmj7op)oduIyYT^QGFovO{cIpPCvK6>!%iu5}k zsLUf;0}B)lfvbQ7W3^V=r5L9k+x#xWF7dc>_E~I2WjFgZg{5WpSx9~w8;f7ED z?#YbjT4{`KwBL;Z8uwYpPx3R_5V}|mA`d84!W=Qn_pswrFIh$3u~lx|!^y4jzCbIgBZ4>BBJT3E(_6%%|AKTcfAJlr>t*)DX4_VUg8E+sCngMflPN zNY|a0bNif7%~e<=Ms=i^$oht@r6$j8fxb>(=hu5nx=%?AqTC1DWx?DB6>Fdys9j*J zX*20jJvf-@o^18v-IiPQTpX9&?|vH%RJU)No?%M(Uz?@gskc^Q?Xf= zcbhzs!X_+rKev`I)(>&tJTT14dvEW*fb7FEkNfoI!mk*T5`pl|9z5q3jLqc+XKT>p zgEyxnbM<{cJdp2_!0tc=;vE2z9y#k>lD8yCHR39PhDPe~BwnEmm#%~6+}CielmF_{ zdg9BH`Y;R~KG3w1bq41m{POEU&%}<@(UcUo!r=4e+3&6reyd97S1>)@$rCiv3O!+d z;I!0ox-OLaNg425hvOwm{)gwVn^$VgC|feNXudHK&mo!)@K42#!B4`*TO~2lDS0@) z|5{to+1Rq17%gK9N!?0{F4aQYtQ(xB+t&^7l(uJsbi&rrdFIu7wusD7mUH*_Do;!`5jbRg_2$WCpoUN(wozfhmGx6!K#p>$@F z`z}U61eVZ-6J7sPR3b-&sID9%_8&<%H!5V+^B@Ur(wRe_*;6Z@R+aCv>*$cf`bqAw z2}-(eurFS0<#;3BpF{q{t1MW{aJ)lDuM_TZ(8wjXqB~o+%KhQMJ+B&VRSpmEkkNPo z_X44wu=Z@PZ-PE>ih*;%)!fmC&CHl4%W-#lmUr*UpTsWR&KFE7`u6@uj*j>(?Xb@f zazeTCV~8*MdVh&Naay*TpbA7shQ2&vaWFPA==#BOhfFwB$L%z8{USyfi1w)KVk6WnY<%{+O_sNKpzC!vixpPrElHJ0s7YmI(RCAV>+ zQrK`X^dK8^|5~IQ_N2k}`f>H-r;Umh!SGs4#O?Nh94Ph7E9qZFF_7<7{RNK|xm*8~ zzt62S>44u3oL@64+m46mx)ivWKi-hhcH#B7yCy)vTYDplrM-PyIRjpWa1O#hUJ?0h z4nE{YA6>KuM}PjANMt=IkI}LyE8%bv9c}znbYkgWb+Wc&l z@JvcBf;Yk{R*11)m_*J|FZg_S-v|&>aePR;u;+Z5=f@m&Ou-oXC_G;csx)UTT5J+` z=!Hs3ib~VYcZw+5Q*^X0FfTV^*I-}S^X^TK_dsSNyW-$1fjt+NjgvK0R#3ynWDODW zgE)mc1;+3;l6#_*hn9&=fuBx16?rUK{j207o72crV20p7+)N1I4yJTQRDnJ!bL|r! zjQ>%j@LZ#O{1>KG7crxskkI@~IzTRnOG6sS^bh%trsWK41K-ATVql%q`WAr*^K0#o zGt9LqRuVzX9Y&%--Ci|pqP=9CrNKch>?ff;Q25ASEhU;sW%S<&siT0o7$eyhb zH$gfzSNi|U=6V2p!@z9!!j3!Lrg#?ig7x7*f16NVhKcN=;4S)=L=sJMQ?EG;To48< zEVchko8ZPhHR*aX*LYi*%Tl1eovvxmJd7dNfCm`ik;36W(P~5Jz2%I&mYMP{E_t-# zg00`Hs7fe;Jr+L+lpCcxJ-yi4<}ewn*+CYHAGc=#X{Yqer)P#;7%0t`gwAIvf&42b z@Sea52q##W;|omGb=8}DjE@A0Gi_L3Yj8>6*e}Cmyr)-|~#qHLVi>O2w#i4@Ge}1b={HUGX7=8dkg%uvFTr3Qjqc4hY5Aem?fl@gp54=d)Hbcn z(X2gWo^T`x$M?1XD>BdCCfowWdArulhM#NjS(FvD5;gsPicSJ41u1qd* zVfg1#N!uXg?=g2;r!|D}H{5Wv3rb<#+Y9}>P}qqjpxtrEvnq@JX2SAQZY<#5is?z*Re1kPS5 zUcD})<-S>@GjH!BAWWfm`_jbUF{Ct0&ZA=V5=Ol9BL;dc87RN|El^7TZ!S5GBWoEzEz%d14{B z{43fn@NMTWs15{5o=`m#5CoQ1*-H38FH4s5?eP1&2$4Jq8CTi$B&-pl4(qR&dE;^O zSCW6u2hvzHX#V`}@qyQ>lbsc!%Hz!nO;Z{2gz-$KOF?P3q{Ms6H|%ozRx<^*<>NBY z#xlJRGHQxe675ht{YMgZWs-_LI|}xzF7VmNoio(R)Wca|9#bm7;yOCQUtt&$*rU~K%*5Q$5Uc%rRNK%dX{TTxSD+V<9ed@ zi2w4x`gadWt3I&HK0v#jN3LduHmwFkI=qBJ^DJtQery6T7tUKLDMyK~sk9|wQn)1U zd8j>0FIoTOkla;YXKvd}9~7-UNa2~#NJv^r#L|t(j>uU2=p!0onPc6<318t4dEQ5N zeNN099bzD|*n_jcHxSO;ba;v>ylud0@u7)9OM=k%D+|B+vzrXg32iI>YIFGSUkX zIyiy=y*WAYL+AB7yk<4G1l0b^8|r@~rA@s6scSOq7DTij_dg$k;^YyR^U*uJ+v*&A z)xl5v<@^U~a+|PYo z3%G|db$BMoSB#))??9#5Po)u@@c;a>Ue-D^8q+b0@h%1pGI)kMwZ{s=2eBs27$B~U71|CSqz*5W?Oe%3;B5jd)C$6q_n5AVVm!{NzD{}+N#}F(npMi~P*OFWV~euKjSU(KRfR1|Ek`GOcYM#8 z={kAg8;6N<$ie#@4j-WKGDC^y!isV82`Ayrk6Nb;b26c9&N#0>%fjt{EJ+3^>CHuF zgX8hb1==oXlWzS!$F$SV)0cSxnVfk&w-2Xc;Mvwi;ze?!vV``2Uu?47w+n3pFyiQ zFUiO4efV0vVIr$8MFn1|bOh>5ivr@RGyQ~aEbEPypTC*;k>;0-+B6)P9gzN_))E8< zT)Qg;Tr2=yuEvdBo2NTiI?vCIS9<2Xr112K_?Lu!*2@Y!C@SPkG?go;2QswiwTjPThtfR{7F9Yx%m|WE*AXv(z2$Fi4Zx z|KPA^JV73;Uc0y=Xz-q;OMz?GRv78*Qd2<%;}-($lAaBmZ&LN7t3P~@Uu&`3Cf)&| ziD1zuu(fGBO3+Tlt@Y8MPmKjprK}+>>WTbcr!vl*I^%jW*F3_TSlu-jauhC0O-<-- z7ux6?B#O-%AwOIZ;0aQ?H}Pzx@`q@*8a-MPe;0X5ou z?zPtzbZBdz7z#jkNH2X77x`}ll`J1|7rcC>8a!yfsFFlAk{LKiZM+jRyMkp*a?UJ& zzPcQ@&jnmc%+$rABmV*FQ3myCXeoqz4i zcqgUbmVYh{`1?z%w#$^g+%d~tnET;`A2`q=Tzg{Ud&u9C_w5r$zn=t z06A+zSe%lnhP6{261;hA+j+k88|hL4OD*YiJG(LS9SLX8P_Ucu z-Z^gTUO4p{j|*m9V*L!PmeD@^&LZ}Eeof;d$n|w3dqqD7b_?O4Oufj`$}rndQCFLG zwH1>2vAQoIGwAuc+-igqr)roIUPt$tEzn1=C!!L_Af`=-pEQxS}t>oE!;SW7p;spE~>OzC{1+F{^YIE%l$&{ABnr_ zowcX)MSsgFI-u2D8Eb!px_hyXeGgc+>Vznf1j$=XT9IE(eCwXB3-D_s@m;HMmJVmj zaE_c!rjF&V9p77;cQ!FZbpg;3h(_{3XauYW@c^{)=jzK0$SwM-Ypd$a-Qz@?Un;h(+4eboMMv%_;aPuZ3DwrX^X%G^ zg`jmzd2mxxHiP4Dr|D%{gllO#8s7Qc9A`L?sZ(L$`{iYM@pL2sA&g z6@6KouqpFMB6-2ae!6uCM|G`f(Y+#a+c}I0rxsLV?NU3|=~pK8N-%IVq@-3~=%PnajvSTT3k(#SN|p?s6)Gbon&`+2Cstruku$?^jc z9r`asy4*u0Z!d;T&pe_mu+bg3F;sN4{Y6wqep&@L)7#3~kT!*FYjU90`vGtII2KEk7~zh9=- zjb6Y>Ac{CInNIf*vu`*j(>Prqu`)upQseEAchGnGAsFHgEmuY0DZAWOc zOhvK{9^c+_OCYV~&ev_{J@1h5#aWyh@s8U14DRO|h|0|bm6+>SX4xseFu#j^=5FtH z=kLjXuYa)8PH?}Zy?B`1nWyEheE&yW8-WauhNHbce@90Drm zFy2X#^M54KIoz$)lak1L6!ltd96di@M*Fdl&Zlzt_L`rFioUVguCg2ER&43@^+i;H z4-I`VJgXakj?wHCn%|kflO|yqXti0Dz%ulq5tUuqk}o_VMjwC@Sy|HOMv# zG0ifJM_m1z2CvhcaIB$#V(qV(E5Yw8_7y3DNtSrP+AL;fs z9+)dm!ORL(P>G-qjhMWrCU^4YbRySUN#HMf`Jf*|)(xd{z@^GWN;NItKy^zNy{Xj| zGvc{qvle|xmejoVnjQL-8_ar04R7cwEq89ttqXJ|`zKEK}i_Re&8um7AC% z*m;Xx=bKm;3(JPAdG?rOi3bLbv>&?t!+U`peG!#OMODesU333}Kb9|8($xQySqJgF zj#@AxmA(1ZX)?B=6o5f>I$Nk=n3AA*R+dlGrlbT+qNX&?90!XZIB~388?8O?k<0s7+dQuWbU; zpA#|^Z?1Y$2i9I5;^jCH&W$gyL8zd|L2S|jpA?(8biFK14%N`&gDWlN-=uV7|B=uD z8Y3Xi7F5Rl_sXRiYrFO0$?(cb`sG}u|H<#$jGIf$qXihk@9B~6 zF~jnj`zsXp*Kgzr1MZG1iCx$B$n#^|Q|>O>&pFTj#*X>Is0#CZ+^EX4>FBjwZ{ z(wu}QbIm;u3$7pGjlA8bWb|I3h!t_I)heWcY?hP z!T^6CUku1OL@A)xe`~3?eTgP%K*GGUBH(Q#JO@ylO7=q6UU+l6gC@w`J0JC=H8OV+*Mg5& zde_`ekn2jw`7p7-?`yhWhoMi7fy$F|BRsLi?VOKDeKl62@sGqKH#Pv>f5RCYc8lo( zH*4>8cB@BVYg&ZdPW}3Rw~+au3;PeoMnL=! zu82ql&6m!`(00^$eYiUrrK@% zbOMe?Z#Dh!{WIAEn=gL&n~rcc@#L2?dHu!nc`P zAB&12S!n?4PFUB#-KA2D^u#7uLgSNB{~n~->68(-yY>S`aY$CIOvO&i+^Wt(!k&XD zV|{yK{UXEdX+L+3{Fy?T@XVdCF%K=WWp8hR4ycP3^bj^j_FrwA;4aidDWi;!DZjYhm*n`8BOol4fMGj~~OhacH>QJPMC|<#yqJ0^*0h zojYP*KaOyUaS(7TZTFF8cQ*Yvm~sUhLA=Mi(4zdRPR(1@ubX(m=%TOtI_iUxbjD-4R&enb}$D!YbwR3>G5Ht#HXXuw{gD511j zhs{cB5btGK_#>TIPYPQF<_?uo2f?s|Of>3(!9#$%o>ws~3I*r* zo1;?R>V9570RbLTrmlY^C%bDbl(I)2_reJRCq6y;;2mp;8JqCJI<_c62T5ei=<5}F z3cl=4G<@nG3Fm#kYY@aR$}DtiCNnV(Saf$i`4a?lt=;~9y+ zL9AP^vH(PXjK~Z82&J>M36AcdACD7ZrEPumfsU#q3N|1Il-_MqDyzUc&fEYh8 zLo|O;&a*+=@@)e2lW}jofbXT&&}v)9)J(=WQ&x65#}lc$ivLcihN;sg)?X7`jgo2L%~f(ZJBO1~0_YN<=YV!rSij>reV zXUep?>w;hX>D6gQrBn!=d|F9XGg;XaqpB(^c076}p&g7rLS@R_@s%LiAAcBaic z-(r5IiIT^&kkz`(et)#;~-jwckr_7tC!{BmGRG zcE-x4cmZW(C#=w#Tkr*6_NRoYPL5zHH6QOO z0lvus8FI9B{GH2;eYoXoWn%l7ki&NwM|xc`{-l)+#Do&eu z3jea_+`O}K^_BA<37JL_2afEGN4yl3B3c=|Xn9e}WD26m`_G4uX#;c-qe4)6Hf_8G zzTxU6OkEP>_Dp~99hLkMzZ{C{!HE3dGR%n%!~CvnE?HN|Xv(fJ?WwXap{|vD4sSUo z=^kQ$$;n($_^#ukj;pX*o`NLBulv zs>5=@h^A;ihN05iVK;Gc+7pTjzk+gvYC>4y$M3R>BA621^xjYFNNk~a{K*GwpFZi4 z`%HGHKw%cUj4H*xm^_0;%PSYHf~f+fcO^@WJn!lLkO&1ZkScxmk_#UuN}s4Bb<~mq z{kf?i0b^$(G^V|)@Ef?oh05Ae-;;vEs&*sTCbfO)=bbj{GfCv!GB#@p`j&4BQ6VVk zr~jKOaMSHrYMNFgkliaW%qyvvU+&O)>RJ#-_7b?cp{0=5)4tjYy3G^t|EPY=@=&n~ zN8Do8`IwtY4#|r@O{^K;J82(y#ZQ+t9&_e&Ft)M}j~;_4jEXFwLRJ0?5s)jWh9Ei=pO+l^nJuE9Tq-ilgJcytord{}n;BO|LsfzE+ihk}o6)aT8)1?8FB zoM3rGuT*sc!*BM-W*P>v>k+r%3B}9UbP1v^p}tWMJkRuxqzHT+^sRGKvYl0Iy-3Pq zO*+}JZ8^1Vou$~!zm`_tI^W69Aw*m1N9~-&XXw(6M)k9clw^N^ULWbOwM=jSo<_mH z{5SKd-Q6!L)M)uP+BFqkFz=}I^z_Ji7G%p?p)CIkfrtH_G?Zm!1~2bB19Iem=X_3> zZEo1|bcwb8r2OpVeD_5{0p1Do+D$AVZGU8LPuWiV~ipS%Z@A>-|1!_4C0SIC9SWuDK#&-)`lt&fVjk_Vn@o zQf%_pT81v6;YglnbW&Io>J69f*1yK8k;VIRu!R52*LGA{1d{<2fViTk4? zfttG1#D2|t&q1FV&qWT*)&U3}l&V>!e0gYMj}0EXDcl%H zFp<&bhZQGrGAZSoB2nC~kCR68$-ArfBr7qj7osVgiRt7rqjRt zsU0ttbkvFQ-RA4M=SH$FtMs5~Wn>&&bg___N#DC_ z*pRM&&`o;WzAos;{MM2u)PK75p!I2juAsj}pZwQCR+lBQaopx3{!s9zS1(w=WjH#3m zbE`a|x|Y@vBQ(cm?L+p)uu!zTont`2GxrmhS$^sU4@fkmZY=Y9K5kZ>1{$aP-bLRz zd?iC%aHyitS5tog7azchl)U&M$FO)EcIWpCvUVl0P&aeU#;qiBdO`6g$8{?=PnUOL z&>F}xC{99>!fj*o4=T#5)njUXU!$;cPP|Vuoq9N)-z;kt<;qKYYK)&wko;Spj&f`H zG`te2C;Un1<_BBHQh3ACva8=KskVH(xa{-XujhQSh^r*l8_l!vH^RP)?=OBpu~9@} zkBRcjK1@()9mq}Hiwv#`H_IWkX+_1|I7Ybb;NNWiyJG6%OAHw#bg8+8YFUT-N&L6f zAoy8>$@*b_E-1KPWhZv9e8w$SJIADu9%@Q#o`E8f|>j7J^lX~jkvVs@?(*4MRC0VGbJFu{?Qg%@xi!92~vFl%t^WYSzskqyNs72R34&b-NF4IamexAWIxJAZGYpS&vr(En7|9VhHGz zxYJ>lED+?A8q6USpF@9;KRer%r-Y`6a}SS#cKP^f8*aEo7++j86uGx}elp>$ZN{N_ zBVntGyA>6IB76UiWhL(anrIg){|ld5WNPOoAeZyf5K9~N396_|M$aMZZw_5_CnzQSIqWv;SMHexI!#RHqfp4)l%vm^JjON2bpCW57E6e>mX3{cAHGbRHc=$`G-KRxo zIf{v8=>m%Mh&y>l%m84mJCSNHPcZFpzQ4kqlgIat^q!ci$+U61dds#!pN^Ol4$y<= z#b=YtOsD$l*EX#?If!j

_%?ai}- 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

Vvo6Xwr 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 J*QbBi{sX{Gol*b* diff --git a/finetune_prune_model.sh b/finetune_prune_model.sh deleted file mode 100644 index 6728b83..0000000 --- a/finetune_prune_model.sh +++ /dev/null @@ -1 +0,0 @@ -python3 tools/det_train.py --config ./config/det_DB_mobilev3.yaml --log_str total_prune_20201015_distil3 --pruned_model_dict_path ./checkpoint/ag_DB_bb_mobilenet_v3_small_he_DB_Head_bs_16_ep_1200_mobile_slim_all/pruned/pruned_dict.dict --prune_model_path ./checkpoint/ag_DB_bb_mobilenet_v3_small_he_DB_Head_bs_16_ep_1200_mobile_slim_all/pruned/pruned_dict.pth --prune_type total --n_epoch 200 --start_val 30 --base_lr 0.0008 --gpu_id 2 --t_ratio 0.1 --t_model_path ./checkpoint/ag_DB_bb_resnet50_he_DB_Head_bs_8_ep_1201/DB_best.pth.tar --t_config ./config/det_DB_resnet50_3_3.yaml \ No newline at end of file diff --git a/infer.sh b/infer.sh deleted file mode 100644 index c111f04..0000000 --- a/infer.sh +++ /dev/null @@ -1 +0,0 @@ -python3 ./tools/det_infer.py --config ./config/det_DB_mobilev3.yaml --model_path ./checkpoint/ag_DB_bb_mobilenet_v3_small_he_DB_Head_bs_16_ep_1200/DB_best.pth.tar --img_path /src/notebooks/detect_text/icdar2015/ch4_test_images --result_save_path ./result --onnx_path ./onnx/DBnet.onnx --trt_path ./onnx/DBnet_batch.engine --batch_size 2 --max_size 1536 --add_padding \ No newline at end of file diff --git a/onnx/onnx-simple.sh b/onnx/onnx-simple.sh deleted file mode 100644 index eed4d41..0000000 --- a/onnx/onnx-simple.sh +++ /dev/null @@ -1 +0,0 @@ -python3 -m onnxsim $1 $2 \ No newline at end of file diff --git "a/pre_model/\346\226\260\345\273\272\346\226\207\346\234\254\346\226\207\346\241\243.txt" "b/pre_model/\346\226\260\345\273\272\346\226\207\346\234\254\346\226\207\346\241\243.txt" deleted file mode 100644 index e69de29..0000000 diff --git a/ptocr/__init__.py b/ptocr/__init__.py deleted file mode 100644 index eab50c3..0000000 --- a/ptocr/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -#-*- coding:utf-8 _*- -""" -@author:fxw -@file: __init__.py.py -@time: 2020/08/07 -""" \ No newline at end of file diff --git a/ptocr/dataloader/DetLoad/DBProcess.py b/ptocr/dataloader/DetLoad/DBProcess.py deleted file mode 100644 index 7fbe42b..0000000 --- a/ptocr/dataloader/DetLoad/DBProcess.py +++ /dev/null @@ -1,123 +0,0 @@ -#-*- coding:utf-8 _*- -""" -@author:fxw -@file: DBProcess.py -@time: 2020/08/11 -""" -import cv2 -import torch -import numpy as np -from PIL import Image -from torch.utils import data -from .MakeBorderMap import MakeBorderMap -from .transform_img import Random_Augment -from .MakeSegMap import MakeSegMap -import torchvision.transforms as transforms -from ptocr.utils.util_function import resize_image - -class DBProcessTrain(data.Dataset): - def __init__(self,config): - super(DBProcessTrain,self).__init__() - self.crop_shape = config['base']['crop_shape'] - self.MBM = MakeBorderMap() - self.TSM = Random_Augment(self.crop_shape) - self.MSM = MakeSegMap(shrink_ratio = config['base']['shrink_ratio']) - img_list, label_list = self.get_base_information(config['trainload']['train_file']) - self.img_list = img_list - self.label_list = label_list - - def order_points(self, pts): - rect = np.zeros((4, 2), dtype="float32") - s = pts.sum(axis=1) - rect[0] = pts[np.argmin(s)] - rect[2] = pts[np.argmax(s)] - diff = np.diff(pts, axis=1) - rect[1] = pts[np.argmin(diff)] - rect[3] = pts[np.argmax(diff)] - return rect - - def get_bboxes(self,gt_path): - polys = [] - tags = [] - with open(gt_path, 'r', encoding='utf-8') as fid: - lines = fid.readlines() - for line in lines: - line = line.replace('\ufeff', '').replace('\xef\xbb\xbf', '') - gt = line.split(',') - if "#" in gt[-1]: - tags.append(True) - else: - tags.append(False) - # box = [int(gt[i]) for i in range(len(gt)//2*2)] - box = [int(gt[i]) for i in range(8)] - polys.append(box) - return np.array(polys), tags - - def get_base_information(self,train_txt_file): - label_list = [] - img_list = [] - with open(train_txt_file,'r',encoding='utf-8') as fid: - lines = fid.readlines() - for line in lines: - line = line.strip('\n').split('\t') - img_list.append(line[0]) - result = self.get_bboxes(line[1]) - label_list.append(result) - return img_list,label_list - - def __len__(self): - return len(self.img_list) - - def __getitem__(self, index): - - img = Image.open(self.img_list[index]).convert('RGB') - img = np.array(img)[:,:,::-1] - - polys, dontcare = self.label_list[index] - - img, polys = self.TSM.random_scale(img, polys, self.crop_shape[0]) - img, polys = self.TSM.random_rotate(img, polys) - img, polys = self.TSM.random_flip(img, polys) - img, polys, dontcare = self.TSM.random_crop_db(img, polys, dontcare) - img, gt, gt_mask = self.MSM.process(img, polys, dontcare) - img, thresh_map, thresh_mask = self.MBM.process(img, polys, dontcare) - - img = Image.fromarray(img).convert('RGB') - img = transforms.ColorJitter(brightness=32.0 / 255, saturation=0.5)(img) - img = self.TSM.normalize_img(img) - - - gt = torch.from_numpy(gt).float() - gt_mask = torch.from_numpy(gt_mask).float() - thresh_map = torch.from_numpy(thresh_map).float() - thresh_mask = torch.from_numpy(thresh_mask).float() - - return img,gt,gt_mask,thresh_map,thresh_mask - - - -class DBProcessTest(data.Dataset): - def __init__(self,config): - super(DBProcessTest,self).__init__() - self.img_list = self.get_img_files(config['testload']['test_file']) - self.TSM = Random_Augment(config['base']['crop_shape']) - self.test_size = config['testload']['test_size'] - self.config =config - - def get_img_files(self,test_txt_file): - img_list = [] - with open(test_txt_file, 'r', encoding='utf-8') as fid: - lines = fid.readlines() - for line in lines: - line = line.strip('\n') - img_list.append(line) - return img_list - - def __len__(self): - return len(self.img_list) - def __getitem__(self, index): - ori_img = cv2.imread(self.img_list[index]) - img = resize_image(ori_img,self.config['base']['algorithm'], self.test_size,stride = self.config['testload']['stride']) - img = Image.fromarray(img).convert('RGB') - img = self.TSM.normalize_img(img) - return img,ori_img \ No newline at end of file diff --git a/ptocr/dataloader/DetLoad/MakeBorderMap.py b/ptocr/dataloader/DetLoad/MakeBorderMap.py deleted file mode 100644 index 3275993..0000000 --- a/ptocr/dataloader/DetLoad/MakeBorderMap.py +++ /dev/null @@ -1,114 +0,0 @@ -#-*- coding:utf-8 _*- -""" -@author:fxw -@file: MakeBorderMap.py.py -@time: 2020/08/11 -""" - -import numpy as np -import cv2 -from shapely.geometry import Polygon -import pyclipper - -class MakeBorderMap(): - def __init__(self,shrink_ratio=0.4,thresh_min=0.3,thresh_max=0.7): - super(MakeBorderMap,self).__init__() - self.shrink_ratio = shrink_ratio - self.thresh_min = thresh_min - self.thresh_max = thresh_max - def process(self, img,polys,dontcare): - thresh_map = np.zeros(img.shape[:2], dtype=np.float32) - thresh_mask = np.zeros(img.shape[:2], dtype=np.float32) - - for i in range(len(polys)): - if dontcare[i]: - continue - self.draw_border_map(polys[i], thresh_map, mask=thresh_mask) - thresh_map = thresh_map * (self.thresh_max - self.thresh_min) + self.thresh_min - return img,thresh_map,thresh_mask - - def draw_border_map(self, polygon, canvas, mask): - polygon = np.array(polygon) - assert polygon.ndim == 2 - assert polygon.shape[1] == 2 - - polygon_shape = Polygon(polygon) - distance = polygon_shape.area * \ - (1 - np.power(self.shrink_ratio, 2)) / polygon_shape.length - subject = [tuple(l) for l in polygon] - padding = pyclipper.PyclipperOffset() - padding.AddPath(subject, pyclipper.JT_ROUND, - pyclipper.ET_CLOSEDPOLYGON) - padded_polygon = np.array(padding.Execute(distance)[0]) - cv2.fillPoly(mask, [padded_polygon.astype(np.int32)], 1.0) - - xmin = padded_polygon[:, 0].min() - xmax = padded_polygon[:, 0].max() - ymin = padded_polygon[:, 1].min() - ymax = padded_polygon[:, 1].max() - width = xmax - xmin + 1 - height = ymax - ymin + 1 - - polygon[:, 0] = polygon[:, 0] - xmin - polygon[:, 1] = polygon[:, 1] - ymin - - xs = np.broadcast_to( - np.linspace(0, width - 1, num=width).reshape(1, width), (height, width)) - ys = np.broadcast_to( - np.linspace(0, height - 1, num=height).reshape(height, 1), (height, width)) - - distance_map = np.zeros( - (polygon.shape[0], height, width), dtype=np.float32) - for i in range(polygon.shape[0]): - j = (i + 1) % polygon.shape[0] - absolute_distance = self.distance(xs, ys, polygon[i], polygon[j]) - distance_map[i] = np.clip(absolute_distance / distance, 0, 1) - distance_map = distance_map.min(axis=0) - - xmin_valid = min(max(0, xmin), canvas.shape[1] - 1) - xmax_valid = min(max(0, xmax), canvas.shape[1] - 1) - ymin_valid = min(max(0, ymin), canvas.shape[0] - 1) - ymax_valid = min(max(0, ymax), canvas.shape[0] - 1) - canvas[ymin_valid:ymax_valid + 1, xmin_valid:xmax_valid + 1] = np.fmax( - 1 - distance_map[ - ymin_valid-ymin:ymax_valid-ymax+height, - xmin_valid-xmin:xmax_valid-xmax+width], - canvas[ymin_valid:ymax_valid + 1, xmin_valid:xmax_valid + 1]) - - def distance(self, xs, ys, point_1, point_2): - ''' - compute the distance from point to a line - ys: coordinates in the first axis - xs: coordinates in the second axis - point_1, point_2: (x, y), the end of the line - ''' - height, width = xs.shape[:2] - square_distance_1 = np.square( - xs - point_1[0]) + np.square(ys - point_1[1]) - square_distance_2 = np.square( - xs - point_2[0]) + np.square(ys - point_2[1]) - square_distance = np.square( - point_1[0] - point_2[0]) + np.square(point_1[1] - point_2[1]) - - cosin = (square_distance - square_distance_1 - square_distance_2) / \ - (2 * np.sqrt(square_distance_1 * square_distance_2)) - square_sin = 1 - np.square(cosin) - square_sin = np.nan_to_num(square_sin) - result = np.sqrt(square_distance_1 * square_distance_2 * - square_sin / square_distance) - - result[cosin < 0] = np.sqrt(np.fmin( - square_distance_1, square_distance_2))[cosin < 0] - # self.extend_line(point_1, point_2, result) - return result - - def extend_line(self, point_1, point_2, result): - ex_point_1 = (int(round(point_1[0] + (point_1[0] - point_2[0]) * (1 + self.shrink_ratio))), - int(round(point_1[1] + (point_1[1] - point_2[1]) * (1 + self.shrink_ratio)))) - cv2.line(result, tuple(ex_point_1), tuple(point_1), - 4096.0, 1, lineType=cv2.LINE_AA, shift=0) - ex_point_2 = (int(round(point_2[0] + (point_2[0] - point_1[0]) * (1 + self.shrink_ratio))), - int(round(point_2[1] + (point_2[1] - point_1[1]) * (1 + self.shrink_ratio)))) - cv2.line(result, tuple(ex_point_2), tuple(point_2), - 4096.0, 1, lineType=cv2.LINE_AA, shift=0) - return ex_point_1, ex_point_2 \ No newline at end of file diff --git a/ptocr/dataloader/DetLoad/MakeSegMap.py b/ptocr/dataloader/DetLoad/MakeSegMap.py deleted file mode 100644 index 3a72c7d..0000000 --- a/ptocr/dataloader/DetLoad/MakeSegMap.py +++ /dev/null @@ -1,171 +0,0 @@ -#-*- coding:utf-8 _*- -""" -@author:fxw -@file: MakeSegMap.py -@time: 2020/08/11 -""" -#-*- coding:utf-8 _*- -""" -@author:fxw -@file: MakeSegMap.py -@time: 2020/04/28 -""" - -import cv2 -import pyclipper -from shapely.geometry import Polygon -import numpy as np -import Polygon as plg - -class MakeSegMap(): - r''' - Making binary mask from detection data with ICDAR format. - Typically following the process of class `MakeICDARData`. - ''' - def __init__(self, algorithm='DB',min_text_size = 8,shrink_ratio = 0.4,is_training = True): - self.min_text_size =min_text_size - self.shrink_ratio = shrink_ratio - self.is_training = is_training - self.algorithm = algorithm - def process(self, img,polys,dontcare): - ''' - requied keys: - image, polygons, ignore_tags, filename - adding keys: - mask - ''' - h, w = img.shape[:2] - if self.is_training: - polys, dontcare = self.validate_polygons( - polys, dontcare, h, w) - gt = np.zeros((h, w), dtype=np.float32) - mask = np.ones((h, w), dtype=np.float32) - if self.algorithm =='PAN': - gt_text = np.zeros((h, w), dtype=np.float32) - gt_text_key = np.zeros((h, w), dtype=np.float32) - gt_kernel_key = np.zeros((h, w), dtype=np.float32) - - for i in range(len(polys)): - polygon = polys[i] - height = max(polygon[:, 1]) - min(polygon[:, 1]) - width = max(polygon[:, 0]) - min(polygon[:, 0]) - if dontcare[i] or min(height, width) < self.min_text_size: - cv2.fillPoly(mask, polygon.astype( - np.int32)[np.newaxis, :, :], 0) - dontcare[i] = True - else: - if self.algorithm == 'PAN': - cv2.fillPoly(gt_text, [polygon.astype(np.int32)], 1) - cv2.fillPoly(gt_text_key, [polygon.astype(np.int32)], i + 1) - polygon_shape = Polygon(polygon) - distance = polygon_shape.area * \ - (1 - np.power(self.shrink_ratio, 2)) / polygon_shape.length - subject = [tuple(l) for l in polys[i]] - padding = pyclipper.PyclipperOffset() - padding.AddPath(subject, pyclipper.JT_ROUND, - pyclipper.ET_CLOSEDPOLYGON) - shrinked = padding.Execute(-distance) - if shrinked == []: - cv2.fillPoly(mask, polygon.astype( - np.int32)[np.newaxis, :, :], 0) - dontcare[i] = True - continue - shrinked = np.array(shrinked[0]).reshape(-1, 2) - cv2.fillPoly(gt, [shrinked.astype(np.int32)], 1) - if self.algorithm == 'PAN': - cv2.fillPoly(gt_kernel_key, [shrinked.astype(np.int32)], i + 1) - if self.algorithm == 'PAN': - return img,gt_text,gt_text_key,gt,gt_kernel_key,mask - return img,gt,mask - - def validate_polygons(self, polygons, ignore_tags, h, w): - ''' - polygons (numpy.array, required): of shape (num_instances, num_points, 2) - ''' - if len(polygons) == 0: - return polygons, ignore_tags - assert len(polygons) == len(ignore_tags) - for polygon in polygons: - polygon[:, 0] = np.clip(polygon[:, 0], 0, w - 1) - polygon[:, 1] = np.clip(polygon[:, 1], 0, h - 1) - - for i in range(len(polygons)): - area = self.polygon_area(polygons[i]) - if abs(area) < 1: - ignore_tags[i] = True - if area > 0: - polygons[i] = polygons[i][::-1, :] - return polygons, ignore_tags - - def polygon_area(self, polygon): - edge = 0 - for i in range(polygon.shape[0]): - next_index = (i + 1) % polygon.shape[0] - edge += (polygon[next_index, 0] - polygon[i, 0]) * (polygon[next_index, 1] - polygon[i, 1]) - - return edge / 2. - - -class MakeSegPSE(): - - def __init__(self, kernel_num = 7,shrink_ratio = 0.4): - self.kernel_num = kernel_num - self.shrink_ratio = shrink_ratio - - def dist(self,a, b): - return np.sqrt(np.sum((a - b) ** 2)) - - def perimeter(self,bbox): - peri = 0.0 - for i in range(bbox.shape[0]): - peri += self.dist(bbox[i], bbox[(i + 1) % bbox.shape[0]]) - return peri - - def shrink(self,bboxes, rate, max_shr=20): - rate = rate * rate - shrinked_bboxes = [] - for bbox in bboxes: - area = plg.Polygon(bbox).area() - peri = self.perimeter(bbox) - - pco = pyclipper.PyclipperOffset() - pco.AddPath(bbox, pyclipper.JT_ROUND, pyclipper.ET_CLOSEDPOLYGON) - offset = min((int)(area * (1 - rate) / (peri + 0.001) + 0.5), max_shr) - - shrinked_bbox = pco.Execute(-offset) - if len(shrinked_bbox) == 0: - shrinked_bboxes.append(bbox) - continue - - shrinked_bbox = np.array(shrinked_bbox)[0] - shrinked_bbox = np.array(shrinked_bbox) - if shrinked_bbox.shape[0] <= 2: - shrinked_bboxes.append(bbox) - continue - - shrinked_bboxes.append(shrinked_bbox) - - return np.array(shrinked_bboxes) - - def process(self,img, bboxes, tags): - - bboxes = np.array(bboxes).astype(np.int) - gt_text = np.zeros(img.shape[0:2], dtype='uint8').copy() - training_mask = np.ones(img.shape[0:2], dtype='uint8').copy() - - if bboxes.shape[0] > 0: - for i in range(bboxes.shape[0]): - cv2.drawContours(gt_text, [bboxes[i]], -1, 1, -1) - if tags[i]: - cv2.drawContours(training_mask, [bboxes[i]], -1, 0, -1) - gt_kernels = [] - for i in range(1, self.kernel_num): - rate = 1.0 - (1.0 - self.shrink_ratio) / (self.kernel_num - 1) * i - gt_kernel = np.zeros(img.shape[0:2], dtype='uint8') - kernel_bboxes = self.shrink(bboxes, rate) - for j in range(bboxes.shape[0]): - cv2.drawContours(gt_kernel, [kernel_bboxes[j]], -1, 1, -1) - gt_kernels.append(gt_kernel) - return img, training_mask, gt_text, gt_kernels - - diff --git a/ptocr/dataloader/DetLoad/PANProcess.py b/ptocr/dataloader/DetLoad/PANProcess.py deleted file mode 100644 index 8da1e7c..0000000 --- a/ptocr/dataloader/DetLoad/PANProcess.py +++ /dev/null @@ -1,121 +0,0 @@ -#-*- coding:utf-8 _*- -""" -@author:fxw -@file: PANProcess.py -@time: 2020/08/11 -""" -import cv2 -import torch -import numpy as np -from PIL import Image -from torch.utils import data -from .transform_img import Random_Augment -from .MakeSegMap import MakeSegMap -import torchvision.transforms as transforms -from ptocr.utils.util_function import resize_image - -class PANProcessTrain(data.Dataset): - def __init__(self,config): - super(PANProcessTrain,self).__init__() - self.crop_shape = config['base']['crop_shape'] - self.TSM = Random_Augment(self.crop_shape) - self.MSM = MakeSegMap(algorithm=config['base']['algorithm'],shrink_ratio = config['base']['shrink_ratio']) - img_list, label_list = self.get_base_information(config['trainload']['train_file']) - self.img_list = img_list - self.label_list = label_list - - def order_points(self, pts): - rect = np.zeros((4, 2), dtype="float32") - s = pts.sum(axis=1) - rect[0] = pts[np.argmin(s)] - rect[2] = pts[np.argmax(s)] - diff = np.diff(pts, axis=1) - rect[1] = pts[np.argmin(diff)] - rect[3] = pts[np.argmax(diff)] - return rect - - def get_bboxes(self,gt_path): - polys = [] - tags = [] - with open(gt_path, 'r', encoding='utf-8') as fid: - lines = fid.readlines() - for line in lines: - line = line.replace('\ufeff', '').replace('\xef\xbb\xbf', '') - gt = line.split(',') - if "###" in gt[-1]: - tags.append(True) - else: - tags.append(False) - # box = [int(gt[i]) for i in range(len(gt)//2*2)] - box = [int(gt[i]) for i in range(8)] - polys.append(box) - return np.array(polys), tags - - def get_base_information(self,train_txt_file): - label_list = [] - img_list = [] - with open(train_txt_file,'r',encoding='utf-8') as fid: - lines = fid.readlines() - for line in lines: - line = line.strip('\n').split('\t') - img_list.append(line[0]) - result = self.get_bboxes(line[1]) - label_list.append(result) - return img_list,label_list - - def __len__(self): - return len(self.img_list) - - def __getitem__(self, index): - img = cv2.imread(self.img_list[index]) - polys, dontcare = self.label_list[index] - - img, polys = self.TSM.random_scale_pan(img, polys) - img, polys = self.TSM.random_rotate(img, polys) - img, polys = self.TSM.random_flip(img, polys) - img,gt_text,gt_text_key,gt_kernel,gt_kernel_key,train_mask = self.MSM.process(img, polys, dontcare) - - imgs = [img, gt_text, gt_text_key, gt_kernel, gt_kernel_key, train_mask] - imgs = self.TSM.random_crop_pan(imgs) - img, gt_text, gt_text_key, gt_kernel, gt_kernel_key, train_mask = imgs[0], imgs[1], imgs[2], imgs[3], imgs[ - 4], imgs[5] - - img = Image.fromarray(img).convert('RGB') - img = transforms.ColorJitter(brightness=32.0 / 255, saturation=0.5)(img) - img = self.TSM.normalize_img(img) - - - gt_text = torch.from_numpy(gt_text).float() - gt_text_key = torch.from_numpy(gt_text_key).float() - gt_kernel = torch.from_numpy(gt_kernel).float() - gt_kernel_key = torch.from_numpy(gt_kernel_key).float() - train_mask = torch.from_numpy(train_mask).float() - - return img,gt_text, gt_text_key, gt_kernel, gt_kernel_key, train_mask - - -class PANProcessTest(): - def __init__(self, config): - super(PANProcessTest, self).__init__() - self.img_list = self.get_img_files(config['testload']['test_file']) - self.TSM = Random_Augment(config['base']['crop_shape']) - self.test_size = config['testload']['test_size'] - self.config = config - - def get_img_files(self, test_txt_file): - img_list = [] - with open(test_txt_file, 'r', encoding='utf-8') as fid: - lines = fid.readlines() - for line in lines: - line = line.strip('\n') - img_list.append(line) - return img_list - def __len__(self): - return len(self.img_list) - def __getitem__(self, index): - ori_img = cv2.imread(self.img_list[index]) - img = resize_image(ori_img,self.config['base']['algorithm'], self.test_size,stride = self.config['testload']['stride']) - img = Image.fromarray(img).convert('RGB') - img = self.TSM.normalize_img(img) - return img, ori_img - diff --git a/ptocr/dataloader/DetLoad/PSEProcess.py b/ptocr/dataloader/DetLoad/PSEProcess.py deleted file mode 100644 index e637786..0000000 --- a/ptocr/dataloader/DetLoad/PSEProcess.py +++ /dev/null @@ -1,120 +0,0 @@ -#-*- coding:utf-8 _*- -""" -@author:fxw -@file: PSEProcess.py -@time: 2020/08/11 -""" -import cv2 -import torch -import numpy as np -from PIL import Image -from torch.utils import data -from .transform_img import Random_Augment -from .MakeSegMap import MakeSegPSE -import torchvision.transforms as transforms -from ptocr.utils.util_function import resize_image - -class PSEProcessTrain(data.Dataset): - def __init__(self,config): - super(PSEProcessTrain,self).__init__() - self.crop_shape = config['base']['crop_shape'] - self.TSM = Random_Augment(self.crop_shape) - self.MSM = MakeSegPSE(config['base']['classes'],config['base']['shrink_ratio']) - img_list, label_list = self.get_base_information(config['trainload']['train_file']) - self.img_list = img_list - self.label_list = label_list - - def order_points(self, pts): - rect = np.zeros((4, 2), dtype="float32") - s = pts.sum(axis=1) - rect[0] = pts[np.argmin(s)] - rect[2] = pts[np.argmax(s)] - diff = np.diff(pts, axis=1) - rect[1] = pts[np.argmin(diff)] - rect[3] = pts[np.argmax(diff)] - return rect - - def get_bboxes(self,gt_path): - polys = [] - tags = [] - with open(gt_path, 'r', encoding='utf-8') as fid: - lines = fid.readlines() - for line in lines: - line = line.replace('\ufeff', '').replace('\xef\xbb\xbf', '') - gt = line.split(',') - if "###" in gt[-1]: - tags.append(True) - else: - tags.append(False) - # box = [int(gt[i]) for i in range(len(gt)//2*2)] - box = [int(gt[i]) for i in range(8)] - polys.append(box) - return np.array(polys), tags - - def get_base_information(self,train_txt_file): - label_list = [] - img_list = [] - with open(train_txt_file,'r',encoding='utf-8') as fid: - lines = fid.readlines() - for line in lines: - line = line.strip('\n').split('\t') - img_list.append(line[0]) - result = self.get_bboxes(line[1]) - label_list.append(result) - return img_list,label_list - - def __len__(self): - return len(self.img_list) - - def __getitem__(self, index): - img = cv2.imread(self.img_list[index]) - polys, dontcare = self.label_list[index] - - img, polys = self.TSM.random_scale(img, polys, self.crop_shape[0]) - img, polys = self.TSM.random_flip(img, polys) - img, polys = self.TSM.random_rotate(img, polys) - img, train_mask, gt_text, gt_kernels = self.MSM.process(img, polys, dontcare) - - imgs = [img, gt_text, train_mask] - imgs.extend(gt_kernels) - imgs = self.TSM.random_crop_pse(imgs) - img, gt_text, train_mask, gt_kernels = imgs[0], imgs[1], imgs[2], imgs[3:] - - - img = Image.fromarray(img).convert('RGB') - img = transforms.ColorJitter(brightness=32.0 / 255, saturation=0.5)(img) - img = self.TSM.normalize_img(img) - - gt_text = torch.from_numpy(gt_text).float() - train_mask = torch.from_numpy(train_mask).float() - gt_kernels = torch.from_numpy(np.array(gt_kernels)).float() - - return img,gt_text,gt_kernels,train_mask - - - -class PSEProcessTest(): - def __init__(self, config): - super(PSEProcessTest, self).__init__() - self.img_list = self.get_img_files(config['testload']['test_file']) - self.TSM = Random_Augment(config['base']['crop_shape']) - self.test_size = config['testload']['test_size'] - self.config = config - - def get_img_files(self, test_txt_file): - img_list = [] - with open(test_txt_file, 'r', encoding='utf-8') as fid: - lines = fid.readlines() - for line in lines: - line = line.strip('\n') - img_list.append(line) - return img_list - def __len__(self): - return len(self.img_list) - def __getitem__(self, index): - ori_img = cv2.imread(self.img_list[index]) - img = resize_image(ori_img,self.config['base']['algorithm'], self.test_size,stride = self.config['testload']['stride']) - img = Image.fromarray(img).convert('RGB') - img = self.TSM.normalize_img(img) - return img, ori_img - diff --git a/ptocr/dataloader/DetLoad/SASTProcess.py b/ptocr/dataloader/DetLoad/SASTProcess.py deleted file mode 100644 index c2f64f5..0000000 --- a/ptocr/dataloader/DetLoad/SASTProcess.py +++ /dev/null @@ -1,504 +0,0 @@ -""" -#!-*- coding=utf-8 -*- -@author: BADBADBADBADBOY -@contact: 2441124901@qq.com -@software: PyCharm Community Edition -@file: SASTProcess.py -@time: 2020/8/23 14:16 - -""" - -import math -import cv2 -import numpy as np -import torch -from PIL import Image -import torch.utils.data as data -import torchvision.transforms as transforms -from .transform_img import Random_Augment -from ptocr.utils.util_function import resize_image - - -def get_img(img_path): - img = Image.open(img_path).convert('RGB') - img = np.array(img) - return img - -class SASTProcessTrain(data.Dataset): - """ - SAST process function for training - """ - - def __init__(self, config): - self.TSM = Random_Augment(config['base']['crop_shape'],max_tries=20, - min_crop_side_ratio=config['trainload']['min_crop_side_ratio']) - self.img_set_dir = config['trainload']['train_file'] - image_shape = config['base']['crop_shape'] - self.input_size = image_shape[0] - self.min_text_size = config['trainload']['min_text_size'] - self.max_text_size = image_shape[0] - - self.img_files = [] - self.gt_files = [] - with open(self.img_set_dir, 'r') as fid: - lines = fid.readlines() - for line in lines: - img_file = line.strip('\n').split('\t')[0] - gt_file = line.strip('\n').split('\t')[1] - self.img_files.append(img_file) - self.gt_files.append(gt_file) - - def adjust_point(self, poly): - """ - adjust point order. - """ - point_num = poly.shape[0] - if point_num == 4: - len_1 = np.linalg.norm(poly[0] - poly[1]) - len_2 = np.linalg.norm(poly[1] - poly[2]) - len_3 = np.linalg.norm(poly[2] - poly[3]) - len_4 = np.linalg.norm(poly[3] - poly[0]) - - if (len_1 + len_3) * 1.5 < (len_2 + len_4): - poly = poly[[1, 2, 3, 0], :] - - elif point_num > 4: - vector_1 = poly[0] - poly[1] - vector_2 = poly[1] - poly[2] - cos_theta = np.dot(vector_1, vector_2) / (np.linalg.norm(vector_1) * np.linalg.norm(vector_2) + 1e-6) - theta = np.arccos(np.round(cos_theta, decimals=4)) - - if abs(theta) > (70 / 180 * math.pi): - index = list(range(1, point_num)) + [0] - poly = poly[np.array(index), :] - return poly - - def gen_min_area_quad_from_poly(self, poly): - """ - Generate min area quad from poly. - """ - point_num = poly.shape[0] - min_area_quad = np.zeros((4, 2), dtype=np.float32) - if point_num == 4: - min_area_quad = poly - center_point = np.sum(poly, axis=0) / 4 - else: - rect = cv2.minAreaRect(poly.astype(np.int32)) # (center (x,y), (width, height), angle of rotation) - center_point = rect[0] - box = np.array(cv2.boxPoints(rect)) - - first_point_idx = 0 - min_dist = 1e4 - for i in range(4): - dist = np.linalg.norm(box[(i + 0) % 4] - poly[0]) + \ - np.linalg.norm(box[(i + 1) % 4] - poly[point_num // 2 - 1]) + \ - np.linalg.norm(box[(i + 2) % 4] - poly[point_num // 2]) + \ - np.linalg.norm(box[(i + 3) % 4] - poly[-1]) - if dist < min_dist: - min_dist = dist - first_point_idx = i - - for i in range(4): - min_area_quad[i] = box[(first_point_idx + i) % 4] - - return min_area_quad, center_point - - def shrink_quad_along_width(self, quad, begin_width_ratio=0., end_width_ratio=1.): - """ - Generate shrink_quad_along_width. - """ - ratio_pair = np.array([[begin_width_ratio], [end_width_ratio]], dtype=np.float32) - p0_1 = quad[0] + (quad[1] - quad[0]) * ratio_pair - p3_2 = quad[3] + (quad[2] - quad[3]) * ratio_pair - return np.array([p0_1[0], p0_1[1], p3_2[1], p3_2[0]]) - - def shrink_poly_along_width(self, quads, shrink_ratio_of_width, expand_height_ratio=1.0): - """ - shrink poly with given length. - """ - upper_edge_list = [] - - def get_cut_info(edge_len_list, cut_len): - for idx, edge_len in enumerate(edge_len_list): - cut_len -= edge_len - if cut_len <= 0.000001: - ratio = (cut_len + edge_len_list[idx]) / edge_len_list[idx] - return idx, ratio - - for quad in quads: - upper_edge_len = np.linalg.norm(quad[0] - quad[1]) - upper_edge_list.append(upper_edge_len) - - # length of left edge and right edge. - left_length = np.linalg.norm(quads[0][0] - quads[0][3]) * expand_height_ratio - right_length = np.linalg.norm(quads[-1][1] - quads[-1][2]) * expand_height_ratio - - shrink_length = min(left_length, right_length, sum(upper_edge_list)) * shrink_ratio_of_width - # shrinking length - upper_len_left = shrink_length - upper_len_right = sum(upper_edge_list) - shrink_length - - left_idx, left_ratio = get_cut_info(upper_edge_list, upper_len_left) - left_quad = self.shrink_quad_along_width(quads[left_idx], begin_width_ratio=left_ratio, end_width_ratio=1) - right_idx, right_ratio = get_cut_info(upper_edge_list, upper_len_right) - right_quad = self.shrink_quad_along_width(quads[right_idx], begin_width_ratio=0, end_width_ratio=right_ratio) - - out_quad_list = [] - if left_idx == right_idx: - out_quad_list.append([left_quad[0], right_quad[1], right_quad[2], left_quad[3]]) - else: - out_quad_list.append(left_quad) - for idx in range(left_idx + 1, right_idx): - out_quad_list.append(quads[idx]) - out_quad_list.append(right_quad) - - return np.array(out_quad_list), list(range(left_idx, right_idx + 1)) - - def vector_angle(self, A, B): - """ - Calculate the angle between vector AB and x-axis positive direction. - """ - AB = np.array([B[1] - A[1], B[0] - A[0]]) - return np.arctan2(*AB) - - def theta_line_cross_point(self, theta, point): - """ - Calculate the line through given point and angle in ax + by + c =0 form. - """ - x, y = point - cos = np.cos(theta) - sin = np.sin(theta) - return [sin, -cos, cos * y - sin * x] - - def line_cross_two_point(self, A, B): - """ - Calculate the line through given point A and B in ax + by + c =0 form. - """ - angle = self.vector_angle(A, B) - return self.theta_line_cross_point(angle, A) - - def average_angle(self, poly): - """ - Calculate the average angle between left and right edge in given poly. - """ - p0, p1, p2, p3 = poly - angle30 = self.vector_angle(p3, p0) - angle21 = self.vector_angle(p2, p1) - return (angle30 + angle21) / 2 - - def line_cross_point(self, line1, line2): - """ - line1 and line2 in 0=ax+by+c form, compute the cross point of line1 and line2 - """ - a1, b1, c1 = line1 - a2, b2, c2 = line2 - d = a1 * b2 - a2 * b1 - - if d == 0: - # print("line1", line1) - # print("line2", line2) - print('Cross point does not exist') - return np.array([0, 0], dtype=np.float32) - else: - x = (b1 * c2 - b2 * c1) / d - y = (a2 * c1 - a1 * c2) / d - - return np.array([x, y], dtype=np.float32) - - def quad2tcl(self, poly, ratio): - """ - Generate center line by poly clock-wise point. (4, 2) - """ - ratio_pair = np.array([[0.5 - ratio / 2], [0.5 + ratio / 2]], dtype=np.float32) - p0_3 = poly[0] + (poly[3] - poly[0]) * ratio_pair - p1_2 = poly[1] + (poly[2] - poly[1]) * ratio_pair - return np.array([p0_3[0], p1_2[0], p1_2[1], p0_3[1]]) - - def poly2tcl(self, poly, ratio): - """ - Generate center line by poly clock-wise point. - """ - ratio_pair = np.array([[0.5 - ratio / 2], [0.5 + ratio / 2]], dtype=np.float32) - tcl_poly = np.zeros_like(poly) - point_num = poly.shape[0] - - for idx in range(point_num // 2): - point_pair = poly[idx] + (poly[point_num - 1 - idx] - poly[idx]) * ratio_pair - tcl_poly[idx] = point_pair[0] - tcl_poly[point_num - 1 - idx] = point_pair[1] - return tcl_poly - - def gen_quad_tbo(self, quad, tcl_mask, tbo_map): - """ - Generate tbo_map for give quad. - """ - # upper and lower line function: ax + by + c = 0; - up_line = self.line_cross_two_point(quad[0], quad[1]) - lower_line = self.line_cross_two_point(quad[3], quad[2]) - - quad_h = 0.5 * (np.linalg.norm(quad[0] - quad[3]) + np.linalg.norm(quad[1] - quad[2])) - quad_w = 0.5 * (np.linalg.norm(quad[0] - quad[1]) + np.linalg.norm(quad[2] - quad[3])) - - # average angle of left and right line. - angle = self.average_angle(quad) - - xy_in_poly = np.argwhere(tcl_mask == 1) - for y, x in xy_in_poly: - point = (x, y) - line = self.theta_line_cross_point(angle, point) - cross_point_upper = self.line_cross_point(up_line, line) - cross_point_lower = self.line_cross_point(lower_line, line) - ##FIX, offset reverse - upper_offset_x, upper_offset_y = cross_point_upper - point - lower_offset_x, lower_offset_y = cross_point_lower - point - tbo_map[y, x, 0] = upper_offset_y - tbo_map[y, x, 1] = upper_offset_x - tbo_map[y, x, 2] = lower_offset_y - tbo_map[y, x, 3] = lower_offset_x - tbo_map[y, x, 4] = 1.0 / max(min(quad_h, quad_w), 1.0) * 2 - return tbo_map - - def poly2quads(self, poly): - """ - Split poly into quads. - """ - quad_list = [] - point_num = poly.shape[0] - - # point pair - point_pair_list = [] - for idx in range(point_num // 2): - point_pair = [poly[idx], poly[point_num - 1 - idx]] - point_pair_list.append(point_pair) - - quad_num = point_num // 2 - 1 - for idx in range(quad_num): - # reshape and adjust to clock-wise - quad_list.append((np.array(point_pair_list)[[idx, idx + 1]]).reshape(4, 2)[[0, 2, 3, 1]]) - - return np.array(quad_list) - - def generate_tcl_label(self, hw, polys, tags, ds_ratio, - tcl_ratio=0.3, shrink_ratio_of_width=0.15): - """ - Generate polygon. - """ - h, w = hw - h, w = int(h * ds_ratio), int(w * ds_ratio) - polys = polys * ds_ratio - - score_map = np.zeros((h, w,), dtype=np.float32) - tbo_map = np.zeros((h, w, 5), dtype=np.float32) - training_mask = np.ones((h, w,), dtype=np.float32) - direction_map = np.ones((h, w, 3)) * np.array([0, 0, 1]).reshape([1, 1, 3]).astype(np.float32) - - for poly_idx, poly_tag in enumerate(zip(polys, tags)): - poly = poly_tag[0] - tag = poly_tag[1] - - # generate min_area_quad - min_area_quad, center_point = self.gen_min_area_quad_from_poly(poly) - min_area_quad_h = 0.5 * (np.linalg.norm(min_area_quad[0] - min_area_quad[3]) + - np.linalg.norm(min_area_quad[1] - min_area_quad[2])) - min_area_quad_w = 0.5 * (np.linalg.norm(min_area_quad[0] - min_area_quad[1]) + - np.linalg.norm(min_area_quad[2] - min_area_quad[3])) - - if min(min_area_quad_h, min_area_quad_w) < self.min_text_size * ds_ratio \ - or min(min_area_quad_h, min_area_quad_w) > self.max_text_size * ds_ratio: - continue - - if tag: - # continue - cv2.fillPoly(training_mask, poly.astype(np.int32)[np.newaxis, :, :], 0.15) - else: - tcl_poly = self.poly2tcl(poly, tcl_ratio) - tcl_quads = self.poly2quads(tcl_poly) - poly_quads = self.poly2quads(poly) - # stcl map - stcl_quads, quad_index = self.shrink_poly_along_width(tcl_quads, - shrink_ratio_of_width=shrink_ratio_of_width, - expand_height_ratio=1.0 / tcl_ratio) - # generate tcl map - cv2.fillPoly(score_map, np.round(stcl_quads).astype(np.int32), 1.0) - - # generate tbo map - for idx, quad in enumerate(stcl_quads): - quad_mask = np.zeros((h, w), dtype=np.float32) - quad_mask = cv2.fillPoly(quad_mask, np.round(quad[np.newaxis, :, :]).astype(np.int32), 1.0) - tbo_map = self.gen_quad_tbo(poly_quads[quad_index[idx]], quad_mask, tbo_map) - return score_map, tbo_map, training_mask - - def generate_tvo_and_tco(self, hw, polys, tags, tcl_ratio=0.3, ds_ratio=0.25): - """ - Generate tcl map, tvo map and tbo map. - """ - h, w = hw - h, w = int(h * ds_ratio), int(w * ds_ratio) - polys = polys * ds_ratio - poly_mask = np.zeros((h, w), dtype=np.float32) - - tvo_map = np.ones((9, h, w), dtype=np.float32) - tvo_map[0:-1:2] = np.tile(np.arange(0, w), (h, 1)) - tvo_map[1:-1:2] = np.tile(np.arange(0, w), (h, 1)).T - poly_tv_xy_map = np.zeros((8, h, w), dtype=np.float32) - - # tco map - tco_map = np.ones((3, h, w), dtype=np.float32) - tco_map[0] = np.tile(np.arange(0, w), (h, 1)) - tco_map[1] = np.tile(np.arange(0, w), (h, 1)).T - poly_tc_xy_map = np.zeros((2, h, w), dtype=np.float32) - - poly_short_edge_map = np.ones((h, w), dtype=np.float32) - - for poly, poly_tag in zip(polys, tags): - - if poly_tag == True: - continue - - # adjust point order for vertical poly - poly = self.adjust_point(poly) - - # generate min_area_quad - min_area_quad, center_point = self.gen_min_area_quad_from_poly(poly) - min_area_quad_h = 0.5 * (np.linalg.norm(min_area_quad[0] - min_area_quad[3]) + - np.linalg.norm(min_area_quad[1] - min_area_quad[2])) - min_area_quad_w = 0.5 * (np.linalg.norm(min_area_quad[0] - min_area_quad[1]) + - np.linalg.norm(min_area_quad[2] - min_area_quad[3])) - - # generate tcl map and text, 128 * 128 - tcl_poly = self.poly2tcl(poly, tcl_ratio) - - # generate poly_tv_xy_map - for idx in range(4): - cv2.fillPoly(poly_tv_xy_map[2 * idx], - np.round(tcl_poly[np.newaxis, :, :]).astype(np.int32), - float(min(max(min_area_quad[idx, 0], 0), w))) - cv2.fillPoly(poly_tv_xy_map[2 * idx + 1], - np.round(tcl_poly[np.newaxis, :, :]).astype(np.int32), - float(min(max(min_area_quad[idx, 1], 0), h))) - - # generate poly_tc_xy_map - for idx in range(2): - cv2.fillPoly(poly_tc_xy_map[idx], - np.round(tcl_poly[np.newaxis, :, :]).astype(np.int32), float(center_point[idx])) - - # generate poly_short_edge_map - cv2.fillPoly(poly_short_edge_map, - np.round(tcl_poly[np.newaxis, :, :]).astype(np.int32), - float(max(min(min_area_quad_h, min_area_quad_w), 1.0))) - - # generate poly_mask and training_mask - cv2.fillPoly(poly_mask, np.round(tcl_poly[np.newaxis, :, :]).astype(np.int32), 1) - - tvo_map *= poly_mask - tvo_map[:8] -= poly_tv_xy_map - tvo_map[-1] /= poly_short_edge_map - tvo_map = tvo_map.transpose((1, 2, 0)) - - tco_map *= poly_mask - tco_map[:2] -= poly_tc_xy_map - tco_map[-1] /= poly_short_edge_map - tco_map = tco_map.transpose((1, 2, 0)) - - return tvo_map, tco_map - - def get_bboxes(self, gt_path): - polys = [] - tags = [] - with open(gt_path, 'r', encoding='utf-8') as fid: - lines = fid.readlines() - for line in lines: - line = line.replace('\ufeff', '').replace('\xef\xbb\xbf', '') - gt = line.split(',') - if "###" in gt[-1]: - tags.append(True) - else: - tags.append(False) - # box = [int(gt[i]) for i in range(len(gt)//2*2)] - box = [int(gt[i]) for i in range(8)] - polys.append(box) - return np.array(polys), tags - - def __len__(self): - return len(self.img_files) - - def __getitem__(self, index): - - img = get_img(self.img_files[index]) - polys, dontcare = self.get_bboxes(self.gt_files[index]) - - img, polys = self.TSM.random_scale(img, polys, self.input_size) - img, polys = self.TSM.random_rotate(img, polys) - img, polys = self.TSM.random_flip(img, polys) - img, text_polys, text_tags = self.TSM.random_crop_db(img, polys, dontcare) - text_polys = np.array(text_polys) - - score_map, border_map, training_mask = self.generate_tcl_label((self.input_size, self.input_size), - text_polys, text_tags, 0.25) - - # SAST head - tvo_map, tco_map = self.generate_tvo_and_tco((self.input_size, self.input_size), text_polys, text_tags, - tcl_ratio=0.3, ds_ratio=0.25) - - # add gaussian blur - if np.random.rand() < 0.1 * 0.5: - ks = np.random.permutation(5)[0] + 1 - ks = int(ks / 2) * 2 + 1 - img = cv2.GaussianBlur(img, ksize=(ks, ks), sigmaX=0, sigmaY=0) - # add brighter - if np.random.rand() < 0.1 * 0.5: - img = img * (1.0 + np.random.rand() * 0.5) - img = np.clip(img, 0.0, 255.0) - # add darker - if np.random.rand() < 0.1 * 0.5: - img = img * (1.0 - np.random.rand() * 0.5) - img = np.clip(img, 0.0, 255.0) - - img = Image.fromarray(img.astype(np.uint8)).convert('RGB') - img.save('img.jpg') - - cv2.imwrite('score.jpg',np.array(score_map)*255) - - - img = self.TSM.normalize_img(img) - - score_map = torch.Tensor(score_map[np.newaxis, :, :]) - border_map = torch.Tensor(border_map.transpose((2, 0, 1))) - training_mask = torch.Tensor(training_mask[np.newaxis, :, :]) - tvo_map = torch.Tensor(tvo_map.transpose((2, 0, 1))) - tco_map = torch.Tensor(tco_map.transpose((2, 0, 1))) - - return img, score_map, border_map, training_mask, tvo_map, tco_map - - - -class SASTProcessTest(data.Dataset): - """ - SAST process function for test - """ - - def __init__(self, config): - super(SASTProcessTest, self).__init__() - self.img_list = self.get_img_files(config['testload']['test_file']) - self.TSM = Random_Augment(config['base']['crop_shape']) - self.test_size = config['testload']['test_size'] - self.config = config - - def get_img_files(self, test_txt_file): - img_list = [] - with open(test_txt_file, 'r', encoding='utf-8') as fid: - lines = fid.readlines() - for line in lines: - line = line.strip('\n') - img_list.append(line) - return img_list - - def __len__(self): - return len(self.img_list) - - def __getitem__(self, index): - ori_img = get_img(self.img_list[index]) - img = resize_image(ori_img, self.config['base']['algorithm'], self.test_size,self.config['testload']['stride']) - img = Image.fromarray(img).convert('RGB') - img = self.TSM.normalize_img(img) - return img, ori_img diff --git a/ptocr/dataloader/DetLoad/SASTProcess_ori.py b/ptocr/dataloader/DetLoad/SASTProcess_ori.py deleted file mode 100644 index 5760d1e..0000000 --- a/ptocr/dataloader/DetLoad/SASTProcess_ori.py +++ /dev/null @@ -1,800 +0,0 @@ -#-*- coding:utf-8 _*- -""" -@author:fxw -@file: SASTProcess.py -@time: 2020/08/18 -""" - -import math -import cv2 -import numpy as np -import torch -from PIL import Image -import torch.utils.data as data -import torchvision.transforms as transforms -from .transform_img import Random_Augment -from ptocr.utils.util_function import resize_image - -class SASTProcessTrain(data.Dataset): - """ - SAST process function for training - """ - - def __init__(self,config): - self.TSM = Random_Augment(config['base']['crop_shape']) - self.img_set_dir = config['trainload']['train_file'] - self.min_crop_side_ratio = config['trainload']['min_crop_side_ratio'] - self.min_crop_size = config['trainload']['min_crop_size'] - image_shape = config['base']['crop_shape'] - self.input_size = image_shape[0] - self.min_text_size = config['trainload']['min_text_size'] - self.max_text_size = image_shape[0] - - self.img_files = [] - self.gt_files = [] - with open(self.img_set_dir, 'r') as fid: - lines = fid.readlines() - for line in lines: - img_file = line.strip('\n').split('\t')[0] - gt_file = line.strip('\n').split('\t')[1] - self.img_files.append(img_file) - self.gt_files.append(gt_file) - - def quad_area(self, poly): - """ - compute area of a polygon - :param poly: - :return: - """ - edge = [ - (poly[1][0] - poly[0][0]) * (poly[1][1] + poly[0][1]), - (poly[2][0] - poly[1][0]) * (poly[2][1] + poly[1][1]), - (poly[3][0] - poly[2][0]) * (poly[3][1] + poly[2][1]), - (poly[0][0] - poly[3][0]) * (poly[0][1] + poly[3][1]) - ] - return np.sum(edge) / 2. - - def gen_quad_from_poly(self, poly): - """ - Generate min area quad from poly. - """ - point_num = poly.shape[0] - min_area_quad = np.zeros((4, 2), dtype=np.float32) - if True: - rect = cv2.minAreaRect(poly.astype(np.int32)) # (center (x,y), (width, height), angle of rotation) - center_point = rect[0] - box = np.array(cv2.boxPoints(rect)) - - first_point_idx = 0 - min_dist = 1e4 - for i in range(4): - dist = np.linalg.norm(box[(i + 0) % 4] - poly[0]) + \ - np.linalg.norm(box[(i + 1) % 4] - poly[point_num // 2 - 1]) + \ - np.linalg.norm(box[(i + 2) % 4] - poly[point_num // 2]) + \ - np.linalg.norm(box[(i + 3) % 4] - poly[-1]) - if dist < min_dist: - min_dist = dist - first_point_idx = i - for i in range(4): - min_area_quad[i] = box[(first_point_idx + i) % 4] - - return min_area_quad - - def check_and_validate_polys(self, polys, tags, xxx_todo_changeme): - """ - check so that the text poly is in the same direction, - and also filter some invalid polygons - :param polys: - :param tags: - :return: - """ - (h, w) = xxx_todo_changeme - if polys.shape[0] == 0: - return polys, np.array([]), np.array([]) - polys[:, :, 0] = np.clip(polys[:, :, 0], 0, w - 1) - polys[:, :, 1] = np.clip(polys[:, :, 1], 0, h - 1) - - validated_polys = [] - validated_tags = [] - hv_tags = [] - for poly, tag in zip(polys, tags): - quad = self.gen_quad_from_poly(poly) - p_area = self.quad_area(quad) - if abs(p_area) < 1: - print('invalid poly') - continue - if p_area > 0: - if tag == False: - print('poly in wrong direction') - tag = True # reversed cases should be ignore - poly = poly[(0, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1), :] - quad = quad[(0, 3, 2, 1), :] - - len_w = np.linalg.norm(quad[0] - quad[1]) + np.linalg.norm(quad[3] - quad[2]) - len_h = np.linalg.norm(quad[0] - quad[3]) + np.linalg.norm(quad[1] - quad[2]) - hv_tag = 1 - - if len_w * 2.0 < len_h: - hv_tag = 0 - - validated_polys.append(poly) - validated_tags.append(tag) - hv_tags.append(hv_tag) - return np.array(validated_polys), np.array(validated_tags), np.array(hv_tags) - - def crop_area(self, im, polys, tags, hv_tags, txts, crop_background=False, max_tries=25): - """ - make random crop from the input image - :param im: - :param polys: - :param tags: - :param crop_background: - :param max_tries: 50 -> 25 - :return: - """ - h, w, _ = im.shape - pad_h = h // 10 - pad_w = w // 10 - h_array = np.zeros((h + pad_h * 2), dtype=np.int32) - w_array = np.zeros((w + pad_w * 2), dtype=np.int32) - for poly in polys: - poly = np.round(poly, decimals=0).astype(np.int32) - minx = np.min(poly[:, 0]) - maxx = np.max(poly[:, 0]) - w_array[minx + pad_w: maxx + pad_w] = 1 - miny = np.min(poly[:, 1]) - maxy = np.max(poly[:, 1]) - h_array[miny + pad_h: maxy + pad_h] = 1 - # ensure the cropped area not across a text - h_axis = np.where(h_array == 0)[0] - w_axis = np.where(w_array == 0)[0] - if len(h_axis) == 0 or len(w_axis) == 0: - return im, polys, tags, hv_tags, txts - for i in range(max_tries): - xx = np.random.choice(w_axis, size=2) - xmin = np.min(xx) - pad_w - xmax = np.max(xx) - pad_w - xmin = np.clip(xmin, 0, w - 1) - xmax = np.clip(xmax, 0, w - 1) - yy = np.random.choice(h_axis, size=2) - ymin = np.min(yy) - pad_h - ymax = np.max(yy) - pad_h - ymin = np.clip(ymin, 0, h - 1) - ymax = np.clip(ymax, 0, h - 1) - # if xmax - xmin < ARGS.min_crop_side_ratio * w or \ - # ymax - ymin < ARGS.min_crop_side_ratio * h: - if xmax - xmin < self.min_crop_size or \ - ymax - ymin < self.min_crop_size: - # area too small - continue - if polys.shape[0] != 0: - poly_axis_in_area = (polys[:, :, 0] >= xmin) & (polys[:, :, 0] <= xmax) \ - & (polys[:, :, 1] >= ymin) & (polys[:, :, 1] <= ymax) - selected_polys = np.where(np.sum(poly_axis_in_area, axis=1) == 4)[0] - else: - selected_polys = [] - if len(selected_polys) == 0: - # no text in this area - if crop_background: - txts_tmp = [] - for selected_poly in selected_polys: - txts_tmp.append(txts[selected_poly]) - txts = txts_tmp - return im[ymin: ymax + 1, xmin: xmax + 1, :], \ - polys[selected_polys], tags[selected_polys], hv_tags[selected_polys], txts - else: - continue - im = im[ymin: ymax + 1, xmin: xmax + 1, :] - polys = polys[selected_polys] - tags = tags[selected_polys] - hv_tags = hv_tags[selected_polys] - txts_tmp = [] - for selected_poly in selected_polys: - txts_tmp.append(txts[selected_poly]) - txts = txts_tmp - polys[:, :, 0] -= xmin - polys[:, :, 1] -= ymin - return im, polys, tags, hv_tags, txts - - return im, polys, tags, hv_tags, txts - - def generate_direction_map(self, poly_quads, direction_map): - """ - """ - width_list = [] - height_list = [] - for quad in poly_quads: - quad_w = (np.linalg.norm(quad[0] - quad[1]) + np.linalg.norm(quad[2] - quad[3])) / 2.0 - quad_h = (np.linalg.norm(quad[0] - quad[3]) + np.linalg.norm(quad[2] - quad[1])) / 2.0 - width_list.append(quad_w) - height_list.append(quad_h) - norm_width = max(sum(width_list) / (len(width_list) + 1e-6), 1.0) - average_height = max(sum(height_list) / (len(height_list) + 1e-6), 1.0) - - for quad in poly_quads: - direct_vector_full = ((quad[1] + quad[2]) - (quad[0] + quad[3])) / 2.0 - direct_vector = direct_vector_full / (np.linalg.norm(direct_vector_full) + 1e-6) * norm_width - direction_label = tuple(map(float, [direct_vector[0], direct_vector[1], 1.0 / (average_height + 1e-6)])) - cv2.fillPoly(direction_map, quad.round().astype(np.int32)[np.newaxis, :, :], direction_label) - return direction_map - - def calculate_average_height(self, poly_quads): - """ - """ - height_list = [] - for quad in poly_quads: - quad_h = (np.linalg.norm(quad[0] - quad[3]) + np.linalg.norm(quad[2] - quad[1])) / 2.0 - height_list.append(quad_h) - average_height = max(sum(height_list) / len(height_list), 1.0) - return average_height - - def generate_tcl_label(self, hw, polys, tags, ds_ratio, - tcl_ratio=0.3, shrink_ratio_of_width=0.15): - """ - Generate polygon. - """ - h, w = hw - h, w = int(h * ds_ratio), int(w * ds_ratio) - polys = polys * ds_ratio - - score_map = np.zeros((h, w,), dtype=np.float32) - tbo_map = np.zeros((h, w, 5), dtype=np.float32) - training_mask = np.ones((h, w,), dtype=np.float32) - direction_map = np.ones((h, w, 3)) * np.array([0, 0, 1]).reshape([1, 1, 3]).astype(np.float32) - - for poly_idx, poly_tag in enumerate(zip(polys, tags)): - poly = poly_tag[0] - tag = poly_tag[1] - - # generate min_area_quad - min_area_quad, center_point = self.gen_min_area_quad_from_poly(poly) - min_area_quad_h = 0.5 * (np.linalg.norm(min_area_quad[0] - min_area_quad[3]) + - np.linalg.norm(min_area_quad[1] - min_area_quad[2])) - min_area_quad_w = 0.5 * (np.linalg.norm(min_area_quad[0] - min_area_quad[1]) + - np.linalg.norm(min_area_quad[2] - min_area_quad[3])) - - if min(min_area_quad_h, min_area_quad_w) < self.min_text_size * ds_ratio \ - or min(min_area_quad_h, min_area_quad_w) > self.max_text_size * ds_ratio: - continue - - if tag: - # continue - cv2.fillPoly(training_mask, poly.astype(np.int32)[np.newaxis, :, :], 0.15) - else: - tcl_poly = self.poly2tcl(poly, tcl_ratio) - tcl_quads = self.poly2quads(tcl_poly) - poly_quads = self.poly2quads(poly) - # stcl map - stcl_quads, quad_index = self.shrink_poly_along_width(tcl_quads, - shrink_ratio_of_width=shrink_ratio_of_width, - expand_height_ratio=1.0 / tcl_ratio) - # generate tcl map - cv2.fillPoly(score_map, np.round(stcl_quads).astype(np.int32), 1.0) - - # generate tbo map - for idx, quad in enumerate(stcl_quads): - quad_mask = np.zeros((h, w), dtype=np.float32) - quad_mask = cv2.fillPoly(quad_mask, np.round(quad[np.newaxis, :, :]).astype(np.int32), 1.0) - tbo_map = self.gen_quad_tbo(poly_quads[quad_index[idx]], quad_mask, tbo_map) - return score_map, tbo_map, training_mask - - def generate_tvo_and_tco(self, hw, polys, tags, tcl_ratio=0.3, ds_ratio=0.25): - """ - Generate tcl map, tvo map and tbo map. - """ - h, w = hw - h, w = int(h * ds_ratio), int(w * ds_ratio) - polys = polys * ds_ratio - poly_mask = np.zeros((h, w), dtype=np.float32) - - tvo_map = np.ones((9, h, w), dtype=np.float32) - tvo_map[0:-1:2] = np.tile(np.arange(0, w), (h, 1)) - tvo_map[1:-1:2] = np.tile(np.arange(0, w), (h, 1)).T - poly_tv_xy_map = np.zeros((8, h, w), dtype=np.float32) - - # tco map - tco_map = np.ones((3, h, w), dtype=np.float32) - tco_map[0] = np.tile(np.arange(0, w), (h, 1)) - tco_map[1] = np.tile(np.arange(0, w), (h, 1)).T - poly_tc_xy_map = np.zeros((2, h, w), dtype=np.float32) - - poly_short_edge_map = np.ones((h, w), dtype=np.float32) - - for poly, poly_tag in zip(polys, tags): - - if poly_tag == True: - continue - - # adjust point order for vertical poly - poly = self.adjust_point(poly) - - # generate min_area_quad - min_area_quad, center_point = self.gen_min_area_quad_from_poly(poly) - min_area_quad_h = 0.5 * (np.linalg.norm(min_area_quad[0] - min_area_quad[3]) + - np.linalg.norm(min_area_quad[1] - min_area_quad[2])) - min_area_quad_w = 0.5 * (np.linalg.norm(min_area_quad[0] - min_area_quad[1]) + - np.linalg.norm(min_area_quad[2] - min_area_quad[3])) - - # generate tcl map and text, 128 * 128 - tcl_poly = self.poly2tcl(poly, tcl_ratio) - - # generate poly_tv_xy_map - for idx in range(4): - cv2.fillPoly(poly_tv_xy_map[2 * idx], - np.round(tcl_poly[np.newaxis, :, :]).astype(np.int32), - float(min(max(min_area_quad[idx, 0], 0), w))) - cv2.fillPoly(poly_tv_xy_map[2 * idx + 1], - np.round(tcl_poly[np.newaxis, :, :]).astype(np.int32), - float(min(max(min_area_quad[idx, 1], 0), h))) - - # generate poly_tc_xy_map - for idx in range(2): - cv2.fillPoly(poly_tc_xy_map[idx], - np.round(tcl_poly[np.newaxis, :, :]).astype(np.int32), float(center_point[idx])) - - # generate poly_short_edge_map - cv2.fillPoly(poly_short_edge_map, - np.round(tcl_poly[np.newaxis, :, :]).astype(np.int32), - float(max(min(min_area_quad_h, min_area_quad_w), 1.0))) - - # generate poly_mask and training_mask - cv2.fillPoly(poly_mask, np.round(tcl_poly[np.newaxis, :, :]).astype(np.int32), 1) - - tvo_map *= poly_mask - tvo_map[:8] -= poly_tv_xy_map - tvo_map[-1] /= poly_short_edge_map - tvo_map = tvo_map.transpose((1, 2, 0)) - - tco_map *= poly_mask - tco_map[:2] -= poly_tc_xy_map - tco_map[-1] /= poly_short_edge_map - tco_map = tco_map.transpose((1, 2, 0)) - - return tvo_map, tco_map - - def adjust_point(self, poly): - """ - adjust point order. - """ - point_num = poly.shape[0] - if point_num == 4: - len_1 = np.linalg.norm(poly[0] - poly[1]) - len_2 = np.linalg.norm(poly[1] - poly[2]) - len_3 = np.linalg.norm(poly[2] - poly[3]) - len_4 = np.linalg.norm(poly[3] - poly[0]) - - if (len_1 + len_3) * 1.5 < (len_2 + len_4): - poly = poly[[1, 2, 3, 0], :] - - elif point_num > 4: - vector_1 = poly[0] - poly[1] - vector_2 = poly[1] - poly[2] - cos_theta = np.dot(vector_1, vector_2) / (np.linalg.norm(vector_1) * np.linalg.norm(vector_2) + 1e-6) - theta = np.arccos(np.round(cos_theta, decimals=4)) - - if abs(theta) > (70 / 180 * math.pi): - index = list(range(1, point_num)) + [0] - poly = poly[np.array(index), :] - return poly - - def gen_min_area_quad_from_poly(self, poly): - """ - Generate min area quad from poly. - """ - point_num = poly.shape[0] - min_area_quad = np.zeros((4, 2), dtype=np.float32) - if point_num == 4: - min_area_quad = poly - center_point = np.sum(poly, axis=0) / 4 - else: - rect = cv2.minAreaRect(poly.astype(np.int32)) # (center (x,y), (width, height), angle of rotation) - center_point = rect[0] - box = np.array(cv2.boxPoints(rect)) - - first_point_idx = 0 - min_dist = 1e4 - for i in range(4): - dist = np.linalg.norm(box[(i + 0) % 4] - poly[0]) + \ - np.linalg.norm(box[(i + 1) % 4] - poly[point_num // 2 - 1]) + \ - np.linalg.norm(box[(i + 2) % 4] - poly[point_num // 2]) + \ - np.linalg.norm(box[(i + 3) % 4] - poly[-1]) - if dist < min_dist: - min_dist = dist - first_point_idx = i - - for i in range(4): - min_area_quad[i] = box[(first_point_idx + i) % 4] - - return min_area_quad, center_point - - def shrink_quad_along_width(self, quad, begin_width_ratio=0., end_width_ratio=1.): - """ - Generate shrink_quad_along_width. - """ - ratio_pair = np.array([[begin_width_ratio], [end_width_ratio]], dtype=np.float32) - p0_1 = quad[0] + (quad[1] - quad[0]) * ratio_pair - p3_2 = quad[3] + (quad[2] - quad[3]) * ratio_pair - return np.array([p0_1[0], p0_1[1], p3_2[1], p3_2[0]]) - - def shrink_poly_along_width(self, quads, shrink_ratio_of_width, expand_height_ratio=1.0): - """ - shrink poly with given length. - """ - upper_edge_list = [] - - def get_cut_info(edge_len_list, cut_len): - for idx, edge_len in enumerate(edge_len_list): - cut_len -= edge_len - if cut_len <= 0.000001: - ratio = (cut_len + edge_len_list[idx]) / edge_len_list[idx] - return idx, ratio - - for quad in quads: - upper_edge_len = np.linalg.norm(quad[0] - quad[1]) - upper_edge_list.append(upper_edge_len) - - # length of left edge and right edge. - left_length = np.linalg.norm(quads[0][0] - quads[0][3]) * expand_height_ratio - right_length = np.linalg.norm(quads[-1][1] - quads[-1][2]) * expand_height_ratio - - shrink_length = min(left_length, right_length, sum(upper_edge_list)) * shrink_ratio_of_width - # shrinking length - upper_len_left = shrink_length - upper_len_right = sum(upper_edge_list) - shrink_length - - left_idx, left_ratio = get_cut_info(upper_edge_list, upper_len_left) - left_quad = self.shrink_quad_along_width(quads[left_idx], begin_width_ratio=left_ratio, end_width_ratio=1) - right_idx, right_ratio = get_cut_info(upper_edge_list, upper_len_right) - right_quad = self.shrink_quad_along_width(quads[right_idx], begin_width_ratio=0, end_width_ratio=right_ratio) - - out_quad_list = [] - if left_idx == right_idx: - out_quad_list.append([left_quad[0], right_quad[1], right_quad[2], left_quad[3]]) - else: - out_quad_list.append(left_quad) - for idx in range(left_idx + 1, right_idx): - out_quad_list.append(quads[idx]) - out_quad_list.append(right_quad) - - return np.array(out_quad_list), list(range(left_idx, right_idx + 1)) - - def vector_angle(self, A, B): - """ - Calculate the angle between vector AB and x-axis positive direction. - """ - AB = np.array([B[1] - A[1], B[0] - A[0]]) - return np.arctan2(*AB) - - def theta_line_cross_point(self, theta, point): - """ - Calculate the line through given point and angle in ax + by + c =0 form. - """ - x, y = point - cos = np.cos(theta) - sin = np.sin(theta) - return [sin, -cos, cos * y - sin * x] - - def line_cross_two_point(self, A, B): - """ - Calculate the line through given point A and B in ax + by + c =0 form. - """ - angle = self.vector_angle(A, B) - return self.theta_line_cross_point(angle, A) - - def average_angle(self, poly): - """ - Calculate the average angle between left and right edge in given poly. - """ - p0, p1, p2, p3 = poly - angle30 = self.vector_angle(p3, p0) - angle21 = self.vector_angle(p2, p1) - return (angle30 + angle21) / 2 - - def line_cross_point(self, line1, line2): - """ - line1 and line2 in 0=ax+by+c form, compute the cross point of line1 and line2 - """ - a1, b1, c1 = line1 - a2, b2, c2 = line2 - d = a1 * b2 - a2 * b1 - - if d == 0: - # print("line1", line1) - # print("line2", line2) - print('Cross point does not exist') - return np.array([0, 0], dtype=np.float32) - else: - x = (b1 * c2 - b2 * c1) / d - y = (a2 * c1 - a1 * c2) / d - - return np.array([x, y], dtype=np.float32) - - def quad2tcl(self, poly, ratio): - """ - Generate center line by poly clock-wise point. (4, 2) - """ - ratio_pair = np.array([[0.5 - ratio / 2], [0.5 + ratio / 2]], dtype=np.float32) - p0_3 = poly[0] + (poly[3] - poly[0]) * ratio_pair - p1_2 = poly[1] + (poly[2] - poly[1]) * ratio_pair - return np.array([p0_3[0], p1_2[0], p1_2[1], p0_3[1]]) - - def poly2tcl(self, poly, ratio): - """ - Generate center line by poly clock-wise point. - """ - ratio_pair = np.array([[0.5 - ratio / 2], [0.5 + ratio / 2]], dtype=np.float32) - tcl_poly = np.zeros_like(poly) - point_num = poly.shape[0] - - for idx in range(point_num // 2): - point_pair = poly[idx] + (poly[point_num - 1 - idx] - poly[idx]) * ratio_pair - tcl_poly[idx] = point_pair[0] - tcl_poly[point_num - 1 - idx] = point_pair[1] - return tcl_poly - - def gen_quad_tbo(self, quad, tcl_mask, tbo_map): - """ - Generate tbo_map for give quad. - """ - # upper and lower line function: ax + by + c = 0; - up_line = self.line_cross_two_point(quad[0], quad[1]) - lower_line = self.line_cross_two_point(quad[3], quad[2]) - - quad_h = 0.5 * (np.linalg.norm(quad[0] - quad[3]) + np.linalg.norm(quad[1] - quad[2])) - quad_w = 0.5 * (np.linalg.norm(quad[0] - quad[1]) + np.linalg.norm(quad[2] - quad[3])) - - # average angle of left and right line. - angle = self.average_angle(quad) - - xy_in_poly = np.argwhere(tcl_mask == 1) - for y, x in xy_in_poly: - point = (x, y) - line = self.theta_line_cross_point(angle, point) - cross_point_upper = self.line_cross_point(up_line, line) - cross_point_lower = self.line_cross_point(lower_line, line) - ##FIX, offset reverse - upper_offset_x, upper_offset_y = cross_point_upper - point - lower_offset_x, lower_offset_y = cross_point_lower - point - tbo_map[y, x, 0] = upper_offset_y - tbo_map[y, x, 1] = upper_offset_x - tbo_map[y, x, 2] = lower_offset_y - tbo_map[y, x, 3] = lower_offset_x - tbo_map[y, x, 4] = 1.0 / max(min(quad_h, quad_w), 1.0) * 2 - return tbo_map - - def poly2quads(self, poly): - """ - Split poly into quads. - """ - quad_list = [] - point_num = poly.shape[0] - - # point pair - point_pair_list = [] - for idx in range(point_num // 2): - point_pair = [poly[idx], poly[point_num - 1 - idx]] - point_pair_list.append(point_pair) - - quad_num = point_num // 2 - 1 - for idx in range(quad_num): - # reshape and adjust to clock-wise - quad_list.append((np.array(point_pair_list)[[idx, idx + 1]]).reshape(4, 2)[[0, 2, 3, 1]]) - - return np.array(quad_list) - - def extract_polys(self, poly_txt_path): - """ - Read text_polys, txt_tags, txts from give txt file. - """ - text_polys, txt_tags, txts = [], [], [] - - with open(poly_txt_path, 'r', encoding='utf-8') as f: - for line in f.readlines(): - poly_str = line.strip().replace('\ufeff', '').split(',') - txt = poly_str[-1] - poly_str = poly_str[:8] - poly = list(map(float, poly_str)) - text_polys.append(np.array(poly, dtype=np.float32).reshape(-1, 2)) - txts.append(txt) - if txt == '###': - txt_tags.append(True) - else: - txt_tags.append(False) - - return np.array(text_polys), \ - np.array(txt_tags, dtype=np.bool), txts - - def __len__(self): - return len(self.img_files) - - def __getitem__(self, index): - - im_path = self.img_files[index] - text_polys, text_tags, text_strs = self.extract_polys(self.gt_files[index]) - im = cv2.imread(im_path) - - return im,text_polys, text_tags, text_strs - - def img_tranform(self,im,text_polys, text_tags, text_strs): - - if im is None: - return None - if text_polys.shape[0] == 0: - return None - - h, w, _ = im.shape - text_polys, text_tags, hv_tags = self.check_and_validate_polys(text_polys, text_tags, (h, w)) - - if text_polys.shape[0] == 0: - return None - - # set aspect ratio and keep area fix - asp_scales = np.arange(1.0, 1.55, 0.1) - asp_scale = np.random.choice(asp_scales) - - if np.random.rand() < 0.5: - asp_scale = 1.0 / asp_scale - asp_scale = math.sqrt(asp_scale) - - asp_wx = asp_scale - asp_hy = 1.0 / asp_scale - im = cv2.resize(im, dsize=None, fx=asp_wx, fy=asp_hy) - text_polys[:, :, 0] *= asp_wx - text_polys[:, :, 1] *= asp_hy - - h, w, _ = im.shape - if max(h, w) > 2048: - rd_scale = 2048.0 / max(h, w) - im = cv2.resize(im, dsize=None, fx=rd_scale, fy=rd_scale) - text_polys *= rd_scale - h, w, _ = im.shape - if min(h, w) < 16: - return None - - # no background - im, text_polys, text_tags, hv_tags, text_strs = self.crop_area(im, text_polys, text_tags, hv_tags, text_strs,crop_background=False) - if text_polys.shape[0] == 0: - return None - # continue for all ignore case - if np.sum((text_tags * 1.0)) >= text_tags.size: - return None - new_h, new_w, _ = im.shape - if (new_h is None) or (new_w is None): - return None - # resize image - std_ratio = float(self.input_size) / max(new_w, new_h) - rand_scales = np.array([0.25, 0.375, 0.5, 0.625, 0.75, 0.875, 1.0, 1.0, 1.0, 1.0, 1.0]) - rz_scale = std_ratio * np.random.choice(rand_scales) - - - im = cv2.resize(im, dsize=None, fx=rz_scale, fy=rz_scale) - # Padding the im to [input_size, input_size] - new_h, new_w, _ = im.shape - if min(new_w, new_h) < self.input_size * 0.5: - return None - - text_polys[:, :, 0] *= rz_scale - text_polys[:, :, 1] *= rz_scale - - # add gaussian blur - if np.random.rand() < 0.1 * 0.5: - ks = np.random.permutation(5)[0] + 1 - ks = int(ks / 2) * 2 + 1 - im = cv2.GaussianBlur(im, ksize=(ks, ks), sigmaX=0, sigmaY=0) - # add brighter - if np.random.rand() < 0.1 * 0.5: - im = im * (1.0 + np.random.rand() * 0.5) - im = np.clip(im, 0.0, 255.0) - # add darker - if np.random.rand() < 0.1 * 0.5: - im = im * (1.0 - np.random.rand() * 0.5) - im = np.clip(im, 0.0, 255.0) - - - im_padded = np.ones((self.input_size, self.input_size, 3), dtype=np.float32) - im_padded[:, :, 2] = 0.485 * 255 - im_padded[:, :, 1] = 0.456 * 255 - im_padded[:, :, 0] = 0.406 * 255 - - # Random the start position - del_h = self.input_size - new_h - del_w = self.input_size - new_w - sh, sw = 0, 0 - if del_h > 1: - sh = int(np.random.rand() * del_h) - if del_w > 1: - sw = int(np.random.rand() * del_w) - - # Padding - im_padded[sh: sh + new_h, sw: sw + new_w, :] = im.copy() - text_polys[:, :, 0] += sw - text_polys[:, :, 1] += sh - - score_map, border_map, training_mask = self.generate_tcl_label((self.input_size, self.input_size), - text_polys, text_tags, 0.25) - - # SAST head - tvo_map, tco_map = self.generate_tvo_and_tco((self.input_size, self.input_size), text_polys, text_tags, - tcl_ratio=0.3, ds_ratio=0.25) - # print("test--------tvo_map shape:", tvo_map.shape) - - cv2.imwrite('result.jpg',np.array(im_padded)) - cv2.imwrite('score.jpg',np.array(score_map)*255) - - im_padded = im_padded.astype(np.uint8) - im_padded = Image.fromarray(im_padded).convert('RGB') - im_padded = self.TSM.normalize_img(im_padded) - - score_map = torch.Tensor(score_map[np.newaxis, :, :]) - border_map = torch.Tensor(border_map.transpose((2, 0, 1))) - training_mask = torch.Tensor(training_mask[np.newaxis, :, :]) - tvo_map = torch.Tensor(tvo_map.transpose((2, 0, 1))) - tco_map = torch.Tensor(tco_map.transpose((2, 0, 1))) - - return im_padded, score_map, border_map, training_mask, tvo_map, tco_map - - -class alignCollate(object): - - def __init__(self, train_dataset): - self.train_dataset = train_dataset - - def __call__(self, batch): - new_batch = [] - for item in batch: - im, text_polys, text_tags, text_strs = item - max_tried = 30 - i = 0 - while True: - out = self.train_dataset.img_tranform(im, text_polys, text_tags, text_strs) - if(out is not None or i > max_tried): - break - i+=1 - if(out is not None): - new_batch.append(out) - data = zip(*new_batch) - images, score_map, border_map, training_mask, tvo_map, tco_map = data - images = torch.stack(images, 0) - score_map = torch.stack(score_map, 0) - border_map = torch.stack(border_map, 0) - training_mask = torch.stack(training_mask, 0) - tvo_map = torch.stack(tvo_map, 0) - tco_map = torch.stack(tco_map, 0) - return images, score_map, border_map, training_mask, tvo_map, tco_map - - -class SASTProcessTest(data.Dataset): - """ - SAST process function for test - """ - - def __init__(self, config): - super(SASTProcessTest, self).__init__() - self.img_list = self.get_img_files(config['testload']['test_file']) - self.TSM = Random_Augment(config['base']['crop_shape']) - self.test_size = config['testload']['test_size'] - self.config = config - - def get_img_files(self, test_txt_file): - img_list = [] - with open(test_txt_file, 'r', encoding='utf-8') as fid: - lines = fid.readlines() - for line in lines: - line = line.strip('\n') - img_list.append(line) - return img_list - def __len__(self): - return len(self.img_list) - - def __getitem__(self, index): - ori_img = cv2.imread(self.img_list[index]) - img = resize_image(ori_img,self.config['base']['algorithm'],self.test_size,self.config['testload']['stride']) - img = Image.fromarray(img).convert('RGB') - img = self.TSM.normalize_img(img) - return img, ori_img - - - - - - diff --git a/ptocr/dataloader/DetLoad/SASTProcess_ori1.py b/ptocr/dataloader/DetLoad/SASTProcess_ori1.py deleted file mode 100644 index 27154e3..0000000 --- a/ptocr/dataloader/DetLoad/SASTProcess_ori1.py +++ /dev/null @@ -1,862 +0,0 @@ -#-*- coding:utf-8 _*- -""" -@author:fxw -@file: SASTProcess.py -@time: 2020/08/18 -""" -import os -import math -import cv2 -import json -import numpy as np -import torch -from PIL import Image -import torch.utils.data as data -import torchvision.transforms as transforms -from .transform_img import Random_Augment -from ptocr.utils.util_function import resize_image - -class SASTProcessTrain(data.Dataset): - """ - SAST process function for training - """ - - def __init__(self,config): - self.TSM = Random_Augment(config['base']['crop_shape']) - self.min_crop_side_ratio = config['trainload']['min_crop_side_ratio'] - self.min_crop_size = config['trainload']['min_crop_size'] - image_shape = config['base']['crop_shape'] - self.input_size = image_shape[0] - self.min_text_size = config['trainload']['min_text_size'] - self.max_text_size = image_shape[0] - - data_num = len(config['trainload']['train_file_extre']) - train_file_extre = config['trainload']['train_file_extre'] - train_file_target = config['trainload']['train_file_target'] - extre_img_all = [] - extre_gt_all = {} - self.target_files = [] - self.target_gts = [] - - for i in range(data_num): - with open(os.path.join(config['trainload']['data_dir'],train_file_extre[i]), 'r',encoding='utf-8') as fid: - lines = fid.readlines() - for line in lines: - line = line.strip('\n').split('\t') - img_file = line[0] - gt = json.loads(line[1]) - extre_img_all.append(os.path.join(config['trainload']['data_dir'],img_file)) - extre_gt_all[os.path.join(config['trainload']['data_dir'],img_file)] = gt - - with open(os.path.join(config['trainload']['data_dir'],train_file_target), 'r',encoding='utf-8') as fid: - lines = fid.readlines() - for line in lines: - line = line.strip('\n').split('\t') - img_file = line[0] - gt = json.loads(line[1]) - self.target_files.append(os.path.join(config['trainload']['data_dir'],img_file)) - self.target_gts.append(gt) - - self.config = config - self.extre_img_all = extre_img_all - self.extre_gt_all = extre_gt_all - self.train_file_ratio = config['trainload']['train_file_ratio'] - self.data_num = data_num - self.gen_train_img() -# import pdb -# pdb.set_trace() - - def gen_train_img(self,): - self.img_files = [] - self.train_gts = [] - self.img_files.extend(self.target_files) - self.train_gts.extend(self.target_gts) - extre_num = int(self.config['trainload']['train_file_ratio']*len(self.target_files)) - for i in range(extre_num): - self.img_files.append(self.extre_img_all[i]) - self.train_gts.append(self.extre_gt_all[self.extre_img_all[i]]) - - np.random.shuffle(self.extre_img_all) - print(self.img_files[-1]) - - def order_points(self, pts): - rect = np.zeros((4, 2), dtype="float32") - s = pts.sum(axis=1) - rect[0] = pts[np.argmin(s)] - rect[2] = pts[np.argmax(s)] - diff = np.diff(pts, axis=1) - rect[1] = pts[np.argmin(diff)] - rect[3] = pts[np.argmax(diff)] - return rect - - def quad_area(self, poly): - """ - compute area of a polygon - :param poly: - :return: - """ - edge = [ - (poly[1][0] - poly[0][0]) * (poly[1][1] + poly[0][1]), - (poly[2][0] - poly[1][0]) * (poly[2][1] + poly[1][1]), - (poly[3][0] - poly[2][0]) * (poly[3][1] + poly[2][1]), - (poly[0][0] - poly[3][0]) * (poly[0][1] + poly[3][1]) - ] - return np.sum(edge) / 2. - - def gen_quad_from_poly(self, poly): - """ - Generate min area quad from poly. - """ - point_num = poly.shape[0] - min_area_quad = np.zeros((4, 2), dtype=np.float32) - if True: - rect = cv2.minAreaRect(poly.astype(np.int32)) # (center (x,y), (width, height), angle of rotation) - center_point = rect[0] - box = np.array(cv2.boxPoints(rect)) - - first_point_idx = 0 - min_dist = 1e4 - for i in range(4): - dist = np.linalg.norm(box[(i + 0) % 4] - poly[0]) + \ - np.linalg.norm(box[(i + 1) % 4] - poly[point_num // 2 - 1]) + \ - np.linalg.norm(box[(i + 2) % 4] - poly[point_num // 2]) + \ - np.linalg.norm(box[(i + 3) % 4] - poly[-1]) - if dist < min_dist: - min_dist = dist - first_point_idx = i - for i in range(4): - min_area_quad[i] = box[(first_point_idx + i) % 4] - - return min_area_quad - - def check_and_validate_polys(self, polys, tags, xxx_todo_changeme): - """ - check so that the text poly is in the same direction, - and also filter some invalid polygons - :param polys: - :param tags: - :return: - """ - (h, w) = xxx_todo_changeme - if polys.shape[0] == 0: - return polys, np.array([]), np.array([]) - polys[:, :, 0] = np.clip(polys[:, :, 0], 0, w - 1) - polys[:, :, 1] = np.clip(polys[:, :, 1], 0, h - 1) - - validated_polys = [] - validated_tags = [] - hv_tags = [] - for poly, tag in zip(polys, tags): - quad = self.gen_quad_from_poly(poly) - p_area = self.quad_area(quad) - if abs(p_area) < 1: - print('invalid poly') - continue - if p_area > 0: - if tag == False: - print('poly in wrong direction') - tag = True # reversed cases should be ignore - poly = poly[(0, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1), :] - quad = quad[(0, 3, 2, 1), :] - - len_w = np.linalg.norm(quad[0] - quad[1]) + np.linalg.norm(quad[3] - quad[2]) - len_h = np.linalg.norm(quad[0] - quad[3]) + np.linalg.norm(quad[1] - quad[2]) - hv_tag = 1 - - if len_w * 2.0 < len_h: - hv_tag = 0 - - validated_polys.append(poly) - validated_tags.append(tag) - hv_tags.append(hv_tag) - return np.array(validated_polys), np.array(validated_tags), np.array(hv_tags) - - def crop_area(self, im, polys, tags, hv_tags, txts, crop_background=False, max_tries=25): - """ - make random crop from the input image - :param im: - :param polys: - :param tags: - :param crop_background: - :param max_tries: 50 -> 25 - :return: - """ - h, w, _ = im.shape - pad_h = h // 10 - pad_w = w // 10 - h_array = np.zeros((h + pad_h * 2), dtype=np.int32) - w_array = np.zeros((w + pad_w * 2), dtype=np.int32) - for poly in polys: - poly = np.round(poly, decimals=0).astype(np.int32) - minx = np.min(poly[:, 0]) - maxx = np.max(poly[:, 0]) - w_array[minx + pad_w: maxx + pad_w] = 1 - miny = np.min(poly[:, 1]) - maxy = np.max(poly[:, 1]) - h_array[miny + pad_h: maxy + pad_h] = 1 - # ensure the cropped area not across a text - h_axis = np.where(h_array == 0)[0] - w_axis = np.where(w_array == 0)[0] - if len(h_axis) == 0 or len(w_axis) == 0: - return im, polys, tags, hv_tags, txts - for i in range(max_tries): - xx = np.random.choice(w_axis, size=2) - xmin = np.min(xx) - pad_w - xmax = np.max(xx) - pad_w - xmin = np.clip(xmin, 0, w - 1) - xmax = np.clip(xmax, 0, w - 1) - yy = np.random.choice(h_axis, size=2) - ymin = np.min(yy) - pad_h - ymax = np.max(yy) - pad_h - ymin = np.clip(ymin, 0, h - 1) - ymax = np.clip(ymax, 0, h - 1) - # if xmax - xmin < ARGS.min_crop_side_ratio * w or \ - # ymax - ymin < ARGS.min_crop_side_ratio * h: - if xmax - xmin < self.min_crop_size or \ - ymax - ymin < self.min_crop_size: - # area too small - continue - if polys.shape[0] != 0: - poly_axis_in_area = (polys[:, :, 0] >= xmin) & (polys[:, :, 0] <= xmax) \ - & (polys[:, :, 1] >= ymin) & (polys[:, :, 1] <= ymax) - selected_polys = np.where(np.sum(poly_axis_in_area, axis=1) == 4)[0] - else: - selected_polys = [] - if len(selected_polys) == 0: - # no text in this area - if crop_background: - txts_tmp = [] - for selected_poly in selected_polys: - txts_tmp.append(txts[selected_poly]) - txts = txts_tmp - return im[ymin: ymax + 1, xmin: xmax + 1, :], \ - polys[selected_polys], tags[selected_polys], hv_tags[selected_polys], txts - else: - continue - im = im[ymin: ymax + 1, xmin: xmax + 1, :] - polys = polys[selected_polys] - tags = tags[selected_polys] - hv_tags = hv_tags[selected_polys] - txts_tmp = [] - for selected_poly in selected_polys: - txts_tmp.append(txts[selected_poly]) - txts = txts_tmp - polys[:, :, 0] -= xmin - polys[:, :, 1] -= ymin - return im, polys, tags, hv_tags, txts - - return im, polys, tags, hv_tags, txts - - def generate_direction_map(self, poly_quads, direction_map): - """ - """ - width_list = [] - height_list = [] - for quad in poly_quads: - quad_w = (np.linalg.norm(quad[0] - quad[1]) + np.linalg.norm(quad[2] - quad[3])) / 2.0 - quad_h = (np.linalg.norm(quad[0] - quad[3]) + np.linalg.norm(quad[2] - quad[1])) / 2.0 - width_list.append(quad_w) - height_list.append(quad_h) - norm_width = max(sum(width_list) / (len(width_list) + 1e-6), 1.0) - average_height = max(sum(height_list) / (len(height_list) + 1e-6), 1.0) - - for quad in poly_quads: - direct_vector_full = ((quad[1] + quad[2]) - (quad[0] + quad[3])) / 2.0 - direct_vector = direct_vector_full / (np.linalg.norm(direct_vector_full) + 1e-6) * norm_width - direction_label = tuple(map(float, [direct_vector[0], direct_vector[1], 1.0 / (average_height + 1e-6)])) - cv2.fillPoly(direction_map, quad.round().astype(np.int32)[np.newaxis, :, :], direction_label) - return direction_map - - def calculate_average_height(self, poly_quads): - """ - """ - height_list = [] - for quad in poly_quads: - quad_h = (np.linalg.norm(quad[0] - quad[3]) + np.linalg.norm(quad[2] - quad[1])) / 2.0 - height_list.append(quad_h) - average_height = max(sum(height_list) / len(height_list), 1.0) - return average_height - - def generate_tcl_label(self, hw, polys, tags, ds_ratio, - tcl_ratio=0.3, shrink_ratio_of_width=0.15): - """ - Generate polygon. - """ - h, w = hw - h, w = int(h * ds_ratio), int(w * ds_ratio) - polys = polys * ds_ratio - - score_map = np.zeros((h, w,), dtype=np.float32) - tbo_map = np.zeros((h, w, 5), dtype=np.float32) - training_mask = np.ones((h, w,), dtype=np.float32) - direction_map = np.ones((h, w, 3)) * np.array([0, 0, 1]).reshape([1, 1, 3]).astype(np.float32) - - for poly_idx, poly_tag in enumerate(zip(polys, tags)): - poly = poly_tag[0] - tag = poly_tag[1] - - # generate min_area_quad - min_area_quad, center_point = self.gen_min_area_quad_from_poly(poly) - min_area_quad_h = 0.5 * (np.linalg.norm(min_area_quad[0] - min_area_quad[3]) + - np.linalg.norm(min_area_quad[1] - min_area_quad[2])) - min_area_quad_w = 0.5 * (np.linalg.norm(min_area_quad[0] - min_area_quad[1]) + - np.linalg.norm(min_area_quad[2] - min_area_quad[3])) - - if min(min_area_quad_h, min_area_quad_w) < self.min_text_size * ds_ratio \ - or min(min_area_quad_h, min_area_quad_w) > self.max_text_size * ds_ratio: - continue - - if tag: - # continue - cv2.fillPoly(training_mask, poly.astype(np.int32)[np.newaxis, :, :], 0.15) - else: - tcl_poly = self.poly2tcl(poly, tcl_ratio) - tcl_quads = self.poly2quads(tcl_poly) - poly_quads = self.poly2quads(poly) - # stcl map - stcl_quads, quad_index = self.shrink_poly_along_width(tcl_quads, - shrink_ratio_of_width=shrink_ratio_of_width, - expand_height_ratio=1.0 / tcl_ratio) - # generate tcl map - cv2.fillPoly(score_map, np.round(stcl_quads).astype(np.int32), 1.0) - - # generate tbo map - for idx, quad in enumerate(stcl_quads): - quad_mask = np.zeros((h, w), dtype=np.float32) - quad_mask = cv2.fillPoly(quad_mask, np.round(quad[np.newaxis, :, :]).astype(np.int32), 1.0) - tbo_map = self.gen_quad_tbo(poly_quads[quad_index[idx]], quad_mask, tbo_map) - return score_map, tbo_map, training_mask - - def generate_tvo_and_tco(self, hw, polys, tags, tcl_ratio=0.3, ds_ratio=0.25): - """ - Generate tcl map, tvo map and tbo map. - """ - h, w = hw - h, w = int(h * ds_ratio), int(w * ds_ratio) - polys = polys * ds_ratio - poly_mask = np.zeros((h, w), dtype=np.float32) - - tvo_map = np.ones((9, h, w), dtype=np.float32) - tvo_map[0:-1:2] = np.tile(np.arange(0, w), (h, 1)) - tvo_map[1:-1:2] = np.tile(np.arange(0, w), (h, 1)).T - poly_tv_xy_map = np.zeros((8, h, w), dtype=np.float32) - - # tco map - tco_map = np.ones((3, h, w), dtype=np.float32) - tco_map[0] = np.tile(np.arange(0, w), (h, 1)) - tco_map[1] = np.tile(np.arange(0, w), (h, 1)).T - poly_tc_xy_map = np.zeros((2, h, w), dtype=np.float32) - - poly_short_edge_map = np.ones((h, w), dtype=np.float32) - - for poly, poly_tag in zip(polys, tags): - - if poly_tag == True: - continue - - # adjust point order for vertical poly - poly = self.adjust_point(poly) - - # generate min_area_quad - min_area_quad, center_point = self.gen_min_area_quad_from_poly(poly) - min_area_quad_h = 0.5 * (np.linalg.norm(min_area_quad[0] - min_area_quad[3]) + - np.linalg.norm(min_area_quad[1] - min_area_quad[2])) - min_area_quad_w = 0.5 * (np.linalg.norm(min_area_quad[0] - min_area_quad[1]) + - np.linalg.norm(min_area_quad[2] - min_area_quad[3])) - - # generate tcl map and text, 128 * 128 - tcl_poly = self.poly2tcl(poly, tcl_ratio) - - # generate poly_tv_xy_map - for idx in range(4): - cv2.fillPoly(poly_tv_xy_map[2 * idx], - np.round(tcl_poly[np.newaxis, :, :]).astype(np.int32), - float(min(max(min_area_quad[idx, 0], 0), w))) - cv2.fillPoly(poly_tv_xy_map[2 * idx + 1], - np.round(tcl_poly[np.newaxis, :, :]).astype(np.int32), - float(min(max(min_area_quad[idx, 1], 0), h))) - - # generate poly_tc_xy_map - for idx in range(2): - cv2.fillPoly(poly_tc_xy_map[idx], - np.round(tcl_poly[np.newaxis, :, :]).astype(np.int32), float(center_point[idx])) - - # generate poly_short_edge_map - cv2.fillPoly(poly_short_edge_map, - np.round(tcl_poly[np.newaxis, :, :]).astype(np.int32), - float(max(min(min_area_quad_h, min_area_quad_w), 1.0))) - - # generate poly_mask and training_mask - cv2.fillPoly(poly_mask, np.round(tcl_poly[np.newaxis, :, :]).astype(np.int32), 1) - - tvo_map *= poly_mask - tvo_map[:8] -= poly_tv_xy_map - tvo_map[-1] /= poly_short_edge_map - tvo_map = tvo_map.transpose((1, 2, 0)) - - tco_map *= poly_mask - tco_map[:2] -= poly_tc_xy_map - tco_map[-1] /= poly_short_edge_map - tco_map = tco_map.transpose((1, 2, 0)) - - return tvo_map, tco_map - - def adjust_point(self, poly): - """ - adjust point order. - """ - point_num = poly.shape[0] - if point_num == 4: - len_1 = np.linalg.norm(poly[0] - poly[1]) - len_2 = np.linalg.norm(poly[1] - poly[2]) - len_3 = np.linalg.norm(poly[2] - poly[3]) - len_4 = np.linalg.norm(poly[3] - poly[0]) - - if (len_1 + len_3) * 1.5 < (len_2 + len_4): - poly = poly[[1, 2, 3, 0], :] - - elif point_num > 4: - vector_1 = poly[0] - poly[1] - vector_2 = poly[1] - poly[2] - cos_theta = np.dot(vector_1, vector_2) / (np.linalg.norm(vector_1) * np.linalg.norm(vector_2) + 1e-6) - theta = np.arccos(np.round(cos_theta, decimals=4)) - - if abs(theta) > (70 / 180 * math.pi): - index = list(range(1, point_num)) + [0] - poly = poly[np.array(index), :] - return poly - - def gen_min_area_quad_from_poly(self, poly): - """ - Generate min area quad from poly. - """ - point_num = poly.shape[0] - min_area_quad = np.zeros((4, 2), dtype=np.float32) - if point_num == 4: - min_area_quad = poly - center_point = np.sum(poly, axis=0) / 4 - else: - rect = cv2.minAreaRect(poly.astype(np.int32)) # (center (x,y), (width, height), angle of rotation) - center_point = rect[0] - box = np.array(cv2.boxPoints(rect)) - - first_point_idx = 0 - min_dist = 1e4 - for i in range(4): - dist = np.linalg.norm(box[(i + 0) % 4] - poly[0]) + \ - np.linalg.norm(box[(i + 1) % 4] - poly[point_num // 2 - 1]) + \ - np.linalg.norm(box[(i + 2) % 4] - poly[point_num // 2]) + \ - np.linalg.norm(box[(i + 3) % 4] - poly[-1]) - if dist < min_dist: - min_dist = dist - first_point_idx = i - - for i in range(4): - min_area_quad[i] = box[(first_point_idx + i) % 4] - - return min_area_quad, center_point - - def shrink_quad_along_width(self, quad, begin_width_ratio=0., end_width_ratio=1.): - """ - Generate shrink_quad_along_width. - """ - ratio_pair = np.array([[begin_width_ratio], [end_width_ratio]], dtype=np.float32) - p0_1 = quad[0] + (quad[1] - quad[0]) * ratio_pair - p3_2 = quad[3] + (quad[2] - quad[3]) * ratio_pair - return np.array([p0_1[0], p0_1[1], p3_2[1], p3_2[0]]) - - def shrink_poly_along_width(self, quads, shrink_ratio_of_width, expand_height_ratio=1.0): - """ - shrink poly with given length. - """ - upper_edge_list = [] - - def get_cut_info(edge_len_list, cut_len): - for idx, edge_len in enumerate(edge_len_list): - cut_len -= edge_len - if cut_len <= 0.000001: - ratio = (cut_len + edge_len_list[idx]) / edge_len_list[idx] - return idx, ratio - - for quad in quads: - upper_edge_len = np.linalg.norm(quad[0] - quad[1]) - upper_edge_list.append(upper_edge_len) - - # length of left edge and right edge. - left_length = np.linalg.norm(quads[0][0] - quads[0][3]) * expand_height_ratio - right_length = np.linalg.norm(quads[-1][1] - quads[-1][2]) * expand_height_ratio - - shrink_length = min(left_length, right_length, sum(upper_edge_list)) * shrink_ratio_of_width - # shrinking length - upper_len_left = shrink_length - upper_len_right = sum(upper_edge_list) - shrink_length - - left_idx, left_ratio = get_cut_info(upper_edge_list, upper_len_left) - left_quad = self.shrink_quad_along_width(quads[left_idx], begin_width_ratio=left_ratio, end_width_ratio=1) - right_idx, right_ratio = get_cut_info(upper_edge_list, upper_len_right) - right_quad = self.shrink_quad_along_width(quads[right_idx], begin_width_ratio=0, end_width_ratio=right_ratio) - - out_quad_list = [] - if left_idx == right_idx: - out_quad_list.append([left_quad[0], right_quad[1], right_quad[2], left_quad[3]]) - else: - out_quad_list.append(left_quad) - for idx in range(left_idx + 1, right_idx): - out_quad_list.append(quads[idx]) - out_quad_list.append(right_quad) - - return np.array(out_quad_list), list(range(left_idx, right_idx + 1)) - - def vector_angle(self, A, B): - """ - Calculate the angle between vector AB and x-axis positive direction. - """ - AB = np.array([B[1] - A[1], B[0] - A[0]]) - return np.arctan2(*AB) - - def theta_line_cross_point(self, theta, point): - """ - Calculate the line through given point and angle in ax + by + c =0 form. - """ - x, y = point - cos = np.cos(theta) - sin = np.sin(theta) - return [sin, -cos, cos * y - sin * x] - - def line_cross_two_point(self, A, B): - """ - Calculate the line through given point A and B in ax + by + c =0 form. - """ - angle = self.vector_angle(A, B) - return self.theta_line_cross_point(angle, A) - - def average_angle(self, poly): - """ - Calculate the average angle between left and right edge in given poly. - """ - p0, p1, p2, p3 = poly - angle30 = self.vector_angle(p3, p0) - angle21 = self.vector_angle(p2, p1) - return (angle30 + angle21) / 2 - - def line_cross_point(self, line1, line2): - """ - line1 and line2 in 0=ax+by+c form, compute the cross point of line1 and line2 - """ - a1, b1, c1 = line1 - a2, b2, c2 = line2 - d = a1 * b2 - a2 * b1 - - if d == 0: - # print("line1", line1) - # print("line2", line2) - print('Cross point does not exist') - return np.array([0, 0], dtype=np.float32) - else: - x = (b1 * c2 - b2 * c1) / d - y = (a2 * c1 - a1 * c2) / d - - return np.array([x, y], dtype=np.float32) - - def quad2tcl(self, poly, ratio): - """ - Generate center line by poly clock-wise point. (4, 2) - """ - ratio_pair = np.array([[0.5 - ratio / 2], [0.5 + ratio / 2]], dtype=np.float32) - p0_3 = poly[0] + (poly[3] - poly[0]) * ratio_pair - p1_2 = poly[1] + (poly[2] - poly[1]) * ratio_pair - return np.array([p0_3[0], p1_2[0], p1_2[1], p0_3[1]]) - - def poly2tcl(self, poly, ratio): - """ - Generate center line by poly clock-wise point. - """ - ratio_pair = np.array([[0.5 - ratio / 2], [0.5 + ratio / 2]], dtype=np.float32) - tcl_poly = np.zeros_like(poly) - point_num = poly.shape[0] - - for idx in range(point_num // 2): - point_pair = poly[idx] + (poly[point_num - 1 - idx] - poly[idx]) * ratio_pair - tcl_poly[idx] = point_pair[0] - tcl_poly[point_num - 1 - idx] = point_pair[1] - return tcl_poly - - def gen_quad_tbo(self, quad, tcl_mask, tbo_map): - """ - Generate tbo_map for give quad. - """ - # upper and lower line function: ax + by + c = 0; - up_line = self.line_cross_two_point(quad[0], quad[1]) - lower_line = self.line_cross_two_point(quad[3], quad[2]) - - quad_h = 0.5 * (np.linalg.norm(quad[0] - quad[3]) + np.linalg.norm(quad[1] - quad[2])) - quad_w = 0.5 * (np.linalg.norm(quad[0] - quad[1]) + np.linalg.norm(quad[2] - quad[3])) - - # average angle of left and right line. - angle = self.average_angle(quad) - - xy_in_poly = np.argwhere(tcl_mask == 1) - for y, x in xy_in_poly: - point = (x, y) - line = self.theta_line_cross_point(angle, point) - cross_point_upper = self.line_cross_point(up_line, line) - cross_point_lower = self.line_cross_point(lower_line, line) - ##FIX, offset reverse - upper_offset_x, upper_offset_y = cross_point_upper - point - lower_offset_x, lower_offset_y = cross_point_lower - point - tbo_map[y, x, 0] = upper_offset_y - tbo_map[y, x, 1] = upper_offset_x - tbo_map[y, x, 2] = lower_offset_y - tbo_map[y, x, 3] = lower_offset_x - tbo_map[y, x, 4] = 1.0 / max(min(quad_h, quad_w), 1.0) * 2 - return tbo_map - - def poly2quads(self, poly): - """ - Split poly into quads. - """ - quad_list = [] - point_num = poly.shape[0] - - # point pair - point_pair_list = [] - for idx in range(point_num // 2): - point_pair = [poly[idx], poly[point_num - 1 - idx]] - point_pair_list.append(point_pair) - - quad_num = point_num // 2 - 1 - for idx in range(quad_num): - # reshape and adjust to clock-wise - quad_list.append((np.array(point_pair_list)[[idx, idx + 1]]).reshape(4, 2)[[0, 2, 3, 1]]) - - return np.array(quad_list) - -# def extract_polys(self, poly_txt_path): -# """ -# Read text_polys, txt_tags, txts from give txt file. -# """ -# text_polys, txt_tags, txts = [], [], [] - -# with open(poly_txt_path, 'r', encoding='utf-8') as f: -# for line in f.readlines(): -# poly_str = line.strip().replace('\ufeff', '').split(',') -# txt = poly_str[-1] -# poly_str = poly_str[:8] -# poly = list(map(float, poly_str)) -# text_polys.append(np.array(poly, dtype=np.float32).reshape(-1, 2)) -# txts.append(txt) -# if txt == '###': -# txt_tags.append(True) -# else: -# txt_tags.append(False) - -# return np.array(text_polys), \ -# np.array(txt_tags, dtype=np.bool), txts - - def extract_polys(self, label_data): - text_polys, txt_tags, txts = [], [], [] - for rect in label_data: - points = np.array(rect['points']).astype(np.float32) - transcription = rect['transcription'] - points = self.order_points(points) - if cv2.contourArea(points) > 0: - text_polys.append(points) - txts.append(transcription) - txt_tags.append(transcription in ['*','###']) - return np.array(text_polys),np.array(txt_tags, dtype=np.bool), txts - - - def __len__(self): - return len(self.img_files) - - def __getitem__(self, index): - - im_path = self.img_files[index] - text_polys, text_tags, text_strs = self.extract_polys(self.train_gts[index]) - im = cv2.imread(im_path) - - return im,text_polys, text_tags, text_strs - - def img_tranform(self,im,text_polys, text_tags, text_strs): - - if im is None: - return None - if text_polys.shape[0] == 0: - return None - - h, w, _ = im.shape - text_polys, text_tags, hv_tags = self.check_and_validate_polys(text_polys, text_tags, (h, w)) - - if text_polys.shape[0] == 0: - return None - - # set aspect ratio and keep area fix - asp_scales = np.arange(1.0, 1.55, 0.1) - asp_scale = np.random.choice(asp_scales) - - if np.random.rand() < 0.5: - asp_scale = 1.0 / asp_scale - asp_scale = math.sqrt(asp_scale) - - asp_wx = asp_scale - asp_hy = 1.0 / asp_scale - im = cv2.resize(im, dsize=None, fx=asp_wx, fy=asp_hy) - text_polys[:, :, 0] *= asp_wx - text_polys[:, :, 1] *= asp_hy - - h, w, _ = im.shape - if max(h, w) > 2048: - rd_scale = 2048.0 / max(h, w) - im = cv2.resize(im, dsize=None, fx=rd_scale, fy=rd_scale) - text_polys *= rd_scale - h, w, _ = im.shape - if min(h, w) < 16: - return None - - # no background - im, text_polys, text_tags, hv_tags, text_strs = self.crop_area(im, text_polys, text_tags, hv_tags, text_strs,crop_background=False) - if text_polys.shape[0] == 0: - return None - # continue for all ignore case - if np.sum((text_tags * 1.0)) >= text_tags.size: - return None - new_h, new_w, _ = im.shape - if (new_h is None) or (new_w is None): - return None - # resize image - std_ratio = float(self.input_size) / max(new_w, new_h) - rand_scales = np.array([0.25, 0.375, 0.5, 0.625, 0.75, 0.875, 1.0, 1.0, 1.0, 1.0, 1.0]) - rz_scale = std_ratio * np.random.choice(rand_scales) - - - im = cv2.resize(im, dsize=None, fx=rz_scale, fy=rz_scale) - # Padding the im to [input_size, input_size] - new_h, new_w, _ = im.shape - if min(new_w, new_h) < self.input_size * 0.5: - return None - - text_polys[:, :, 0] *= rz_scale - text_polys[:, :, 1] *= rz_scale - - # add gaussian blur - if np.random.rand() < 0.1 * 0.5: - ks = np.random.permutation(5)[0] + 1 - ks = int(ks / 2) * 2 + 1 - im = cv2.GaussianBlur(im, ksize=(ks, ks), sigmaX=0, sigmaY=0) - # add brighter - if np.random.rand() < 0.1 * 0.5: - im = im * (1.0 + np.random.rand() * 0.5) - im = np.clip(im, 0.0, 255.0) - # add darker - if np.random.rand() < 0.1 * 0.5: - im = im * (1.0 - np.random.rand() * 0.5) - im = np.clip(im, 0.0, 255.0) - - - im_padded = np.ones((self.input_size, self.input_size, 3), dtype=np.float32) - im_padded[:, :, 2] = 0.485 * 255 - im_padded[:, :, 1] = 0.456 * 255 - im_padded[:, :, 0] = 0.406 * 255 - - # Random the start position - del_h = self.input_size - new_h - del_w = self.input_size - new_w - sh, sw = 0, 0 - if del_h > 1: - sh = int(np.random.rand() * del_h) - if del_w > 1: - sw = int(np.random.rand() * del_w) - - # Padding - im_padded[sh: sh + new_h, sw: sw + new_w, :] = im.copy() - text_polys[:, :, 0] += sw - text_polys[:, :, 1] += sh - - score_map, border_map, training_mask = self.generate_tcl_label((self.input_size, self.input_size), - text_polys, text_tags, 0.25) - - # SAST head - tvo_map, tco_map = self.generate_tvo_and_tco((self.input_size, self.input_size), text_polys, text_tags, - tcl_ratio=0.3, ds_ratio=0.25) - # print("test--------tvo_map shape:", tvo_map.shape) - - cv2.imwrite('result.jpg',np.array(im_padded)) - cv2.imwrite('score.jpg',np.array(score_map)*255) - - im_padded = im_padded.astype(np.uint8) - im_padded = Image.fromarray(im_padded).convert('RGB') - im_padded = self.TSM.normalize_img(im_padded) - - score_map = torch.Tensor(score_map[np.newaxis, :, :]) - border_map = torch.Tensor(border_map.transpose((2, 0, 1))) - training_mask = torch.Tensor(training_mask[np.newaxis, :, :]) - tvo_map = torch.Tensor(tvo_map.transpose((2, 0, 1))) - tco_map = torch.Tensor(tco_map.transpose((2, 0, 1))) - - return im_padded, score_map, border_map, training_mask, tvo_map, tco_map - - -class alignCollate(object): - - def __init__(self, train_dataset): - self.train_dataset = train_dataset - - def __call__(self, batch): - new_batch = [] - for item in batch: - im, text_polys, text_tags, text_strs = item - max_tried = 30 - i = 0 - while True: - out = self.train_dataset.img_tranform(im, text_polys, text_tags, text_strs) - if(out is not None or i > max_tried): - break - i+=1 - if(out is not None): - new_batch.append(out) - data = zip(*new_batch) - images, score_map, border_map, training_mask, tvo_map, tco_map = data - images = torch.stack(images, 0) - score_map = torch.stack(score_map, 0) - border_map = torch.stack(border_map, 0) - training_mask = torch.stack(training_mask, 0) - tvo_map = torch.stack(tvo_map, 0) - tco_map = torch.stack(tco_map, 0) - return images, score_map, border_map, training_mask, tvo_map, tco_map - - -class SASTProcessTest(data.Dataset): - """ - SAST process function for test - """ - - def __init__(self, config): - super(SASTProcessTest, self).__init__() - self.img_list = self.get_img_files(config['testload']['test_file']) - self.TSM = Random_Augment(config['base']['crop_shape']) - self.test_size = config['testload']['test_size'] - self.config = config - - def get_img_files(self, test_txt_file): - img_list = [] - with open(test_txt_file, 'r', encoding='utf-8') as fid: - lines = fid.readlines() - for line in lines: - line = line.strip('\n') - img_list.append(line) - return img_list - def __len__(self): - return len(self.img_list) - - def __getitem__(self, index): - ori_img = cv2.imread(self.img_list[index]) - img = resize_image(ori_img,self.config['base']['algorithm'],self.test_size,self.config['testload']['stride']) - img = Image.fromarray(img).convert('RGB') - img = self.TSM.normalize_img(img) - return img, ori_img - - - - - - diff --git a/ptocr/dataloader/DetLoad/__init__.py b/ptocr/dataloader/DetLoad/__init__.py deleted file mode 100644 index 8529416..0000000 --- a/ptocr/dataloader/DetLoad/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -#-*- coding:utf-8 _*- -""" -@author:fxw -@file: __init__.py.py -@time: 2020/08/11 -""" diff --git a/ptocr/dataloader/DetLoad/transform_img.py b/ptocr/dataloader/DetLoad/transform_img.py deleted file mode 100644 index ce02ead..0000000 --- a/ptocr/dataloader/DetLoad/transform_img.py +++ /dev/null @@ -1,329 +0,0 @@ -#-*- coding:utf-8 _*- -""" -@author:fxw -@file: transform_img.py -@time: 2020/08/11 -""" -import cv2 -import numpy as np -import imgaug.augmenters as aug_img -import imgaug -import random -import math -import torchvision.transforms as transforms - -def solve_polys(polys): - len_max = 0 - for poly in polys: - if (len(poly) // 2 > len_max): - len_max = len(poly) // 2 - new_polys = [] - for poly in polys: - new_poly = [] - if (len(poly) // 2 < len_max): - new_poly.extend(poly) - for i in range(len(poly) // 2, len_max): - new_poly.extend([poly[0], poly[1]]) - else: - new_poly = poly - new_polys.append(new_poly) - return np.array(new_polys), len_max - - -def scale_aligned(img, h_scale, w_scale): - h, w = img.shape[0:2] - h = int(h * h_scale + 0.5) - w = int(w * w_scale + 0.5) - if h % 32 != 0: - h = h + (32 - h % 32) - if w % 32 != 0: - w = w + (32 - w % 32) - img = cv2.resize(img, dsize=(w, h)) - return img - - -class RandomCropData(): - def __init__(self, max_tries=10, min_crop_side_ratio=0.1, crop_size=(640, 640)): - self.size = crop_size - self.min_crop_side_ratio = min_crop_side_ratio - self.max_tries = max_tries - - def process(self, img, polys, dont_care): - all_care_polys = [] - for i in range(len(dont_care)): - if (dont_care[i] is False): - all_care_polys.append(polys[i]) - crop_x, crop_y, crop_w, crop_h = self.crop_area(img, all_care_polys) - scale_w = self.size[0] / crop_w - scale_h = self.size[1] / crop_h - scale = min(scale_w, scale_h) - h = int(crop_h * scale) - w = int(crop_w * scale) - padimg = np.zeros( - (self.size[1], self.size[0], img.shape[2]), img.dtype) - padimg[:h, :w] = cv2.resize( - img[crop_y:crop_y + crop_h, crop_x:crop_x + crop_w], (w, h)) - img = padimg - - new_polys = [] - new_dotcare = [] - for i in range(len(polys)): - poly = polys[i] - poly = ((np.array(poly) - - (crop_x, crop_y)) * scale) - if not self.is_poly_outside_rect(poly, 0, 0, w, h): - new_polys.append(poly) - new_dotcare.append(dont_care[i]) - - return img, new_polys, new_dotcare - - def is_poly_in_rect(self, poly, x, y, w, h): - poly = np.array(poly) - if poly[:, 0].min() < x or poly[:, 0].max() > x + w: - return False - if poly[:, 1].min() < y or poly[:, 1].max() > y + h: - return False - return True - - def is_poly_outside_rect(self, poly, x, y, w, h): - poly = np.array(poly) - if poly[:, 0].max() < x or poly[:, 0].min() > x + w: - return True - if poly[:, 1].max() < y or poly[:, 1].min() > y + h: - return True - return False - - def split_regions(self, axis): - regions = [] - min_axis = 0 - for i in range(1, axis.shape[0]): - if axis[i] != axis[i - 1] + 1: - region = axis[min_axis:i] - min_axis = i - regions.append(region) - return regions - - def random_select(self, axis, max_size): - xx = np.random.choice(axis, size=2) - xmin = np.min(xx) - xmax = np.max(xx) - xmin = np.clip(xmin, 0, max_size - 1) - xmax = np.clip(xmax, 0, max_size - 1) - return xmin, xmax - - def region_wise_random_select(self, regions, max_size): - selected_index = list(np.random.choice(len(regions), 2)) - selected_values = [] - for index in selected_index: - axis = regions[index] - xx = int(np.random.choice(axis, size=1)) - selected_values.append(xx) - xmin = min(selected_values) - xmax = max(selected_values) - return xmin, xmax - - def crop_area(self, img, polys): - h, w, _ = img.shape - h_array = np.zeros(h, dtype=np.int32) - w_array = np.zeros(w, dtype=np.int32) - for points in polys: - points = np.round(points, decimals=0).astype(np.int32) - minx = np.min(points[:, 0]) - maxx = np.max(points[:, 0]) - w_array[minx:maxx] = 1 - miny = np.min(points[:, 1]) - maxy = np.max(points[:, 1]) - h_array[miny:maxy] = 1 - # ensure the cropped area not across a text - h_axis = np.where(h_array == 0)[0] - w_axis = np.where(w_array == 0)[0] - - if len(h_axis) == 0 or len(w_axis) == 0: - return 0, 0, w, h - - h_regions = self.split_regions(h_axis) - w_regions = self.split_regions(w_axis) - - for i in range(self.max_tries): - if len(w_regions) > 1: - xmin, xmax = self.region_wise_random_select(w_regions, w) - else: - xmin, xmax = self.random_select(w_axis, w) - if len(h_regions) > 1: - ymin, ymax = self.region_wise_random_select(h_regions, h) - else: - ymin, ymax = self.random_select(h_axis, h) - - if xmax - xmin < self.min_crop_side_ratio * w or ymax - ymin < self.min_crop_side_ratio * h: - # area too small - continue - num_poly_in_rect = 0 - for poly in polys: - if not self.is_poly_outside_rect(poly, xmin, ymin, xmax - xmin, ymax - ymin): - num_poly_in_rect += 1 - break - - if num_poly_in_rect > 0: - return xmin, ymin, xmax - xmin, ymax - ymin - - return 0, 0, w, h - - -class Random_Augment(): - def __init__(self,crop_size, max_tries=10, min_crop_side_ratio=0.1): - super(Random_Augment, self).__init__() - self.random_crop_data = RandomCropData(crop_size = crop_size, max_tries=max_tries, min_crop_side_ratio=min_crop_side_ratio) - self.crop_size = crop_size - - def normalize_img(self,img): - img = transforms.ToTensor()(img) - img = transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])(img) - return img - - def augment_poly(self, aug, img_shape, poly): - keypoints = [imgaug.Keypoint(p[0], p[1]) for p in poly] - keypoints = aug.augment_keypoints([imgaug.KeypointsOnImage(keypoints, shape=img_shape[:2])])[0].keypoints - poly = [(p.x, p.y) for p in keypoints] - return np.array(poly) - - def random_rotate(self, img, polys, random_range=[-10,10]): - angle = np.random.randint(random_range[0], random_range[1]) - aug_bin = aug_img.Sequential([aug_img.Affine(rotate=angle)]) - img = aug_bin.augment_image(img) - new_polys = [] - for poly in polys: - poly = self.augment_poly(aug_bin, img.shape, poly) - poly = np.maximum(poly, 0) - new_polys.append(poly) - return img, new_polys - - def random_scale(self, img, polys, min_size): - polys, len_max = solve_polys(polys) - h, w = img.shape[0:2] - new_polys = [] - for poly in polys: - poly = np.asarray(poly) - poly = poly / ([w * 1.0, h * 1.0] * len_max) - new_polys.append(poly) - new_polys = np.array(new_polys) - if max(h, w) > 1280: - scale = 1280.0 / max(h, w) - img = cv2.resize(img, dsize=None, fx=scale, fy=scale) - h, w = img.shape[0:2] - random_scale = np.array([0.5,1.0,2.0,3.0]) - scale = np.random.choice(random_scale) - if min(h, w) * scale <= min_size: - scale = (min_size + 10) * 1.0 / min(h, w) - img = cv2.resize(img, dsize=None, fx=scale, fy=scale) - new_polys = np.reshape(new_polys * ([img.shape[1], img.shape[0]] * len_max), - (new_polys.shape[0], polys.shape[1] // 2, 2)) - return img, new_polys - - def random_scale_pan(self, img, polys, short_size=736): - - polys, len_max = solve_polys(polys) - h, w = img.shape[0:2] - new_polys = [] - for poly in polys: - poly = np.asarray(poly) - poly = poly / ([w * 1.0, h * 1.0] * len_max) - new_polys.append(poly) - new_polys = np.array(new_polys) - - scale = np.random.choice(np.array([0.7, 0.8, 0.9, 1.0, 1.1, 1.2, 1.3])) - scale = (scale * short_size) / min(h, w) - - aspect = np.random.choice(np.array([0.9, 0.95, 1.0, 1.05, 1.1])) - h_scale = scale * math.sqrt(aspect) - w_scale = scale / math.sqrt(aspect) - - img = scale_aligned(img, h_scale, w_scale) - - new_polys = np.reshape(new_polys * ([img.shape[1], img.shape[0]] * len_max), - (new_polys.shape[0], polys.shape[1] // 2, 2)) - return img, new_polys - - def random_flip(self, img, polys): - if (np.random.rand(1)[0] > 0.5): - aug_bin = aug_img.Sequential([aug_img.Fliplr((1))]) - img = aug_bin.augment_image(img) - new_polys = [] - for poly in polys: - poly = self.augment_poly(aug_bin, img.shape, poly) - poly = np.maximum(poly, 0) - new_polys.append(poly) - else: - new_polys = polys - return img, new_polys - - def random_crop_db(self, img, polys, dont_care): - img, new_polys, new_dotcare = self.random_crop_data.process(img, polys, dont_care) - return img, new_polys, new_dotcare - - def random_crop_pse(self, imgs, ): - h, w = imgs[0].shape[0:2] - th, tw = self.crop_size - if w == tw and h == th: - return imgs - - if random.random() > 3.0 / 8.0 and np.max(imgs[1]) > 0: - tl = np.min(np.where(imgs[1] > 0), axis=1) - self.crop_size - tl[tl < 0] = 0 - br = np.max(np.where(imgs[1] > 0), axis=1) - self.crop_size - br[br < 0] = 0 - br[0] = min(br[0], h - th) - br[1] = min(br[1], w - tw) - - i = random.randint(tl[0], br[0]) - j = random.randint(tl[1], br[1]) - else: - i = random.randint(0, h - th) - j = random.randint(0, w - tw) - - # return i, j, th, tw - for idx in range(len(imgs)): - if len(imgs[idx].shape) == 3: - imgs[idx] = imgs[idx][i:i + th, j:j + tw, :] - else: - imgs[idx] = imgs[idx][i:i + th, j:j + tw] - return imgs - - def random_crop_pan(self,imgs,): - h, w = imgs[0].shape[0:2] - t_w, t_h = self.crop_size - p_w, p_h = self.crop_size - if w == t_w and h == t_h: - return imgs - - t_h = t_h if t_h < h else h - t_w = t_w if t_w < w else w - - if random.random() > 3.0 / 8.0 and np.max(imgs[1]) > 0: - # make sure to crop the text region - tl = np.min(np.where(imgs[1] > 0), axis=1) - (t_h, t_w) - tl[tl < 0] = 0 - br = np.max(np.where(imgs[1] > 0), axis=1) - (t_h, t_w) - br[br < 0] = 0 - br[0] = min(br[0], h - t_h) - br[1] = min(br[1], w - t_w) - - i = random.randint(tl[0], br[0]) if tl[0] < br[0] else 0 - j = random.randint(tl[1], br[1]) if tl[1] < br[1] else 0 - else: - i = random.randint(0, h - t_h) if h - t_h > 0 else 0 - j = random.randint(0, w - t_w) if w - t_w > 0 else 0 - - n_imgs = [] - for idx in range(len(imgs)): - if len(imgs[idx].shape) == 3: - s3_length = int(imgs[idx].shape[-1]) - img = imgs[idx][i:i + t_h, j:j + t_w, :] - img_p = cv2.copyMakeBorder(img, 0, p_h - t_h, 0, p_w - t_w, borderType=cv2.BORDER_CONSTANT, - value=tuple(0 for i in range(s3_length))) - else: - img = imgs[idx][i:i + t_h, j:j + t_w] - img_p = cv2.copyMakeBorder(img, 0, p_h - t_h, 0, p_w - t_w, borderType=cv2.BORDER_CONSTANT, value=(0,)) - n_imgs.append(img_p) - return n_imgs - - diff --git a/ptocr/dataloader/RecLoad/CRNNProcess.py b/ptocr/dataloader/RecLoad/CRNNProcess.py deleted file mode 100644 index 68a370a..0000000 --- a/ptocr/dataloader/RecLoad/CRNNProcess.py +++ /dev/null @@ -1,129 +0,0 @@ -import torch -import torchvision.transforms as transforms -from torch.utils.data import Dataset -from .DataAgument import transform_image_add,transform_img_shape,transform_image_one -from PIL import Image - -def get_img(path,is_gray = False): - img = Image.open(path).convert('RGB') - if(is_gray): - img = img.convert('L') - return img - -class CRNNProcessTrain(Dataset): - def __init__(self, config): - super(CRNNProcessTrain,self).__init__() - with open(config['trainload']['train_file'],'r',encoding='utf-8') as fid: - self.label_list = [] - self.image_list = [] - - for line in fid.readlines(): - line = line.strip('\n').replace('\ufeff','').split('\t') - self.label_list.append(line[1]) - self.image_list.append(line[0]) - - self.config = config - - def __len__(self): - return len(self.label_list) - - def __getitem__(self, index): - img = get_img(self.image_list[index],is_gray=self.config['base']['is_gray']) -# img = transform_image_add(img) - img = transform_image_one(img) - img = transform_img_shape(img,self.config['base']['img_shape']) - img = Image.fromarray(img) - img = transforms.ToTensor()(img) - img.sub_(0.5).div_(0.5) - label = self.label_list[index] - return (img,label) - - -class CRNNProcessTrainLmdb(Dataset): - def __init__(self, config): - self.env = lmdb.open( - config['trainload']['train_file'], - max_readers=1, - readonly=True, - lock=False, - readahead=False, - meminit=False) - - if not self.env: - print('cannot creat lmdb from %s' % (root)) - sys.exit(0) - - with self.env.begin(write=False) as txn: - nSamples = int(txn.get('num-samples'.encode('utf-8'))) - self.nSamples = nSamples - - self.config = config - - def __len__(self): - return self.nSamples - - def __getitem__(self, index): - assert index <= len(self), 'index range error' - index += 1 - with self.env.begin(write=False) as txn: - img_key = 'image-%09d' % index - imgbuf = txn.get(img_key.encode('utf-8')) - - buf = six.BytesIO() - buf.write(imgbuf) - buf.seek(0) - try: - img = Image.open(buf).convert('RGB') - except IOError: - print('Corrupted image for %d' % index) - return self[index + 1] - - label_key = 'label-%09d' % index - label = txn.get(label_key.encode('utf-8')) - - if self.config['base']['is_gray']: - img = img.convert('L') - img = np.array(img) - - img = transform_image_one(img) - img = transform_img_shape(img,self.config['base']['img_shape']) - img = Image.fromarray(img) - img = transforms.ToTensor()(img) - img.sub_(0.5).div_(0.5) - - return (img, label) - -class alignCollate(object): - - def __init__(self,): - pass - def __call__(self, batch): - images, labels = zip(*batch) - images = torch.stack(images,0) - return images,labels - -class CRNNProcessTest(Dataset): - def __init__(self, config): - super(CRNNProcessTest,self).__init__() - with open(config['testload']['test_file'],'r',encoding='utf-8') as fid: - self.label_list = [] - self.image_list = [] - - for line in fid.readlines(): - line = line.strip('\n').replace('\ufeff','').split('\t') - self.label_list.append(line[1]) - self.image_list.append(line[0]) - - self.config = config - - def __len__(self): - return len(self.label_list) - - def __getitem__(self, index): - img = get_img(self.image_list[index],is_gray=self.config['base']['is_gray']) - img = transform_img_shape(img,self.config['base']['img_shape']) - img = Image.fromarray(img) - img = transforms.ToTensor()(img) - img.sub_(0.5).div_(0.5) - label = self.label_list[index] - return (img,label) \ No newline at end of file diff --git a/ptocr/dataloader/RecLoad/DataAgument.py b/ptocr/dataloader/RecLoad/DataAgument.py deleted file mode 100644 index 1e797ce..0000000 --- a/ptocr/dataloader/RecLoad/DataAgument.py +++ /dev/null @@ -1,428 +0,0 @@ -# -*- coding:utf-8 _*- -""" -@author:fxw -@file: DataAgument.py -@time: 2019/06/06 -""" -import cv2 -import numpy as np -from skimage.util import random_noise -import os -from math import floor -from PIL import Image -import numpy as np -import random -import cv2 - - -def Add_Padding(image, top, bottom, left, right, color): - padded_image = cv2.copyMakeBorder(image, top, bottom, - left, right, cv2.BORDER_CONSTANT, value=color) - return padded_image - - -def cvtColor(img): - """ - cvtColor - """ - hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) - delta = 0.001 * random.random() * (1 if random.random() > 0.5000001 else -1) - hsv[:, :, 2] = hsv[:, :, 2] * (1 + delta) - new_img = cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR) - return new_img - - -class Operation(object): - def __init__(self, probability): - self.probability = probability - - def __str__(self): - return self.__class__.__name__ - - def perform_operation(self, images): - raise RuntimeError("Illegal call to base class.") - - -class Distort(Operation): - def __init__(self, probability, grid_width, grid_height, magnitude): - Operation.__init__(self, probability) - self.grid_width = grid_width - self.grid_height = grid_height - self.magnitude = abs(magnitude) - # TODO: Implement non-random magnitude. - self.randomise_magnitude = True - - def perform_operation(self, images): - w, h = images[0].size - - horizontal_tiles = self.grid_width - vertical_tiles = self.grid_height - - width_of_square = int(floor(w / float(horizontal_tiles))) - height_of_square = int(floor(h / float(vertical_tiles))) - - width_of_last_square = w - (width_of_square * (horizontal_tiles - 1)) - height_of_last_square = h - (height_of_square * (vertical_tiles - 1)) - - dimensions = [] - - for vertical_tile in range(vertical_tiles): - for horizontal_tile in range(horizontal_tiles): - if vertical_tile == (vertical_tiles - 1) and horizontal_tile == (horizontal_tiles - 1): - dimensions.append([horizontal_tile * width_of_square, - vertical_tile * height_of_square, - width_of_last_square + (horizontal_tile * width_of_square), - height_of_last_square + (height_of_square * vertical_tile)]) - elif vertical_tile == (vertical_tiles - 1): - dimensions.append([horizontal_tile * width_of_square, - vertical_tile * height_of_square, - width_of_square + (horizontal_tile * width_of_square), - height_of_last_square + (height_of_square * vertical_tile)]) - elif horizontal_tile == (horizontal_tiles - 1): - dimensions.append([horizontal_tile * width_of_square, - vertical_tile * height_of_square, - width_of_last_square + (horizontal_tile * width_of_square), - height_of_square + (height_of_square * vertical_tile)]) - else: - dimensions.append([horizontal_tile * width_of_square, - vertical_tile * height_of_square, - width_of_square + (horizontal_tile * width_of_square), - height_of_square + (height_of_square * vertical_tile)]) - - # For loop that generates polygons could be rewritten, but maybe harder to read? - # polygons = [x1,y1, x1,y2, x2,y2, x2,y1 for x1,y1, x2,y2 in dimensions] - - # last_column = [(horizontal_tiles - 1) + horizontal_tiles * i for i in range(vertical_tiles)] - last_column = [] - for i in range(vertical_tiles): - last_column.append((horizontal_tiles - 1) + horizontal_tiles * i) - - last_row = range((horizontal_tiles * vertical_tiles) - horizontal_tiles, horizontal_tiles * vertical_tiles) - - polygons = [] - for x1, y1, x2, y2 in dimensions: - polygons.append([x1, y1, x1, y2, x2, y2, x2, y1]) - - polygon_indices = [] - for i in range((vertical_tiles * horizontal_tiles) - 1): - if i not in last_row and i not in last_column: - polygon_indices.append([i, i + 1, i + horizontal_tiles, i + 1 + horizontal_tiles]) - - for a, b, c, d in polygon_indices: - dx = random.randint(-self.magnitude, self.magnitude) - dy = random.randint(-self.magnitude, self.magnitude) - - x1, y1, x2, y2, x3, y3, x4, y4 = polygons[a] - polygons[a] = [x1, y1, - x2, y2, - x3 + dx, y3 + dy, - x4, y4] - - x1, y1, x2, y2, x3, y3, x4, y4 = polygons[b] - polygons[b] = [x1, y1, - x2 + dx, y2 + dy, - x3, y3, - x4, y4] - - x1, y1, x2, y2, x3, y3, x4, y4 = polygons[c] - polygons[c] = [x1, y1, - x2, y2, - x3, y3, - x4 + dx, y4 + dy] - - x1, y1, x2, y2, x3, y3, x4, y4 = polygons[d] - polygons[d] = [x1 + dx, y1 + dy, - x2, y2, - x3, y3, - x4, y4] - - generated_mesh = [] - for i in range(len(dimensions)): - generated_mesh.append([dimensions[i], polygons[i]]) - - def do(image): - - return image.transform(image.size, Image.MESH, generated_mesh, resample=Image.BICUBIC) - - augmented_images = [] - - for image in images: - augmented_images.append(do(image)) - - return augmented_images - - -def GetRandomDistortImage(images): - # images need use Image.open() - width = np.random.randint(2, 4) - height = np.random.randint(2, 4) - mag = np.random.randint(2, 5) - d = Distort(probability=1, grid_width=width, grid_height=height, magnitude=mag) - tansformed_images = d.perform_operation(images) - tansformed_images = [np.array(item) for item in tansformed_images] - return tansformed_images - - -def random_dilute(image, sele_value=50, set_value=20, num_ratio=[0.1, 0.2]): - index = np.where(image < (image.min() + sele_value)) - tag = [] - for i in range(len(index[0])): - tag.append([index[0][i], index[1][i]]) - np.random.shuffle(tag) - tag = np.array(tag) - total_num = len(tag[:, 0]) - num = int(total_num * np.random.choice(num_ratio, 1)[0]) - tag1 = tag[:num, 0] - tag2 = tag[:num, 1] - index = (tag1, tag2) - start = image.min() + sele_value + set_value - if (start >= 230): - start = start - 50 - ra_value = min(np.random.randint(min(225,start), 230), 230) - image[index] = ra_value - return image - - -def RandomAddLine(image): - a = np.random.randint(0, 15) - line_color = (a, a, a) - thickness = np.random.randint(1, 3) - if (np.random.randint(0, 10) > 7): - tag1 = np.random.randint(1, 10) - tag2 = np.random.randint(1, 5) - image = cv2.line(image, - (tag1, image.shape[0] - tag2), - (image.shape[1] - tag1, image.shape[0] - tag2), - color=line_color, - thickness=thickness, - lineType=cv2.LINE_AA) - if (np.random.randint(0, 10) > 7): - tag1 = np.random.randint(1, 10) - tag2 = np.random.randint(1, 5) - image = cv2.line(image, - (tag1, tag2), - (image.shape[1] - tag1, tag2), - color=line_color, - thickness=thickness, - lineType=cv2.LINE_AA) - if (np.random.randint(0, 10) > 7): - tag1 = np.random.randint(1, 5) - tag2 = np.random.randint(0, 4) - image = cv2.line(image, - (tag1, tag2), - (tag1, image.shape[0] - tag2), - color=line_color, - thickness=thickness, - lineType=cv2.LINE_AA) - if (np.random.randint(0, 10) > 7): - tag1 = np.random.randint(1, 5) - tag2 = np.random.randint(0, 4) - image = cv2.line(image, - (image.shape[1] - tag1, tag2), - (image.shape[1] - tag1, image.shape[0] - tag2), - color=line_color, - thickness=thickness, - lineType=cv2.LINE_AA) - return image - - -class DataAugmentatonMore(): - def __init__(self, image): - self.image = image - - def motion_blur(self, degree=5, angle=180): - # degree建议:2 - 5 - # angle建议:0 - 360 - # 都为整数 - image = np.array(self.image) - # 这里生成任意角度的运动模糊kernel的矩阵, degree越大,模糊程度越高 - M = cv2.getRotationMatrix2D((degree / 2, degree / 2), angle, 1) - motion_blur_kernel = np.diag(np.ones(degree)) - motion_blur_kernel = cv2.warpAffine(motion_blur_kernel, M, (degree, degree)) - - motion_blur_kernel = motion_blur_kernel / degree - blurred = cv2.filter2D(image, -1, motion_blur_kernel) - - # convert to uint8 - cv2.normalize(blurred, blurred, 0, 255, cv2.NORM_MINMAX) - blurred_image = np.array(blurred, dtype=np.uint8) - return blurred_image - - def gaussian_blur(self, k_size=7, sigmaX=0, sigmaY=0): - # k_size越大越模糊,且为奇数,建议[1,3,5,7,9] - blurred_image = cv2.GaussianBlur(self.image, ksize=(k_size, k_size), - sigmaX=sigmaX, sigmaY=sigmaY) - return blurred_image - - def Contrast_and_Brightness(self, alpha, beta=0): - # alpha:调节亮度,越小越暗,越大越亮,等于1为原始亮度 - # 建议使用0.6-1.3 - blank = np.zeros(self.image.shape, self.image.dtype) - # dst = alpha * img + beta * blank - brighted_image = cv2.addWeighted(self.image, alpha, blank, 1 - alpha, beta) - return brighted_image - - def Add_Padding(self, top, bottom, left, right, color): - padded_image = cv2.copyMakeBorder(self.image, top, bottom, - left, right, cv2.BORDER_CONSTANT, value=color) - return padded_image - - def Add_gaussian_noise(self, mode='gaussian'): - ##mode : 'gaussian' ,'salt' , 'pepper ' - noise_image = random_noise(self.image, mode=mode) - return noise_image - - def Perspective(self, ratio=30, type='top'): - h, w = self.image.shape[:2] - pts1 = np.float32([[0, 0], [0, h - 1], [w - 1, h - 1], [w - 1, 0]]) - if type == 'top': - pts2 = np.float32([[ratio, ratio], [0, h], [w, h], [w - ratio, ratio]]) - M = cv2.getPerspectiveTransform(pts1, pts2) - dst = cv2.warpPerspective(self.image, M, (w, h)) - dst = dst[ratio:, :] - elif (type == 'left'): - pts2 = np.float32([[ratio, ratio // 2], [ratio, h - ratio // 2], [w, h], [w, 0]]) - M = cv2.getPerspectiveTransform(pts1, pts2) - dst = cv2.warpPerspective(self.image, M, (w, h)) - dst = dst[:, ratio:] - elif (type == 'right'): - pts2 = np.float32([[0, 0], [0, h], [w - ratio, h - ratio // 2], [w - ratio, ratio // 2]]) - M = cv2.getPerspectiveTransform(pts1, pts2) - dst = cv2.warpPerspective(self.image, M, (w - ratio, h)) - else: - pts2 = np.float32([[0, 0], [0 + ratio, h - ratio], [w - ratio, h - ratio], [w, 0]]) - M = cv2.getPerspectiveTransform(pts1, pts2) - dst = cv2.warpPerspective(self.image, M, (w, h - ratio)) - return dst - - def resize_blur(self, ratio): - image = cv2.resize(self.image, dsize=None, fy=ratio, fx=ratio) - new_width = np.round((self.image.shape[0] / image.shape[0]) * image.shape[1]).astype(np.int) - image = cv2.resize(image, dsize=(new_width, self.image.shape[0])) - return image - - -def transform_img_shape(img, img_shape): - img = np.array(img) - H, W = img_shape - h, w = img.shape[:2] - new_w = int((float(H)/ h) * w) - if (new_w > W): - img = cv2.resize(img, (W, H)) - else: - img = cv2.resize(img, (new_w, H)) - img = Add_Padding(img, 0, 0, 0, W - new_w, color=(0, 0, 0)) - return img - -def random_crop(image): - h, w= image.shape[:2] - top_min = 1 - top_max = h//5 - top_crop = int(random.randint(top_min, top_max)) - top_crop = min(top_crop, h - 1) - crop_img = image.copy() - ratio = random.randint(0, 1) - if ratio: - crop_img = crop_img[top_crop:h, :] - else: - crop_img = crop_img[0:h - top_crop, :] - return crop_img - -def transform_image_one(image): - image = np.array(image) - if (np.random.choice([True, False], 1)[0]): - dataAu = DataAugmentatonMore(image) - index = np.random.randint(0,10) - if (index == 0): - degree = np.random.randint(2, 6) - angle = np.random.randint(0, 360) - image = dataAu.motion_blur(degree, angle) - elif (index == 1): - id = np.random.randint(0, 4) - k_size = [1, 3, 5, 7,9] - image = dataAu.gaussian_blur(k_size[id]) - elif (index == 2): - alpha = np.random.uniform(0.6, 1.3) - image = dataAu.Contrast_and_Brightness(alpha) - elif (index == 3): - types = ['top', 'botttom'] - id = np.random.randint(0, 2) - ratio = np.random.randint(0, image.shape[0]//3) - image = dataAu.Perspective(ratio, types[id]) - elif (index == 4): - ratio = np.random.uniform(0.35, 0.5) - image = dataAu.resize_blur(ratio) - elif(index==5): - image = random_dilute(image) - elif(index==6): - image = Image.fromarray(image) - image = GetRandomDistortImage([image])[0] - elif(index==7): - image = Image.fromarray(image).convert('RGB') - image = np.array(image) - image = cvtColor(image) - image = Image.fromarray(image).convert('L') - image = np.array(image) - elif(index==8): - image = RandomAddLine(image) - elif(index==9): - image = random_crop(image) - del dataAu - - return image - - -def transform_image_add(image): - image = np.array(image) - is_transform = np.random.choice([True, False], 1)[0] - # image = RandomAddLine(image) - if (is_transform): - dataAu = DataAugmentatonMore(image) - is_transform = np.random.choice([True, False], 1)[0] - if (is_transform): - image = dataAu.image - image = random_dilute(image) - dataAu.image = image - is_transform = np.random.choice([True, False], 1)[0] - if (is_transform): - image = dataAu.image - image = Image.fromarray(image) - image = GetRandomDistortImage([image])[0] - dataAu.image = image - is_transform = np.random.choice([True, False], 1)[0] - if (is_transform): - degree = np.random.randint(2, 8) - angle = np.random.randint(0, 360) - image = dataAu.motion_blur(degree, angle) - dataAu.image = image - is_transform = np.random.choice([True, False], 1)[0] - if (is_transform): - id = np.random.randint(0, 3) - k_size = [3, 5, 7] - image = dataAu.gaussian_blur(k_size[id]) - dataAu.image = image - is_transform = np.random.choice([True, False], 1)[0] - if (is_transform): - alpha = np.random.uniform(0.6, 1.3) - image = dataAu.Contrast_and_Brightness(alpha) - dataAu.image = image - is_transform = np.random.choice([True, False], 1)[0] - if (is_transform): - types = ['top', 'botttom'] - id = np.random.randint(0, 1) - ratio = np.random.randint(0, 5) - image = dataAu.Perspective(ratio, types[id]) - dataAu.image = image - is_transform = np.random.choice([True, False], 1)[0] - if (is_transform and image.shape[0] > 20): - ratio = np.random.uniform(0.45, 0.7) - image = dataAu.resize_blur(ratio) - dataAu.image = image - del dataAu - return image - - - - - - diff --git a/ptocr/dataloader/RecLoad/__init__.py b/ptocr/dataloader/RecLoad/__init__.py deleted file mode 100644 index 8529416..0000000 --- a/ptocr/dataloader/RecLoad/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -#-*- coding:utf-8 _*- -""" -@author:fxw -@file: __init__.py.py -@time: 2020/08/11 -""" diff --git a/ptocr/dataloader/__init__.py b/ptocr/dataloader/__init__.py deleted file mode 100644 index 8529416..0000000 --- a/ptocr/dataloader/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -#-*- coding:utf-8 _*- -""" -@author:fxw -@file: __init__.py.py -@time: 2020/08/11 -""" diff --git a/ptocr/model/CommonFunction.py b/ptocr/model/CommonFunction.py deleted file mode 100644 index 373b502..0000000 --- a/ptocr/model/CommonFunction.py +++ /dev/null @@ -1,77 +0,0 @@ -#-*- coding:utf-8 _*- -""" -@author:fxw -@file: CommonFunction.py -@time: 2020/08/07 -""" -import torch.nn.functional as F -import torch.nn as nn - -class DeConvBnRelu(nn.Module): - def __init__(self,in_channels,out_channels,kernel_size=4,stride=2,with_relu=False,padding=1,bias=False): - """ - :param in_channels: - :param out_channels: - :param kernel_size: - :param stride: - :param padding: - :param bias: - """ - super(DeConvBnRelu,self).__init__() - self.conv = nn.ConvTranspose2d(in_channels, out_channels, kernel_size, stride, padding=padding,bias=bias) # Reduce channels - self.bn = nn.BatchNorm2d(out_channels) - self.relu = nn.ReLU(inplace=True) - self.with_relu = with_relu - - def forward(self, x): - x = self.conv(x) - x = self.bn(x) - if self.with_relu: - x = self.relu(x) - return x - -class ConvBnRelu(nn.Module): - def __init__(self,in_channels,out_channels,kernel_size,stride,padding,with_relu=True,bias=False): - """ - :param in_channels: - :param out_channels: - :param kernel_size: - :param stride: - :param padding: - :param bias: - """ - super(ConvBnRelu,self).__init__() - self.conv = nn.Conv2d(in_channels, out_channels, kernel_size, stride, padding,bias=bias) - self.bn = nn.BatchNorm2d(out_channels) - self.relu = nn.ReLU(inplace=True) - self.with_relu = with_relu - def forward(self, x): - x = self.conv(x) - x = self.bn(x) - if self.with_relu: - x = self.relu(x) - return x - -class DWBlock(nn.Module): - def __init__(self,in_channels,out_channels,kernel_size,stride,bias=False): - super(DWBlock,self).__init__() - self.dw_conv = nn.Conv2d(in_channels,out_channels,kernel_size,stride,padding=kernel_size//2,groups=out_channels,bias=bias) - self.point_conv = nn.Conv2d(out_channels,out_channels,kernel_size=1,stride=1,padding=0,bias=bias) - self.point_bn = nn.BatchNorm2d(out_channels) - self.point_relu = nn.ReLU() - - def forward(self, x): - x = self.dw_conv(x) - x = self.point_relu(self.point_bn(self.point_conv(x))) - return x - - -def upsample(x, y, scale=1): - _, _, H, W = y.size() - # return F.upsample(x, size=(H // scale, W // scale), mode='nearest') - return F.interpolate(x, size=(H // scale, W // scale), mode='nearest') - -def upsample_add(x, y): - _, _, H, W = y.size() - # return F.upsample(x, size=(H, W), mode='nearest') + y - return F.interpolate(x, size=(H, W), mode='nearest') + y \ No newline at end of file diff --git a/ptocr/model/CommonFunction_Q.py b/ptocr/model/CommonFunction_Q.py deleted file mode 100644 index 43d89e8..0000000 --- a/ptocr/model/CommonFunction_Q.py +++ /dev/null @@ -1,48 +0,0 @@ -#-*- coding:utf-8 _*- -""" -@author:fxw -@file: CommonFunction_Q.py -@time: 2020/11/02 -""" -import torch.nn as nn - -class ConvBnRelu(nn.Module): - def __init__(self,in_channels,out_channels,kernel_size,stride,padding,groups,bias=False): - """ - :param in_channels: - :param out_channels: - :param kernel_size: - :param stride: - :param padding: - :param bias: - """ - super(ConvBnRelu,self).__init__() - self.conv = nn.Conv2d(in_channels, out_channels, kernel_size, stride, padding,groups=groups,bias=bias) - self.bn = nn.BatchNorm2d(out_channels) - self.relu = nn.ReLU(inplace=False) - - def forward(self, x): - x = self.conv(x) - x = self.bn(x) - x = self.relu(x) - return x - - -class ConvBn(nn.Module): - def __init__(self,in_channels,out_channels,kernel_size,stride,padding,groups,bias=False): - """ - :param in_channels: - :param out_channels: - :param kernel_size: - :param stride: - :param padding: - :param bias: - """ - super(ConvBn,self).__init__() - self.conv = nn.Conv2d(in_channels, out_channels, kernel_size, stride, padding,groups=groups,bias=bias) - self.bn = nn.BatchNorm2d(out_channels) - - def forward(self, x): - x = self.conv(x) - x = self.bn(x) - return x \ No newline at end of file diff --git a/ptocr/model/__init__.py b/ptocr/model/__init__.py deleted file mode 100644 index a441246..0000000 --- a/ptocr/model/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -#-*- coding:utf-8 _*- -""" -@author:fxw -@file: __init__.py.py -@time: 2020/08/07 -""" -from ..utils.util_function import create_module \ No newline at end of file diff --git a/ptocr/model/architectures/__init__.py b/ptocr/model/architectures/__init__.py deleted file mode 100644 index 35de2ad..0000000 --- a/ptocr/model/architectures/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -#-*- coding:utf-8 _*- -""" -@author:fxw -@file: __init__.py.py -@time: 2020/08/07 -""" diff --git a/ptocr/model/architectures/__pycache__/__init__.cpython-35.pyc b/ptocr/model/architectures/__pycache__/__init__.cpython-35.pyc deleted file mode 100644 index d7e0ddd42e457c8b1b60153f40062bcd5c9fe045..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 233 zcmYjMu?oU45KSFa01{Wt!2l2S~ zxO+Svcim2hE}wna6G9${UpY7h2t$!#LJr6d*}9F}mFdndZ9rgGvW6DWFalU z;9OzTz{(uekvH;&-gM|q=*o5K7C@{xz@6v@rhe(QUHEKggf=XRisQAs2+6{Vr;*M> utxkelgS%Clr|ZRrovq@UiieK#(_cM?(q# diff --git a/ptocr/model/architectures/__pycache__/__init__.cpython-36.pyc b/ptocr/model/architectures/__pycache__/__init__.cpython-36.pyc deleted file mode 100644 index 31b44817f27e6d223f951457272a0e5b3847ed0a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 187 zcmXr!<>fk+sv94{z`*brh~a<{$Z`PUVh$jY!Vtxf!Whh;$y8;^<&aofl969zl~z&C z<&c({lWL_9AD@|*SrQ+wS5OH=Tn;6fxj<1P10w@{0}Fivb1pwk##`+1@hSPq@$oAe zikN_Af{9=1din(=`N>84x%nxnIr@o3$r+g?smUd!MXAO5NXFjcumP$l%}KRm133za F834G1Fs=Xq diff --git a/ptocr/model/architectures/__pycache__/det_model.cpython-35.pyc b/ptocr/model/architectures/__pycache__/det_model.cpython-35.pyc deleted file mode 100644 index 157039b011ec4b7b7ec055d16ffda3dcd6666dcb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3746 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 diff --git a/ptocr/model/architectures/__pycache__/rec_model.cpython-36.pyc b/ptocr/model/architectures/__pycache__/rec_model.cpython-36.pyc deleted file mode 100644 index 608dd163e64bbfe89f0cc0fdaed616ee1b2670f4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1737 zcmb7EOOG2x5bmD0C-$xr63FrhUc@1Du~*3`+q=;TrS7MvjC zw+>)JO)r-s(*ifFN|EE-zPUR^eF};o;4Vp>H)LYnzKj~DFG=dXAs$J6aL6~&Gm(FX zXXj`i8~!^WXifbJpoyj%vUTRpmQb<*Ye$4TM`S~ot3Bwu(D#nWmhR?!m}^!AFzUcM z->wV)u`X)Yby_WMwHvM0ZM9yj9e}OFUqY}QZ~`~!kBxVpDPcNUIjv=(XA2XZXZgi> zT?rFSR#mQxx=Q1v6k29QCAbL-#ilZQWxSclxQSLuu)MB*GCkBXtwn``^165U94tm;5zv-3>nGZRg9qg|!C$uY7aL35Y{Lf418~C0A#sO&x+ywCc j!c%VPW;b4)(rC6iqR*Na|8uMi8$^#rP#|6R*6qIm%5ru& diff --git a/ptocr/model/architectures/det_model.py b/ptocr/model/architectures/det_model.py deleted file mode 100644 index 0f91513..0000000 --- a/ptocr/model/architectures/det_model.py +++ /dev/null @@ -1,120 +0,0 @@ -# -*- coding:utf-8 _*- -""" -@author:fxw -@file: det_model.py -@time: 2020/08/07 -""" -import torch -import torch.nn as nn -from .. import create_module - - -class DetModel(nn.Module): - def __init__(self, config): - super(DetModel, self).__init__() - self.algorithm = config['base']['algorithm'] - self.backbone = create_module(config['backbone']['function'])(config['base']['pretrained']) - if(self.algorithm == 'SAST'): - self.head = create_module(config['head']['function'])(config['base']['with_attention']) - else: - self.head = create_module(config['head']['function']) \ - (config['base']['in_channels'], - config['base']['inner_channels']) - - if (config['base']['algorithm']) == 'DB': - self.seg_out = create_module(config['segout']['function'])(config['base']['inner_channels'], - config['base']['k'], - config['base']['adaptive']) - elif (config['base']['algorithm']) == 'PAN': - self.seg_out = create_module(config['segout']['function'])(config['base']['inner_channels'], - config['base']['classes']) - - elif (config['base']['algorithm']) == 'PSE': - self.seg_out = create_module(config['segout']['function'])(config['base']['inner_channels'], - config['base']['classes']) - elif (config['base']['algorithm']) == 'SAST': - self.seg_out = create_module(config['segout']['function'])() - else: - assert True == False, ('not support this algorithm !!!') - - def forward(self, data): - if self.training: - if self.algorithm == "DB": - img, gt, gt_mask, thresh_map, thresh_mask = data - if torch.cuda.is_available(): - img, gt, gt_mask, thresh_map, thresh_mask = \ - img.cuda(), gt.cuda(), gt_mask.cuda(), thresh_map.cuda(), thresh_mask.cuda() - gt_batch = dict(gt=gt) - gt_batch['mask'] = gt_mask - gt_batch['thresh_map'] = thresh_map - gt_batch['thresh_mask'] = thresh_mask - - elif self.algorithm == "PSE": - img, gt_text, gt_kernels, train_mask = data - if torch.cuda.is_available(): - img, gt_text, gt_kernels, train_mask = \ - img.cuda(), gt_text.cuda(), gt_kernels.cuda(), train_mask.cuda() - gt_batch = dict(gt_text=gt_text) - gt_batch['gt_kernel'] = gt_kernels - gt_batch['train_mask'] = train_mask - - elif self.algorithm == "PAN": - img, gt_text, gt_text_key, gt_kernel, gt_kernel_key, train_mask = data - if torch.cuda.is_available(): - img, gt_text, gt_text_key, gt_kernel, gt_kernel_key, train_mask = \ - img.cuda(), gt_text.cuda(), gt_text_key.cuda(), gt_kernel.cuda(), gt_kernel_key.cuda(), train_mask.cuda() - - gt_batch = dict(gt_text=gt_text) - gt_batch['gt_text_key'] = gt_text_key - gt_batch['gt_kernel'] = gt_kernel - gt_batch['gt_kernel_key'] = gt_kernel_key - gt_batch['train_mask'] = train_mask - - elif self.algorithm == "SAST": - img, score_map, border_map, training_mask, tvo_map, tco_map = data - if torch.cuda.is_available(): - img, score_map, border_map, training_mask, tvo_map, tco_map = \ - img.cuda(), score_map.cuda(), border_map.cuda(), training_mask.cuda(), tvo_map.cuda(), tco_map.cuda() - - gt_batch = dict(input_score=score_map) - gt_batch['input_border'] = border_map - gt_batch['input_mask'] = training_mask - gt_batch['input_tvo'] = tvo_map - gt_batch['input_tco'] = tco_map - else: - img = data - - x = self.backbone(img) - x = self.head(x) - x = self.seg_out(x, img) - - if self.training: - return x, gt_batch - return x - - -class DetLoss(nn.Module): - def __init__(self, config): - super(DetLoss, self).__init__() - self.algorithm = config['base']['algorithm'] - if (config['base']['algorithm']) == 'DB': - self.loss = create_module(config['loss']['function'])(config['loss']['l1_scale'], - config['loss']['bce_scale']) - elif (config['base']['algorithm']) == 'PAN': - self.loss = create_module(config['loss']['function'])(config['loss']['kernel_rate'], - config['loss']['agg_dis_rate']) - elif (config['base']['algorithm']) == 'PSE': - self.loss = create_module(config['loss']['function'])(config['loss']['text_tatio']) - - elif (config['base']['algorithm']) == 'SAST': - self.loss = create_module(config['loss']['function'])(config['loss']['tvo_lw'], - config['loss']['tco_lw'], - config['loss']['score_lw'], - config['loss']['border_lw'] - ) - else: - assert True == False, ('not support this algorithm !!!') - - def forward(self, pre_batch, gt_batch): - return self.loss(pre_batch, gt_batch) - diff --git a/ptocr/model/architectures/det_model_q.py b/ptocr/model/architectures/det_model_q.py deleted file mode 100644 index a050e6e..0000000 --- a/ptocr/model/architectures/det_model_q.py +++ /dev/null @@ -1,85 +0,0 @@ -# -*- coding:utf-8 _*- -""" -@author:fxw -@file: det_model.py -@time: 2020/08/07 -""" -import torch -import torch.nn as nn -from .. import create_module -from torch.quantization import QuantStub, DeQuantStub - -class DetModel(nn.Module): - def __init__(self, config): - super(DetModel, self).__init__() - self.algorithm = config['base']['algorithm'] - self.backbone = create_module(config['backbone']['function'])(config['base']['pretrained']) - - self.head = create_module(config['head']['function']) \ - (config['base']['in_channels'], - config['base']['inner_channels']) - - if (config['base']['algorithm']) == 'DB': - self.seg_out = create_module(config['segout']['function'])(config['base']['inner_channels']) - - self.quant = QuantStub() - self.dequant = DeQuantStub() - self.k = config['base']['k'] - - def step_function(self, x, y): - return torch.reciprocal(1 + torch.exp(-self.k * (x - y))) - - def forward(self, data): - if self.training: - if self.algorithm == "DB": - img, gt, gt_mask, thresh_map, thresh_mask = data - if torch.cuda.is_available(): - img, gt, gt_mask, thresh_map, thresh_mask = \ - img.cuda(), gt.cuda(), gt_mask.cuda(), thresh_map.cuda(), thresh_mask.cuda() - gt_batch = dict(gt=gt) - gt_batch['mask'] = gt_mask - gt_batch['thresh_map'] = thresh_map - gt_batch['thresh_mask'] = thresh_mask - else: - img = data - img = self.quant(img) - f_map= self.backbone(img) - head_map = self.head(f_map[-1],f_map[-2],f_map[-3],f_map[-4]) - thresh,binary = self.seg_out(head_map) - thresh = self.dequant(thresh) - binary = self.dequant(binary) - thresh_binary = self.step_function(binary,thresh) - out = {} - out['binary'] = binary - out['thresh'] = thresh - out['thresh_binary'] = thresh_binary - if self.training: - return out, gt_batch - return out - - -class DetLoss(nn.Module): - def __init__(self, config): - super(DetLoss, self).__init__() - self.algorithm = config['base']['algorithm'] - if (config['base']['algorithm']) == 'DB': - self.loss = create_module(config['loss']['function'])(config['loss']['l1_scale'], - config['loss']['bce_scale']) - elif (config['base']['algorithm']) == 'PAN': - self.loss = create_module(config['loss']['function'])(config['loss']['kernel_rate'], - config['loss']['agg_dis_rate']) - elif (config['base']['algorithm']) == 'PSE': - self.loss = create_module(config['loss']['function'])(config['loss']['text_tatio']) - - elif (config['base']['algorithm']) == 'SAST': - self.loss = create_module(config['loss']['function'])(config['loss']['tvo_lw'], - config['loss']['tco_lw'], - config['loss']['score_lw'], - config['loss']['border_lw'] - ) - else: - assert True == False, ('not support this algorithm !!!') - - def forward(self, pre_batch, gt_batch): - return self.loss(pre_batch, gt_batch) - diff --git a/ptocr/model/architectures/rec_model.py b/ptocr/model/architectures/rec_model.py deleted file mode 100644 index 9df17b2..0000000 --- a/ptocr/model/architectures/rec_model.py +++ /dev/null @@ -1,43 +0,0 @@ -# -*- coding:utf-8 _*- -""" -@author:fxw -@file: det_model.py -@time: 2020/08/07 -""" -import torch -import torch.nn as nn -from .. import create_module - - -class RecModel(nn.Module): - def __init__(self, config): - super(RecModel, self).__init__() - self.algorithm = config['base']['algorithm'] - self.backbone = create_module(config['backbone']['function'])(config['base']['pretrained'],config['base']['is_gray']) - self.head = create_module(config['head']['function'])( - use_conv=config['base']['use_conv'], - use_attention=config['base']['use_attention'], - use_lstm=config['base']['use_lstm'], - lstm_num=config['base']['lstm_num'], - inchannel=config['base']['inchannel'], - hiddenchannel=config['base']['hiddenchannel'], - classes=config['base']['classes']) - - def forward(self, img): - x = self.backbone(img) - x = self.head(x) - return x - - -class RecLoss(nn.Module): - def __init__(self, config): - super(RecLoss, self).__init__() - self.algorithm = config['base']['algorithm'] - if (config['base']['algorithm']) == 'CRNN': - self.loss = create_module(config['loss']['function'])(config) - else: - assert True == False, ('not support this algorithm !!!') - - def forward(self, pre_batch, gt_batch): - return self.loss(pre_batch, gt_batch) - diff --git a/ptocr/model/backbone/__init__.py b/ptocr/model/backbone/__init__.py deleted file mode 100644 index 35de2ad..0000000 --- a/ptocr/model/backbone/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -#-*- coding:utf-8 _*- -""" -@author:fxw -@file: __init__.py.py -@time: 2020/08/07 -""" diff --git a/ptocr/model/backbone/__pycache__/__init__.cpython-35.pyc b/ptocr/model/backbone/__pycache__/__init__.cpython-35.pyc deleted file mode 100644 index 8bfa25d9e351fe9f178fa6ca79c55d3c14ff8195..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 228 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 diff --git a/ptocr/model/backbone/__pycache__/det_resnet.cpython-35.pyc b/ptocr/model/backbone/__pycache__/det_resnet.cpython-35.pyc deleted file mode 100644 index f93a5b2004929fd653b21543fcb0d624e174107d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9756 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 T@}tQgH|mWMJoU!8M*V*PkcM-~ diff --git a/ptocr/model/backbone/__pycache__/det_resnet.cpython-36.pyc b/ptocr/model/backbone/__pycache__/det_resnet.cpython-36.pyc deleted file mode 100644 index 58ed83d48f9dc631ded50352c54e190a24bbaefe..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9459 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 diff --git a/ptocr/model/backbone/__pycache__/det_resnet_3_3.cpython-36.pyc b/ptocr/model/backbone/__pycache__/det_resnet_3_3.cpython-36.pyc deleted file mode 100644 index bf5e35e5343645e02b1da81838bec537abd783e2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6798 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 F{~L*!3B&*Z diff --git a/ptocr/model/backbone/__pycache__/det_resnet_sast_3_3.cpython-36.pyc b/ptocr/model/backbone/__pycache__/det_resnet_sast_3_3.cpython-36.pyc deleted file mode 100644 index 705ca8faf647c5df50fd88634c4eab6a8efe8a1f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6900 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 DF{qel diff --git a/ptocr/model/backbone/det_densenet.py b/ptocr/model/backbone/det_densenet.py deleted file mode 100644 index 4bf2353..0000000 --- a/ptocr/model/backbone/det_densenet.py +++ /dev/null @@ -1,242 +0,0 @@ -#-*- coding:utf-8 _*- -""" -@author:fxw -@file: det_densenet.py -@time: 2020/08/07 -""" -import re -import torch -import torch.nn as nn -import torch.nn.functional as F -import torch.utils.model_zoo as model_zoo -from collections import OrderedDict - -__all__ = ['DenseNet', 'densenet121', 'densenet169', 'densenet201', 'densenet161'] - - -model_urls = { - 'densenet121': 'https://download.pytorch.org/models/densenet121-a639ec97.pth', - 'densenet169': 'https://download.pytorch.org/models/densenet169-b2777c0a.pth', - 'densenet201': 'https://download.pytorch.org/models/densenet201-c1103571.pth', - 'densenet161': 'https://download.pytorch.org/models/densenet161-8d451a50.pth', -} - - -class _DenseLayer(nn.Sequential): - def __init__(self, num_input_features, growth_rate, bn_size, drop_rate): - super(_DenseLayer, self).__init__() - self.add_module('norm1', nn.BatchNorm2d(num_input_features)), - self.add_module('relu1', nn.ReLU(inplace=True)), - self.add_module('conv1', nn.Conv2d(num_input_features, bn_size * - growth_rate, kernel_size=1, stride=1, bias=False)), - self.add_module('norm2', nn.BatchNorm2d(bn_size * growth_rate)), - self.add_module('relu2', nn.ReLU(inplace=True)), - self.add_module('conv2', nn.Conv2d(bn_size * growth_rate, growth_rate, - kernel_size=3, stride=1, padding=1, bias=False)), - self.drop_rate = drop_rate - - def forward(self, x): - new_features = super(_DenseLayer, self).forward(x) - if self.drop_rate > 0: - new_features = F.dropout(new_features, p=self.drop_rate, training=self.training) - return torch.cat([x, new_features], 1) - - -class _DenseBlock(nn.Sequential): - def __init__(self, num_layers, num_input_features, bn_size, growth_rate, drop_rate): - super(_DenseBlock, self).__init__() - for i in range(num_layers): - layer = _DenseLayer(num_input_features + i * growth_rate, growth_rate, bn_size, drop_rate) - self.add_module('denselayer%d' % (i + 1), layer) - - -class _Transition(nn.Sequential): - def __init__(self, num_input_features, num_output_features): - super(_Transition, self).__init__() - self.add_module('norm', nn.BatchNorm2d(num_input_features)) - self.add_module('relu', nn.ReLU(inplace=True)) - self.add_module('conv', nn.Conv2d(num_input_features, num_output_features, - kernel_size=1, stride=1, bias=False)) - self.add_module('pool', nn.AvgPool2d(kernel_size=2, stride=2)) - - -class DenseNet(nn.Module): - r"""Densenet-BC model class, based on - `"Densely Connected Convolutional Networks" `_ - - Args: - growth_rate (int) - how many filters to add each layer (`k` in paper) - block_config (list of 4 ints) - how many layers in each pooling block - num_init_features (int) - the number of filters to learn in the first convolution layer - bn_size (int) - multiplicative factor for number of bottle neck layers - (i.e. bn_size * k features in the bottleneck layer) - drop_rate (float) - dropout rate after each dense layer - num_classes (int) - number of classification classes - """ - - def __init__(self, growth_rate=32, block_config=(6, 12, 24, 16), - num_init_features=64, bn_size=4, drop_rate=0, num_classes=1000): - - super(DenseNet, self).__init__() - - # First convolution - self.features = nn.Sequential(OrderedDict([ - ('conv0', nn.Conv2d(3, num_init_features, kernel_size=7, stride=2, padding=3, bias=False)), - ('norm0', nn.BatchNorm2d(num_init_features)), - ('relu0', nn.ReLU(inplace=True)), - ('pool0', nn.MaxPool2d(kernel_size=3, stride=2, padding=1)), - ])) - - # Each denseblock - num_features = num_init_features - for i, num_layers in enumerate(block_config): - block = _DenseBlock(num_layers=num_layers, num_input_features=num_features, - bn_size=bn_size, growth_rate=growth_rate, drop_rate=drop_rate) - self.features.add_module('denseblock%d' % (i + 1), block) - num_features = num_features + num_layers * growth_rate - if i != len(block_config) - 1: - trans = _Transition(num_input_features=num_features, num_output_features=num_features // 2) - self.features.add_module('transition%d' % (i + 1), trans) - num_features = num_features // 2 - - # Final batch norm - self.features.add_module('norm5', nn.BatchNorm2d(num_features)) - - # Linear layer - self.classifier = nn.Linear(num_features, num_classes) - - # Official init from torch repo. - for m in self.modules(): - if isinstance(m, nn.Conv2d): - nn.init.kaiming_normal_(m.weight) - elif isinstance(m, nn.BatchNorm2d): - nn.init.constant_(m.weight, 1) - nn.init.constant_(m.bias, 0) - elif isinstance(m, nn.Linear): - nn.init.constant_(m.bias, 0) - - # def forward(self, x): - # features = self.features(x) - # out = F.relu(features, inplace=True) - # out = F.adaptive_avg_pool2d(out, (1, 1)).view(features.size(0), -1) - # out = self.classifier(out) - # return out - - def forward(self, x): - out_list = [] - for layer in self.features.children(): - x = layer(x) - if isinstance(layer, _DenseBlock): - out_list.append(x) - p1, p2, p3, p4 = out_list - return p1, p2, p3, p4 - - - -def densenet121(pretrained=False, **kwargs): - r"""Densenet-121 model from - `"Densely Connected Convolutional Networks" `_ - - Args: - pretrained (bool): If True, returns a model pre-trained on ImageNet - """ - model = DenseNet(num_init_features=64, growth_rate=32, block_config=(6, 12, 24, 16), - **kwargs) - if pretrained: - # '.'s are no longer allowed in module names, but pervious _DenseLayer - # has keys 'norm.1', 'relu.1', 'conv.1', 'norm.2', 'relu.2', 'conv.2'. - # They are also in the checkpoints in model_urls. This pattern is used - # to find such keys. - pattern = re.compile( - r'^(.*denselayer\d+\.(?:norm|relu|conv))\.((?:[12])\.(?:weight|bias|running_mean|running_var))$') - state_dict = model_zoo.load_url(model_urls['densenet121']) - for key in list(state_dict.keys()): - res = pattern.match(key) - if res: - new_key = res.group(1) + res.group(2) - state_dict[new_key] = state_dict[key] - del state_dict[key] - model.load_state_dict(state_dict) - return model - - -def densenet169(pretrained=False, **kwargs): - r"""Densenet-169 model from - `"Densely Connected Convolutional Networks" `_ - - Args: - pretrained (bool): If True, returns a model pre-trained on ImageNet - """ - model = DenseNet(num_init_features=64, growth_rate=32, block_config=(6, 12, 32, 32), - **kwargs) - if pretrained: - # '.'s are no longer allowed in module names, but pervious _DenseLayer - # has keys 'norm.1', 'relu.1', 'conv.1', 'norm.2', 'relu.2', 'conv.2'. - # They are also in the checkpoints in model_urls. This pattern is used - # to find such keys. - pattern = re.compile( - r'^(.*denselayer\d+\.(?:norm|relu|conv))\.((?:[12])\.(?:weight|bias|running_mean|running_var))$') - state_dict = model_zoo.load_url(model_urls['densenet169']) - for key in list(state_dict.keys()): - res = pattern.match(key) - if res: - new_key = res.group(1) + res.group(2) - state_dict[new_key] = state_dict[key] - del state_dict[key] - model.load_state_dict(state_dict) - return model - - -def densenet201(pretrained=False, **kwargs): - r"""Densenet-201 model from - `"Densely Connected Convolutional Networks" `_ - - Args: - pretrained (bool): If True, returns a model pre-trained on ImageNet - """ - model = DenseNet(num_init_features=64, growth_rate=32, block_config=(6, 12, 48, 32), - **kwargs) - if pretrained: - # '.'s are no longer allowed in module names, but pervious _DenseLayer - # has keys 'norm.1', 'relu.1', 'conv.1', 'norm.2', 'relu.2', 'conv.2'. - # They are also in the checkpoints in model_urls. This pattern is used - # to find such keys. - pattern = re.compile( - r'^(.*denselayer\d+\.(?:norm|relu|conv))\.((?:[12])\.(?:weight|bias|running_mean|running_var))$') - state_dict = model_zoo.load_url(model_urls['densenet201']) - for key in list(state_dict.keys()): - res = pattern.match(key) - if res: - new_key = res.group(1) + res.group(2) - state_dict[new_key] = state_dict[key] - del state_dict[key] - model.load_state_dict(state_dict) - return model - - -def densenet161(pretrained=False, **kwargs): - r"""Densenet-161 model from - `"Densely Connected Convolutional Networks" `_ - - Args: - pretrained (bool): If True, returns a model pre-trained on ImageNet - """ - model = DenseNet(num_init_features=96, growth_rate=48, block_config=(6, 12, 36, 24), - **kwargs) - if pretrained: - # '.'s are no longer allowed in module names, but pervious _DenseLayer - # has keys 'norm.1', 'relu.1', 'conv.1', 'norm.2', 'relu.2', 'conv.2'. - # They are also in the checkpoints in model_urls. This pattern is used - # to find such keys. - pattern = re.compile( - r'^(.*denselayer\d+\.(?:norm|relu|conv))\.((?:[12])\.(?:weight|bias|running_mean|running_var))$') - state_dict = model_zoo.load_url(model_urls['densenet161']) - for key in list(state_dict.keys()): - res = pattern.match(key) - if res: - new_key = res.group(1) + res.group(2) - state_dict[new_key] = state_dict[key] - del state_dict[key] - model.load_state_dict(state_dict) - return model - diff --git a/ptocr/model/backbone/det_mobilev3.py b/ptocr/model/backbone/det_mobilev3.py deleted file mode 100644 index 806b07e..0000000 --- a/ptocr/model/backbone/det_mobilev3.py +++ /dev/null @@ -1,244 +0,0 @@ -#-*- coding:utf-8 _*- -""" -@author:fxw -@file: det_mobilev3.py -@time: 2020/08/07 -""" - -from torch import nn -import torch -import torch.nn.functional as F -from torch.nn import init -import numpy as np - -__all__ = ['mobilenet_v3_small','mobilenet_v3_large'] - -class hswish(nn.Module): - def forward(self, x): - out = x * F.relu6(x + 3, inplace=True) / 6 - return out - -class hswish1(nn.Module): - def forward(self, x): - out = x * (F.relu(x + 3, inplace=True)-F.relu(x - 3, inplace=True)) / 6 - return out - -class hsigmoid(nn.Module): - def forward(self, x): - out = F.relu6(x + 3, inplace=True) / 6 - return out - -class hsigmoid1(nn.Module): - def forward(self, x): - out =(F.relu(x + 3, inplace=True)-F.relu(x - 3, inplace=True))/ 6 - return out - - -class SeModule(nn.Module): - def __init__(self, in_size, reduction=4): - super(SeModule, self).__init__() - self.avg_pool = nn.AdaptiveAvgPool2d(1) - - self.se = nn.Sequential( - nn.Conv2d(in_size, in_size // reduction, kernel_size=1, stride=1, padding=0, bias=False), - nn.BatchNorm2d(in_size // reduction), - nn.ReLU(inplace=True), - nn.Conv2d(in_size // reduction, in_size, kernel_size=1, stride=1, padding=0, bias=False), - nn.BatchNorm2d(in_size), - hsigmoid() - ) - - def forward(self, x): - return x * self.se(x) - - -class Block(nn.Module): - '''expand + depthwise + pointwise''' - def __init__(self, kernel_size, in_size, expand_size, out_size, nolinear, semodule, stride): - super(Block, self).__init__() - self.stride = stride - - - self.conv1 = nn.Conv2d(in_size, expand_size, kernel_size=1, stride=1, padding=0, bias=False) - self.bn1 = nn.BatchNorm2d(expand_size) - self.nolinear1 = nolinear - self.conv2 = nn.Conv2d(expand_size, expand_size, kernel_size=kernel_size, stride=stride, padding=kernel_size//2, groups=expand_size, bias=False) - self.bn2 = nn.BatchNorm2d(expand_size) - self.nolinear2 = nolinear - self.conv3 = nn.Conv2d(expand_size, out_size, kernel_size=1, stride=1, padding=0, bias=False) - self.bn3 = nn.BatchNorm2d(out_size) - - self.shortcut = nn.Sequential() - - self.se = semodule - - if stride == 1 and in_size != out_size: - self.shortcut = nn.Sequential( - nn.Conv2d(in_size, out_size, kernel_size=1, stride=1, padding=0, bias=False), - nn.BatchNorm2d(out_size), - ) - - def forward(self, x): - out = self.nolinear1(self.bn1(self.conv1(x))) - out = self.nolinear2(self.bn2(self.conv2(out))) - out = self.bn3(self.conv3(out)) - if self.se != None: - out = self.se(out) - out = out + self.shortcut(x) if self.stride==1 else out - return out - - -class MobileNetV3_Large(nn.Module): - def __init__(self, ): - super(MobileNetV3_Large, self).__init__() - self.conv1 = nn.Conv2d(3, 16, kernel_size=3, stride=2, padding=1, bias=False) - self.bn1 = nn.BatchNorm2d(16) - self.hs1 = hswish() - - self.bneck = nn.Sequential( - Block(3, 16, 16, 16, nn.ReLU(inplace=True), None, 1), - Block(3, 16, 64, 24, nn.ReLU(inplace=True), None, 2), - Block(3, 24, 72, 24, nn.ReLU(inplace=True), None, 1), - Block(5, 24, 72, 40, nn.ReLU(inplace=True), SeModule(40), 2), - Block(5, 40, 120, 40, nn.ReLU(inplace=True), SeModule(40), 1), - Block(5, 40, 120, 40, nn.ReLU(inplace=True), SeModule(40), 1), - Block(3, 40, 240, 80, hswish(), None, 2), - Block(3, 80, 200, 80, hswish(), None, 1), - Block(3, 80, 184, 80, hswish(), None, 1), - Block(3, 80, 184, 80, hswish(), None, 1), - Block(3, 80, 480, 112, hswish(), SeModule(112), 2), - Block(3, 112, 672, 112, hswish(), SeModule(112), 1), - Block(5, 112, 672, 160, hswish(), SeModule(160), 1), - Block(5, 160, 672, 160, hswish(), SeModule(160), 1), - Block(5, 160, 960, 160, hswish(), SeModule(160), 1), - ) - self.init_params() - - def init_params(self): - for m in self.modules(): - if isinstance(m, nn.Conv2d): - init.kaiming_normal_(m.weight, mode='fan_out') - if m.bias is not None: - init.constant_(m.bias, 0) - elif isinstance(m, nn.BatchNorm2d): - init.constant_(m.weight, 1) - init.constant_(m.bias, 0) - elif isinstance(m, nn.Linear): - init.normal_(m.weight, std=0.001) - if m.bias is not None: - init.constant_(m.bias, 0) - - def forward(self, x): - out = self.hs1(self.bn1(self.conv1(x))) - i = 0 - model_list = [] - for solution in self.bneck.children(): - out = solution(out) - if(i==2 or i==5 or i==9 or i==14): - model_list.append(out) - i+=1 - p1 = model_list[0] - p2 = model_list[1] - p3 = model_list[2] - p4 = model_list[3] - return p1,p2,p3,p4 - - - -class MobileNetV3_Small(nn.Module): - def __init__(self, ): - super(MobileNetV3_Small, self).__init__() - self.conv1 = nn.Conv2d(3, 16, kernel_size=3, stride=2, padding=1, bias=False) - self.bn1 = nn.BatchNorm2d(16) - self.hs1 = hswish() - - self.bneck = nn.Sequential( - Block(3, 16, 16, 16, nn.ReLU(inplace=True), SeModule(16), 1), - Block(3, 16, 72, 24, nn.ReLU(inplace=True), None, 2), - Block(3, 24, 88, 24, nn.ReLU(inplace=True), None, 1), - Block(5, 24, 96, 40, hswish(), SeModule(40), 2), - Block(5, 40, 240, 40, hswish(), SeModule(40), 1), - Block(5, 40, 240, 40, hswish(), SeModule(40), 1), - Block(5, 40, 120, 48, hswish(), SeModule(48), 2), - Block(5, 48, 144, 48, hswish(), SeModule(48), 1), - Block(5, 48, 288, 96, hswish(), SeModule(96), 2), - Block(5, 96, 576, 96, hswish(), SeModule(96), 1), - Block(5, 96, 576, 96, hswish(), SeModule(96), 1), - ) -# self.bneck = nn.Sequential( -# Block(3, 16, 16, 16, nn.ReLU(inplace=True), None, 1), -# Block(3, 16, 72, 24, nn.ReLU(inplace=True), SeModule(24), 2), -# Block(3, 24, 88, 24, nn.ReLU(inplace=True), None, 1), -# Block(5, 24, 96, 40, hswish(), SeModule(40), 2), -# Block(5, 40, 240, 40, hswish(), SeModule(40), 1), -# Block(5, 40, 240, 40, hswish(), None, 1), -# Block(5, 40, 120, 48, hswish(), SeModule(48), 2), -# Block(5, 48, 144, 48, hswish(), None, 1), -# Block(5, 48, 288, 96, hswish(), SeModule(96), 2), -# Block(5, 96, 576, 96, hswish(), SeModule(96), 1), -# Block(5, 96, 576, 96, hswish(), None, 1), -# ) - self.init_params() - - def init_params(self): - for m in self.modules(): - if isinstance(m, nn.Conv2d): - init.kaiming_normal_(m.weight, mode='fan_out') - if m.bias is not None: - init.constant_(m.bias, 0) - elif isinstance(m, nn.BatchNorm2d): - init.constant_(m.weight, 1) - init.constant_(m.bias, 0) - elif isinstance(m, nn.Linear): - init.normal_(m.weight, std=0.001) - if m.bias is not None: - init.constant_(m.bias, 0) - - def forward(self, x): - out = self.hs1(self.bn1(self.conv1(x))) - i = 0 - model_list = [] - for solution in self.bneck.children(): - out = solution(out) - if (i == 2 or i == 5 or i == 7 or i == 10): - model_list.append(out) - i += 1 - p1 = model_list[0] - p2 = model_list[1] - p3 = model_list[2] - p4 = model_list[3] - return p1, p2, p3, p4 - -def mobilenet_v3_small(pretrained,**kwargs): - model = MobileNetV3_Small() - if pretrained: - if torch.cuda.is_available(): - pretrained_dict = torch.load('./pre_model/mbv3_small.old.pth.tar')['state_dict'] - else: - pretrained_dict = torch.load('./pre_model/mbv3_small.old.pth.tar',map_location='cpu')['state_dict'] - try: - model.load_state_dict(pretrained_dict) - except: - state = model.state_dict() - for key in state.keys(): - if 'module.' + key in pretrained_dict.keys(): - state[key] = pretrained_dict['module.' + key] - model.load_state_dict(state) - return model - -def mobilenet_v3_large(pretrained,**kwargs): - model = MobileNetV3_Large() - if pretrained: - if torch.cuda.is_available(): - pretrained_dict = torch.load('./pre_model/mbv3_small.old.pth.tar')['state_dict'] - else: - pretrained_dict = torch.load('./pre_model/mbv3_small.old.pth.tar', map_location='cpu')['state_dict'] - try: - model.load_state_dict(pretrained_dict) - except: - state = model.state_dict() - for key in state.keys(): - if 'module.'+key in pretrained_dict.keys(): - state[key] = pretrained_dict['module.'+key] - model.load_state_dict(state) - return model \ No newline at end of file diff --git a/ptocr/model/backbone/det_mobilev3_pytorch_qua.py b/ptocr/model/backbone/det_mobilev3_pytorch_qua.py deleted file mode 100644 index c35c44c..0000000 --- a/ptocr/model/backbone/det_mobilev3_pytorch_qua.py +++ /dev/null @@ -1,137 +0,0 @@ -#-*- coding:utf-8 _*- -""" -@author:fxw -@file: mobilev3_new.py -@time: 2020/11/02 -""" -import torch -from torch.quantization import QuantStub, DeQuantStub, fuse_modules -import torch.nn as nn -from torch.nn import init -from ..CommonFunction_Q import ConvBnRelu,ConvBn - - -class SeModule(nn.Module): - def __init__(self, in_size, reduction=4): - super(SeModule, self).__init__() - self.se = nn.Sequential( - ConvBnRelu(in_size, in_size // reduction, kernel_size=1, stride=1, padding=0,groups=1,bias=False), - ConvBn(in_size // reduction, in_size, kernel_size=1, stride=1, padding=0, groups=1,bias=False), - nn.Sigmoid() - ) - self.skip = nn.quantized.FloatFunctional() - - def forward(self, x): - return self.skip.mul(x,self.se(x)) - # return x*self.se(x) - - - -class Block(nn.Module): - - def __init__(self, kernel_size, in_size, expand_size, out_size, semodule, stride): - super(Block, self).__init__() - self.stride = stride - - self.conv_bn_relu1 = ConvBnRelu(in_size, expand_size, kernel_size=1, stride=1, padding=0,groups=1,bias=False) - - self.conv_bn_relu2 = ConvBnRelu(expand_size, expand_size, kernel_size=kernel_size, stride=stride, - padding=kernel_size // 2, groups=expand_size, bias=False) - self.conv_bn1 = ConvBn(expand_size, out_size, kernel_size=1, stride=1, padding=0,groups=1,bias=False) - - self.skip = nn.quantized.FloatFunctional() - - self.shortcut = nn.Sequential() - - self.se = semodule - - if stride == 1 and in_size != out_size: - self.shortcut = nn.Sequential( - ConvBn(in_size, out_size, kernel_size=1, stride=1, padding=0, groups=1,bias=False), - ) - - def forward(self, x): - - out =self.conv_bn_relu1(x) - out =self.conv_bn_relu2(out) - out = self.conv_bn1(out) - - out = self.se(out) - out = self.skip.add(out , self.shortcut(x)) if self.stride == 1 else out - return out - - - -class MobileNetV3_Small(nn.Module): - def __init__(self, ): - super(MobileNetV3_Small, self).__init__() - self.conv_bn_relu = ConvBnRelu(3, 16, kernel_size=3, stride=2, padding=1,groups=1, bias=False) - self.layer1 = nn.Sequential( - Block(3, 16, 16, 16,SeModule(16), 1), - Block(3, 16, 72, 24,SeModule(24), 2), - Block(3, 24, 88, 24, SeModule(24), 1)) - self.layer2 = nn.Sequential( - Block(5, 24, 96, 40, SeModule(40), 2), - Block(5, 40, 240, 40, SeModule(40), 1), - Block(5, 40, 240, 40, SeModule(40), 1)) - self.layer3 = nn.Sequential( - Block(5, 40, 120, 48, SeModule(48), 2), - Block(5, 48, 144, 48, SeModule(48), 1)) - self.layer4 = nn.Sequential( - Block(5, 48, 288, 96, SeModule(96), 2), - Block(5, 96, 576, 96, SeModule(96), 1), - Block(5, 96, 576, 96, SeModule(96), 1)) - - self.init_params() - self.fuse_model() - - def init_params(self): - for m in self.modules(): - if isinstance(m, nn.Conv2d): - init.kaiming_normal_(m.weight, mode='fan_out') - if m.bias is not None: - init.constant_(m.bias, 0) - elif isinstance(m, nn.BatchNorm2d): - init.constant_(m.weight, 1) - init.constant_(m.bias, 0) - elif isinstance(m, nn.Linear): - init.normal_(m.weight, std=0.001) - if m.bias is not None: - init.constant_(m.bias, 0) - - def forward(self, x): - x = self.conv_bn_relu(x) - p1 = self.layer1(x) - p2 = self.layer2(p1) - p3 = self.layer3(p2) - p4 = self.layer4(p3) - return [p1,p2,p3,p4] - - def fuse_model(self): - for m in self.modules(): - if type(m) == ConvBnRelu: - fuse_modules(m, ['conv', 'bn', 'relu'], inplace=True) - if type(m) == ConvBn: - fuse_modules(m, ['conv', 'bn'], inplace=False) - - -def mobilenet_v3_small(pretrained,**kwargs): - model = MobileNetV3_Small() - - if pretrained: - if torch.cuda.is_available(): - pretrained_dict = torch.load('./pre_model/mbv3_small.old.pth.tar')['state_dict'] - else: - pretrained_dict = torch.load('./pre_model/mbv3_small.old.pth.tar',map_location='cpu')['state_dict'] - try: - model.load_state_dict(pretrained_dict) - except: - state = model.state_dict() - for key in state.keys(): - if 'module.' + key in pretrained_dict.keys(): - state[key] = pretrained_dict['module.' + key] - model.load_state_dict(state) - return model - - - diff --git a/ptocr/model/backbone/det_resnet.py b/ptocr/model/backbone/det_resnet.py deleted file mode 100644 index f8a74f8..0000000 --- a/ptocr/model/backbone/det_resnet.py +++ /dev/null @@ -1,368 +0,0 @@ -#-*- coding:utf-8 _*- -""" -@author:fxw -@file: det_resnet.py.py -@time: 2020/08/07 -""" -import torch -import torch.nn as nn -import math -import torch.utils.model_zoo as model_zoo - -BatchNorm2d = nn.BatchNorm2d - -__all__ = ['ResNet', 'resnet18', 'resnet34', 'resnet50', 'resnet101', - 'resnet152'] - -model_urls = { - 'resnet18': 'https://download.pytorch.org/models/resnet18-5c106cde.pth', - 'resnet34': 'https://download.pytorch.org/models/resnet34-333f7ec4.pth', - 'resnet50': 'https://download.pytorch.org/models/resnet50-19c8e357.pth', - 'resnet101': 'https://download.pytorch.org/models/resnet101-5d3b4d8f.pth', - 'resnet152': 'https://download.pytorch.org/models/resnet152-b121ed2d.pth', -} - - -def load_pre_model(model,pre_model_path): - pre_dict = torch.load(pre_model_path) - model_pre_dict = {} - for key in model.state_dict().keys(): - if('model.module.backbone.'+key in pre_dict.keys()): - model_pre_dict[key] = pre_dict['model.module.backbone.'+key] - else: - model_pre_dict[key] = model.state_dict()[key] - model.load_state_dict(model_pre_dict) - return model - - -def constant_init(module, constant, bias=0): - nn.init.constant_(module.weight, constant) - if hasattr(module, 'bias'): - nn.init.constant_(module.bias, bias) - - -def conv3x3(in_planes, out_planes, stride=1): - """3x3 convolution with padding""" - return nn.Conv2d(in_planes, out_planes, kernel_size=3, stride=stride, - padding=1, bias=False) - - -class BasicBlock(nn.Module): - expansion = 1 - - def __init__(self, inplanes, planes, stride=1, downsample=None, dcn=None): - super(BasicBlock, self).__init__() - self.with_dcn = dcn is not None - self.conv1 = conv3x3(inplanes, planes, stride) - self.bn1 = BatchNorm2d(planes) - self.relu = nn.ReLU(inplace=True) - self.with_modulated_dcn = False - if self.with_dcn: - fallback_on_stride = dcn.get('fallback_on_stride', False) - self.with_modulated_dcn = dcn.get('modulated', False) - # self.conv2 = conv3x3(planes, planes) - if not self.with_dcn or fallback_on_stride: - self.conv2 = nn.Conv2d(planes, planes, kernel_size=3, - padding=1, bias=False) - else: - deformable_groups = dcn.get('deformable_groups', 1) - if not self.with_modulated_dcn: - from models.dcn import DeformConv - conv_op = DeformConv - offset_channels = 18 - else: - from models.dcn import ModulatedDeformConv - conv_op = ModulatedDeformConv - offset_channels = 27 - self.conv2_offset = nn.Conv2d( - planes, - deformable_groups * offset_channels, - kernel_size=3, - padding=1) - self.conv2 = conv_op( - planes, - planes, - kernel_size=3, - padding=1, - deformable_groups=deformable_groups, - bias=False) - self.bn2 = BatchNorm2d(planes) - self.downsample = downsample - self.stride = stride - - def forward(self, x): - residual = x - - out = self.conv1(x) - out = self.bn1(out) - out = self.relu(out) - - # out = self.conv2(out) - if not self.with_dcn: - out = self.conv2(out) - elif self.with_modulated_dcn: - offset_mask = self.conv2_offset(out) - offset = offset_mask[:, :18, :, :] - mask = offset_mask[:, -9:, :, :].sigmoid() - out = self.conv2(out, offset, mask) - else: - offset = self.conv2_offset(out) - out = self.conv2(out, offset) - out = self.bn2(out) - - if self.downsample is not None: - residual = self.downsample(x) - - out += residual - out = self.relu(out) - - return out - - -class Bottleneck(nn.Module): - expansion = 4 - - def __init__(self, inplanes, planes, stride=1, downsample=None, dcn=None): - super(Bottleneck, self).__init__() - self.with_dcn = dcn is not None - self.conv1 = nn.Conv2d(inplanes, planes, kernel_size=1, bias=False) - self.bn1 = BatchNorm2d(planes) - fallback_on_stride = False - self.with_modulated_dcn = False - if self.with_dcn: - fallback_on_stride = dcn.get('fallback_on_stride', False) - self.with_modulated_dcn = dcn.get('modulated', False) - if not self.with_dcn or fallback_on_stride: - self.conv2 = nn.Conv2d(planes, planes, kernel_size=3, - stride=stride, padding=1, bias=False) - else: - deformable_groups = dcn.get('deformable_groups', 1) - if not self.with_modulated_dcn: - from models.dcn import DeformConv - conv_op = DeformConv - offset_channels = 18 - else: - from models.dcn import ModulatedDeformConv - conv_op = ModulatedDeformConv - offset_channels = 27 - self.conv2_offset = nn.Conv2d( - planes, deformable_groups * offset_channels, - kernel_size=3, - padding=1) - self.conv2 = conv_op( - planes, planes, kernel_size=3, padding=1, stride=stride, - deformable_groups=deformable_groups, bias=False) - self.bn2 = BatchNorm2d(planes) - self.conv3 = nn.Conv2d(planes, planes * 4, kernel_size=1, bias=False) - self.bn3 = BatchNorm2d(planes * 4) - self.relu = nn.ReLU(inplace=True) - self.downsample = downsample - self.stride = stride - self.dcn = dcn - self.with_dcn = dcn is not None - - def forward(self, x): - residual = x - - out = self.conv1(x) - out = self.bn1(out) - out = self.relu(out) - - # out = self.conv2(out) - if not self.with_dcn: - out = self.conv2(out) - elif self.with_modulated_dcn: - offset_mask = self.conv2_offset(out) - offset = offset_mask[:, :18, :, :] - mask = offset_mask[:, -9:, :, :].sigmoid() - out = self.conv2(out, offset, mask) - else: - offset = self.conv2_offset(out) - out = self.conv2(out, offset) - out = self.bn2(out) - out = self.relu(out) - - out = self.conv3(out) - out = self.bn3(out) - - if self.downsample is not None: - residual = self.downsample(x) - - out += residual - out = self.relu(out) - - return out - - -class ResNet(nn.Module): - def __init__(self, block, layers, num_classes=1000, - dcn=None, stage_with_dcn=(False, False, False, False)): - self.dcn = dcn - self.stage_with_dcn = stage_with_dcn - self.inplanes = 64 - super(ResNet, self).__init__() - self.conv1 = nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3, - bias=False) - self.bn1 = BatchNorm2d(64) - self.relu = nn.ReLU(inplace=True) - self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1) - self.layer1 = self._make_layer(block, 64, layers[0]) - self.layer2 = self._make_layer( - block, 128, layers[1], stride=2, dcn=dcn) - self.layer3 = self._make_layer( - block, 256, layers[2], stride=2, dcn=dcn) - self.layer4 = self._make_layer( - block, 512, layers[3], stride=2, dcn=dcn) - # self.avgpool = nn.AvgPool2d(7, stride=1) - # self.fc = nn.Linear(512 * block.expansion, num_classes) - - # self.smooth = nn.Conv2d(2048, 256, kernel_size=1, stride=1, padding=1) - - for m in self.modules(): - if isinstance(m, nn.Conv2d): - n = m.kernel_size[0] * m.kernel_size[1] * m.out_channels - m.weight.data.normal_(0, math.sqrt(2. / n)) - elif isinstance(m, BatchNorm2d): - m.weight.data.fill_(1) - m.bias.data.zero_() - if self.dcn is not None: - for m in self.modules(): - if isinstance(m, Bottleneck) or isinstance(m, BasicBlock): - if hasattr(m, 'conv2_offset'): - constant_init(m.conv2_offset, 0) - - def _make_layer(self, block, planes, blocks, stride=1, dcn=None): - downsample = None - if stride != 1 or self.inplanes != planes * block.expansion: - downsample = nn.Sequential( - nn.Conv2d(self.inplanes, planes * block.expansion, - kernel_size=1, stride=stride, bias=False), - BatchNorm2d(planes * block.expansion), - ) - - layers = [] - layers.append(block(self.inplanes, planes, - stride, downsample, dcn=dcn)) - self.inplanes = planes * block.expansion - for i in range(1, blocks): - layers.append(block(self.inplanes, planes, dcn=dcn)) - - return nn.Sequential(*layers) - - def forward(self, x): - x = self.conv1(x) - x = self.bn1(x) - x = self.relu(x) - x = self.maxpool(x) - - x2 = self.layer1(x) - x3 = self.layer2(x2) - x4 = self.layer3(x3) - x5 = self.layer4(x4) - - return x2, x3, x4, x5 - - -def resnet18(pretrained=True, load_url=True,**kwargs): - """Constructs a ResNet-18 model. - Args: - pretrained (bool): If True, returns a model pre-trained on ImageNet - """ - model = ResNet(BasicBlock, [2, 2, 2, 2], **kwargs) - if pretrained: - if load_url: - model.load_state_dict(model_zoo.load_url( - model_urls['resnet18']), strict=False) - else: - model = load_pre_model(model,'./pre_model/pre-trained-model-synthtext-resnet18.pth') - return model - - -def deformable_resnet18(pretrained=True,load_url=True, **kwargs): - """Constructs a ResNet-18 model. - Args: - pretrained (bool): If True, returns a model pre-trained on ImageNet - """ - model = ResNet(BasicBlock, [2, 2, 2, 2], - dcn=dict(modulated=True, - deformable_groups=1, - fallback_on_stride=False), - stage_with_dcn=[False, True, True, True], **kwargs) - if pretrained: - if load_url: - model.load_state_dict(model_zoo.load_url( - model_urls['resnet18']), strict=False) - else: - model = load_pre_model(model,'./pre_model/pre-trained-model-synthtext-resnet18.pth') - return model - - -def resnet34(pretrained=True, **kwargs): - """Constructs a ResNet-34 model. - Args: - pretrained (bool): If True, returns a model pre-trained on ImageNet - """ - model = ResNet(BasicBlock, [3, 4, 6, 3], **kwargs) - if pretrained: - model.load_state_dict(model_zoo.load_url( - model_urls['resnet34']), strict=False) - return model - - -def resnet50(pretrained=True,load_url=True,**kwargs): - """Constructs a ResNet-50 model. - Args: - pretrained (bool): If True, returns a model pre-trained on ImageNet - """ - model = ResNet(Bottleneck, [3, 4, 6, 3], **kwargs) - if pretrained: - if load_url: - model.load_state_dict(model_zoo.load_url( - model_urls['resnet50']), strict=False) - else: - model = load_pre_model(model,'./pre_model/pre-trained-model-synthtext-resnet50.pth') - return model - - -def deformable_resnet50(pretrained=True,load_url=True, **kwargs): - """Constructs a ResNet-50 model with deformable conv. - Args: - pretrained (bool): If True, returns a model pre-trained on ImageNet - """ - model = ResNet(Bottleneck, [3, 4, 6, 3], - dcn=dict(modulated=True, - deformable_groups=1, - fallback_on_stride=False), - stage_with_dcn=[False, True, True, True], - **kwargs) - if pretrained: - if load_url: - model.load_state_dict(model_zoo.load_url( - model_urls['resnet50']), strict=False) - else: - model = load_pre_model(model,'./pre_model/pre-trained-model-synthtext-resnet50.pth') - return model - - -def resnet101(pretrained=True, **kwargs): - """Constructs a ResNet-101 model. - Args: - pretrained (bool): If True, returns a model pre-trained on ImageNet - """ - model = ResNet(Bottleneck, [3, 4, 23, 3], **kwargs) - if pretrained: - model.load_state_dict(model_zoo.load_url( - model_urls['resnet101']), strict=False) - return model - - -def resnet152(pretrained=True, **kwargs): - """Constructs a ResNet-152 model. - Args: - pretrained (bool): If True, returns a model pre-trained on ImageNet - """ - model = ResNet(Bottleneck, [3, 8, 36, 3], **kwargs) - if pretrained: - model.load_state_dict(model_zoo.load_url( - model_urls['resnet152']), strict=False) - return model diff --git a/ptocr/model/backbone/det_resnet_3_3.py b/ptocr/model/backbone/det_resnet_3_3.py deleted file mode 100644 index 5e3e713..0000000 --- a/ptocr/model/backbone/det_resnet_3_3.py +++ /dev/null @@ -1,233 +0,0 @@ - -import os -import sys -import torch -import torch.nn as nn -import math - -try: - from urllib import urlretrieve -except ImportError: - from urllib.request import urlretrieve - - -__all__ = ['resnet18', 'resnet50', 'resnet101'] - -model_urls = { - 'resnet18': 'http://sceneparsing.csail.mit.edu/model/pretrained_resnet/resnet18-imagenet.pth', - 'resnet50': 'http://sceneparsing.csail.mit.edu/model/pretrained_resnet/resnet50-imagenet.pth', - 'resnet101': 'http://sceneparsing.csail.mit.edu/model/pretrained_resnet/resnet101-imagenet.pth' -} - - -def conv3x3(in_planes, out_planes, stride=1): - "3x3 convolution with padding" - return nn.Conv2d(in_planes, out_planes, kernel_size=3, stride=stride, - padding=1, bias=False) - - -class BasicBlock(nn.Module): - expansion = 1 - - def __init__(self, inplanes, planes, stride=1, downsample=None): - super(BasicBlock, self).__init__() - self.conv1 = conv3x3(inplanes, planes, stride) - self.bn1 = nn.BatchNorm2d(planes) - self.relu = nn.ReLU(inplace=True) - self.conv2 = conv3x3(planes, planes) - self.bn2 = nn.BatchNorm2d(planes) - self.downsample = downsample - self.stride = stride - - def forward(self, x): - residual = x - - out = self.conv1(x) - out = self.bn1(out) - out = self.relu(out) - - out = self.conv2(out) - out = self.bn2(out) - - if self.downsample is not None: - residual = self.downsample(x) - - out += residual - out = self.relu(out) - - return out - - -class Bottleneck(nn.Module): - expansion = 4 - - def __init__(self, inplanes, planes, stride=1, downsample=None): - super(Bottleneck, self).__init__() - self.conv1 = nn.Conv2d(inplanes, planes, kernel_size=1, bias=False) - self.bn1 = nn.BatchNorm2d(planes) - self.conv2 = nn.Conv2d(planes, planes, kernel_size=3, stride=stride, - padding=1, bias=False) - self.bn2 = nn.BatchNorm2d(planes) - self.conv3 = nn.Conv2d(planes, planes * 4, kernel_size=1, bias=False) - self.bn3 = nn.BatchNorm2d(planes * 4) - self.relu = nn.ReLU(inplace=True) - self.downsample = downsample - self.stride = stride - - def forward(self, x): - residual = x - - out = self.conv1(x) - out = self.bn1(out) - out = self.relu(out) - - out = self.conv2(out) - out = self.bn2(out) - out = self.relu(out) - - out = self.conv3(out) - out = self.bn3(out) - - if self.downsample is not None: - residual = self.downsample(x) - - out += residual - out = self.relu(out) - - return out - - -class Convkxk(nn.Module): - def __init__(self, in_planes, out_planes, kernel_size=1, stride=1, padding=0): - super(Convkxk, self).__init__() - self.conv = nn.Conv2d(in_planes, out_planes, kernel_size=kernel_size, stride=stride, padding=padding, - bias=False) - self.bn = nn.BatchNorm2d(out_planes) - self.relu = nn.ReLU(inplace=True) - - for m in self.modules(): - if isinstance(m, nn.Conv2d): - n = m.kernel_size[0] * m.kernel_size[1] * m.out_channels - m.weight.data.normal_(0, math.sqrt(2. / n)) - elif isinstance(m, nn.BatchNorm2d): - m.weight.data.fill_(1) - m.bias.data.zero_() - - def forward(self, x): - return self.relu(self.bn(self.conv(x))) - - -class ResNet(nn.Module): - - def __init__(self, block, layers, num_classes=1000): - super(ResNet, self).__init__() - self.inplanes = 128 - self.conv1 = conv3x3(3, 64, stride=2) - self.bn1 = nn.BatchNorm2d(64) - self.relu1 = nn.ReLU(inplace=True) - self.conv2 = conv3x3(64, 64) - self.bn2 = nn.BatchNorm2d(64) - self.relu2 = nn.ReLU(inplace=True) - self.conv3 = conv3x3(64, 128) - self.bn3 = nn.BatchNorm2d(128) - self.relu3 = nn.ReLU(inplace=True) - self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1) - - self.layer1 = self._make_layer(block, 64, layers[0]) - self.layer2 = self._make_layer(block, 128, layers[1], stride=2) - self.layer3 = self._make_layer(block, 256, layers[2], stride=2) - self.layer4 = self._make_layer(block, 512, layers[3], stride=2) - # self.avgpool = nn.AvgPool2d(7, stride=1) - # self.fc = nn.Linear(512 * block.expansion, num_classes) - - for m in self.modules(): - if isinstance(m, nn.Conv2d): - n = m.kernel_size[0] * m.kernel_size[1] * m.out_channels - m.weight.data.normal_(0, math.sqrt(2. / n)) - elif isinstance(m, nn.BatchNorm2d): - m.weight.data.fill_(1) - m.bias.data.zero_() - - def _make_layer(self, block, planes, blocks, stride=1): - downsample = None - if stride != 1 or self.inplanes != planes * block.expansion: - downsample = nn.Sequential( - nn.Conv2d(self.inplanes, planes * block.expansion, - kernel_size=1, stride=stride, bias=False), - nn.BatchNorm2d(planes * block.expansion), - ) - - layers = [] - layers.append(block(self.inplanes, planes, stride, downsample)) - self.inplanes = planes * block.expansion - for i in range(1, blocks): - layers.append(block(self.inplanes, planes)) - - return nn.Sequential(*layers) - - def forward(self, x): - x = self.relu1(self.bn1(self.conv1(x))) - x = self.relu2(self.bn2(self.conv2(x))) - x = self.relu3(self.bn3(self.conv3(x))) - x = self.maxpool(x) - - f = [] - x = self.layer1(x) - f.append(x) - x = self.layer2(x) - f.append(x) - x = self.layer3(x) - f.append(x) - x = self.layer4(x) - f.append(x) - - return tuple(f) - - # x = self.avgpool(x) - # x = x.view(x.size(0), -1) - # x = self.fc(x) - - # return x - - -def resnet18(pretrained=False, **kwargs): - """Constructs a ResNet-18 model. - Args: - pretrained (bool): If True, returns a model pre-trained on Places - """ - model = ResNet(BasicBlock, [2, 2, 2, 2], **kwargs) - if pretrained: - model.load_state_dict(load_url(model_urls['resnet18']), strict=False) - return model - - -def resnet50(pretrained=False, **kwargs): - """Constructs a ResNet-50 model. - Args: - pretrained (bool): If True, returns a model pre-trained on Places - """ - model = ResNet(Bottleneck, [3, 4, 6, 3], **kwargs) - if pretrained: - model.load_state_dict(load_url(model_urls['resnet50']), strict=False) - return model - - -def resnet101(pretrained=False, **kwargs): - """Constructs a ResNet-101 model. - Args: - pretrained (bool): If True, returns a model pre-trained on Places - """ - model = ResNet(Bottleneck, [3, 4, 23, 3], **kwargs) - if pretrained: - model.load_state_dict(load_url(model_urls['resnet101']), strict=False) - return model - -def load_url(url, model_dir='./pre_model', map_location=None): - if not os.path.exists(model_dir): - os.makedirs(model_dir) - filename = url.split('/')[-1] - cached_file = os.path.join(model_dir, filename) - if not os.path.exists(cached_file): - sys.stderr.write('Downloading: "{}" to {}\n'.format(url, cached_file)) - urlretrieve(url, cached_file) - return torch.load(cached_file, map_location=map_location) \ No newline at end of file diff --git a/ptocr/model/backbone/det_resnet_sast.py b/ptocr/model/backbone/det_resnet_sast.py deleted file mode 100644 index b6da208..0000000 --- a/ptocr/model/backbone/det_resnet_sast.py +++ /dev/null @@ -1,362 +0,0 @@ -#-*- coding:utf-8 _*- -""" -@author:fxw -@file: det_resnet.py.py -@time: 2020/08/07 -""" -import torch -import torch.nn as nn -import math -import torch.utils.model_zoo as model_zoo - -BatchNorm2d = nn.BatchNorm2d - -__all__ = ['ResNet', 'resnet18', 'resnet34', 'resnet50', 'resnet101', - 'resnet152'] - -model_urls = { - 'resnet18': 'https://download.pytorch.org/models/resnet18-5c106cde.pth', - 'resnet34': 'https://download.pytorch.org/models/resnet34-333f7ec4.pth', - 'resnet50': 'https://download.pytorch.org/models/resnet50-19c8e357.pth', - 'resnet101': 'https://download.pytorch.org/models/resnet101-5d3b4d8f.pth', - 'resnet152': 'https://download.pytorch.org/models/resnet152-b121ed2d.pth', -} - -def load_pre_model(model,pre_model_path): - pre_dict = torch.load(pre_model_path) - model_pre_dict = {} - for key in model.state_dict().keys(): - if('model.module.backbone.'+key in pre_dict.keys()): - model_pre_dict[key] = pre_dict['model.module.backbone.'+key] - else: - model_pre_dict[key] = model.state_dict()[key] - model.load_state_dict(model_pre_dict) - return model - -def constant_init(module, constant, bias=0): - nn.init.constant_(module.weight, constant) - if hasattr(module, 'bias'): - nn.init.constant_(module.bias, bias) - - -def conv3x3(in_planes, out_planes, stride=1): - """3x3 convolution with padding""" - return nn.Conv2d(in_planes, out_planes, kernel_size=3, stride=stride, - padding=1, bias=False) - - -class BasicBlock(nn.Module): - expansion = 1 - - def __init__(self, inplanes, planes, stride=1, downsample=None, dcn=None): - super(BasicBlock, self).__init__() - self.with_dcn = dcn is not None - self.conv1 = conv3x3(inplanes, planes, stride) - self.bn1 = BatchNorm2d(planes) - self.relu = nn.ReLU(inplace=True) - self.with_modulated_dcn = False - if self.with_dcn: - fallback_on_stride = dcn.get('fallback_on_stride', False) - self.with_modulated_dcn = dcn.get('modulated', False) - # self.conv2 = conv3x3(planes, planes) - if not self.with_dcn or fallback_on_stride: - self.conv2 = nn.Conv2d(planes, planes, kernel_size=3, - padding=1, bias=False) - else: - deformable_groups = dcn.get('deformable_groups', 1) - if not self.with_modulated_dcn: - from models.dcn import DeformConv - conv_op = DeformConv - offset_channels = 18 - else: - from models.dcn import ModulatedDeformConv - conv_op = ModulatedDeformConv - offset_channels = 27 - self.conv2_offset = nn.Conv2d( - planes, - deformable_groups * offset_channels, - kernel_size=3, - padding=1) - self.conv2 = conv_op( - planes, - planes, - kernel_size=3, - padding=1, - deformable_groups=deformable_groups, - bias=False) - self.bn2 = BatchNorm2d(planes) - self.downsample = downsample - self.stride = stride - - def forward(self, x): - residual = x - - out = self.conv1(x) - out = self.bn1(out) - out = self.relu(out) - - # out = self.conv2(out) - if not self.with_dcn: - out = self.conv2(out) - elif self.with_modulated_dcn: - offset_mask = self.conv2_offset(out) - offset = offset_mask[:, :18, :, :] - mask = offset_mask[:, -9:, :, :].sigmoid() - out = self.conv2(out, offset, mask) - else: - offset = self.conv2_offset(out) - out = self.conv2(out, offset) - out = self.bn2(out) - - if self.downsample is not None: - residual = self.downsample(x) - - out += residual - out = self.relu(out) - - return out - - -class Bottleneck(nn.Module): - expansion = 4 - - def __init__(self, inplanes, planes, stride=1, downsample=None, dcn=None): - super(Bottleneck, self).__init__() - self.with_dcn = dcn is not None - self.conv1 = nn.Conv2d(inplanes, planes, kernel_size=1, bias=False) - self.bn1 = BatchNorm2d(planes) - fallback_on_stride = False - self.with_modulated_dcn = False - if self.with_dcn: - fallback_on_stride = dcn.get('fallback_on_stride', False) - self.with_modulated_dcn = dcn.get('modulated', False) - if not self.with_dcn or fallback_on_stride: - self.conv2 = nn.Conv2d(planes, planes, kernel_size=3, - stride=stride, padding=1, bias=False) - else: - deformable_groups = dcn.get('deformable_groups', 1) - if not self.with_modulated_dcn: - from models.dcn import DeformConv - conv_op = DeformConv - offset_channels = 18 - else: - from models.dcn import ModulatedDeformConv - conv_op = ModulatedDeformConv - offset_channels = 27 - self.conv2_offset = nn.Conv2d( - planes, deformable_groups * offset_channels, - kernel_size=3, - padding=1) - self.conv2 = conv_op( - planes, planes, kernel_size=3, padding=1, stride=stride, - deformable_groups=deformable_groups, bias=False) - self.bn2 = BatchNorm2d(planes) - self.conv3 = nn.Conv2d(planes, planes * 4, kernel_size=1, bias=False) - self.bn3 = BatchNorm2d(planes * 4) - self.relu = nn.ReLU(inplace=True) - self.downsample = downsample - self.stride = stride - self.dcn = dcn - self.with_dcn = dcn is not None - - def forward(self, x): - residual = x - - out = self.conv1(x) - out = self.bn1(out) - out = self.relu(out) - - # out = self.conv2(out) - if not self.with_dcn: - out = self.conv2(out) - elif self.with_modulated_dcn: - offset_mask = self.conv2_offset(out) - offset = offset_mask[:, :18, :, :] - mask = offset_mask[:, -9:, :, :].sigmoid() - out = self.conv2(out, offset, mask) - else: - offset = self.conv2_offset(out) - out = self.conv2(out, offset) - out = self.bn2(out) - out = self.relu(out) - - out = self.conv3(out) - out = self.bn3(out) - - if self.downsample is not None: - residual = self.downsample(x) - - out += residual - out = self.relu(out) - - return out - - -class ResNet(nn.Module): - def __init__(self, block, layers, num_classes=1000, - dcn=None, stage_with_dcn=(False, False, False, False)): - self.dcn = dcn - self.stage_with_dcn = stage_with_dcn - self.inplanes = 64 - super(ResNet, self).__init__() - self.conv1 = nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3, - bias=False) - self.bn1 = BatchNorm2d(64) - self.relu = nn.ReLU(inplace=True) - self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1) - self.layer1 = self._make_layer(block, 64, layers[0]) - self.layer2 = self._make_layer( - block, 128, layers[1], stride=2, dcn=dcn) - self.layer3 = self._make_layer( - block, 256, layers[2], stride=2, dcn=dcn) - self.layer4 = self._make_layer( - block, 512, layers[3], stride=2, dcn=dcn) - self.layer5 = self._make_layer( - block, 512, layers[4], stride=2, dcn=dcn) - # self.avgpool = nn.AvgPool2d(7, stride=1) - # self.fc = nn.Linear(512 * block.expansion, num_classes) - - # self.smooth = nn.Conv2d(2048, 256, kernel_size=1, stride=1, padding=1) - - for m in self.modules(): - if isinstance(m, nn.Conv2d): - n = m.kernel_size[0] * m.kernel_size[1] * m.out_channels - m.weight.data.normal_(0, math.sqrt(2. / n)) - elif isinstance(m, BatchNorm2d): - m.weight.data.fill_(1) - m.bias.data.zero_() - if self.dcn is not None: - for m in self.modules(): - if isinstance(m, Bottleneck) or isinstance(m, BasicBlock): - if hasattr(m, 'conv2_offset'): - constant_init(m.conv2_offset, 0) - - def _make_layer(self, block, planes, blocks, stride=1, dcn=None): - downsample = None - if stride != 1 or self.inplanes != planes * block.expansion: - downsample = nn.Sequential( - nn.Conv2d(self.inplanes, planes * block.expansion, - kernel_size=1, stride=stride, bias=False), - BatchNorm2d(planes * block.expansion), - ) - - layers = [] - layers.append(block(self.inplanes, planes, - stride, downsample, dcn=dcn)) - self.inplanes = planes * block.expansion - for i in range(1, blocks): - layers.append(block(self.inplanes, planes, dcn=dcn)) - - return nn.Sequential(*layers) - - def forward(self, x): - x0 = x.clone() - x = self.conv1(x) - x = self.bn1(x) - x = self.relu(x) - x1 = x.clone() - x = self.maxpool(x) - - x2 = self.layer1(x) - x3 = self.layer2(x2) - x4 = self.layer3(x3) - x5 = self.layer4(x4) - x6 = self.layer5(x5) - - return x6,x5, x4, x3, x2,x1,x0 - - -def resnet18(pretrained=True, **kwargs): - """Constructs a ResNet-18 model. - Args: - pretrained (bool): If True, returns a model pre-trained on ImageNet - """ - model = ResNet(BasicBlock, [2, 2, 2, 2], **kwargs) - if pretrained: - model.load_state_dict(model_zoo.load_url( - model_urls['resnet18']), strict=False) - return model - - -def deformable_resnet18(pretrained=True, **kwargs): - """Constructs a ResNet-18 model. - Args: - pretrained (bool): If True, returns a model pre-trained on ImageNet - """ - model = ResNet(BasicBlock, [2, 2, 2, 2], - dcn=dict(modulated=True, - deformable_groups=1, - fallback_on_stride=False), - stage_with_dcn=[False, True, True, True], **kwargs) - if pretrained: - model.load_state_dict(model_zoo.load_url( - model_urls['resnet18']), strict=False) - return model - - -def resnet34(pretrained=True, **kwargs): - """Constructs a ResNet-34 model. - Args: - pretrained (bool): If True, returns a model pre-trained on ImageNet - """ - model = ResNet(BasicBlock, [3, 4, 6, 3], **kwargs) - if pretrained: - model.load_state_dict(model_zoo.load_url( - model_urls['resnet34']), strict=False) - return model - - -def resnet50(pretrained=True,load_url=False,**kwargs): - """Constructs a ResNet-50 model. - Args: - pretrained (bool): If True, returns a model pre-trained on ImageNet - """ - model = ResNet(Bottleneck, [3, 4, 6, 3,3], **kwargs) - if pretrained: - if load_url: - model.load_state_dict(model_zoo.load_url( - model_urls['resnet50']), strict=False) - else: - model = load_pre_model(model,'./pre_model/pre-trained-model-synthtext-resnet50.pth') - return model - - -def deformable_resnet50(pretrained=True, **kwargs): - """Constructs a ResNet-50 model with deformable conv. - Args: - pretrained (bool): If True, returns a model pre-trained on ImageNet - """ - model = ResNet(Bottleneck, [3, 4, 6, 3,3], - dcn=dict(modulated=True, - deformable_groups=1, - fallback_on_stride=False), - stage_with_dcn=[False, True, True, True], - **kwargs) - if pretrained: - model.load_state_dict(model_zoo.load_url( - model_urls['resnet50']), strict=False) - return model - - -def resnet101(pretrained=True, **kwargs): - """Constructs a ResNet-101 model. - Args: - pretrained (bool): If True, returns a model pre-trained on ImageNet - """ - model = ResNet(Bottleneck, [3, 4, 23, 3], **kwargs) - if pretrained: - model.load_state_dict(model_zoo.load_url( - model_urls['resnet101']), strict=False) - return model - - -def resnet152(pretrained=True, **kwargs): - """Constructs a ResNet-152 model. - Args: - pretrained (bool): If True, returns a model pre-trained on ImageNet - """ - model = ResNet(Bottleneck, [3, 8, 36, 3], **kwargs) - if pretrained: - model.load_state_dict(model_zoo.load_url( - model_urls['resnet152']), strict=False) - return model diff --git a/ptocr/model/backbone/det_resnet_sast_3_3.py b/ptocr/model/backbone/det_resnet_sast_3_3.py deleted file mode 100644 index 2089232..0000000 --- a/ptocr/model/backbone/det_resnet_sast_3_3.py +++ /dev/null @@ -1,238 +0,0 @@ - -import os -import sys -import torch -import torch.nn as nn -import math - -try: - from urllib import urlretrieve -except ImportError: - from urllib.request import urlretrieve - - -__all__ = ['resnet18', 'resnet50', 'resnet101'] - -model_urls = { - 'resnet18': 'http://sceneparsing.csail.mit.edu/model/pretrained_resnet/resnet18-imagenet.pth', - 'resnet50': 'http://sceneparsing.csail.mit.edu/model/pretrained_resnet/resnet50-imagenet.pth', - 'resnet101': 'http://sceneparsing.csail.mit.edu/model/pretrained_resnet/resnet101-imagenet.pth' -} - - -def conv3x3(in_planes, out_planes, stride=1): - "3x3 convolution with padding" - return nn.Conv2d(in_planes, out_planes, kernel_size=3, stride=stride, - padding=1, bias=False) - - -class BasicBlock(nn.Module): - expansion = 1 - - def __init__(self, inplanes, planes, stride=1, downsample=None): - super(BasicBlock, self).__init__() - self.conv1 = conv3x3(inplanes, planes, stride) - self.bn1 = nn.BatchNorm2d(planes) - self.relu = nn.ReLU(inplace=True) - self.conv2 = conv3x3(planes, planes) - self.bn2 = nn.BatchNorm2d(planes) - self.downsample = downsample - self.stride = stride - - def forward(self, x): - residual = x - - out = self.conv1(x) - out = self.bn1(out) - out = self.relu(out) - - out = self.conv2(out) - out = self.bn2(out) - - if self.downsample is not None: - residual = self.downsample(x) - - out += residual - out = self.relu(out) - - return out - - -class Bottleneck(nn.Module): - expansion = 4 - - def __init__(self, inplanes, planes, stride=1, downsample=None): - super(Bottleneck, self).__init__() - self.conv1 = nn.Conv2d(inplanes, planes, kernel_size=1, bias=False) - self.bn1 = nn.BatchNorm2d(planes) - self.conv2 = nn.Conv2d(planes, planes, kernel_size=3, stride=stride, - padding=1, bias=False) - self.bn2 = nn.BatchNorm2d(planes) - self.conv3 = nn.Conv2d(planes, planes * 4, kernel_size=1, bias=False) - self.bn3 = nn.BatchNorm2d(planes * 4) - self.relu = nn.ReLU(inplace=True) - self.downsample = downsample - self.stride = stride - - def forward(self, x): - residual = x - - out = self.conv1(x) - out = self.bn1(out) - out = self.relu(out) - - out = self.conv2(out) - out = self.bn2(out) - out = self.relu(out) - - out = self.conv3(out) - out = self.bn3(out) - - if self.downsample is not None: - residual = self.downsample(x) - - out += residual - out = self.relu(out) - - return out - - -class Convkxk(nn.Module): - def __init__(self, in_planes, out_planes, kernel_size=1, stride=1, padding=0): - super(Convkxk, self).__init__() - self.conv = nn.Conv2d(in_planes, out_planes, kernel_size=kernel_size, stride=stride, padding=padding, - bias=False) - self.bn = nn.BatchNorm2d(out_planes) - self.relu = nn.ReLU(inplace=True) - - for m in self.modules(): - if isinstance(m, nn.Conv2d): - n = m.kernel_size[0] * m.kernel_size[1] * m.out_channels - m.weight.data.normal_(0, math.sqrt(2. / n)) - elif isinstance(m, nn.BatchNorm2d): - m.weight.data.fill_(1) - m.bias.data.zero_() - - def forward(self, x): - return self.relu(self.bn(self.conv(x))) - - -class ResNet(nn.Module): - - def __init__(self, block, layers, num_classes=1000): - super(ResNet, self).__init__() - self.inplanes = 128 - self.conv1 = conv3x3(3, 64, stride=2) - self.bn1 = nn.BatchNorm2d(64) - self.relu1 = nn.ReLU(inplace=True) - self.conv2 = conv3x3(64, 64) - self.bn2 = nn.BatchNorm2d(64) - self.relu2 = nn.ReLU(inplace=True) - self.conv3 = conv3x3(64, 128) - self.bn3 = nn.BatchNorm2d(128) - self.relu3 = nn.ReLU(inplace=True) - self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1) - - self.layer1 = self._make_layer(block, 64, layers[0]) - self.layer2 = self._make_layer(block, 128, layers[1], stride=2) - self.layer3 = self._make_layer(block, 256, layers[2], stride=2) - self.layer4 = self._make_layer(block, 512, layers[3], stride=2) - self.layer5 = self._make_layer(block, 512, layers[3], stride=2) - # self.avgpool = nn.AvgPool2d(7, stride=1) - # self.fc = nn.Linear(512 * block.expansion, num_classes) - - for m in self.modules(): - if isinstance(m, nn.Conv2d): - n = m.kernel_size[0] * m.kernel_size[1] * m.out_channels - m.weight.data.normal_(0, math.sqrt(2. / n)) - elif isinstance(m, nn.BatchNorm2d): - m.weight.data.fill_(1) - m.bias.data.zero_() - - def _make_layer(self, block, planes, blocks, stride=1): - downsample = None - if stride != 1 or self.inplanes != planes * block.expansion: - downsample = nn.Sequential( - nn.Conv2d(self.inplanes, planes * block.expansion, - kernel_size=1, stride=stride, bias=False), - nn.BatchNorm2d(planes * block.expansion), - ) - - layers = [] - layers.append(block(self.inplanes, planes, stride, downsample)) - self.inplanes = planes * block.expansion - for i in range(1, blocks): - layers.append(block(self.inplanes, planes)) - - return nn.Sequential(*layers) - - def forward(self, x): - x0 = x.clone() - x = self.relu1(self.bn1(self.conv1(x))) - x = self.relu2(self.bn2(self.conv2(x))) - x = self.relu3(self.bn3(self.conv3(x))) - x1 = x.clone() - x = self.maxpool(x) - - - x = self.layer1(x) - x2 = x.clone() - x = self.layer2(x) - x3 = x.clone() - x = self.layer3(x) - x4 = x.clone() - x = self.layer4(x) - x5 = x.clone() - x = self.layer5(x) - x6 = x.clone() - - return x6,x5, x4, x3, x2,x1,x0 - - # x = self.avgpool(x) - # x = x.view(x.size(0), -1) - # x = self.fc(x) - - # return x - - -def resnet18(pretrained=False, **kwargs): - """Constructs a ResNet-18 model. - Args: - pretrained (bool): If True, returns a model pre-trained on Places - """ - model = ResNet(BasicBlock, [2, 2, 2, 2], **kwargs) - if pretrained: - model.load_state_dict(load_url(model_urls['resnet18']), strict=False) - return model - - -def resnet50(pretrained=False, **kwargs): - """Constructs a ResNet-50 model. - Args: - pretrained (bool): If True, returns a model pre-trained on Places - """ - model = ResNet(Bottleneck, [3, 4, 6, 3], **kwargs) - if pretrained: - model.load_state_dict(load_url(model_urls['resnet50']), strict=False) - return model - - -def resnet101(pretrained=False, **kwargs): - """Constructs a ResNet-101 model. - Args: - pretrained (bool): If True, returns a model pre-trained on Places - """ - model = ResNet(Bottleneck, [3, 4, 23, 3], **kwargs) - if pretrained: - model.load_state_dict(load_url(model_urls['resnet101']), strict=False) - return model - -def load_url(url, model_dir='./pre_model', map_location=None): - if not os.path.exists(model_dir): - os.makedirs(model_dir) - filename = url.split('/')[-1] - cached_file = os.path.join(model_dir, filename) - if not os.path.exists(cached_file): - sys.stderr.write('Downloading: "{}" to {}\n'.format(url, cached_file)) - urlretrieve(url, cached_file) - return torch.load(cached_file, map_location=map_location) \ No newline at end of file diff --git a/ptocr/model/backbone/det_scnet.py b/ptocr/model/backbone/det_scnet.py deleted file mode 100644 index 714b84b..0000000 --- a/ptocr/model/backbone/det_scnet.py +++ /dev/null @@ -1,294 +0,0 @@ -#-*- coding:utf-8 _*- -""" -@author:fxw -@file: det_scnet.py -@time: 2020/08/07 -""" -import torch -import torch.nn as nn -import torch.nn.functional as F -import torch.utils.model_zoo as model_zoo - -__all__ = ['SCNet', 'scnet50', 'scnet50_v1d'] - -model_urls = { - 'scnet50': 'https://backseason.oss-cn-beijing.aliyuncs.com/scnet/scnet50-dc6a7e87.pth', - 'scnet50_v1d': 'https://backseason.oss-cn-beijing.aliyuncs.com/scnet/scnet50_v1d-4109d1e1.pth', -} - -class SCConv(nn.Module): - def __init__(self, inplanes, planes, stride, padding, dilation, groups, pooling_r, norm_layer): - super(SCConv, self).__init__() - self.k2 = nn.Sequential( - nn.AvgPool2d(kernel_size=pooling_r, stride=pooling_r,padding=1), - nn.Conv2d(inplanes, planes, kernel_size=3, stride=1, - padding=padding, dilation=dilation, - groups=groups, bias=False), - norm_layer(planes), - ) - self.k3 = nn.Sequential( - nn.Conv2d(inplanes, planes, kernel_size=3, stride=1, - padding=padding, dilation=dilation, - groups=groups, bias=False), - norm_layer(planes), - ) - self.k4 = nn.Sequential( - nn.Conv2d(inplanes, planes, kernel_size=3, stride=stride, - padding=padding, dilation=dilation, - groups=groups, bias=False), - norm_layer(planes), - ) - - def forward(self, x): - identity = x - - out = torch.sigmoid(torch.add(identity, F.interpolate(self.k2(x), identity.size()[2:]))) # sigmoid(identity + k2) - out = torch.mul(self.k3(x), out) # k3 * sigmoid(identity + k2) - out = self.k4(out) # k4 - - return out - -class SCBottleneck(nn.Module): - """SCNet SCBottleneck - """ - expansion = 4 - pooling_r = 4 # down-sampling rate of the avg pooling layer in the K3 path of SC-Conv. - - def __init__(self, inplanes, planes, stride=1, downsample=None, - cardinality=1, bottleneck_width=32, - avd=False, dilation=1, is_first=False, - norm_layer=None): - super(SCBottleneck, self).__init__() - group_width = int(planes * (bottleneck_width / 64.)) * cardinality - self.conv1_a = nn.Conv2d(inplanes, group_width, kernel_size=1, bias=False) - self.bn1_a = norm_layer(group_width) - self.conv1_b = nn.Conv2d(inplanes, group_width, kernel_size=1, bias=False) - self.bn1_b = norm_layer(group_width) - self.avd = avd and (stride > 1 or is_first) - - if self.avd: - self.avd_layer = nn.AvgPool2d(3, stride, padding=1) - stride = 1 - - self.k1 = nn.Sequential( - nn.Conv2d( - group_width, group_width, kernel_size=3, stride=stride, - padding=dilation, dilation=dilation, - groups=cardinality, bias=False), - norm_layer(group_width), - ) - - self.scconv = SCConv( - group_width, group_width, stride=stride, - padding=dilation, dilation=dilation, - groups=cardinality, pooling_r=self.pooling_r, norm_layer=norm_layer) - - self.conv3 = nn.Conv2d( - group_width * 2, planes * 4, kernel_size=1, bias=False) - self.bn3 = norm_layer(planes*4) - - self.relu = nn.ReLU(inplace=True) - self.downsample = downsample - self.dilation = dilation - self.stride = stride - - def forward(self, x): - residual = x - - out_a= self.conv1_a(x) - out_a = self.bn1_a(out_a) - out_b = self.conv1_b(x) - out_b = self.bn1_b(out_b) - out_a = self.relu(out_a) - out_b = self.relu(out_b) - - out_a = self.k1(out_a) - out_b = self.scconv(out_b) - out_a = self.relu(out_a) - out_b = self.relu(out_b) - - if self.avd: - out_a = self.avd_layer(out_a) - out_b = self.avd_layer(out_b) - - out = self.conv3(torch.cat([out_a, out_b], dim=1)) - out = self.bn3(out) - - if self.downsample is not None: - residual = self.downsample(x) - - out += residual - out = self.relu(out) - - return out - -class SCNet(nn.Module): - """ SCNet Variants Definations - Parameters - ---------- - block : Block - Class for the residual block. - layers : list of int - Numbers of layers in each block. - classes : int, default 1000 - Number of classification classes. - dilated : bool, default False - Applying dilation strategy to pretrained SCNet yielding a stride-8 model. - deep_stem : bool, default False - Replace 7x7 conv in input stem with 3 3x3 conv. - avg_down : bool, default False - Use AvgPool instead of stride conv when - downsampling in the bottleneck. - norm_layer : object - Normalization layer used (default: :class:`torch.nn.BatchNorm2d`). - Reference: - - He, Kaiming, et al. "Deep residual learning for image recognition." - Proceedings of the IEEE conference on computer vision and pattern recognition. 2016. - - Yu, Fisher, and Vladlen Koltun. "Multi-scale context aggregation by dilated convolutions." - """ - def __init__(self, block, layers, groups=1, bottleneck_width=32, - num_classes=1000, dilated=False, dilation=1, - deep_stem=False, stem_width=64, avg_down=False, - avd=False, norm_layer=nn.BatchNorm2d): - self.cardinality = groups - self.bottleneck_width = bottleneck_width - # ResNet-D params - self.inplanes = stem_width*2 if deep_stem else 64 - self.avg_down = avg_down - self.avd = avd - - super(SCNet, self).__init__() - conv_layer = nn.Conv2d - if deep_stem: - self.conv1 = nn.Sequential( - conv_layer(3, stem_width, kernel_size=3, stride=2, padding=1, bias=False), - norm_layer(stem_width), - nn.ReLU(inplace=True), - conv_layer(stem_width, stem_width, kernel_size=3, stride=1, padding=1, bias=False), - norm_layer(stem_width), - nn.ReLU(inplace=True), - conv_layer(stem_width, stem_width*2, kernel_size=3, stride=1, padding=1, bias=False), - ) - else: - self.conv1 = conv_layer(3, 64, kernel_size=7, stride=2, padding=3, - bias=False) - self.bn1 = norm_layer(self.inplanes) - self.relu = nn.ReLU(inplace=True) - self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1) - self.layer1 = self._make_layer(block, 64, layers[0], norm_layer=norm_layer, is_first=False) - self.layer2 = self._make_layer(block, 128, layers[1], stride=2, norm_layer=norm_layer) - if dilated or dilation == 4: - self.layer3 = self._make_layer(block, 256, layers[2], stride=1, - dilation=2, norm_layer=norm_layer) - self.layer4 = self._make_layer(block, 512, layers[3], stride=1, - dilation=4, norm_layer=norm_layer) - elif dilation==2: - self.layer3 = self._make_layer(block, 256, layers[2], stride=2, - dilation=1, norm_layer=norm_layer) - self.layer4 = self._make_layer(block, 512, layers[3], stride=1, - dilation=2, norm_layer=norm_layer) - else: - self.layer3 = self._make_layer(block, 256, layers[2], stride=2, - norm_layer=norm_layer) - self.layer4 = self._make_layer(block, 512, layers[3], stride=2, - norm_layer=norm_layer) - self.avgpool = nn.AdaptiveAvgPool2d((1, 1)) - self.fc = nn.Linear(512 * block.expansion, num_classes) - - for m in self.modules(): - if isinstance(m, nn.Conv2d): - nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu') - elif isinstance(m, norm_layer): - nn.init.constant_(m.weight, 1) - nn.init.constant_(m.bias, 0) - - def _make_layer(self, block, planes, blocks, stride=1, dilation=1, norm_layer=None, - is_first=True): - downsample = None - if stride != 1 or self.inplanes != planes * block.expansion: - down_layers = [] - if self.avg_down: - if dilation == 1: - down_layers.append(nn.AvgPool2d(kernel_size=stride, stride=stride, - ceil_mode=True, count_include_pad=False)) - else: - down_layers.append(nn.AvgPool2d(kernel_size=1, stride=1, - ceil_mode=True, count_include_pad=False)) - down_layers.append(nn.Conv2d(self.inplanes, planes * block.expansion, - kernel_size=1, stride=1, bias=False)) - else: - down_layers.append(nn.Conv2d(self.inplanes, planes * block.expansion, - kernel_size=1, stride=stride, bias=False)) - down_layers.append(norm_layer(planes * block.expansion)) - downsample = nn.Sequential(*down_layers) - - layers = [] - if dilation == 1 or dilation == 2: - layers.append(block(self.inplanes, planes, stride, downsample=downsample, - cardinality=self.cardinality, - bottleneck_width=self.bottleneck_width, - avd=self.avd, dilation=1, is_first=is_first, - norm_layer=norm_layer)) - elif dilation == 4: - layers.append(block(self.inplanes, planes, stride, downsample=downsample, - cardinality=self.cardinality, - bottleneck_width=self.bottleneck_width, - avd=self.avd, dilation=2, is_first=is_first, - norm_layer=norm_layer)) - else: - raise RuntimeError("=> unknown dilation size: {}".format(dilation)) - - self.inplanes = planes * block.expansion - for i in range(1, blocks): - layers.append(block(self.inplanes, planes, - cardinality=self.cardinality, - bottleneck_width=self.bottleneck_width, - avd=self.avd, dilation=dilation, - norm_layer=norm_layer)) - - return nn.Sequential(*layers) - - def forward(self, x): - x = self.conv1(x) - x = self.bn1(x) - x = self.relu(x) - x = self.maxpool(x) - - x2 = self.layer1(x) - x3 = self.layer2(x2) - x4 = self.layer3(x3) - x5 = self.layer4(x4) - - return x2, x3, x4, x5 - - - -def scnet50(pretrained=False, **kwargs): - """Constructs a SCNet-50 model. - Args: - pretrained (bool): If True, returns a model pre-trained on ImageNet - """ - model = SCNet(SCBottleneck, [3, 4, 6, 3], - deep_stem=False, stem_width=32, avg_down=False, - avd=False, **kwargs) - if pretrained: - model.load_state_dict(model_zoo.load_url(model_urls['scnet50']),strict=False) - return model - -def scnet50_v1d(pretrained=False, **kwargs): - """Constructs a SCNet-50_v1d model described in - `Bag of Tricks `_. - `ResNeSt: Split-Attention Networks `_. - Compared with default SCNet(SCNetv1b), SCNetv1d replaces the 7x7 conv - in the input stem with three 3x3 convs. And in the downsampling block, - a 3x3 avg_pool with stride 2 is added before conv, whose stride is - changed to 1. - Args: - pretrained (bool): If True, returns a model pre-trained on ImageNet - """ - model = SCNet(SCBottleneck, [3, 4, 6, 3], - deep_stem=True, stem_width=32, avg_down=True, - avd=True, **kwargs) - if pretrained: - model.load_state_dict(model_zoo.load_url(model_urls['scnet50_v1d']),strict=False) - return model diff --git a/ptocr/model/backbone/rec_crnn_backbone.py b/ptocr/model/backbone/rec_crnn_backbone.py deleted file mode 100644 index c401800..0000000 --- a/ptocr/model/backbone/rec_crnn_backbone.py +++ /dev/null @@ -1,79 +0,0 @@ -#-*- coding:utf-8 _*- -""" -@author:fxw -@file: crnn_backbone.py -@time: 2020/10/12 -""" -import torch -import torch.nn as nn - -class conv_bn_relu(nn.Module): - def __init__(self,in_c,out_c,k_s,s,p,with_bn=True): - super(conv_bn_relu, self).__init__() - self.conv = nn.Conv2d(in_c,out_c,k_s,s,p) - self.bn = nn.BatchNorm2d(out_c) - self.relu = nn.ReLU() - self.with_bn = with_bn - def forward(self, x): - x = self.conv(x) - if self.with_bn: - x = self.bn(x) - x = self.relu(x) - return x - -class crnn_backbone(nn.Module): - def __init__(self,is_gray): - super(crnn_backbone, self).__init__() - if(is_gray): - nc = 1 - else: - nc = 3 - base_channel = 64 - self.cnn = nn.Sequential( - conv_bn_relu(nc,base_channel,3,1,1), - nn.MaxPool2d(2,2), - conv_bn_relu(base_channel,base_channel*2,3,1,1), - nn.MaxPool2d(2, 2), - conv_bn_relu(base_channel*2,base_channel*4,3,1,1), - conv_bn_relu(base_channel*4,base_channel*4,3,1,1), - nn.MaxPool2d((2,1),(2,1)), - conv_bn_relu(base_channel*4,base_channel*8,3,1,1,with_bn=True), - conv_bn_relu(base_channel*8,base_channel*8,3,1,1,with_bn=True), - nn.MaxPool2d((2,1), (2,1)), - conv_bn_relu(base_channel*8,base_channel*8,(2,1),1,0) - # conv_bn_relu(base_channel*8,base_channel*8,2,1,0) - ) - - for m in self.modules(): - if isinstance(m, nn.Conv2d): - nn.init.kaiming_normal_(m.weight.data) - elif isinstance(m, nn.BatchNorm2d): - m.weight.data.fill_(1.) - m.bias.data.fill_(1e-4) - - def forward(self, x): - x = self.cnn(x) - return x - -def rec_crnn_backbone(pretrained=False, is_gray=False,**kwargs): - """VGG 19-layer model (configuration 'E') with batch normalization - - Args: - pretrained (bool): If True, returns a model pre-trained on ImageNet - """ - if pretrained: - kwargs['init_weights'] = False - model = crnn_backbone(is_gray) - if pretrained: - pretrained_model = torch.load('./pre_model/crnn_backbone.pth') - state = model.state_dict() - for key in state.keys(): - if key in pretrained_model.keys(): - if (key=='features.0.weight' and is_gray): - state[key] = torch.mean(pretrained_model[key],1).unsqueeze(1) - else: - state[key] = pretrained_model[key] - model.load_state_dict(state) - return model - - diff --git a/ptocr/model/backbone/rec_vgg.py b/ptocr/model/backbone/rec_vgg.py deleted file mode 100644 index 40d1c95..0000000 --- a/ptocr/model/backbone/rec_vgg.py +++ /dev/null @@ -1,258 +0,0 @@ -#-*- coding:utf-8 _*- -""" -@author:fxw -@file: vgg.py -@time: 2020/07/24 -""" -import torch -import torch.nn as nn -import torch.utils.model_zoo as model_zoo - - -__all__ = [ - 'VGG', 'vgg11', 'vgg11_bn', 'vgg13', 'vgg13_bn', 'vgg16', 'vgg16_bn', - 'vgg19_bn', 'vgg19', -] - - -model_urls = { - 'vgg11': 'https://download.pytorch.org/models/vgg11-bbd30ac9.pth', - 'vgg13': 'https://download.pytorch.org/models/vgg13-c768596a.pth', - 'vgg16': 'https://download.pytorch.org/models/vgg16-397923af.pth', - 'vgg19': 'https://download.pytorch.org/models/vgg19-dcbb9e9d.pth', - 'vgg11_bn': 'https://download.pytorch.org/models/vgg11_bn-6002323d.pth', - 'vgg13_bn': 'https://download.pytorch.org/models/vgg13_bn-abd245e5.pth', - 'vgg16_bn': 'https://download.pytorch.org/models/vgg16_bn-6c64b313.pth', - 'vgg19_bn': 'https://download.pytorch.org/models/vgg19_bn-c79401a0.pth', -} - - -class VGG(nn.Module): - - def __init__(self, features, num_classes=1000, init_weights=True): - super(VGG, self).__init__() - self.features = features - if init_weights: - self._initialize_weights() - - def forward(self, x): - x = self.features(x) - return x - - def _initialize_weights(self): - for m in self.modules(): - if isinstance(m, nn.Conv2d): - nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu') - if m.bias is not None: - nn.init.constant_(m.bias, 0) - elif isinstance(m, nn.BatchNorm2d): - nn.init.constant_(m.weight, 1) - nn.init.constant_(m.bias, 0) - elif isinstance(m, nn.Linear): - nn.init.normal_(m.weight, 0, 0.01) - nn.init.constant_(m.bias, 0) - - -def make_layers(cfg, is_gray=False,batch_norm=False): - layers = [] - in_channels = 3 - if is_gray: - in_channels = 1 - for v in cfg: - if v == 'M': - layers += [nn.MaxPool2d(kernel_size=2, stride=2)] - elif v=='N': - layers += [nn.MaxPool2d(kernel_size=2, stride=(2,1))] - else: - conv2d = nn.Conv2d(in_channels, v, kernel_size=3, padding=1) - if batch_norm: - layers += [conv2d, nn.BatchNorm2d(v), nn.ReLU(inplace=True)] - else: - layers += [conv2d, nn.ReLU(inplace=True)] - in_channels = v - return nn.Sequential(*layers) - - -cfg = { - 'A': [64, 'M', 128, 'M', 256, 256, 'N', 512, 512, 'N', 512, 512, 'N'], - 'B': [64, 64, 'M', 128, 128, 'M', 256, 256, 'N', 512, 512, 'N', 512, 512, 'N'], - 'D': [64, 64, 'M', 128, 128, 'M', 256, 256, 256, 'N', 512, 512, 512, 'N', 512, 512, 512, 'N'], - 'E': [64, 64, 'M', 128, 128, 'M', 256, 256, 256, 256, 'M', 512, 512, 512, 512, 'N', 512, 512, 512, 512, 'N'], -} - - -def vgg11(pretrained=False,is_gray=False, **kwargs): - """VGG 11-layer model (configuration "A") - - Args: - pretrained (bool): If True, returns a model pre-trained on ImageNet - """ - if pretrained: - kwargs['init_weights'] = False - model = VGG(make_layers(cfg['A'],is_gray=is_gray), **kwargs) - if pretrained: - pretrained_model = model_zoo.load_url(model_urls['vgg11']) - state = model.state_dict() - for key in state.keys(): - if key in pretrained_model.keys(): - if (key=='features.0.weight' and is_gray): - state[key] = torch.mean(pretrained_model[key],1).unsqueeze(1) - else: - state[key] = pretrained_model[key] - model.load_state_dict(state) - return model - - -def vgg11_bn(pretrained=False,is_gray=False, **kwargs): - """VGG 11-layer model (configuration "A") with batch normalization - - Args: - pretrained (bool): If True, returns a model pre-trained on ImageNet - """ - if pretrained: - kwargs['init_weights'] = False - model = VGG(make_layers(cfg['A'], is_gray=is_gray,batch_norm=True), **kwargs) - if pretrained: - pretrained_model = model_zoo.load_url(model_urls['vgg11_bn']) - state = model.state_dict() - for key in state.keys(): - if key in pretrained_model.keys(): - if (key=='features.0.weight' and is_gray): - state[key] = torch.mean(pretrained_model[key],1).unsqueeze(1) - else: - state[key] = pretrained_model[key] - model.load_state_dict(state) - return model - - -def vgg13(pretrained=False,is_gray=False, **kwargs): - """VGG 13-layer model (configuration "B") - - Args: - pretrained (bool): If True, returns a model pre-trained on ImageNet - """ - if pretrained: - kwargs['init_weights'] = False - model = VGG(make_layers(cfg['B'],is_gray=is_gray), **kwargs) - if pretrained: - pretrained_model = model_zoo.load_url(model_urls['vgg13']) - state = model.state_dict() - for key in state.keys(): - if key in pretrained_model.keys(): - if (key=='features.0.weight' and is_gray): - state[key] = torch.mean(pretrained_model[key],1).unsqueeze(1) - else: - state[key] = pretrained_model[key] - model.load_state_dict(state) - return model - - -def vgg13_bn(pretrained=False, is_gray=False,**kwargs): - """VGG 13-layer model (configuration "B") with batch normalization - - Args: - pretrained (bool): If True, returns a model pre-trained on ImageNet - """ - if pretrained: - kwargs['init_weights'] = False - model = VGG(make_layers(cfg['B'],is_gray=is_gray, batch_norm=True), **kwargs) - if pretrained: - pretrained_model = model_zoo.load_url(model_urls['vgg13_bn']) - state = model.state_dict() - for key in state.keys(): - if key in pretrained_model.keys(): - if (key=='features.0.weight' and is_gray): - state[key] = torch.mean(pretrained_model[key],1).unsqueeze(1) - else: - state[key] = pretrained_model[key] - model.load_state_dict(state) - return model - - -def vgg16(pretrained=False,is_gray=False, **kwargs): - """VGG 16-layer model (configuration "D") - - Args: - pretrained (bool): If True, returns a model pre-trained on ImageNet - """ - if pretrained: - kwargs['init_weights'] = False - model = VGG(make_layers(cfg['D'],is_gray=is_gray), **kwargs) - if pretrained: - pretrained_model = model_zoo.load_url(model_urls['vgg16']) - state = model.state_dict() - for key in state.keys(): - if key in pretrained_model.keys(): - if (key=='features.0.weight' and is_gray): - state[key] = torch.mean(pretrained_model[key],1).unsqueeze(1) - else: - state[key] = pretrained_model[key] - model.load_state_dict(state) - return model - - -def vgg16_bn(pretrained=False,is_gray=False, **kwargs): - """VGG 16-layer model (configuration "D") with batch normalization - - Args: - pretrained (bool): If True, returns a model pre-trained on ImageNet - """ - if pretrained: - kwargs['init_weights'] = False - model = VGG(make_layers(cfg['D'],is_gray=is_gray, batch_norm=True), **kwargs) - if pretrained: - pretrained_model = model_zoo.load_url(model_urls['vgg16_bn']) - state = model.state_dict() - for key in state.keys(): - if key in pretrained_model.keys(): - if (key=='features.0.weight' and is_gray): - state[key] = torch.mean(pretrained_model[key],1).unsqueeze(1) - else: - state[key] = pretrained_model[key] - model.load_state_dict(state) - return model - - -def vgg19(pretrained=False, is_gray=False,**kwargs): - """VGG 19-layer model (configuration "E") - - Args: - pretrained (bool): If True, returns a model pre-trained on ImageNet - """ - if pretrained: - kwargs['init_weights'] = False - model = VGG(make_layers(cfg['E'],is_gray=is_gray), **kwargs) - if pretrained: - pretrained_model = model_zoo.load_url(model_urls['vgg19']) - state = model.state_dict() - for key in state.keys(): - if key in pretrained_model.keys(): - if (key=='features.0.weight' and is_gray): - state[key] = torch.mean(pretrained_model[key],1).unsqueeze(1) - else: - state[key] = pretrained_model[key] - model.load_state_dict(state) - return model - - -def vgg19_bn(pretrained=False, is_gray=False,**kwargs): - """VGG 19-layer model (configuration 'E') with batch normalization - - Args: - pretrained (bool): If True, returns a model pre-trained on ImageNet - """ - if pretrained: - kwargs['init_weights'] = False - model = VGG(make_layers(cfg['E'],is_gray=is_gray, batch_norm=True), **kwargs) - if pretrained: - pretrained_model = model_zoo.load_url(model_urls['vgg19_bn']) - state = model.state_dict() - for key in state.keys(): - if key in pretrained_model.keys(): - if (key=='features.0.weight' and is_gray): - state[key] = torch.mean(pretrained_model[key],1).unsqueeze(1) - else: - state[key] = pretrained_model[key] - model.load_state_dict(state) - return model - diff --git a/ptocr/model/backbone/reg_mobilev3.py b/ptocr/model/backbone/reg_mobilev3.py deleted file mode 100644 index 5c799cd..0000000 --- a/ptocr/model/backbone/reg_mobilev3.py +++ /dev/null @@ -1,218 +0,0 @@ -""" -#!-*- coding=utf-8 -*- -@author: BADBADBADBADBOY -@contact: 2441124901@qq.com -@software: PyCharm Community Edition -@file: mobilenet.py -@time: 2020/4/5 19:12 -""" -from torch import nn -import torch -import torch.nn.functional as F -from torch.nn import init - -__all__ = ['mobilenet_v3_small','mobilenet_v3_large'] - -class hswish(nn.Module): - def forward(self, x): - out = x * F.relu6(x + 3, inplace=True) / 6 - return out - - -class hsigmoid(nn.Module): - def forward(self, x): - out = F.relu6(x + 3, inplace=True) / 6 - return out - - -class SeModule(nn.Module): - def __init__(self, in_size, reduction=4): - super(SeModule, self).__init__() - self.avg_pool = nn.AdaptiveAvgPool2d(1) - - self.se = nn.Sequential( - nn.Conv2d(in_size, in_size // reduction, kernel_size=1, stride=1, padding=0, bias=False), - nn.BatchNorm2d(in_size // reduction), - nn.ReLU(inplace=True), - nn.Conv2d(in_size // reduction, in_size, kernel_size=1, stride=1, padding=0, bias=False), - nn.BatchNorm2d(in_size), - hsigmoid() - ) - - def forward(self, x): - return x * self.se(x) - - -class Block(nn.Module): - '''expand + depthwise + pointwise''' - def __init__(self, kernel_size, in_size, expand_size, out_size, nolinear, semodule, stride): - super(Block, self).__init__() - self.stride = stride - self.se = semodule - - self.conv1 = nn.Conv2d(in_size, expand_size, kernel_size=1, stride=1, padding=0, bias=False) - self.bn1 = nn.BatchNorm2d(expand_size) - self.nolinear1 = nolinear - self.conv2 = nn.Conv2d(expand_size, expand_size, kernel_size=kernel_size, stride=stride, padding=kernel_size//2, groups=expand_size, bias=False) - self.bn2 = nn.BatchNorm2d(expand_size) - self.nolinear2 = nolinear - self.conv3 = nn.Conv2d(expand_size, out_size, kernel_size=1, stride=1, padding=0, bias=False) - self.bn3 = nn.BatchNorm2d(out_size) - - self.shortcut = nn.Sequential() - if stride == 1 and in_size != out_size: - self.shortcut = nn.Sequential( - nn.Conv2d(in_size, out_size, kernel_size=1, stride=1, padding=0, bias=False), - nn.BatchNorm2d(out_size), - ) - - def forward(self, x): - out = self.nolinear1(self.bn1(self.conv1(x))) - out = self.nolinear2(self.bn2(self.conv2(out))) - out = self.bn3(self.conv3(out)) - if self.se != None: - out = self.se(out) - out = out + self.shortcut(x) if self.stride==1 else out - return out - - -class MobileNetV3_Large(nn.Module): - def __init__(self, is_gray): - super(MobileNetV3_Large, self).__init__() - if(is_gray): - self.conv1 = nn.Conv2d(1, 16, kernel_size=3, stride=2, padding=1, bias=False) - else: - self.conv1 = nn.Conv2d(3, 16, kernel_size=3, stride=2, padding=1, bias=False) - self.bn1 = nn.BatchNorm2d(16) - self.hs1 = hswish() - - self.bneck = nn.Sequential( - Block(3, 16, 16, 16, nn.ReLU(inplace=True), None, 1), - Block(3, 16, 64, 24, nn.ReLU(inplace=True), None, (2,1)), - Block(3, 24, 72, 24, nn.ReLU(inplace=True), None, 1), - Block(5, 24, 72, 40, nn.ReLU(inplace=True), SeModule(40), (2,1)), - Block(5, 40, 120, 40, nn.ReLU(inplace=True), SeModule(40), 1), - Block(5, 40, 120, 40, nn.ReLU(inplace=True), SeModule(40), 1), - Block(3, 40, 240, 80, hswish(), None, 2), - Block(3, 80, 200, 80, hswish(), None, 1), - Block(3, 80, 184, 80, hswish(), None, 1), - Block(3, 80, 184, 80, hswish(), None, 1), - Block(3, 80, 480, 112, hswish(), SeModule(112), (2,1)), - Block(3, 112, 672, 112, hswish(), SeModule(112), 1), - Block(5, 112, 672, 160, hswish(), SeModule(160), 1), - Block(5, 160, 672, 160, hswish(), SeModule(160), 1), - Block(5, 160, 960, 160, hswish(), SeModule(160), 1), - ) - self.init_params() - - def init_params(self): - for m in self.modules(): - if isinstance(m, nn.Conv2d): - init.kaiming_normal_(m.weight, mode='fan_out') - if m.bias is not None: - init.constant_(m.bias, 0) - elif isinstance(m, nn.BatchNorm2d): - init.constant_(m.weight, 1) - init.constant_(m.bias, 0) - elif isinstance(m, nn.Linear): - init.normal_(m.weight, std=0.001) - if m.bias is not None: - init.constant_(m.bias, 0) - - def forward(self, x): - out = self.hs1(self.bn1(self.conv1(x))) - out = self.bneck(out) - return out - - - -class MobileNetV3_Small(nn.Module): - def __init__(self, is_gray): - super(MobileNetV3_Small, self).__init__() - if(is_gray): - self.conv1 = nn.Conv2d(1, 16, kernel_size=3, stride=2, padding=1, bias=False) - else: - self.conv1 = nn.Conv2d(3, 16, kernel_size=3, stride=2, padding=1, bias=False) - self.bn1 = nn.BatchNorm2d(16) - self.hs1 = hswish() - - self.bneck = nn.Sequential( - Block(3, 16, 16, 16, nn.ReLU(inplace=True), SeModule(16), (2,1)), - Block(3, 16, 72, 24, nn.ReLU(inplace=True), None, 1), - Block(3, 24, 88, 24, nn.ReLU(inplace=True), None, (2,1)), - Block(5, 24, 96, 40, hswish(), SeModule(40), 1), - Block(5, 40, 240, 40, hswish(), SeModule(40), 1), - Block(5, 40, 240, 40, hswish(), SeModule(40), 2), - Block(5, 40, 120, 48, hswish(), SeModule(48), 1), - Block(5, 48, 144, 48, hswish(), SeModule(48), (2,1)), - Block(5, 48, 288, 96, hswish(), SeModule(96), 1), - Block(5, 96, 576, 96, hswish(), SeModule(96), 1), - Block(5, 96, 576, 96, hswish(), SeModule(96), 1), - ) - -# self.bneck = nn.Sequential( -# Block(3, 16, 16, 16, nn.ReLU(inplace=True), SeModule(16),1), -# Block(3, 16, 72, 24, nn.ReLU(inplace=True), None, (2,1)), -# Block(3, 24, 88, 24, nn.ReLU(inplace=True), None, 1), -# Block(5, 24, 96, 40, hswish(), SeModule(40), (2,1)), -# Block(5, 40, 240, 40, hswish(), SeModule(40), 1), -# Block(5, 40, 240, 40, hswish(), SeModule(40), 1), -# Block(5, 40, 120, 48, hswish(), SeModule(48), 2), -# Block(5, 48, 144, 48, hswish(), SeModule(48), 1), -# Block(5, 48, 288, 96, hswish(), SeModule(96),(2,1)), -# Block(5, 96, 576, 96, hswish(), SeModule(96), 1), -# Block(5, 96, 576, 96, hswish(), SeModule(96), 1), -# ) - - self.init_params() - - def init_params(self): - for m in self.modules(): - if isinstance(m, nn.Conv2d): - init.kaiming_normal_(m.weight, mode='fan_out') - if m.bias is not None: - init.constant_(m.bias, 0) - elif isinstance(m, nn.BatchNorm2d): - init.constant_(m.weight, 1) - init.constant_(m.bias, 0) - elif isinstance(m, nn.Linear): - init.normal_(m.weight, std=0.001) - if m.bias is not None: - init.constant_(m.bias, 0) - - def forward(self, x): - out = self.hs1(self.bn1(self.conv1(x))) - out = self.bneck(out) - return out - -def mobilenet_v3_small(pretrained, is_gray=False,**kwargs): - model = MobileNetV3_Small(is_gray=is_gray) - if pretrained: - pretrained_dict = torch.load('./pre_model/mbv3_small.old.pth.tar')['state_dict'] - - state = model.state_dict() - for key in state.keys(): - if 'module.' + key in pretrained_dict.keys(): - if(key=='conv1.weight'and is_gray): - state[key] = torch.mean(pretrained_dict['module.' + key],1).unsqueeze(1) - else: - state[key] = pretrained_dict['module.' + key] - model.load_state_dict(state) - return model - -def mobilenet_v3_large(pretrained,is_gray=False,**kwargs): - model = MobileNetV3_Large(is_gray=is_gray) - if pretrained: - pretrained_dict = torch.load('./pre_model/mbv3_large.old.pth.tar')['state_dict'] - - state = model.state_dict() - for key in state.keys(): - if 'module.'+key in pretrained_dict.keys(): - if(key=='conv1.weight'and is_gray): - state[key] = torch.mean(pretrained_dict['module.' + key],1).unsqueeze(1) - else: - state[key] = pretrained_dict['module.' + key] - model.load_state_dict(state) - return model - - diff --git a/ptocr/model/backbone/reg_resnet.py b/ptocr/model/backbone/reg_resnet.py deleted file mode 100644 index 832aa58..0000000 --- a/ptocr/model/backbone/reg_resnet.py +++ /dev/null @@ -1,368 +0,0 @@ -#-*- coding:utf-8 _*- -""" -@author:fxw -@file: det_resnet.py.py -@time: 2020/08/07 -""" -import torch -import torch.nn as nn -import math -import torch.utils.model_zoo as model_zoo - -BatchNorm2d = nn.BatchNorm2d - -__all__ = ['ResNet', 'resnet18', 'resnet34', 'resnet50', 'resnet101', - 'resnet152'] - -model_urls = { - 'resnet18': 'https://download.pytorch.org/models/resnet18-5c106cde.pth', - 'resnet34': 'https://download.pytorch.org/models/resnet34-333f7ec4.pth', - 'resnet50': 'https://download.pytorch.org/models/resnet50-19c8e357.pth', - 'resnet101': 'https://download.pytorch.org/models/resnet101-5d3b4d8f.pth', - 'resnet152': 'https://download.pytorch.org/models/resnet152-b121ed2d.pth', -} - - -def load_pre_model(model,pre_model_path): - pre_dict = torch.load(pre_model_path) - model_pre_dict = {} - for key in model.state_dict().keys(): - if('model.module.backbone.'+key in pre_dict.keys()): - model_pre_dict[key] = pre_dict['model.module.backbone.'+key] - else: - model_pre_dict[key] = model.state_dict()[key] - model.load_state_dict(model_pre_dict) - return model - - -def constant_init(module, constant, bias=0): - nn.init.constant_(module.weight, constant) - if hasattr(module, 'bias'): - nn.init.constant_(module.bias, bias) - - -def conv3x3(in_planes, out_planes, stride=1): - """3x3 convolution with padding""" - return nn.Conv2d(in_planes, out_planes, kernel_size=3, stride=stride, - padding=1, bias=False) - - -class BasicBlock(nn.Module): - expansion = 1 - - def __init__(self, inplanes, planes, stride=1, downsample=None, dcn=None): - super(BasicBlock, self).__init__() - self.with_dcn = dcn is not None - self.conv1 = conv3x3(inplanes, planes, stride) - self.bn1 = BatchNorm2d(planes) - self.relu = nn.ReLU(inplace=True) - self.with_modulated_dcn = False - if self.with_dcn: - fallback_on_stride = dcn.get('fallback_on_stride', False) - self.with_modulated_dcn = dcn.get('modulated', False) - # self.conv2 = conv3x3(planes, planes) - if not self.with_dcn or fallback_on_stride: - self.conv2 = nn.Conv2d(planes, planes, kernel_size=3, - padding=1, bias=False) - else: - deformable_groups = dcn.get('deformable_groups', 1) - if not self.with_modulated_dcn: - from models.dcn import DeformConv - conv_op = DeformConv - offset_channels = 18 - else: - from models.dcn import ModulatedDeformConv - conv_op = ModulatedDeformConv - offset_channels = 27 - self.conv2_offset = nn.Conv2d( - planes, - deformable_groups * offset_channels, - kernel_size=3, - padding=1) - self.conv2 = conv_op( - planes, - planes, - kernel_size=3, - padding=1, - deformable_groups=deformable_groups, - bias=False) - self.bn2 = BatchNorm2d(planes) - self.downsample = downsample - self.stride = stride - - def forward(self, x): - residual = x - - out = self.conv1(x) - out = self.bn1(out) - out = self.relu(out) - - # out = self.conv2(out) - if not self.with_dcn: - out = self.conv2(out) - elif self.with_modulated_dcn: - offset_mask = self.conv2_offset(out) - offset = offset_mask[:, :18, :, :] - mask = offset_mask[:, -9:, :, :].sigmoid() - out = self.conv2(out, offset, mask) - else: - offset = self.conv2_offset(out) - out = self.conv2(out, offset) - out = self.bn2(out) - - if self.downsample is not None: - residual = self.downsample(x) - - out += residual - out = self.relu(out) - - return out - - -class Bottleneck(nn.Module): - expansion = 4 - - def __init__(self, inplanes, planes, stride=1, downsample=None, dcn=None): - super(Bottleneck, self).__init__() - self.with_dcn = dcn is not None - self.conv1 = nn.Conv2d(inplanes, planes, kernel_size=1, bias=False) - self.bn1 = BatchNorm2d(planes) - fallback_on_stride = False - self.with_modulated_dcn = False - if self.with_dcn: - fallback_on_stride = dcn.get('fallback_on_stride', False) - self.with_modulated_dcn = dcn.get('modulated', False) - if not self.with_dcn or fallback_on_stride: - self.conv2 = nn.Conv2d(planes, planes, kernel_size=3, - stride=stride, padding=1, bias=False) - else: - deformable_groups = dcn.get('deformable_groups', 1) - if not self.with_modulated_dcn: - from models.dcn import DeformConv - conv_op = DeformConv - offset_channels = 18 - else: - from models.dcn import ModulatedDeformConv - conv_op = ModulatedDeformConv - offset_channels = 27 - self.conv2_offset = nn.Conv2d( - planes, deformable_groups * offset_channels, - kernel_size=3, - padding=1) - self.conv2 = conv_op( - planes, planes, kernel_size=3, padding=1, stride=stride, - deformable_groups=deformable_groups, bias=False) - self.bn2 = BatchNorm2d(planes) - self.conv3 = nn.Conv2d(planes, planes * 4, kernel_size=1, bias=False) - self.bn3 = BatchNorm2d(planes * 4) - self.relu = nn.ReLU(inplace=True) - self.downsample = downsample - self.stride = stride - self.dcn = dcn - self.with_dcn = dcn is not None - - def forward(self, x): - residual = x - - out = self.conv1(x) - out = self.bn1(out) - out = self.relu(out) - - # out = self.conv2(out) - if not self.with_dcn: - out = self.conv2(out) - elif self.with_modulated_dcn: - offset_mask = self.conv2_offset(out) - offset = offset_mask[:, :18, :, :] - mask = offset_mask[:, -9:, :, :].sigmoid() - out = self.conv2(out, offset, mask) - else: - offset = self.conv2_offset(out) - out = self.conv2(out, offset) - out = self.bn2(out) - out = self.relu(out) - - out = self.conv3(out) - out = self.bn3(out) - - if self.downsample is not None: - residual = self.downsample(x) - - out += residual - out = self.relu(out) - - return out - - -class ResNet(nn.Module): - def __init__(self, block, layers, num_classes=1000, - dcn=None, stage_with_dcn=(False, False, False, False)): - self.dcn = dcn - self.stage_with_dcn = stage_with_dcn - self.inplanes = 64 - super(ResNet, self).__init__() - self.conv1 = nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3, - bias=False) - self.bn1 = BatchNorm2d(64) - self.relu = nn.ReLU(inplace=True) - self.maxpool = nn.MaxPool2d(kernel_size=3, stride=(2,1), padding=1) - self.layer1 = self._make_layer(block, 64, layers[0]) - self.layer2 = self._make_layer( - block, 128, layers[1], stride=2, dcn=dcn) - self.layer3 = self._make_layer( - block, 256, layers[2], stride=(2,1), dcn=dcn) - self.layer4 = self._make_layer( - block, 512, layers[3], stride=(2,1), dcn=dcn) - # self.avgpool = nn.AvgPool2d(7, stride=1) - # self.fc = nn.Linear(512 * block.expansion, num_classes) - - # self.smooth = nn.Conv2d(2048, 256, kernel_size=1, stride=1, padding=1) - - for m in self.modules(): - if isinstance(m, nn.Conv2d): - n = m.kernel_size[0] * m.kernel_size[1] * m.out_channels - m.weight.data.normal_(0, math.sqrt(2. / n)) - elif isinstance(m, BatchNorm2d): - m.weight.data.fill_(1) - m.bias.data.zero_() - if self.dcn is not None: - for m in self.modules(): - if isinstance(m, Bottleneck) or isinstance(m, BasicBlock): - if hasattr(m, 'conv2_offset'): - constant_init(m.conv2_offset, 0) - - def _make_layer(self, block, planes, blocks, stride=1, dcn=None): - downsample = None - if stride != 1 or self.inplanes != planes * block.expansion: - downsample = nn.Sequential( - nn.Conv2d(self.inplanes, planes * block.expansion, - kernel_size=1, stride=stride, bias=False), - BatchNorm2d(planes * block.expansion), - ) - - layers = [] - layers.append(block(self.inplanes, planes, - stride, downsample, dcn=dcn)) - self.inplanes = planes * block.expansion - for i in range(1, blocks): - layers.append(block(self.inplanes, planes, dcn=dcn)) - - return nn.Sequential(*layers) - - def forward(self, x): - x = self.conv1(x) - x = self.bn1(x) - x = self.relu(x) - x = self.maxpool(x) - - x = self.layer1(x) - x = self.layer2(x) - x = self.layer3(x) - x = self.layer4(x) - - return x - - -def resnet18(pretrained=True, load_url=False,**kwargs): - """Constructs a ResNet-18 model. - Args: - pretrained (bool): If True, returns a model pre-trained on ImageNet - """ - model = ResNet(BasicBlock, [2, 2, 2, 2], **kwargs) - if pretrained: - if load_url: - model.load_state_dict(model_zoo.load_url( - model_urls['resnet50']), strict=False) - else: - model = load_pre_model(model,'./pre_model/pre-trained-model-synthtext-resnet18.pth') - return model - - -def deformable_resnet18(pretrained=True,load_url=False, **kwargs): - """Constructs a ResNet-18 model. - Args: - pretrained (bool): If True, returns a model pre-trained on ImageNet - """ - model = ResNet(BasicBlock, [2, 2, 2, 2], - dcn=dict(modulated=True, - deformable_groups=1, - fallback_on_stride=False), - stage_with_dcn=[False, True, True, True], **kwargs) - if pretrained: - if load_url: - model.load_state_dict(model_zoo.load_url( - model_urls['resnet50']), strict=False) - else: - model = load_pre_model(model,'./pre_model/pre-trained-model-synthtext-resnet18.pth') - return model - - -def resnet34(pretrained=True, **kwargs): - """Constructs a ResNet-34 model. - Args: - pretrained (bool): If True, returns a model pre-trained on ImageNet - """ - model = ResNet(BasicBlock, [3, 4, 6, 3], **kwargs) - if pretrained: - model.load_state_dict(model_zoo.load_url( - model_urls['resnet34']), strict=False) - return model - - -def resnet50(pretrained=True,load_url=False,**kwargs): - """Constructs a ResNet-50 model. - Args: - pretrained (bool): If True, returns a model pre-trained on ImageNet - """ - model = ResNet(Bottleneck, [3, 4, 6, 3], **kwargs) - if pretrained: - if load_url: - model.load_state_dict(model_zoo.load_url( - model_urls['resnet50']), strict=False) - else: - model = load_pre_model(model,'./pre_model/pre-trained-model-synthtext-resnet50.pth') - return model - - -def deformable_resnet50(pretrained=True,load_url=False, **kwargs): - """Constructs a ResNet-50 model with deformable conv. - Args: - pretrained (bool): If True, returns a model pre-trained on ImageNet - """ - model = ResNet(Bottleneck, [3, 4, 6, 3], - dcn=dict(modulated=True, - deformable_groups=1, - fallback_on_stride=False), - stage_with_dcn=[False, True, True, True], - **kwargs) - if pretrained: - if load_url: - model.load_state_dict(model_zoo.load_url( - model_urls['resnet50']), strict=False) - else: - model = load_pre_model(model,'./pre_model/pre-trained-model-synthtext-resnet50.pth') - return model - - -def resnet101(pretrained=True, **kwargs): - """Constructs a ResNet-101 model. - Args: - pretrained (bool): If True, returns a model pre-trained on ImageNet - """ - model = ResNet(Bottleneck, [3, 4, 23, 3], **kwargs) - if pretrained: - model.load_state_dict(model_zoo.load_url( - model_urls['resnet101']), strict=False) - return model - - -def resnet152(pretrained=True, **kwargs): - """Constructs a ResNet-152 model. - Args: - pretrained (bool): If True, returns a model pre-trained on ImageNet - """ - model = ResNet(Bottleneck, [3, 8, 36, 3], **kwargs) - if pretrained: - model.load_state_dict(model_zoo.load_url( - model_urls['resnet152']), strict=False) - return model diff --git a/ptocr/model/head/__init__.py b/ptocr/model/head/__init__.py deleted file mode 100644 index 35de2ad..0000000 --- a/ptocr/model/head/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -#-*- coding:utf-8 _*- -""" -@author:fxw -@file: __init__.py.py -@time: 2020/08/07 -""" diff --git a/ptocr/model/head/__pycache__/__init__.cpython-35.pyc b/ptocr/model/head/__pycache__/__init__.cpython-35.pyc deleted file mode 100644 index 307d7364d5bc8d2ead367985f592d2a2439d3ead..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 224 zcmWgR<>mVJMJGOjfq~&M5W@i@kmUfx#auulg@GXoNHQ`6Ycf@taycZHmSp4?S*2B! zb2+4C=A>FF#K&jmWtPOp>lIW25tljhK)s2r}U|@I*#Bjg}WH|tFF$a)HVTfW#VGL%_WU4ada!4#K$;dCVN~}zQ`1q9! wMNB|5!Ne~)J^g}`{Ny72-29Z(9Q};c#1wrb({6Ft03}LuQtjA4ZUJHj0IvHiZvX%Q diff --git a/ptocr/model/head/__pycache__/det_DBHead.cpython-35.pyc b/ptocr/model/head/__pycache__/det_DBHead.cpython-35.pyc deleted file mode 100644 index d8eb8b0e6eb7008be9fac9ff6fb82aef14065eca..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2057 zcma)7-ESL35T8At@0_y}r%BT`RaNzYN;QZyI0>K#(I}}34-l%h2w@qmj=OVm=04oL zGf9K<6h8hIUidHgC-xDZEM9ov3Gwxtb=;%^qV8pW-kF`9otd54T$!EqzLIVEgG=-? zof|ghTcDx?2#N3$3JNVs1a&Z(%q{9ys8gX3e>NpHrB&)wDRrphQ0h|0CGlQZrO=_T zVZfoNLj4+@VABj6d)#u5*1au%SnlU)vv+vlZS~?L+PoD;rM$oWe&mNMgJ<4W8K)4o zSKF(1SKq$7`j$6_SF}5}fUS!5cJl1Y?d(yM40UZdDExGgM7lAZNk0q&-Y^{;9FO7+ zK!o5B4bbASz9Z_H;1JWdh}A@f5a55u=GDh7N4xj8B|G;Zn3}4>(hR7=W`7h!CH@4B zh)$3si%y6{S)mb3*J*iVj+!J9=Zq?JQWemZHk~RpE>NmYm1Zwca;8di7bv+#$s+d0 zq3e88aL_e4am%*Yt=dcK&AgYBX~Yf z{YxzGFHnDxP6UakPsEw(Wuy8;JlE(iQHV-}9*eH^hekx~w{bxq1Ri%TgLThx^}+hAB{SYv2w zJ0F%C2G66O&-M<7II;Wi8SX=+4^`feg0cW#<|^3#Xy;LPQ09T^BAh7c z?xSD3FT5-265MHkikkp(7OmUDeAXRdK5J1dh-J{CIGR6g`ITv21HLXE3m|U)9Z>Nb z08QSt>DZ#<3fJp#)s!&gRW7b$$FO)CY{R;SMGxW;z-rB~Xg<6dV9`-Fjj+Y#0zO9L zfc0pE(gj;LY@J3ZTd)l(Cuc$5;q4>z4dk;^InMIV$>*kW99lmoZyFzF%y#%NPo+nF zoPx%iqY+l;sb}*e!N$>bm(VHJuEPHiYBdz+LoG5~W>{k2{3xz!b%o(7!!?FwfUcSg z)0Mz4TMOzH7KuY`1?`R%tbx`+8~)|GnrY*GR2N|=63Ql&9eu? rEGXkVGp#*ID<@bxADYHBrq0ei7kcfTiM#Le6H#zCI--F%Z&?2T6WGM0 diff --git a/ptocr/model/head/__pycache__/det_DBHead.cpython-36.pyc b/ptocr/model/head/__pycache__/det_DBHead.cpython-36.pyc deleted file mode 100644 index 0820132f464dc027cdd6c41005756fb8cc179d67..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1770 zcmZ`(OK&7K5VqaV>3L-G*j+ZPP$a~okx0TM0Tiv+D2Wyhki&9-v_MwV?#Xo1k6CvQ z2~p2!BJBx@AHjtm!k_RFE<57F8S$#>nQXEmajQO8`LoM*xvIC?ZReNKF+a8l`Hfs_ zI_P^K1%eH0=D8!fRMn^iN}QbjP&6uLc3_d z6?9=-5Kn(bbmAF!P-PwNb3D=G;BvhI%WV)1eLbXvl%%8;nXs5m2qmRfQQ_Pt6@$=# z=L!PzgIN)-*G%&U#;BRr4UAbc?Hd>i7zTTos~BR~>BT%*pF1UKM;ctY$0Ni`ZC`Q9jN)u))oAW4(O3o!ZQd*Sl zAZ#OSA@r1)4oc~PzTwC85mU$pI;D}+eF@T)kc&Q*|7q)1D6t4jgv%^+t{k`Zrv_qF@ z3-G)5;Z|GH zE>C?a-T%ccDKJbn?cA?NTt``)i#&jJ?*O6fI#%mh zCSL{9>m1~1nrDwkSx`oKRu|kX&=_)MepZ)L?dodZYr(J|&QAXk7H)wxqEj1h&i(^Y C{InAQ diff --git a/ptocr/model/head/__pycache__/det_FPEM_FFM_Head.cpython-35.pyc b/ptocr/model/head/__pycache__/det_FPEM_FFM_Head.cpython-35.pyc deleted file mode 100644 index 171d42e497bf380fbc03a3e017a049b91f868c45..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4122 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 L+quQs;(PxEPe3aP diff --git a/ptocr/model/head/__pycache__/det_FPEM_FFM_Head.cpython-36.pyc b/ptocr/model/head/__pycache__/det_FPEM_FFM_Head.cpython-36.pyc deleted file mode 100644 index 1342a4b2411137b9999a95835927901e5269e4f5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3585 zcmbVOTXP&o74Dw<&R%q}Y)eWo28@#pM3UICg zXP<^oml*pOyYyLT@1taA5P}6vaAEX#z;$gTW?-Ua_N=}g*nKB(`flL%y};w_Qzk57 zzhuIW&7?9h10NPgxUjfWOBEJRRA8x0Ej3~MmNos0kNo{;l%Eac!_N5&f4|f1#Sia_ zI1dkh{j;O+@bD=7c^rwY;UE3|yxT`#Yp1odz4PJr&IkT0Y?U21E#*ErdE6VcpDF)f zkpAIu`kS~nQnk@Ai~7S}97aMYZ>F_r((E&*jqDy8F~ezDV0_6s`qVEyoY@rCOBPsP zGK&Q^c5EvXn`-mvM5<{KD%mPT&T?L`iIEr+#&MCD!jGG#va``JmeRox<%MB4?dDU$=E53f)~yPaInEX zU}K{&r@Enav#_SRsdcNcr@Eze`}y4yhBauM-LdHAjwRjJD=lbiujta6hO%Xpp2o`T z#c9)(6&Mvi54?UfB+u>6TdjF(Z{B*Kcvn_2r4)1*%Iyr~i%1GUoMjCN#+UgT3jNmj zMU^gTI;QE6H`1sdhoSPr5EniIVnS65!{?)@SN7<8mTR{O{chPJuntQ4$1~1atkV{JIa?hS}OC)5+swpC%P!DbaT*y?(21-1rk zcF}-s2{yZMVOxgHDVAYdDXK-iSSnUDGk_pnNn4b3LirYnw@G}9#2pgfhG?$pJCk%E zWt_KA_D~+6{3xix?_i4%tg#!oP;}#xwKcQ0E|1-tS@(jv2F84iidOY1S9)5(yr*?_ z&fcPDZ$S(tBM^7Ihmz5`82sgP-*{62g_9TO^dxU$yu3x?HVKV%T1~E#m{Z>jHX1R6 z7V4hy&x($cEBr&(O<#5&VwU-Yt8*Xayb5xkEu1U4&r=#=^Q^2R^RP`ML?4ACZBG-@ zebD@MLZW0+7=nIC48r;|U{_L;u;0+5NlGB$T$`imN+9)I>mlMYgtKZ}@qp*{u1n>l9eKqY}Awnzd$*OQQW!cFMP}{0NV9>}DcK%+zhJfLq z!aHLpzggg!liVsCVccdDp5I2`@QI1BU!SmieZp|&4J1YuZtC}ef0^tkJ0dAnyXk@y zYJ*X}(5la339bmU?nSJeESFsoD|ZOsf`=r5O#9^(dH6bQUbvlM+z3LH65U2e}_1wM-fq}enM#i zWrPBd90#RRZ4^jrs7)f)jx~1AALK~J39P`%{jrw=|AHrO;gwjK7$8beFw&w&EnsFJ z&nT7<`4SELNwugD?(-UIiVUqykbKd?)9Z>(F*WV)SYk%sbxq%X;p0)}9M1t!CR@Uv z#*a?G&4&Dl3=c{Cgv5s=$cfqq$~`STDL;WpIni(!r$Rm=#m)z^eWuJd{7V!eKZa;l z7aSfq=V7l40KZSYTO_tg>_7zG(&9w*_1*sh=H(Rf diff --git a/ptocr/model/head/__pycache__/det_FPNHead.cpython-35.pyc b/ptocr/model/head/__pycache__/det_FPNHead.cpython-35.pyc deleted file mode 100644 index f3652548def3cb8bed56c513cc2a07653d9b830c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2166 zcma)6-ESL35T8At&-U3#+I*FMP(?fdF4Rrj22g}()S$pi)2av|ETh$NcaG1!4|nfO zK8T-!=-D;if z-p0&3AOR8n0zrXAv7inXleI;I5_L+3Em3S!Ql?Is5{EhtB^ByaNPH656qM;ZI4DzC zqCu5TVN_#hpEaH1O?TTH75z+Y^$ri*?OqgzTWdjB$lb?#pN3x08XmdZMU+6=UT?48 zS^wb9`upw~jzxRTl6H5p^qU9ilQ16X#%P#($uJJ37X-RG<$T6Y2Vaawz5x>AVz>#Y z#dmm4)H4?gO7uPMiHl9o-mzJW^Xe+jCHY{x{?u0)uIiX2_WPS4D)19@LUf7Ksi@PSVd7Au<^K=I9338T*a!0*^N+uZXpC4c&$ShUJUEUHgUzX%#gkITnjx@#}S*3eU4 zvKFP%?|W$)#`)H(x-?Y(t^PXl&e@zHu}IgSh6=u99vz3;$qN+)p{@*(@KL(248&%; zGfu`ciwURcJ|@Pm_xEq_-1z0E_paUFJNp~s>@OyNGWi3fc}17=(J)ksb6QuWjMAu( zQn4dlEwW+k9fe9)@+8ZO{>F6Co-Xd{TI>}Dv@u(@XUn_VN>lA{)wWTiKK^AkDs&~u zf>9ji+Kuuk%?mH}Lv8c!df}-TCAghTGnII;)XqT|?e`092VUXn`U9`<`+LyAVHy1+ zmdeF(n`Le1VcgU8b5Ee>BRs_{<_4)n(iFNTr5}5FF69sO`@@~B?&o=^@@~&d_YX(d zvHSQ452=#JDjS4;k%KQX<@X=$Jn0UL%vW7pA&k3y^oH)g8<<+cc^a7cO%QUHtd@04 z;Imo+pS58viA8Z0b4eUuo;H})H2OYAp9eP?ewZE1+`(g`rF3l?TQn|ls*lSi`ynrL zUXIZ!Pz>lqwqa2PXetnbg-OL|(P=71J7I0phhSSWU= z)MCL6DAiAsCL)>_rO)7T7~>h=I;FDItm|T9YYD*_2SCjG7An2B{fj16q)_*VOSJ#R&77e6|ww(`r9NtXETEfAreK`)U5}x1ed%@ zPGW6WN+u%K&IpPmDGs!WmNUG@8AE{=d;k;1A9oLK?Y{WiFK@5i-JksnWA+bqQ{`o;hPM}>zW`g>%R<#`x_t)wXK+iq z5fnHt$4WbSDaKh`X+NowqEcZI#hMj`wy_8LLYsKHzVskWa`-S_lrj%9uI-~ZIT$K! zicp2RbuU!Wa346Zu%O*CE`12MYiYBJvw?1%y9C`y;4;p#-t5#g?^5k?9%W%wasDIu z^X1;gNR^S?fDq$sV+ftH@oW#vWjLb^vw8^xu{&&ytx|Z{8ifZ|9onH+X&dCpl|{St z78#8&qc~bHG1G(2(H6|NVBQJX2q)mtmS&@VU=GGrEZ;y^jd&C7oIxp%LMdK@MfC~@ zLfg!z&pmS;TLI?e^3!`cHSW^Au8nnHgn7)l_Bo#yDd0BehvP7tGtKqOJ|4VuS~yV* zrN__0>QebS@DihI`U$VP!f$tJ=Vqhn`r6@Klo8n70s>nMlF};*iB%P>HAUEKimX~%d(=xwc9knCV(u<*EnvgM6S}8D28RX0IOob@-FbA%#xC$ zN@xKENniU1ioUje>Obfo=u3gV>{Ee05$G={(98FoSuU3p9U*?`l6!W}IdkTA=3Hj3 z=JQ$WuaDiIt||2o6?x()Z{hX+1W;9~1Zb(Mt=g6-4}=M&cJK+x)m#EjNdd~HtzifSRa2& zoho&XRkzi-QdWPo@@!UGHtTzyuu#v9t4k^*q=Kyc>=u$z0UAh4`xm=NNd=oKn%zQr zQ~`CMFd650g41v_!a~dVPqDJEX3Yr;-Qhnam5Z=uE=XSWWq)9cU=cZn!ibU{Q5J+E zqYCOQjwslyMrw)r-E(rgEvjg!pq(N0r3w7ZajV~SHTKL-OU1$ZGpH?vEj9cVN<(VY zDf$U-a9Gni0nAUkZmZksyRIhF&(#mR?%>d^VE5ggz&0naI}_O53GChkc7FnUFo8Xs zz~1-s(cU*SW}y7}fL5)UF-_u|Q~Wzq_+7{H<6f;@$2a4;m3Gc~TPOn;P?>Aw>`&0I; z^BC(qUhg`9vWoUiEBM)K7Jh&??Inx9ldBVUw?5K9g{IFZhc}6L8?Sc^&7O)WukvFn z`NYg8ZaxXM+DND~vC)ID`njdfWY-*kVMIxaar?oHNkTIu#f1b0NNAEIF_Z%^Ktl5* zC8ZV^*uy|m>JNJWLBIq2xdWS)J2k339b;lL9k4Kz^C+8f)xNKr{BcKw_f^o za|7>fyqkFM;JxeH4bU2(H9%{C*0`&=)eQI+B~$gDJ}&F(cYt^s0LpT#;^=1=?5tJb z?^R#xWO;I92JInIz)N(?oto?VS=WUX4B9N`T=%m{H}4|A%1TP zzdwaPn8F`o$I*&L8&51mb{|Lh1g}R64gIryQ~dIpb#iTL^IY`GTnTPn@ydTgA#jcS zB#j?V!4JnK{czw;qkh;j?lkI$;nnHOLO*OgEL<)A^sv}G$Z&xq#d#S|3>Zb>e?t^3 zdRS~JBbE{|)PT=}_l=_Psv(-fK1lCQxjS)z!~fzL{)gb-08Uol!wTs81P=%v65J&C zHo^M@j|e^>cnsjAMKzl4Nxx0-U4nNA9D;WV-XoZFY~ob*K=-Ixg;*+1%`R9|4$V4Q znbu-H1(e#_f zMJr?T-md{>c&nG&l5kp7>yw||>%rtSW`D~aZK0>3yj8%I0@eaDa%BVNNv8DFtQ0$1Ko zXk=YAKjqc>SOM)Pp{P!|FFwSvgGkt#ZN^PB;IVC(F@ASoyi;p=(Rdkf!<6cuSXwO6 zfnt7SPB!qkNQs!8V@)MDLIJ7L1Q-4|%Eci^GOj>2KFJl!#i8^Zy@)vmWf)&}Mi)xKY9hx`zk_;6T^Z+ps<*dUD&h&}YY@*M|G_jhP zCMslQutgax1fwf1`6abAO8q9dl17eoiTn=(lLnFV(KgPIG~yxv%YQ{$N74vy33ORd zyW60IBgDpd-;0bT-x%YRrq|wV>TV5*=J3i5dlwTn&a0NMbCP zNaA#97Cr3z4K~3wT~MbBs=5YU;lOEh1^mr`7l@$}_6KU?2x@e-IMpXd5KPnP3KbS* zuy4s=AsE$_sEFEFoW+$Se5|Xbk*?6@lF?OB#(}IIN-L@cI+v65*_Fv|a=|Q5jY_X5 z6!x6!yO;Q^Ft<1PK3!OY4o0cJ|3KfsXUN0yP%@woQPLj)IB`>ZEKKkCji|9m3lwhr z<%3_=fBU<4w)FQRP4Un%4$7krSYJ{$la2+0`6kr=&%mV@S(zMe9-8Gtxc!E4`}&8N zgP*9hdtDH5C(#-7Ykopb-F`~fOzk8BLub&I>;e&^SMPVqFD1?JW0i8>Ssv#VrBvM& zAr|^g^ioRH-NR<96sx<9Qo7y@s7aJaj^o=v%yLu&vYBahMXF^*TV*~H#+?LR!RPcrmb0j_$9!8WN%L zD{~Vm-r`H#;35x!Pfe&V#y62kKRvn{eO}>q;lnTDv`K@WpfDp1!X+cZDAFEYI3lkx zWrR^=w5E)(BVNeG$&4wCwd-U|F7gabnkg6lvS0FHB=POm#_ zEAyV-<7J4F`voNBb_VasLOA2s-g{KiAJ{y1Sk78bYEqnnpK>w03T`bw0q&>8W2|?( zzTNGXiX&{jKIm4MTbJ8@c3hUZm*%%+LOVef$vn-CWAYQZ;E?MHbG+R`C(?6d`r*;i ePd_#{BHw3t@D>1)P&i$ikw*q2^*6vc5u8`@p1Rx8CXZ6sywIPgZXWH}(>jA;&^;bYY^ z+*KB{NdOA)!3f}U4neN@2RiDQ9D-bOnp5DMh+J|GkjGawGhC7@36X<@gDzH8SNC-H zRDV_7mvcEQ_osW#hc8OfU!ZmZi5PS9A4;lB?HK9cz@TWS}uz6B;wtWT8pB7Bp6@$w8BGv(RK?jqNIT zq;l?X%i69E{nkLd)_8DWZ8zH8`fKOiy6@ci_MN-$)T{35-cPMZmsIXgqIJ?PiuxC?r(i9%g^U1)vDmjh{}bd-@KOFpk4 zxmdGY4CzkacG=6dBW=z|S7xNG8R_bbbZtiZ zR+yW9`?{dL8``nXtw&>;l{e<dg#W^PpdT5A_|XrW=>!b1I|UG=aZRj=M{ zgmRC*h~w0{RnK#r-%5YKu)4PA4{Bl!BdB-RTG-xeb9-InFhv{1dj&+2ZDm=GAEhMY z0bN$IGCztf+t3j`T%OsftI0>K(N?8j7DY$7iQ?ICm;n3!G*N|VimA#}J1sS}BW&nS zY&DXPumL)9WDO|=Nr8k`kW@%1NN5cSTd<=-LNAc8^*dUifHMLkNUcamrmuI5z=Xy` zojKvsF=qxUtkP_ihE*C?R*-O+m`ev5tSq*&U}eE78zfvV=Bz-6RferHu*$&74iYXO za~W(ZeV2EUFt9#?7ENS8#^iD3f}k%V@qR6APp|O=OYC86rzW z&JtNBvI0`hif`aAlplmjYaQhh$_C11l+93SLf3?@30)Jq=B6NTh{&td(r66CK~=b< z_SQip`2za^`GT!jiYZ(4JndgTT$=TlxWBSa`@di9)g33a94Fc;;Ioc%e^~8CHSDC~ zH5y1R_U~F98^AdpW?2?x)C(Fd;|lq;N2_Vd*>kL^L74mqYv}hp!6{EuxHUNFKQla) z;OqZkcsdz~C_JSRpk{*tg(n5@FLN+T0vv@W6SmkDaUi15WUz*UR$@otDbgl(6rM;k z`<<0^m z4l4H_$3Y~w8#(xSEPCYhaS#y{*o6f$-X@MfgEzzzBFBm#SP|EW&G0 zq^$6!6yGF1_Fmp~?7}p=@1c0qWem&8G9H3sDmEawgaRl&HRqQn!I+kq_5y?PufRlb z392&y8vx+c1b`!0O#m364IoPZ7~l;l0$>7JEzlTbbzcSS>d+7X>l4n1Ih{e)U@HSw z2CU2=;nFc@02s~PIZR}n9RCZy@jr?D1Eg$;8<>Wm35i=ozC+|9kyRo)MBXOy4w3JI zl+9?t*GNGaCN2@#AaaGs7Llt&X3>w4=M(5X`qC0FKBJyIhkEki%KWF8;GB4!I(~!5 zn?%@s3DU${L?&-cT!FoS9Q|FC1jts{^=r^feyNyyE+DIk0aApj@7MeOQ}b-=$v}nx zjwi}|qa1r$LjcAJ{=|mBYoek2oU?NRuajB=u#;K>u#;K>F&+Bj(L5c%!V>`YXbQj{ zO#lr3I0axx831*UVgO6;GDtlEy9lwK4p#4>b_%TWgi*&!e<_?(Y|JYz&nq_P6<6jJ zTl0#m^NRl+XdPg>320INhzxR)PtJhWoDZG^ElOl)X&JQkkRstTNl9J^2#FyHT$~Sr z0~i5-RKSadY)|i~Ejvy^X|9pba3V=Kx>6=aw+R%^P?$w*bI4=lVLM>Nhr zA;nxKkilipE3(ocvqCVP4N|bR!Q}!KTppYqXy6Lq@<9$%G3holtr@-q|aB-9cPgaEhxyVtG z`z%iP2|gMrtxqv1;_WMVdl4Zf7ts8Ovw3|=ygtRC#Ot$wzVbmfSZFFPwOxc=A;37m zEg}@1igR@MUX-3ko8N*M@Ap4nFRM`=KWp<`+SF7J2KD`i*MHvl^>1F@7KHstR~Z@v zRdaHbXj#EkG=MLo{r?P{-$H{e7jgP;RdM{^rMY~c$Ze3&fT}n2>!IckM6DGXqRw`q z-mCh(VV8q@Xn2E$->W_lKcI&16XAG&hq$}cPpVe+#RuTZOULoPVl*7?jTpaG_p24H z;q0~Al~luNR?J2#*6QF$t>V84;}$`6zmf(M9mmnG<#;t}*FwAfNxS{H-9EQth^Kb@ z$L;pxcKa2*!QCE5{pj36>A+_w-aQaW)}u3(tkR=cIB~^ADdC)z#i{%}PF{5GvQyFN z>nS!km%&CU!18cq9(Vt{427cGkcP}hEhW1sNHde6P!0V&%6gM~Kv(CRKw$90G;RWs z7R=lP8ljn7NZy@XDUc6QxZ4yY2EKpJOT;i?MM;nIV>jA zIT`0@05mm@bL0X*A;dbMP@IB;O9cw2*{Y8V<3NRmVxB6tK($y_fmoQJE*$hBeoW*d z5#Apk6Gxw>tWE6-_#w>`-i|9bcry-Az22bzB)|M4+IlvK#9oQaSAl;jhlR<+5=WBn zVhmp<+of;vJ9-WKl5FdOH069~I2dLP$Ne8d4o${~)qbCkTa|@GTx|^dHA-iz-OxJb zc@Zl1O`h3KY{Cb}=z33okFV$WiXb{!(lYU@r`BQeg+ud==#t_s%1u1Fo>BQ6kd+Im L(wn7MOLzVarSv%C diff --git a/ptocr/model/head/__pycache__/rec_CRNNHead.cpython-36.pyc b/ptocr/model/head/__pycache__/rec_CRNNHead.cpython-36.pyc deleted file mode 100644 index 86a2251c1581698f19a0a08ec291087c15811b52..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3242 zcmbVO&2J<}6|d^=8GB}JXGsD?p$P#pe5}pxlE9J#vzuKulGjMFNu<%DR=cb0>Diec zySm0ULyv@*6_JoEap253aN@+@z+VudIw5hvcTPC~zgInD?`D$=)vDK3ufFU3e(%+d zR;&59KWrucX)*Rs_C)ef-b5~UAq49(!G$y6eW&Z7 z*M}w$AvED!6F?J*1~iSiCKS#N>og}fnw#lZ_eSd0{^?0`b3Y%*TXCj}BI(IgTs!<_ zb5rL-=&o;E-&o&xWBvNg<~g>*wmaN-d6DakYKuf$rc{0dLb5K$wjB|C$o8>y;XZD3 z-FmZK??dLXu8&QGCfbn?Mq)gW=QKWZX$B;!EF)9B{0D?)npbS*^qm>wOuKy#*{?W0 z=Sy@ub`jy71<(hzcE(W-g)f3LDp7Crt*Gy(dfT4SjLm|IC5_XfV*Tcfy~BR7H+A(= z<o3Dw?!zTGlw&juL$<2q5h7R6tPoM+F#lJa20YPHms=`2p;U zt7vSWbDUF=xYzN_(j%z~IY`QUB27?gl?!RY!&Hd8I56H`o|e0?LzowbgET|aep)1> zu_oILM?!X1jbDxrrBZZJEP+Gx-d((Q=Rp;M{0UNh#6aN2aOD*CC$e zCNYgTcIJXlzGWm_z*X>N=0Fo##CzKW;MQI)a+sAskPgo2-a3(jKV<5gB*+%(3WV`! z7siEyGQn0}$W)nz9PUZWh>mB?(she>i*8ul&xMdh*DHQTjHs{3nk!z+`85>E0D@4n z#wTri$Y+5;7^V~7K`!q@EMW1_nL1Oqa{7G69=cO+>Q4jIoXV?woD|HThUn{7;fznC zDymzAGjpj1NV(gJuJQc8NZ9_jQ3gMV?%r`OPtKuDT;+-OW%veH+PYtT8%fz@QtT%>SbdO9>LS=hmo+R)Gt&8J|uY#K!LgqVJBSJ45^ ziKXYirqaJ$1dC`sEt!bQ(6RD0IMAm7SrG_bn1v-Cu{!Z9N8Qvx<?!G|^!jFj-V@Md&e zF^M0McoV`j@*=}o5pPYaXI;ml8rpb-sK@gK>Yw%_H!;3^4T6Pj9@x)A#0j{^uYze2 zzSxNoe`4oVXrFf`YZthF*4q$M=q~Rdmw$z*c%Q+)c;suy$5@CuWvt;{37+sUj@qz( z25_BH-BS-C@!GL<-VcO3FA*RBad;rSn|5gK&u(M=NPTqrx^7^eriK1X(*QKUJF~xB z1%eaQ0VBJ@(Qg5~=94SJw*gq>+a@pRo{W3;H4u;XV={R>9G5!YlkxW~scQgUK&hqH zF-~oguts{DO4ep~QG&VgaFOtlA)YqFv6i(JZ&NR8%J87ZgL((5PH5eyBLAewQif;h z`}TjL-X*0q=A}i++3g_D-xDkFlmTy2#@FT^`At}Hg};o_WcAXL3m+QZn>#D@1DgJa zByK@;{y*nRG`fLYu0TW_Pv48L{Y6EsoydeqB1Rb=50M_Jtaq&_Y{0A^XTbyeY_mkG gxnZe!dG3qDo9NB@8S*wEu%w3sA$i$Zdtq(ue?J!9=>Px# diff --git a/ptocr/model/head/det_DBHead.py b/ptocr/model/head/det_DBHead.py deleted file mode 100644 index 9fa5593..0000000 --- a/ptocr/model/head/det_DBHead.py +++ /dev/null @@ -1,54 +0,0 @@ -#-*- coding:utf-8 _*- -""" -@author:fxw -@file: det_DBHead.py -@time: 2020/08/07 -""" -import torch -import torch.nn as nn -from ..CommonFunction import ConvBnRelu,upsample,upsample_add - -class DB_Head(nn.Module): - def __init__(self, in_channels, inner_channels, bias=False): - """ - :param in_channels: - :param inner_channels: - :param bias: - """ - super(DB_Head, self).__init__() - - self.in5 = ConvBnRelu(in_channels[-1], inner_channels, 1, 1, 0, bias=bias) - self.in4 = ConvBnRelu(in_channels[-2], inner_channels, 1, 1, 0, bias=bias) - self.in3 = ConvBnRelu(in_channels[-3], inner_channels, 1, 1, 0, bias=bias) - self.in2 = ConvBnRelu(in_channels[-4], inner_channels, 1, 1, 0, bias=bias) - - self.out5 = ConvBnRelu(inner_channels, inner_channels // 4, 3, 1, 1, bias=bias) - self.out4 = ConvBnRelu(inner_channels, inner_channels // 4, 3, 1, 1, bias=bias) - self.out3 = ConvBnRelu(inner_channels, inner_channels // 4, 3, 1, 1, bias=bias) - self.out2 = ConvBnRelu(inner_channels, inner_channels // 4, 3, 1, 1, bias=bias) - - for m in self.modules(): - if isinstance(m, nn.Conv2d): - nn.init.kaiming_normal_(m.weight.data) - elif isinstance(m, nn.BatchNorm2d): - m.weight.data.fill_(1.) - m.bias.data.fill_(1e-4) - - def forward(self, x): - - c2, c3, c4, c5 = x - in5 = self.in5(c5) - in4 = self.in4(c4) - in3 = self.in3(c3) - in2 = self.in2(c2) - - out4 = upsample_add(in5,in4) # 1/16 - out3 = upsample_add(out4,in3) # 1/8 - out2 = upsample_add(out3,in2) # 1/4 - - p5 = upsample(self.out5(in5),out2) - p4 = upsample(self.out4(out4),out2) - p3 = upsample(self.out3(out3),out2) - p2 = self.out2(out2) - fuse = torch.cat((p5, p4, p3, p2), 1) - return fuse \ No newline at end of file diff --git a/ptocr/model/head/det_DBHead_Qua.py b/ptocr/model/head/det_DBHead_Qua.py deleted file mode 100644 index a532d77..0000000 --- a/ptocr/model/head/det_DBHead_Qua.py +++ /dev/null @@ -1,72 +0,0 @@ -#-*- coding:utf-8 _*- -""" -@author:fxw -@file: DB_Head_Qua.py -@time: 2020/11/02 -""" -import torch -import torch.nn as nn -import torch.nn.functional as F -from ..CommonFunction_Q import ConvBnRelu -from torch.quantization import QuantStub, DeQuantStub, fuse_modules - -def upsample(x, y): - _, _, H, W = y.size() - # return F.upsample(x, size=(H // scale, W // scale), mode='nearest') - return F.interpolate(x, size=(H , W), mode='nearest') - -def upsample_add(x, y,skip): - _, _, H, W = y.size() - # return F.upsample(x, size=(H, W), mode='nearest') + y - return skip.add(F.interpolate(x, size=(H, W), mode='nearest'),y) - -class DB_Head(nn.Module): - def __init__(self, in_channels, inner_channels, bias=False): - """ - :param in_channels: - :param inner_channels: - :param bias: - """ - super(DB_Head, self).__init__() - self.skip = nn.quantized.FloatFunctional() - - self.in5 = ConvBnRelu(in_channels[-1], inner_channels, 1, 1, 0,groups=1, bias=bias) - self.in4 = ConvBnRelu(in_channels[-2], inner_channels, 1, 1, 0,groups=1, bias=bias) - self.in3 = ConvBnRelu(in_channels[-3], inner_channels, 1, 1, 0, groups=1,bias=bias) - self.in2 = ConvBnRelu(in_channels[-4], inner_channels, 1, 1, 0,groups=1, bias=bias) - - self.out5 = ConvBnRelu(inner_channels, inner_channels // 4, 3, 1, 1, groups=1,bias=bias) - self.out4 = ConvBnRelu(inner_channels, inner_channels // 4, 3, 1, 1,groups=1, bias=bias) - self.out3 = ConvBnRelu(inner_channels, inner_channels // 4, 3, 1, 1, groups=1,bias=bias) - self.out2 = ConvBnRelu(inner_channels, inner_channels // 4, 3, 1, 1, groups=1,bias=bias) - - for m in self.modules(): - if isinstance(m, nn.Conv2d): - nn.init.kaiming_normal_(m.weight.data) - elif isinstance(m, nn.BatchNorm2d): - m.weight.data.fill_(1.) - m.bias.data.fill_(1e-4) - self.fuse_model() - - def forward(self, x1,x2,x3,x4): - in5 = self.in5(x1) - in4 = self.in4(x2) - in3 = self.in3(x3) - in2 = self.in2(x4) - - out4 = upsample_add(in5,in4,self.skip) # 1/16 - out3 = upsample_add(out4,in3,self.skip) # 1/8 - out2 = upsample_add(out3,in2,self.skip) # 1/4 - - p5 = upsample(self.out5(in5),out2) - p4 = upsample(self.out4(out4),out2) - p3 = upsample(self.out3(out3),out2) - p2 = self.out2(out2) - fuse = self.skip.cat((p5, p4, p3, p2), 1) - return fuse - - def fuse_model(self): - for m in self.modules(): - if type(m) == ConvBnRelu: - fuse_modules(m, ['conv', 'bn', 'relu'], inplace=True) - diff --git a/ptocr/model/head/det_FPEM_FFM_Head.py b/ptocr/model/head/det_FPEM_FFM_Head.py deleted file mode 100644 index ab93e82..0000000 --- a/ptocr/model/head/det_FPEM_FFM_Head.py +++ /dev/null @@ -1,99 +0,0 @@ -#-*- coding:utf-8 _*- -""" -@author:fxw -@file: det_FPEM_FFM_Head.py -@time: 2020/08/07 -""" -import torch -import torch.nn as nn -from ptocr.model.CommonFunction import DWBlock,ConvBnRelu,upsample_add,upsample - -class FFM(nn.Module): - def __init__(self,): - super(FFM,self).__init__() - - def forward(self, x): - map_add1,map_add2,map_add3,map_add4 = x[0] - for i in range(1,len(x)): - map_add1+=x[i][0] - map_add2+=x[i][1] - map_add3+=x[i][2] - map_add4+=x[i][3] - return map_add1,map_add2,map_add3,map_add4 - -class FPEM(nn.Module): - def __init__(self,inner_channels): - super(FPEM,self).__init__() - self.up_block1 = DWBlock(inner_channels,inner_channels,3,1) - self.up_block2 = DWBlock(inner_channels,inner_channels,3,1) - self.up_block3 = DWBlock(inner_channels,inner_channels,3,1) - - self.down_block1 = DWBlock(inner_channels, inner_channels, 3, 2) - self.down_block2 = DWBlock(inner_channels, inner_channels, 3, 2) - self.down_block3 = DWBlock(inner_channels, inner_channels, 3, 2) - - for m in self.modules(): - if isinstance(m, nn.Conv2d): - nn.init.kaiming_normal_(m.weight.data) - elif isinstance(m, nn.BatchNorm2d): - m.weight.data.fill_(1.) - m.bias.data.fill_(1e-4) - - def forward(self, x): - x2, x3, x4, x5 = x - up_add_map1 = upsample_add(x5,x4) - up_map1 = self.up_block1(up_add_map1) - - up_add_map2 = upsample_add(up_map1,x3) - up_map2 = self.up_block2(up_add_map2) - - up_add_map3 = upsample_add(up_map2,x2) - up_map3 = self.up_block3(up_add_map3) - - down_add_map1 = upsample_add(up_map2,up_map3) - down_map1 = self.down_block1(down_add_map1) - - down_add_map2= upsample_add(up_map1, up_map2) - down_map2 = self.down_block2(down_add_map2) - - down_add_map3 = upsample_add(x5, up_map1) - down_map3 = self.down_block3(down_add_map3) - - return up_map3,down_map1,down_map2,down_map3 - -class FPEM_FFM_Head(nn.Module): - def __init__(self, in_channels, inner_channels,FPEM_NUM=2,bias=False): - super(FPEM_FFM_Head, self).__init__() - self.smooth1 = ConvBnRelu(in_channels=in_channels[3], out_channels=inner_channels, kernel_size=1, stride=1, padding=0,bias=bias) - self.smooth2 = ConvBnRelu(in_channels=in_channels[2], out_channels=inner_channels, kernel_size=1, stride=1, padding=0,bias=bias) - self.smooth3 = ConvBnRelu(in_channels=in_channels[1], out_channels=inner_channels, kernel_size=1, stride=1, padding=0,bias=bias) - self.smooth4 = ConvBnRelu(in_channels=in_channels[0], out_channels=inner_channels, kernel_size=1, stride=1, padding=0,bias=bias) - self.out = ConvBnRelu(in_channels=inner_channels*4, out_channels=inner_channels, kernel_size=1, stride=1, padding=0,bias=bias) - - for m in self.modules(): - if isinstance(m, nn.Conv2d): - nn.init.kaiming_normal_(m.weight.data) - elif isinstance(m, nn.BatchNorm2d): - m.weight.data.fill_(1.) - m.bias.data.fill_(1e-4) - - for i in range(FPEM_NUM): - setattr(self, 'fpem_{}'.format(i + 1), FPEM(inner_channels)) - self.FFM_BLOCK = FFM() - self.FPEM_NUM = FPEM_NUM - - def forward(self, x): - x_list = [] - x2, x3, x4, x5 = x - base_map = self.smooth4(x2),self.smooth3(x3),self.smooth2(x4),self.smooth1(x5) - for i in range(self.FPEM_NUM): - base_map = getattr(self, 'fpem_{}'.format(i + 1))(base_map) - x_list.append(base_map) - outMap = self.FFM_BLOCK(x_list) - fuse = torch.cat((outMap[0], - upsample(outMap[1], outMap[0]), - upsample(outMap[2], outMap[0]), - upsample(outMap[3], outMap[0])), 1) - fuse = self.out(fuse) - return fuse - diff --git a/ptocr/model/head/det_FPNHead.py b/ptocr/model/head/det_FPNHead.py deleted file mode 100644 index 4acfde3..0000000 --- a/ptocr/model/head/det_FPNHead.py +++ /dev/null @@ -1,59 +0,0 @@ -#-*- coding:utf-8 _*- -""" -@author:fxw -@file: det_FPNHead.py -@time: 2020/08/07 -""" -import torch -import torch.nn as nn -from ptocr.model.CommonFunction import ConvBnRelu,upsample_add,upsample - -class FPN_Head(nn.Module): - def __init__(self, in_channels, inner_channels,bias=False): - """ - :param in_channels: - :param inner_channels: - :param bias: - """ - super(FPN_Head, self).__init__() - # Top layer - self.toplayer = ConvBnRelu(in_channels[-1], inner_channels, kernel_size=1, stride=1,padding=0,bias=bias) # Reduce channels - # Smooth layers - self.smooth1 = ConvBnRelu(inner_channels, inner_channels, kernel_size=3, stride=1, padding=1,bias=bias) - self.smooth2 = ConvBnRelu(inner_channels, inner_channels, kernel_size=3, stride=1, padding=1,bias=bias) - self.smooth3 = ConvBnRelu(inner_channels, inner_channels, kernel_size=3, stride=1, padding=1,bias=bias) - # Lateral layers - self.latlayer1 = ConvBnRelu(in_channels[-2], inner_channels, kernel_size=1, stride=1, padding=0,bias=bias) - self.latlayer2 = ConvBnRelu(in_channels[-3], inner_channels, kernel_size=1, stride=1, padding=0,bias=bias) - self.latlayer3 = ConvBnRelu(in_channels[-4], inner_channels, kernel_size=1, stride=1, padding=0,bias=bias) - # Out map - self.conv_out = ConvBnRelu(inner_channels * 4, inner_channels, kernel_size=3, stride=1, padding=1,bias=bias) - - for m in self.modules(): - if isinstance(m, nn.Conv2d): - nn.init.kaiming_normal_(m.weight.data) - elif isinstance(m, nn.BatchNorm2d): - m.weight.data.fill_(1.) - m.bias.data.fill_(1e-4) - - def forward(self, x): - c2, c3, c4, c5 = x - ## - p5 = self.toplayer(c5) - c4 = self.latlayer1(c4) - p4 = upsample_add(p5, c4) - p4 = self.smooth1(p4) - c3 = self.latlayer2(c3) - p3 = upsample_add(p4, c3) - p3 = self.smooth2(p3) - c2 = self.latlayer3(c2) - p2 = upsample_add(p3, c2) - p2 = self.smooth3(p2) - ## - p3 = upsample(p3, p2) - p4 = upsample(p4, p2) - p5 = upsample(p5, p2) - - fuse = torch.cat((p2, p3, p4, p5), 1) - fuse = self.conv_out(fuse) - return fuse \ No newline at end of file diff --git a/ptocr/model/head/det_SASTHead.py b/ptocr/model/head/det_SASTHead.py deleted file mode 100644 index 98af128..0000000 --- a/ptocr/model/head/det_SASTHead.py +++ /dev/null @@ -1,216 +0,0 @@ -#-*- coding:utf-8 _*- -""" -@author:fxw -@file: det_SASTHead.py -@time: 2020/08/17 -""" -import torch -import torch.nn as nn -import torch.nn.functional as F -from ..CommonFunction import ConvBnRelu,DeConvBnRelu - - -class FPN_Up_Fusion(nn.Module): - def __init__(self): - super(FPN_Up_Fusion, self).__init__() - - self.fpn_up_conv1 = ConvBnRelu(2048, 256, 1, 1, 0, with_relu=False) - self.fpn_up_conv2 = ConvBnRelu(2048, 256, 1, 1, 0, with_relu=False) - self.fpn_up_conv3 = ConvBnRelu(1024, 192, 1, 1, 0, with_relu=False) - self.fpn_up_conv4 = ConvBnRelu(512, 192, 1, 1, 0, with_relu=False) - self.fpn_up_conv5 = ConvBnRelu(256, 128, 1, 1, 0, with_relu=False) - - self.fpn_up_conv6 = ConvBnRelu(256, 256, 3, 1, 1) - self.fpn_up_conv7 = ConvBnRelu(192, 192, 3, 1, 1) - self.fpn_up_conv8 = ConvBnRelu(192, 192, 3, 1, 1) - - self.fpn_up_conv9 = ConvBnRelu(128, 128, 3, 1, 1) - self.fpn_up_conv10 = ConvBnRelu(128, 128, 1, 1, 0, with_relu=False) - - self.fpn_up_deconv1 = DeConvBnRelu(256, 256) - self.fpn_up_deconv2 = DeConvBnRelu(256, 192) - self.fpn_up_deconv3 = DeConvBnRelu(192, 192) - self.fpn_up_deconv4 = DeConvBnRelu(192, 128) - - for m in self.modules(): - if isinstance(m, nn.Conv2d): - nn.init.kaiming_normal_(m.weight.data) - elif isinstance(m, nn.BatchNorm2d): - m.weight.data.fill_(1.) - m.bias.data.fill_(1e-4) - - def forward(self, x): - x = [x[0], x[1], x[2], x[3], x[4]] - h0 = self.fpn_up_conv1(x[0]) - h1 = self.fpn_up_conv2(x[1]) - h2 = self.fpn_up_conv3(x[2]) - h3 = self.fpn_up_conv4(x[3]) - h4 = self.fpn_up_conv5(x[4]) - - g0 = self.fpn_up_deconv1(h0) - - g1 = g0 + h1 - g1 = F.relu(g1) - g1 = self.fpn_up_conv6(g1) - g1 = self.fpn_up_deconv2(g1) - - g2 = g1 + h2 - g2 = F.relu(g2) - g2 = self.fpn_up_conv7(g2) - g2 = self.fpn_up_deconv3(g2) - - g3 = g2 + h3 - g3 = F.relu(g3) - g3 = self.fpn_up_conv8(g3) - g3 = self.fpn_up_deconv4(g3) - - g4 = g3 + h4 - g4 = F.relu(g4) - g4 = self.fpn_up_conv9(g4) - g4 = self.fpn_up_conv10(g4) - return g4 - - -class FPN_Down_Fusion(nn.Module): - def __init__(self): - super(FPN_Down_Fusion, self).__init__() - - self.fpn_down_conv1 = ConvBnRelu(3, 32, 1, 1, 0, with_relu=False) -# self.fpn_down_conv2 = ConvBnRelu(128, 64, 1, 1, 0, with_relu=False) # for 3*3 - self.fpn_down_conv2 = ConvBnRelu(64, 64, 1, 1, 0, with_relu=False) # for 7*7 - self.fpn_down_conv3 = ConvBnRelu(256, 128, 1, 1, 0, with_relu=False) - - self.fpn_down_conv4 = ConvBnRelu(32, 64, 3, 2, 1, with_relu=False) - - self.fpn_down_conv5 = ConvBnRelu(64, 128, 3, 1, 1) - self.fpn_down_conv6 = ConvBnRelu(128, 128, 3, 2, 1, with_relu=False) - - self.fpn_down_conv7 = ConvBnRelu(128, 128, 3, 1, 1) - self.fpn_down_conv8 = ConvBnRelu(128, 128, 1, 1, 0, with_relu=False) - - for m in self.modules(): - if isinstance(m, nn.Conv2d): - nn.init.kaiming_normal_(m.weight.data) - elif isinstance(m, nn.BatchNorm2d): - m.weight.data.fill_(1.) - m.bias.data.fill_(1e-4) - - def forward(self, x): - x = [x[-1], x[-2], x[-3]] - - h0 = self.fpn_down_conv1(x[0]) - h1 = self.fpn_down_conv2(x[1]) - h2 = self.fpn_down_conv3(x[2]) - - g0 = self.fpn_down_conv4(h0) - g1 = g0 + h1 - g1 = F.relu(g1) - g1 = self.fpn_down_conv5(g1) - g1 = self.fpn_down_conv6(g1) - - g2 = g1 + h2 - g2 = F.relu(g2) - g2 = self.fpn_down_conv7(g2) - g2 = self.fpn_down_conv8(g2) - return g2 - -class cross_attention(nn.Module): - - def __init__(self): - super(cross_attention,self).__init__() - self.conv_attention1 = ConvBnRelu(128,128,1,1,0) - self.conv_attention2 = ConvBnRelu(128,128,1,1,0) - self.conv_attention3 = ConvBnRelu(128,128,1,1,0) - - self.conv_attention4 = ConvBnRelu(128,128,1,1,0,with_relu=False) - self.conv_attention5 = ConvBnRelu(128,128,1,1,0,with_relu=False) - self.conv_attention6 = ConvBnRelu(128,128,1,1,0,with_relu=False) - self.conv_attention7 = ConvBnRelu(128,128,1,1,0,with_relu=False) - - self.conv_attention8 = ConvBnRelu(256,128,1,1,0) - - for m in self.modules(): - if isinstance(m, nn.Conv2d): - nn.init.kaiming_normal_(m.weight.data) - elif isinstance(m, nn.BatchNorm2d): - m.weight.data.fill_(1.) - m.bias.data.fill_(1e-4) - - def forward(self, x): - f_shape = x.shape - f_theta = self.conv_attention1(x) - f_phi = self.conv_attention2(x) - f_g = self.conv_attention3(x) - fh_theta = f_theta - fh_phi = f_phi - fh_g = f_g - # flatten - fh_theta = fh_theta.permute((0, 2, 3, 1)) - fh_theta = torch.reshape(fh_theta, (f_shape[0] * f_shape[2], f_shape[3], 128)) - fh_phi = fh_phi.permute((0, 2, 3, 1)) - fh_phi = torch.reshape(fh_phi, (f_shape[0] * f_shape[2], f_shape[3], 128)) - fh_g = fh_g.permute((0, 2, 3, 1)) - fh_g = torch.reshape(fh_g, (f_shape[0] * f_shape[2], f_shape[3], 128)) - # correlation - fh_attn = torch.matmul(fh_theta, fh_phi.permute((0, 2, 1))) - # scale - fh_attn = fh_attn / (128 ** 0.5) - fh_attn = F.softmax(fh_attn,-1) - # weighted sum - fh_weight = torch.matmul(fh_attn, fh_g) - fh_weight = torch.reshape(fh_weight, (f_shape[0], f_shape[2], f_shape[3], 128)) - # print("fh_weight: {}".format(fh_weight.shape)) - fh_weight = fh_weight.permute((0, 3, 1, 2)) - - fh_weight = self.conv_attention4(fh_weight) - fh_sc = self.conv_attention5(x) - f_h = F.relu(fh_weight + fh_sc) - - # vertical - fv_theta = f_theta.permute((0, 1, 3, 2)) - fv_phi = f_phi.permute((0, 1, 3, 2)) - fv_g = f_g.permute((0, 1, 3, 2)) - # flatten - fv_theta = fv_theta.permute((0, 2, 3, 1)) - fv_theta = torch.reshape(fv_theta, (f_shape[0] * f_shape[3], f_shape[2], 128)) - fv_phi = fv_phi.permute((0, 2, 3, 1)) - fv_phi = torch.reshape(fv_phi, (f_shape[0] * f_shape[3], f_shape[2], 128)) - fv_g = fv_g.permute((0, 2, 3, 1)) - fv_g = torch.reshape(fv_g, (f_shape[0] * f_shape[3], f_shape[2], 128)) - # correlation - fv_attn = torch.matmul(fv_theta, fv_phi.permute((0, 2, 1))) - # scale - fv_attn = fv_attn / (128 ** 0.5) - fv_attn = F.softmax(fv_attn,-1) - # weighted sum - fv_weight = torch.matmul(fv_attn, fv_g) - fv_weight = torch.reshape(fv_weight, (f_shape[0], f_shape[3], f_shape[2], 128)) - # print("fv_weight: {}".format(fv_weight.shape)) - fv_weight = fv_weight.permute((0, 3, 2, 1)) - fv_weight = self.conv_attention6(fv_weight) - # short cut - fv_sc = self.conv_attention7(x) - f_v = F.relu(fv_weight + fv_sc) - ###### - f_attn = torch.cat([f_h, f_v], 1) - f_attn = self.conv_attention8(f_attn) - return f_attn - - -class SASTHead(nn.Module): - def __init__(self,with_attention=True): - super(SASTHead,self).__init__() - self.fpn_up = FPN_Up_Fusion() - self.fpn_down = FPN_Down_Fusion() - self.cross_attention = cross_attention() - self.with_attention = with_attention - def forward(self, x): - f_down = self.fpn_down(x) - f_up = self.fpn_up(x) - f_common = f_down + f_up - f_common = F.relu(f_common) - - if(self.with_attention): - f_common = self.cross_attention(f_common) - - return f_common \ No newline at end of file diff --git a/ptocr/model/head/rec_CRNNHead.py b/ptocr/model/head/rec_CRNNHead.py deleted file mode 100644 index 36c4f54..0000000 --- a/ptocr/model/head/rec_CRNNHead.py +++ /dev/null @@ -1,107 +0,0 @@ -#-*- coding:utf-8 _*- -""" -@author:fxw -@file: crnn_head.py -@time: 2020/07/24 -""" -import torch.nn as nn -from torch.nn import init - -class SeModule(nn.Module): - def __init__(self, in_size, reduction=4): - super(SeModule, self).__init__() - - self.se = nn.Sequential( - nn.Conv2d(in_size, in_size // reduction, kernel_size=1, stride=1, padding=0, bias=False), - nn.BatchNorm2d(in_size // reduction), - nn.ReLU(inplace=True), - nn.Conv2d(in_size // reduction, in_size, kernel_size=1, stride=1, padding=0, bias=False), - nn.BatchNorm2d(in_size), - nn.Sigmoid()) - for m in self.modules(): - if isinstance(m, nn.Conv2d): - init.kaiming_normal_(m.weight, mode='fan_out') - if m.bias is not None: - init.constant_(m.bias, 0) - elif isinstance(m, nn.BatchNorm2d): - init.constant_(m.weight, 1) - init.constant_(m.bias, 0) - - def forward(self, x): - return x * self.se(x) - -class BLSTM(nn.Module): - - def __init__(self, nIn, nHidden, nOut): - super(BLSTM, self).__init__() - - self.rnn = nn.LSTM(nIn, nHidden, bidirectional=True) - self.embedding = nn.Linear(nHidden * 2, nOut) - - def forward(self, input): - recurrent, _ = self.rnn(input) - T, b, h = recurrent.size() - t_rec = recurrent.view(T * b, h) - - output = self.embedding(t_rec) # [T * b, nOut] - output = output.view(T, b, -1) - - return output - -class CRNN_Head(nn.Module): - def __init__(self,use_conv=False, - use_attention=False, - use_lstm=True, - lstm_num=2, - inchannel=512, - hiddenchannel=128, - classes=1000): - super(CRNN_Head,self).__init__() - self.use_lstm = use_lstm - self.lstm_num = lstm_num - self.use_conv = use_conv - if use_attention: - self.attention = SeModule(inchannel) - self.use_attention = use_attention - if(use_lstm): - assert lstm_num>0 ,Exception('lstm_num need to more than 0 if use_lstm = True') - for i in range(lstm_num): - if(i==0): - if(lstm_num==1): - setattr(self, 'lstm_{}'.format(i + 1), BLSTM(inchannel, hiddenchannel,classes)) - else: - setattr(self, 'lstm_{}'.format(i + 1), BLSTM(inchannel,hiddenchannel,hiddenchannel)) - elif(i==lstm_num-1): - setattr(self, 'lstm_{}'.format(i + 1), BLSTM(hiddenchannel, hiddenchannel, classes)) - else: - setattr(self, 'lstm_{}'.format(i + 1), BLSTM(hiddenchannel, hiddenchannel, hiddenchannel)) - elif(use_conv): - self.out = nn.Conv2d(inchannel, classes, kernel_size=1, padding=0) - else: - self.out = nn.Linear(inchannel,classes) - - def forward(self, x): - b, c, h, w = x.size() - assert h == 1, "the height of conv must be 1" - - if self.use_attention: - x = self.attention(x) - - if(self.use_conv): - x = self.out(x) - x = x.squeeze(2) - x = x.permute(2, 0, 1) - return x - - x = x.squeeze(2) - x = x.permute(2, 0, 1) # [w, b, c] - if self.use_lstm: - for i in range(self.lstm_num): - x = getattr(self, 'lstm_{}'.format(i + 1))(x) - else: - x = self.out(x) - return x - - def backward_hook(self, module, grad_input, grad_output): - for g in grad_input: - g[g != g] = 0 \ No newline at end of file diff --git a/ptocr/model/loss/__init__.py b/ptocr/model/loss/__init__.py deleted file mode 100644 index 35de2ad..0000000 --- a/ptocr/model/loss/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -#-*- coding:utf-8 _*- -""" -@author:fxw -@file: __init__.py.py -@time: 2020/08/07 -""" diff --git a/ptocr/model/loss/__pycache__/__init__.cpython-35.pyc b/ptocr/model/loss/__pycache__/__init__.cpython-35.pyc deleted file mode 100644 index 3c4e8228e660116606270808414c5485fbeddeff..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 224 zcmWgR<>k`(suLfI$#{!BK0YNsIX-?R zLlG0uR50<&%h@U>v^ce>I3_JIFTJ9)JT)^WpfWilu_!m7C_gJTxuh7#FUc=T&hU2* oiYX|`PcDkd%}+_qiOIgwJsv94{z`*brh~a<{$Z`PUVh$jY!Vtxf!Whh;$y8;^<&aofl969zl~z&C z<&c({lWL_9AD@|*SrQ+wS5OH=Tn;6fxj<1P10w@{0}Fivb1pwk##`+1@hSPq@$oAe wikN_Af{9;pdin(=`N>84x%nxnIr=&I#l`warrqMO0ZNqSq}s88+ycZ50NKGT)Bpeg diff --git a/ptocr/model/loss/__pycache__/basical_loss.cpython-35.pyc b/ptocr/model/loss/__pycache__/basical_loss.cpython-35.pyc deleted file mode 100644 index d2b078662220924de93d7a98280623200fd76cd4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10424 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 KWhcfl8}YxOn7nBK diff --git a/ptocr/model/loss/__pycache__/db_loss.cpython-35.pyc b/ptocr/model/loss/__pycache__/db_loss.cpython-35.pyc deleted file mode 100644 index ae3171b0579428a4cc58a427bd4df0c188689cdb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1506 zcmZuwJ8v6D5S~3A??uWoVkd@>6hTt4kwq7FAQ*;VISB&TiVz{qfxzZ;cN9tAgV{Yo zmOxg@qw;@9n=VcMLc08cZBr>g>Hx0t%^npgfyvw1+1c4|cfOf{u4H*4y*5Xs?-;usqm# zuycRsll#M+=n@a3gG&K9a82|g(J!A5pTphUdzutUmFD{jt52&&)$=pE5j>lwxz*nN ztWJ~S{|+fbUj4kaq8~xY!2m*rbk?yWU2cYOoTo%PU%P#S!KmP8jI*r5v|(R66pp$lu3E;U`Y!b5u0 z_*Z&B%hn;JrDJ!&FF!*Z40@)k7xP>x9~YQF%4s!iq*PsAh8c*lTq}Nv@rz-*ViHVn zyxuec>>9-7CS;pcLB6Z=Vyte#S8eivPCnNIVL~a>BGFpPpXtpP`@5sBb*}VioKz>T z7N_}iba<9d5>+0mdX}e+#=5ChI{9+{tI@ovQ#C5SG&Si4{`r@!^*qsXr|i=!SzwZ^iOktiWQ899ng0h4Xj=ry>hpyBzj+6@GEH0T`|?|51z>P&I-80VxhZi)6m zdcm|rW{uZORIVo`YG+v{bJP1*hwH|A(E^#CJ=xj@zS>}17e&=5&XMA5-KY<5G0&1F zAB5^QHsWxu@=P8l>BIyl4U{IGv=DxoH)@({ixP*ZzSdv?jmwv+JB)3`U1-K1*XlG; z8Cx`4NDOU(Ji;7_55#T96X&k(8*rMZtPs%5N7*(eBv7Qy&BJ z`qF_=@34VyvaQ9%skx*3;{Cu{(0aaq#HRR2`~!J}uZEyzhDMtKl#?D!<{sQh-0<#)mu{ z;!(#41f(E{@G6oLhrNZL`ob5qqIHl4HA`6?rePhW5rMBk1cF_FV1>VkH-4H3?^_s$ z=LgAQw$k%fKAipVDmk2$Rq^n1G38aO)ZX$eIn-s1lKcDj_b2-gCI|b;B?gC+OM-Ar zP4XgBr_T?bqq@2KB&)I}FODQ~Pa7@U<(a#Pj?27o)SYK-o>l)>$gRt(&sJ$abq_%S zSRJIEh+e~t2r7b$7^`F=ynvLx1`26_b;O1pKd}KW6Eu<@g1_GU{oBv~{PFg2e&x_& ze|yveBs6H!!Nyy78z7+l&fg%@(a{A!|f#IMOERsw-P7#bzYfj{gAH#89sQ diff --git a/ptocr/model/loss/__pycache__/pan_loss.cpython-35.pyc b/ptocr/model/loss/__pycache__/pan_loss.cpython-35.pyc deleted file mode 100644 index 045598a474d3beb6ef9ca47b40107e15bb506b00..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2435 zcmZWrOK;p%6h0n%Y>&s2q)kIxDrhLr;nAk)2CAw}6{JcK(x?R%RwT={Jrhsu@o=xF zZKBC4JXZV##FiZ!{sL>(mkkoDE?6V6;yc%#B$S!lJKsI`+{gLOlJ9xi)C zf6}Rs1-yYKC+HHQ0KGv;i*kbkfEu@GWKv-2p-H(#MVo>)8Hq(nn{t~9hXRKRmjaiH z4h0=5JPJHA?j<%Q4tzP7%0ZSBKr*Vnw~xG8P-4ds3ri7a}Qr_T*Y`j&D(&f;_n3zd6! zIMiFPOxDON_tGMK6jkwF%v;Pqb_3*nfD{zKX7sJb_{5~|hz1a9W52D5m|GP<=u*;Q z3&BX@QPS1e=2Z{-zH)Z%Zu6an^)Y|kX~1uHhTs47%a4Em`t$ABYOcV)X4B*jK!rbp zW<*DDL5q%vjF}#XCe2zr5n5EWX=c(I|zn^ePc$&V=5=?DkLle@ zt50>{Z9!=TB6$`a87uf+M)S2JSB!&|xhwy(;y8CW2#@{w00ZO>=L)g}$pIdbC>UDW zNx-_MjWEP@KGJ@M8e9dh!UynQy%q{si($LY9$PrrTT>`rX7mbouX3l^vo(9-G6c#2 zS!mQ7s%rvY*wTb1kCj=~?B4Dl=n^5J?88i;W zmSF`V^dbkQc}mo(MX(iR&ZLWMAdm%w@CIrFA!31SAcP>01JuzwxIxnyg`{9^czo(~c8exZes$Q}{vw9VjyL9e6pN7++zCh>C zoj+Wp3N;N9A8s=amso4I#1;4!D*<(ZunUk6w8&_{uTF1UyGN{oD)AON)omPw7~L1I zv7~n#=UT=NRA*#XAW=kC?T1eg-=z?5^A>OLq^li^lGq?r!+uvsh;n2$EXphqZ?g6~ z+%2FBdU2G8TtxMoBok$B6G+-~4?>AD~kfUtc*zT8S){FD9uoD84MqMGt(@yTwHw2dl z!L>s_U|jJfzhu5~$@ROwtEK&(a>6hv<1nm!;Ob&{bv#zB@i*&O(f wBAGG$Ooe{(^gE|tH=Xp#{!HIkpKx-eQ^m}^Roir%i;?74$=2z+WL#yDWLO zC9JA5;L86YZMrlm)22$1dMg7d3>T>bxXL%nJ4p!-oSB`SUG9AQ?d)CebfQ15{*e3@ zG4>Zb^SH2g@Kz5X1dExlgk4y%WwbqZVh5H}xTP0+789=U3cn2EpyV+x!#FIXII`G1 zCVUY*Wg^I(ackkkZDD`Q`uu1++DYg2{#4!^K6xDN3`a$N^U5rpB*nCPu$P51LM zd6?GOenwNQ9!im(>I;aRAvG4;qWz2wElTHUh!oA@V^?qyK4q~dBGH2OpD~xk0TSux z;KAKp`t(aP7khDMY5aNT<@3LP{pp|If4QBlzC_wtzSRvFHLESf7WUX)FpDYJiKA>t zxAv5?ptfHJ3kJ>W(CA1oeAn9Tx3pKyXStLd6KS3#qsgdF65Tq;f>nbx^(r@%u1 z32I6Dz77SzGRR2JjHD1kUHYa;(bNR4Zj~29*@auKQJ<5~D&QX`Nmis)l_Wp2*KcnQ zX7x0agK{eJVn8DYXZBHcFp%D=4Z*Cg<>MDw+&bzlk=tA?J)`x|6XK<{eu=j#Ae1#` zV{5?(r(;jKXIj4UgmvI5pX3f3JHlRA$3g9mJryjhv5yf!9guCjd<-0Atb#G89>vBH z5O>h;9CO8it+Ts_PvUuTM+d`Vn%391fwn8sI@L}#n`>_}FK36^PpkTHmTRw`%4}ap zLpd#zrpq(qq>Wj$o6SY4+kiEFl#U7_uI!ikKqBeI(9)8SsdGNW3i zqe)Vx)j{l$+-lS{-NgpwS)B{hS?LzRzZ@nnV)8~@qR({h<@MKw8ut-&0j_ETg0`e< zUBt`Hwsftd3#*O!&xjKi6HQKbr(rRe3k-pv3qHF*+O1I{#x9V)irsR6vk5f?Nf?ZLgOPj>4jljz!byHe-B-yKnuLw>f9bZc`0o%*$zDO| zl7H>fG|!z$a}}A<@uY`#hUjQ0_!>qH0xDF$%M9r`%a(0guCj%5nTTl%7utvRpaWZlFTy2a4DA==PA z=nnL{+89!`?(Q1=<@~tH5uWLCb-B8`9$!_jFD>~$*jwSQ zNAx?Lcx>=H7%Bn5=zIT8d=n}%+#XXroy4FLn5&$aVT$6;ZoP7!lSN7 z;&#%c#HFvWl}o8bqZU2Ja-X-|54@voey}7-4W%J!DTbuqT!c@cjoP_yi29JgIh($27%v@y2Qa@2_gqRm!$a7j#5a7CuxdYOYjIs%&Sty!;f#?>rJXM zmc61((!9rs>Yd1#1gvNYL$QG*&WV;dTCHPqd0}q6$V3}DtauYcc}OJu9}$fN2V)+R zV~1w;e}qk%At-_#AWXoRVRisb01GiYb~%0{taN!gYVvk0bZbei;2A^`$tNryd9~UA z${ro`sq#4qqc(%$0yet!#a%jXamGd+bHWmTM{e+c(x6$3`VFHvgC(b=Y>Gw4*b%2L z)68ef#KZ6C^J;}(Umt_&oKRITxY+$Lj<7pD%P7madbA^vbG+0r0+o|=Pun&s}Id#Tx(|`QX3#7*6@27qa#)@ zn&L%qU7Qg|YYXK4YhBD0!Vg1_c>}+DU{H}rgvSphBwGF1go9;Kl6h^&gU=kZ1dAo0Fs6BCTW=tJ8RO`=xvo$%JiG>mb|_ z{rRu4CN{)H;MEsLD~mMM`hm;6eH;}j_x3PEvz_M5cf#=DG|J~+OtYOkWL)Dr<+%lc zklctR7mm3uINgf-b4P4Ao#l>aqi21mnw1tf%!EEtJkT!e^1gEXD4p55;4k4aUH zNtPNf6Uiz6gI8}}ym|JIsFNTVa_}s8^1Yg!WDHnMy?XWP>wWy*>#J*P@ulfby3Yyu zgRC?L{2p}m84y7dBB=0cnou`-m7n;)`88{TB%nkv5!7K5B~il@PRSi2LJ>V7A}ai< zH}{iRc=yPNAMeI{*;F5P^7{DE!+398*2VRU6IG;jr_|=;alEI?21L8tyW3ma*S2=H z;}@U-*&k8Mzs_Wt-LH!m6eJ_h^4n!z+=YWS?hcD4y`Sm)Fvso7hGt+>9|I`}s8144 z^q-P3C@RE0Y3FLGu|AzKoGc??vFwnsOhATGK3?Rr)k-i zI!$e?i$^-unJzntHrSp3Q=OAWzGbSY$Fh%dznCa+O)pLJI#Vi5e25`rk@dRM;2ha4z9aN7VjZRx8GM!mJpG*(RY>V3V4)l_Zwam&kZ8CL~1W30R!;T#SCW>4a!Y!&~fMGv5rYtRO ze?=hI;k5-hjqk4ADfH*=F@*UWAX4XmAU1~d40KLo=!|aAD=<5LYl)WsoXeNlhHydN zLFO7T8WX@XJ_LwCS}+Jd(@;bz2I0R95b`1X48k0^eGK*mCOAnEiAZ#gmS>TB2g!LP z=+#l^TqiN-Yy_S{_sREh`7(-n&TJ=q&FDTT{}pUTlP|(nh1U@}I58H?d~?Oj<<6HG zkGoMbXXN+5Xvhi!^%0Rd^MM15f>9nK8O-qngE53LfH4AN!663o!k=-N`7ndbfjM)C zXNGS;6@?Xcj+ovCIZI}-4h$F3Gdk;=p6LsIgP7R#$5>l?`_2dQT~I}Sh~zya&Jzyc zRDOhl4}gHvoGUM&;0lrv5?4_eR2n@RIIku>fT4i83gGA9TUTKJ|CyKfTZUe@QB!DH z=IV90MdMCB%%nh7)EOWMG`{$}Azi2E9NdmiE&YG8f8@ys^@N`l{7bN0wpmkPxlB{2 zb<-O8L7G08X7ysl)w+do&deP+Y1dAQWnR>En!ZwQMCB>G9#g^s}7rOYrVEjhKm{e<+=lz2?yL{a6eOJp4F~`yNd2mZNo2|+V0+0 YimYTNzp*gvCgzmF%FP@;2Xx*07k&-bU;qFB diff --git a/ptocr/model/loss/__pycache__/sast_loss.cpython-35.pyc b/ptocr/model/loss/__pycache__/sast_loss.cpython-35.pyc deleted file mode 100644 index d0b6d64c634defb7f599b75a4838c62acb74cf62..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3250 zcmb7G+iu)M82-ISuXl53vTZh`(6p3>1)8P_hqkIpQIQY=3Lk-d~-ZEHD&$u{cSOtCwfAq zuLk)pQhXnoOSFaDAh$w+L0b?qxk7tY+NzR^e~p3~jq0>jC*vKrPHuy~LZt?IRoZLN zV+1z2fz5vN(S~)`-cNSJ=+^MTfpvH22i~pa*p3qsgmJt&d1&2D{1I&HYwK&*)^1$8 ze#1Hh5W0V8AnzN>eA{=t_Yv%{x%jHN5U)b;fEqB7SLj=!VS)M^SPIBMxK(5CX1}h? z&9^o`(QVfAcDUp=ImPcHOYk>H5x-?@aj6Alqw!_Hv zB8Y%ri@jhNHBmV-IcsrdAp>14A)LUDVW3OYicnCi?G!ofyoMCSuLiZ5mEz^sih{Q zmX=INmX=zO+RXhP(V#|YhterZrzxG``NPcdv^1$JThx7QP&!*OXQn(1on`t$dWKw! zT(mlidQdvFu+&TENa`8Iq<#%pQ{+x-LGK#26v7arI1$=oUpeALXeFJew99KJJxj97 z8%=h&af$y7p*6;Rjz{s&t_4Q^&#oTHd6H+TmoDmYgYQM%+%qJf;Zc50lrE86qP;pV zZpiK|tD-^f8II}dw4_T|dZ+_;ue*?*CkasJshd7qaQZCC1w{5V)j6*~m+6|u^f{8? zwxEhxocnibPJ}N02a6XdT_#zl^diaSf)7kunZ-*b7EigJFR^&ab(!P^k{7|^3k8cW zXcn)Kyi%~ZLUN^GaYeJZQm}XhoN!vvWx?W#X7NRm0QDkR>=!KdNj{Ir%VidUF4Hp> zUn1E)$zuB{ES5tt>rY*aFH`yo$p)oYNxo9Bh^F*`Bwr?Z6)ava zSiG)TTqn6+u(&~TqhN7Ev$#>PxDHM@ZRoOKaYM8CDoKEP6)e71u=pCuYlvJcvj}vV zp0W5kNz63S9-j|bn2=2BE=aHo5vRd|LHt<`UqF%P-G{#st*NDd0jZ6YWw5Kev$`uG9?eRGHqnXpQ~0T zbHze*!X2Ee_zO0Tfu>Q@cjV6^ji9EgxW7P=kZ7+zU8SZYDvooB{aa1!XI1LI; z8-dU?)q{*%6yVm{wi9Pp#Qq~s8A2Hc%Gg!lIKhR8tIrR|s=hzw!piSiFr%i6Rz*As ze84}_$6+*5)5S#Q?)ek$+*oRZFYrO+J`eXfI zQZu<8V$tv%sUkX4^!RfSR;FmwZN2Dn$Lz5qx3u$m8f;!qBkk~mWXB6LWU~zQgvDe{ z18uWZpw!ilAnKqae0bSp#Bx7Sj_@5h7+>xv;bZp^J}5apNT=|XEhaaVy;F+ai;jBQJ@3Cyw~dlV#I-`3=M9z>aVh;8laefv92``d5^ORXJNZD;=X}Jeoave&w+Ebt}Z!9(h72O9;H?_5+qX zLVU4r2bo7d=Ar?>9}ll@L>)HpDD^{?wV2P5jxpEJmcFT)Lb#!Wx9cT1l&Sar>UgXw tB!>2U7`(FYCqWNl+nd{) z?akfx)~R~;b*CE5rvg!RM_y1*2ssyt%CYcY-K!YhJj69nH-9=A%F z5f2AGr6vrQSr`ZXAiukPiQOlA_}>~thg6Z z;!`kzf*$)}hnAo(Ept)fq6z}0w}`P9x^ay0kLd3+8|^_7d9>Y&h#$83>a=Iqi58&~ z?-<)4kWy7F_*Ipox$z`zjG=qux!#3_oYg~k!3Bz25230fvLm* zZGtXxstdBc-bod!&G8 zbPHW`gy{FI-Ytw!T4$)s*TjTYS~|xp0-OI^m6S*sWNr)D0c?|OQ?z+6tD>}#&NDr2 zviWY2l}VXZS*@ePcNYNFyOBj!d@r)Z8mzgD(q+JCvt{VD!d6J-8A?~#YPSSfS4owv z;xehRl_nDJD%Yg8j7Wv8E~6$|Lg@*%z)mz#dJ@K|u#;>N+AKBEND=;GZj<^FI)Tzt z40fR8JNE&;me8<_v z|KNKiy~+ydHFj0-ZA|!Hmwcy@4as*JX|pTr8t~l|d^dsb7TXehx7oJfyA6D|1>Y^; zz~61ICEsnycZXePJHYpb;Cq8@K%4f2??!G*zBkz%?C&bHI0d@K_Xlf%RTtJxu;#>` zxy5cl%TtiMN#=hr(%azQ?(pKh^f)s+Sd^8_>fpSpWJU)=zH!BA*c3-$b$WJB8D`8gfUk={?1wR(hcIn&vB1S52*(Wbd3LN&E{0PGF`r+@ z2>GH21&d;f(+|hNk)J6zQw}rbz^TGP;CqQrM3309<#_>f=~Tx9;&>1S0Dt25BieJS z@Vyv+4FakB@Q2@6gH_d+;W}XROM&%3(UpeySt4R7 z1;{lZS67U08D@XOor9(n%75LSu@AiC*GaF;L#SBAkmoa`mbi+I& zE-~~L5XdU>^5gko2)C^X 0.5)) - (int)(np.sum((gt_text > 0.5) & (training_mask <= 0.5))) - - if pos_num == 0: - # selected_mask = gt_text.copy() * 0 # may be not good - selected_mask = training_mask - selected_mask = selected_mask.reshape(1, selected_mask.shape[0], selected_mask.shape[1]).astype('float32') - return selected_mask - - neg_num = (int)(np.sum(gt_text <= 0.5)) - neg_num = (int)(min(pos_num * 3, neg_num)) - - if neg_num == 0: - selected_mask = training_mask - selected_mask = selected_mask.reshape(1, selected_mask.shape[0], selected_mask.shape[1]).astype('float32') - return selected_mask - - neg_score = score[gt_text <= 0.5] - neg_score_sorted = np.sort(-neg_score) - threshold = -neg_score_sorted[neg_num - 1] - - selected_mask = ((score >= threshold) | (gt_text > 0.5)) & (training_mask > 0.5) - selected_mask = selected_mask.reshape(1, selected_mask.shape[0], selected_mask.shape[1]).astype('float32') - return selected_mask - -def ohem_batch(scores, gt_texts, training_masks): - scores = scores.data.cpu().numpy() - gt_texts = gt_texts.data.cpu().numpy() - training_masks = training_masks.data.cpu().numpy() - - selected_masks = [] - for i in range(scores.shape[0]): - selected_masks.append(ohem_single(scores[i, :, :], gt_texts[i, :, :], training_masks[i, :, :])) - - selected_masks = np.concatenate(selected_masks, 0) - selected_masks = torch.from_numpy(selected_masks).float() - - return selected_masks \ No newline at end of file diff --git a/ptocr/model/loss/centerloss.py b/ptocr/model/loss/centerloss.py deleted file mode 100644 index 20383f3..0000000 --- a/ptocr/model/loss/centerloss.py +++ /dev/null @@ -1,95 +0,0 @@ -import torch -import torch.nn as nn - -class CenterLoss(nn.Module): - """Center loss. - - Reference: - Wen et al. A Discriminative Feature Learning Approach for Deep Face Recognition. ECCV 2016. - - Args: - num_classes (int): number of classes. - feat_dim (int): feature dimension. - """ - def __init__(self, num_classes=10, feat_dim=2, use_gpu=True): - super(CenterLoss, self).__init__() - self.num_classes = num_classes - self.feat_dim = feat_dim - self.use_gpu = use_gpu - - if self.use_gpu: - self.centers = nn.Parameter(torch.randn(self.num_classes, self.feat_dim).cuda()) - else: - self.centers = nn.Parameter(torch.randn(self.num_classes, self.feat_dim)) - - nn.init.normal_(self.centers, mean=0, std=1) -# nn.init.kaiming_normal_(tensor) -# nn.init.constant_(tensor,0.5) - - def forward(self, x, labels): - """ - Args: - x: feature matrix with shape (batch_size, feat_dim). - labels: ground truth labels with shape (batch_size). - """ - batch_size = x.size(0) - distmat = torch.pow(x, 2).sum(dim=1, keepdim=True).expand(batch_size, self.num_classes) + \ - torch.pow(self.centers, 2).sum(dim=1, keepdim=True).expand(self.num_classes, batch_size).t() - distmat.addmm_(1, -2, x, self.centers.t()) - - classes = torch.arange(self.num_classes).long() - if self.use_gpu: classes = classes.cuda() - labels = labels.unsqueeze(1).expand(batch_size, self.num_classes) - mask = labels.eq(classes.expand(batch_size, self.num_classes)) - - dist = distmat * mask.float() - loss = dist.clamp(min=1e-12, max=1e+12).sum() / batch_size - - return loss - - - -from torch.autograd.function import Function - -class CenterLoss1(nn.Module): - def __init__(self, num_classes, feat_dim, size_average=True): - super(CenterLoss1, self).__init__() - self.centers = nn.Parameter(torch.randn(num_classes, feat_dim)) - self.centerlossfunc = CenterlossFunc.apply - self.feat_dim = feat_dim - self.size_average = size_average - - def forward(self, feat,label): - batch_size = feat.size(0) - feat = feat.view(batch_size, -1) - # To check the dim of centers and features - if feat.size(1) != self.feat_dim: - raise ValueError("Center's dim: {0} should be equal to input feature's \ - dim: {1}".format(self.feat_dim,feat.size(1))) - batch_size_tensor = feat.new_empty(1).fill_(batch_size if self.size_average else 1) - loss = self.centerlossfunc(feat, label, self.centers, batch_size_tensor) - loss = loss.clamp(min=1e-12, max=1e+12) - return loss - - -class CenterlossFunc(Function): - @staticmethod - def forward(ctx, feature, label, centers, batch_size): - ctx.save_for_backward(feature, label, centers, batch_size) - centers_batch = centers.index_select(0, label.long()) - return (feature - centers_batch).pow(2).sum() / 2.0 / batch_size - - @staticmethod - def backward(ctx, grad_output): - feature, label, centers, batch_size = ctx.saved_tensors - centers_batch = centers.index_select(0, label.long()) - diff = centers_batch - feature - # init every iteration - counts = centers.new_ones(centers.size(0)) - ones = centers.new_ones(label.size(0)) - grad_centers = centers.new_zeros(centers.size()) - - counts = counts.scatter_add_(0, label.long(), ones) - grad_centers.scatter_add_(0, label.unsqueeze(1).expand(feature.size()).long(), diff) - grad_centers = grad_centers/counts.view(-1, 1) - return - grad_output * diff / batch_size, None, grad_centers / batch_size, None \ No newline at end of file diff --git a/ptocr/model/loss/ctc_loss.py b/ptocr/model/loss/ctc_loss.py deleted file mode 100644 index 3aa8c0f..0000000 --- a/ptocr/model/loss/ctc_loss.py +++ /dev/null @@ -1,19 +0,0 @@ -# from torch.nn import CTCLoss as PytorchCTCLoss -from warpctc_pytorch import CTCLoss as PytorchCTCLoss -import torch.nn as nn -from .basical_loss import focal_ctc_loss - -class CTCLoss(nn.Module): - def __init__(self,config): - super(CTCLoss,self).__init__() -# self.criterion = PytorchCTCLoss(reduction = config['loss']['reduction']) - self.criterion = PytorchCTCLoss() - self.config = config - - def forward(self,pre_batch,gt_batch): - preds,preds_size = pre_batch['preds'],pre_batch['preds_size'] - labels,labels_len = gt_batch['labels'],gt_batch['labels_len'] - loss = self.criterion(preds, labels, preds_size, labels_len) - if self.config['loss']['reduction']=='none': - loss = focal_ctc_loss(loss) - return loss/self.config['trainload']['batch_size'] \ No newline at end of file diff --git a/ptocr/model/loss/db_loss.py b/ptocr/model/loss/db_loss.py deleted file mode 100644 index d8ed9e7..0000000 --- a/ptocr/model/loss/db_loss.py +++ /dev/null @@ -1,30 +0,0 @@ -#-*- coding:utf-8 _*- -""" -@author:fxw -@file: db_loss.py -@time: 2020/08/10 -""" -import torch -import torch.nn as nn -from .basical_loss import MaskL1Loss,BalanceCrossEntropyLoss,DiceLoss,FocalCrossEntropyLoss -class DBLoss(nn.Module): - def __init__(self, l1_scale=10, bce_scale=1,eps=1e-6): - super(DBLoss, self).__init__() - self.dice_loss = DiceLoss(eps) - self.l1_loss = MaskL1Loss() - self.bce_loss = BalanceCrossEntropyLoss() - self.l1_scale = l1_scale - self.bce_scale = bce_scale - - def forward(self, pred_bach, gt_batch): - bce_loss = self.bce_loss(pred_bach['binary'][:,0], gt_batch['gt'], gt_batch['mask']) - metrics = dict(loss_bce=bce_loss) - if 'thresh' in pred_bach: - l1_loss, l1_metric = self.l1_loss(pred_bach['thresh'][:,0], gt_batch['thresh_map'], gt_batch['thresh_mask']) - dice_loss = self.dice_loss(pred_bach['thresh_binary'][:,0], gt_batch['gt'], gt_batch['mask']) - metrics['loss_thresh'] = dice_loss - loss = dice_loss + self.l1_scale * l1_loss + bce_loss * self.bce_scale - metrics.update(**l1_metric) - else: - loss = bce_loss - return loss, metrics \ No newline at end of file diff --git a/ptocr/model/loss/pan_loss.py b/ptocr/model/loss/pan_loss.py deleted file mode 100644 index fc755f5..0000000 --- a/ptocr/model/loss/pan_loss.py +++ /dev/null @@ -1,66 +0,0 @@ -#-*- coding:utf-8 _*- -""" -@author:fxw -@file: pan_loss.py -@time: 2020/08/10 -""" -import torch -import torch.nn as nn -from torch.autograd import Variable -from .basical_loss import DiceLoss,Agg_loss,Dis_loss,ohem_batch - -class PANLoss(nn.Module): - def __init__(self,kernel_rate=0.5,agg_dis_rate=0.25,eps=1e-6): - super(PANLoss,self).__init__() - self.kernel_rate = kernel_rate - self.agg_dis_rate = agg_dis_rate - self.dice_loss = DiceLoss(eps) - self.agg_loss = Agg_loss() - self.dis_loss = Dis_loss() - - def GetKernelLoss(self,pre_text,pre_kernel,gt_kernel,train_mask): - mask0 = pre_text.data.cpu().numpy() - mask1 = train_mask.data.cpu().numpy() - selected_masks = ((mask0 > 0.5) & (mask1 > 0.5)).astype('float32') - selected_masks = torch.from_numpy(selected_masks).float() - selected_masks = Variable(selected_masks) - if torch.cuda.is_available(): - selected_masks = selected_masks.cuda() - loss_kernel = self.dice_loss(pre_kernel, gt_kernel, selected_masks) - return loss_kernel - - def GetTextLoss(self,pre_text,gt_text,train_mask): - selected_masks = ohem_batch(pre_text, gt_text, train_mask) - selected_masks = Variable(selected_masks) - if torch.cuda.is_available(): - selected_masks = selected_masks.cuda() - loss_text = self.dice_loss(pre_text, gt_text, selected_masks) - return loss_text - - def forward(self,pred_bach,gt_batch): - pre_text = torch.sigmoid(pred_bach['pre_text']) - pre_kernel = torch.sigmoid(pred_bach['pre_kernel']) - gt_text = gt_batch['gt_text'] - gt_text_key = gt_batch['gt_text_key'] - gt_kernel = gt_batch['gt_kernel'] - gt_kernel_key = gt_batch['gt_kernel_key'] - train_mask = gt_batch['train_mask'] - similarity_vector = pred_bach['similarity_vector'] - - pre_text_select = (pre_text > 0.5).float() - pre_kernel_select = (pre_kernel > 0.5).float() - gt_text_key = gt_text_key*pre_text_select - gt_kernel_key = gt_kernel_key*pre_kernel_select - - - loss_kernel = self.GetKernelLoss(pre_text,pre_kernel,gt_kernel,train_mask) - loss_text = self.GetTextLoss(pre_text,gt_text,train_mask) - loss_agg = self.agg_loss.cal_agg_batch(similarity_vector,gt_kernel_key, gt_text_key,train_mask) - loss_dis = self.dis_loss.cal_Ldis_batch(similarity_vector,gt_kernel_key,train_mask) - loss = loss_text + self.kernel_rate*loss_kernel + self.agg_dis_rate*(loss_agg + loss_dis) - metrics = dict(loss_text=loss_text) - metrics['loss_kernel'] = loss_kernel - metrics['loss_agg'] = loss_agg - metrics['loss_dis'] = loss_dis - return loss,metrics - diff --git a/ptocr/model/loss/pse_loss.py b/ptocr/model/loss/pse_loss.py deleted file mode 100644 index 073ca4f..0000000 --- a/ptocr/model/loss/pse_loss.py +++ /dev/null @@ -1,52 +0,0 @@ -#-*- coding:utf-8 _*- -""" -@author:fxw -@file: pse_loss.py -@time: 2020/08/10 -""" -import torch -import torch.nn as nn -from torch.autograd import Variable -from .basical_loss import DiceLoss,ohem_batch - -class PSELoss(nn.Module): - def __init__(self,text_tatio=0.7,eps=1e-6): - super(PSELoss,self).__init__() - self.text_tatio = text_tatio - self.dice_loss = DiceLoss(eps) - - def GetKernelLoss(self, pre_text, pre_kernel, gt_kernel, train_mask): - mask0 = pre_text.data.cpu().numpy() - mask1 = train_mask.data.cpu().numpy() - selected_masks = ((mask0 > 0.5) & (mask1 > 0.5)).astype('float32') - selected_masks = torch.from_numpy(selected_masks).float() - selected_masks = Variable(selected_masks) - if torch.cuda.is_available(): - selected_masks = selected_masks.cuda() - loss_kernels = [] - for i in range(pre_kernel.shape[1]): - loss_kernel = self.dice_loss(torch.sigmoid(pre_kernel[:,i]), gt_kernel[:,i], selected_masks) - loss_kernels.append(loss_kernel) - return sum(loss_kernels)/len(loss_kernels) - - def GetTextLoss(self, pre_text, gt_text, train_mask): - selected_masks = ohem_batch(pre_text, gt_text, train_mask) - selected_masks = Variable(selected_masks) - if torch.cuda.is_available(): - selected_masks = selected_masks.cuda() - loss_text = self.dice_loss(pre_text, gt_text, selected_masks) - return loss_text - - def forward(self, pred_bach,gt_batch): - pre_text = torch.sigmoid(pred_bach['pre_text']) - pre_kernel = pred_bach['pre_kernel'] - gt_text = gt_batch['gt_text'] - gt_kernel = gt_batch['gt_kernel'] - train_mask = gt_batch['train_mask'] - - loss_text = self.GetTextLoss(pre_text,gt_text,train_mask) - loss_kernel = self.GetKernelLoss(pre_text,pre_kernel,gt_kernel,train_mask) - loss = self.text_tatio*loss_text + (1 - self.text_tatio)*loss_kernel - metrics = dict(loss_text=loss_text) - metrics['loss_kernel'] = loss_kernel - return loss,metrics \ No newline at end of file diff --git a/ptocr/model/loss/sast_loss.py b/ptocr/model/loss/sast_loss.py deleted file mode 100644 index 0cc1e4e..0000000 --- a/ptocr/model/loss/sast_loss.py +++ /dev/null @@ -1,109 +0,0 @@ -#-*- coding:utf-8 _*- -""" -@author:fxw -@file: sast_loss.py -@time: 2020/08/18 -""" - -import torch -import torch.nn as nn -from torch.autograd import Variable -from .basical_loss import DiceLoss,BalanceCrossEntropyLoss,ohem_batch - -class SASTLoss(nn.Module): - def __init__(self,tvo_lw,tco_lw,score_lw,border_lw): - super(SASTLoss,self).__init__() - self.dict_loss = DiceLoss() - self.bce_loss = BalanceCrossEntropyLoss() - self.tvo_lw, self.tco_lw = tvo_lw,tco_lw #1.5, 1.5 - self.score_lw, self.border_lw = score_lw,border_lw #1.0, 1.0 - - def forward(self, predicts,labels): - - f_score = predicts['f_score'] - f_border = predicts['f_border'] - f_tvo = predicts['f_tvo'] - f_tco = predicts['f_tco'] - - l_score = labels['input_score'] - l_border = labels['input_border'] - l_mask = labels['input_mask'] - l_tvo = labels['input_tvo'] - l_tco = labels['input_tco'] - - batch_size,_,w,h = f_score.shape - - -# #score_loss add ohem -# selected_masks = ohem_batch(f_score.squeeze(), l_score.squeeze(), l_mask.squeeze()) -# selected_masks = Variable(selected_masks).unsqueeze(1) -# if torch.cuda.is_available(): -# selected_masks = selected_masks.cuda() -# score_loss = self.dict_loss(f_score,l_score,selected_masks) - - #score_loss no ohem - intersection = torch.sum(f_score * l_score * l_mask) - union = torch.sum(f_score * l_mask) + torch.sum(l_score * l_mask) - score_loss = 1.0 - 2 * intersection / (union + 1e-5) - - # border loss - l_border_split, l_border_norm = l_border[:,0:4,:,:], l_border[:,-1:,:,:] - f_border_split = f_border - l_border_norm_split = l_border_norm.expand((batch_size,4,w,h)) - l_border_score = l_score.expand((batch_size,4,w,h)) - l_border_mask = l_mask.expand((batch_size,4,w,h)) - border_diff = l_border_split - f_border_split - abs_border_diff = torch.abs(border_diff) - border_sign = abs_border_diff < 1.0 - border_sign = border_sign.float() - border_sign.stop_gradient = True - border_in_loss = 0.5 * abs_border_diff * abs_border_diff * border_sign + \ - (abs_border_diff - 0.5) * (1.0 - border_sign) - border_out_loss = l_border_norm_split * border_in_loss - border_loss = torch.sum(border_out_loss * l_border_score * l_border_mask) / \ - (torch.sum(l_border_score * l_border_mask) + 1e-5) - - # tvo_loss - l_tvo_split, l_tvo_norm = l_tvo[:,0:8,:,:],l_tvo[:,-1:,:,:] - f_tvo_split = f_tvo - l_tvo_norm_split = l_tvo_norm.expand((batch_size,8,w,h)) - l_tvo_score = l_score.expand((batch_size,8,w,h)) - l_tvo_mask = l_mask.expand((batch_size,8,w,h)) - # - tvo_geo_diff = l_tvo_split - f_tvo_split - abs_tvo_geo_diff = torch.abs(tvo_geo_diff) - tvo_sign = abs_tvo_geo_diff < 1.0 - tvo_sign = tvo_sign.float() - tvo_sign.stop_gradient = True - tvo_in_loss = 0.5 * abs_tvo_geo_diff * abs_tvo_geo_diff * tvo_sign + \ - (abs_tvo_geo_diff - 0.5) * (1.0 - tvo_sign) - tvo_out_loss = l_tvo_norm_split * tvo_in_loss - tvo_loss = torch.sum(tvo_out_loss * l_tvo_score * l_tvo_mask) / \ - (torch.sum(l_tvo_score * l_tvo_mask) + 1e-5) - - # tco_loss - l_tco_split, l_tco_norm = l_tco[:,0:2,:,:],l_tco[:,-1:,:,:] - f_tco_split = f_tco - l_tco_norm_split = l_tco_norm.expand((batch_size,2,w,h)) - l_tco_score = l_score.expand((batch_size,2,w,h)) - l_tco_mask = l_mask.expand((batch_size,2,w,h)) - # - tco_geo_diff = l_tco_split - f_tco_split - abs_tco_geo_diff = torch.abs(tco_geo_diff) - tco_sign = abs_tco_geo_diff < 1.0 - tco_sign = tco_sign.float() - tco_sign.stop_gradient = True - tco_in_loss = 0.5 * abs_tco_geo_diff * abs_tco_geo_diff * tco_sign + \ - (abs_tco_geo_diff - 0.5) * (1.0 - tco_sign) - tco_out_loss = l_tco_norm_split * tco_in_loss - tco_loss = torch.sum(tco_out_loss * l_tco_score * l_tco_mask) / \ - (torch.sum(l_tco_score * l_tco_mask) + 1e-5) - - # total loss - - total_loss = score_loss * self.score_lw + border_loss * self.border_lw + \ - tvo_loss * self.tvo_lw + tco_loss * self.tco_lw - - metrics = {'loss_total': total_loss, "loss_score": score_loss, \ - "loss_border": border_loss, 'loss_tvo': tvo_loss, 'loss_tco': tco_loss} - return total_loss,metrics diff --git a/ptocr/model/segout/__init__.py b/ptocr/model/segout/__init__.py deleted file mode 100644 index 35de2ad..0000000 --- a/ptocr/model/segout/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -#-*- coding:utf-8 _*- -""" -@author:fxw -@file: __init__.py.py -@time: 2020/08/07 -""" diff --git a/ptocr/model/segout/__pycache__/__init__.cpython-35.pyc b/ptocr/model/segout/__pycache__/__init__.cpython-35.pyc deleted file mode 100644 index c884d2aec2855c2199aa0c50607772c8605985a4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 226 zcmWgR<>mUqrW+r@z`*brh~a<{$Z`PUVlE(&!oUy(BpDfkHJPeRxf~KpOEU6{tkNpV zxg63mb5gAo;^Q;(GE3s)^$IG1h|8fQGZ!doWME{VZ(yNsV9w>I$#{!BK0YNsIX-?R zLlG0uR50<&$Jr_-v^ce>I3_JIFTJ9)JT)^WpfWilu_!m7C_gJTxuh7#FUc=T&hU2* qiYX|`PcDkd%}+_qi78G^&o3>BL9+W6hYe7wG$+-L4diwpW&i+EYCqxt diff --git a/ptocr/model/segout/__pycache__/__init__.cpython-36.pyc b/ptocr/model/segout/__pycache__/__init__.cpython-36.pyc deleted file mode 100644 index 6eb960bfcd2240d8ff63f0c5f80ac4ee3350f81e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 180 zcmXr!<>e|X*Nu;0U|@I*#Bjg}WH|tFF$a)HVTfW#VGL%_WU4ada!4#K$;dCVN~}zQ`1q9! yMNB|5!Ne~GJ^g}`{Ny72-29Z(9R1?d^!(BieI)a4ao7N*N^?@}*g&oUVg>*s5G{rP diff --git a/ptocr/model/segout/__pycache__/det_DB_segout.cpython-35.pyc b/ptocr/model/segout/__pycache__/det_DB_segout.cpython-35.pyc deleted file mode 100644 index bad12e8cd811625d5907840f5cf05a551122df9d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3235 zcma)8OK%*<5w4z{oqh5xQq)A6j`gQ74+N5uw)Fj=c zNkA8nYq7%NAUJ*0eG<>BacTC3uU~ba46|JCg{iKhr=LVtkIH#UlyOj217h-)%SA@rfRw-Q*zD;=N;b7hM4)y4%u5?nB z=8_cAWf!)5ngmy-v59?*H4OV9_AC4=IwQL9XyVZYkvj8u9yRF97sjVbyvVdgXOM>R zitZvHY^z-3tO+N3EYV(UUntS*TwU&h6mNGI+l!aro^86Q&-kvoeCD!VkL|vIHq7ob zo%Ct4BFrkm4t0jhU~i4Um=6?7tcxKXz9jO5?N=VlY>3*%oBD{r;h-09z+v6+>Uo~U zb}#(>SXX0h!lW!-hIxFVjSa`~OC9odSemdXtNYhFaT?F6>?IOwp;wjR-0E<#5msZP z?YPX-urhI0WX0%zUSg+ZS&jF?&xVkh>yP9G&$B!$bZo+`u$7L}upA<(*{Yk-*(!>hdSQSEzRoQJ1ecma_cY~P0K8GZQLU^*{OET zcs9#VT>q8MM&ruLX|BC+yX)7-*A8(({X1wo-|BqmRe^ihjB4_J%<&?U!355X8MIGCn#5o zIchc;f4=|a@vJHnb383mogdebbo@3z8LnWiWcx!9>TRhG{sQ%$T32i8p3pmL)7$cR zHBdY1^wttK++X-R;i&M#VPJkA!=8YkKqsgZMd!c+0S6R4iV0Zt0#&p}Rf8r1x-1zm zHgKsGzg$Q+M7kx?b9tQz0U=PoQkBr_upTdQ0Rq<<#@Yu*K8MtFV8&7BI^ZKr@%-!Y z=*Ro-{_`I{zx(*|KX}ZKusGu6iD^eJL(R`mO9;8 zcJy+>C0r`}G%+}9#Myrb0~ekHZ9OvEf=G%UVGeMH|Cu5L*oN~x;BF1Nm;krnWZ*6& zfN=FT+{kmlnlu&QF2fx}>+OYh#EwN~#j&2?b2ix4GRD{n4X4$b_Nv zelX|q@i6JJ5Fd^_!`sn>p@x!%K)|VsZj7SG3g;}Rkf*SU&#?m+2u7cX3+p7}(7Tly zgQI$PuxfUAwY}(yhTV08!LBLb=^9B~)ivK8&I{>j=p9IyN}E}k#}zt(9mnW9^&#k$ zvuGjdg82+kpa-qG{QV(XvukA2k$K362Xzk=Rr3=*|NU#iklKCgk_R;OFbalX>K4lrjPmy!hd12A>wA>o*j?=wWO2cIJocW%M)w=pCW z88ZjI`5As(*XSghnKFS78v6B28rL{y4ts4N*R>2_e^ROD^0miaHa5-->;reRFKhN!Z#S*EjeT PyWBEx1%QdZTJ!!3RrAWc diff --git a/ptocr/model/segout/__pycache__/det_DB_segout.cpython-36.pyc b/ptocr/model/segout/__pycache__/det_DB_segout.cpython-36.pyc deleted file mode 100644 index 336840749793eed804fe175f94c573fa20151a3a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2929 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 diff --git a/ptocr/model/segout/__pycache__/det_PAN_segout.cpython-36.pyc b/ptocr/model/segout/__pycache__/det_PAN_segout.cpython-36.pyc deleted file mode 100644 index 8022601a1c1b5d7110c9bf0fea0af14b25548621..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1045 zcmZWn&u`N(6tv!(Vj42oexM z1xx6J1x#4NPOQ`pY)ak}Q4`KJ5sqY&+R_Rf!A^-^UmSYP@=7gUDA>P55UB-RuWzl1TMA)D+(zJjqjLRF`G)I5TbE8kF!{FZdjHX z=OoY0d&0P9aTcn0A$`l(g-nK~6=#`LJQ{~ECxxj;NmvxJ00J7ER~ycMl0OIS&Q#}- z>ZG}lNoP%C=Ra0e10LUnzo>|!ExOp>s=a-O>R;&?hW`oX2rwm?kdiLx6$J{Q=q0-X zDke;Q)>a8@3vvTQ4B0ax*mrt$@3YWXf(FNszlj5yZrH}}fZsZWGi;K_jFjp6$5)WWgJOo3i2cJWq(0%%p+H~=Fo3~B0&r}UU6*db9 z4kBk^DnVqAa}Z{h;Ch$yud^^&pHzaV=MZn28$BbLB%I#^@~J`q3UO7sX9F5yqraZj0b&{uPp1qr8k&g3hO^frf0#oJQKeSc8d%TAG8Y?N# J*vzB5>@T@~`w;*D diff --git a/ptocr/model/segout/__pycache__/det_PSE_segout.cpython-35.pyc b/ptocr/model/segout/__pycache__/det_PSE_segout.cpython-35.pyc deleted file mode 100644 index 5e9c4274ba00cb8240fdc66e8c6361feae80b436..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1091 zcmZWo%Wl&^6uo0RPGZ_pDB`iATW{Jb0#Q{JiU^MtrE0_`vNX8%BysQ~%s4cS+EsXb z3qQg~c*`okz=|`ImImQ??#$eKXYRS@IQQ4ry`LXiJg|s<(aPgs-NsNaU}B;kETh<> zl+nndo<*@uu|ug%nL|B?*qgXUu}fc&=#sE#RHL~~Jy##<`t{kq*A6G;u#gA+>6zE= zC#g8tjz!6jyRWzsgJM$djL*GxnPdpG_F8+pd;7b4&%6Z?(9r@2$!Aq#GFD+WPDP~M z*H7(gp`KtN02f$cfzp8fPSnT3ApQpQSX_ezbVLELy44kfm!cF=S;z%3qD38-MfTOz zdUi4Nte(P@co|&~&C#qyb0SvSG;-(y-eqM|d|uN@90he9`)GlyNowP}RZUICLP`g_ zs=;}ZCne{VmFJb)De}*)xN4jxc_@>a@a@V`BJEcjNuCSIqhW|SRaHGoL#2cQJ5A2b zhVviv`=oOayjMc1pdaRg>Eui#!SOkc$?RAbBQ&b8N4L@NUFReimqjFltcXP#EcFQf zlSeiI)Wc93F|jQ+ySGBy&UFWWS<`7+oDPP{VL-(QmeHKiB{K{fxJ(#A!Do9qviH26LM diff --git a/ptocr/model/segout/__pycache__/det_PSE_segout.cpython-36.pyc b/ptocr/model/segout/__pycache__/det_PSE_segout.cpython-36.pyc deleted file mode 100644 index 0d1925d025cd01e2fcae7655609befbd7f38c19b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 999 zcmZWnJ8#=C5GJXo)!66@MY?qgpv5-ap+OK72GZBi1_8VY1qfB7ZMk~56xF1LGC4)2 z{*eBhu3S6$FLdgioHPbXfk*0icl_@A_`KT<-u)W!kBpGtWM|m0AH%GMU<8SXpn_#| z!6GIsVP{ruM>ZvIiExB_MT9Ha!dY99E7*jD-s&(I$F-i9>S%g#9*n1HCXb#8srlLD zHLv8XtaX3+Eg0)Ghl9~@G#m^M2g4V^2JR!L8^A+CYW#Xx#rZOm3A%3wg2PcAfRQAk z0FVjqnoKEzyXr*N7FJ|m6PrX1K=RGKiJZNXS|++w8$t=$c#xi0SP2HM?#Q? zBVqhcX%VY*B}2>DmCUB5mllOoJekL^W|i@hEUqe90RbJ(+aJzN5 ziOe$2@8E<~-N&Mcs(lWqN~?%ZdsgcSJ>zjM$^^>$@G6sezbF(cXL=`Po|na&x=3_d e7F$x>4=tEB_wJ&d=KbR>+?Uu%g~o>7_vl}`_3bSH diff --git a/ptocr/model/segout/__pycache__/det_SAST_segout.cpython-35.pyc b/ptocr/model/segout/__pycache__/det_SAST_segout.cpython-35.pyc deleted file mode 100644 index 1a37c5ebc4218a338479438910086431420be352..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3114 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 qTh^EA4@ccV8K score: - continue - - if points.shape[0] > 2: - box = self.unclip(points, self.unclip_ratio) - if len(box) > 1: - continue - else: - continue - box = box.reshape(-1, 2) - _, sside = self.get_mini_boxes(box.reshape((-1, 1, 2))) - if sside < self.min_size + 2: - continue - - if not isinstance(dest_width, int): - dest_width = dest_width.item() - dest_height = dest_height.item() - - box[:, 0] = np.clip( - np.round(box[:, 0] / width * dest_width), 0, dest_width) - box[:, 1] = np.clip( - np.round(box[:, 1] / height * dest_height), 0, dest_height) - boxes.append(box.tolist()) - scores.append(score) - return boxes, scores - - def boxes_from_bitmap(self, pred, _bitmap, dest_width, dest_height): - ''' - _bitmap: single map with shape (1, H, W), - whose values are binarized as {0, 1} - ''' - - bitmap = _bitmap - height, width = bitmap.shape - - outs = cv2.findContours((bitmap * 255).astype(np.uint8), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE) - if len(outs) == 3: - img, contours, _ = outs[0], outs[1], outs[2] - elif len(outs) == 2: - contours, _ = outs[0], outs[1] - - num_contours = min(len(contours), self.max_candidates) - boxes = np.zeros((num_contours, 4, 2), dtype=np.int16) - scores = np.zeros((num_contours,), dtype=np.float32) - - for index in range(num_contours): - contour = contours[index] - points, sside = self.get_mini_boxes(contour) - if sside < self.min_size: - continue - points = np.array(points) - score = self.box_score_fast(pred, points.reshape(-1, 2)) - if self.box_thresh > score: - continue - - box = self.unclip(points, self.unclip_ratio).reshape(-1, 1, 2) - box, sside = self.get_mini_boxes(box) - if sside < self.min_size + 2: - continue - box = np.array(box) - if not isinstance(dest_width, int): - dest_width = dest_width.item() - dest_height = dest_height.item() - - box[:, 0] = np.clip( - np.round(box[:, 0] / width * dest_width), 0, dest_width) - box[:, 1] = np.clip( - np.round(box[:, 1] / height * dest_height), 0, dest_height) - boxes[index, :, :] = box.astype(np.int16) - scores[index] = score - return boxes, scores - - def unclip(self, box, unclip_ratio=2): - poly = Polygon(box) - distance = poly.area * unclip_ratio / poly.length - offset = pyclipper.PyclipperOffset() - offset.AddPath(box, pyclipper.JT_ROUND, pyclipper.ET_CLOSEDPOLYGON) - expanded = np.array(offset.Execute(distance)) - return expanded - - def get_mini_boxes(self, contour): - bounding_box = cv2.minAreaRect(contour) - points = sorted(list(cv2.boxPoints(bounding_box)), key=lambda x: x[0]) - - index_1, index_2, index_3, index_4 = 0, 1, 2, 3 - if points[1][1] > points[0][1]: - index_1 = 0 - index_4 = 1 - else: - index_1 = 1 - index_4 = 0 - if points[3][1] > points[2][1]: - index_2 = 2 - index_3 = 3 - else: - index_2 = 3 - index_3 = 2 - - box = [ - points[index_1], points[index_2], points[index_3], points[index_4] - ] - return box, min(bounding_box[1]) - - def box_score_fast(self, bitmap, _box): - h, w = bitmap.shape[:2] - box = _box.copy() - xmin = np.clip(np.floor(box[:, 0].min()).astype(np.int), 0, w - 1) - xmax = np.clip(np.ceil(box[:, 0].max()).astype(np.int), 0, w - 1) - ymin = np.clip(np.floor(box[:, 1].min()).astype(np.int), 0, h - 1) - ymax = np.clip(np.ceil(box[:, 1].max()).astype(np.int), 0, h - 1) - - mask = np.zeros((ymax - ymin + 1, xmax - xmin + 1), dtype=np.uint8) - box[:, 0] = box[:, 0] - xmin - box[:, 1] = box[:, 1] - ymin - cv2.fillPoly(mask, box.reshape(1, -1, 2).astype(np.int32), 1) - return cv2.mean(bitmap[ymin:ymax + 1, xmin:xmax + 1], mask)[0] - - def __call__(self, pred, ratio_list): - pred = pred[:, 0, :, :] - segmentation = pred > self.thresh - - boxes_batch = [] - score_batch = [] - for batch_index in range(pred.shape[0]): - height, width = pred.shape[-2:] - if (self.is_poly): - tmp_boxes, tmp_scores = self.polygons_from_bitmap( - pred[batch_index], segmentation[batch_index], width, height) - - boxes = [] - score = [] - for k in range(len(tmp_boxes)): - if tmp_scores[k] > self.box_thresh: - boxes.append(tmp_boxes[k]) - score.append(tmp_scores[k]) - if len(boxes) > 0: - ratio_w, ratio_h = ratio_list[batch_index] - for i in range(len(boxes)): - boxes[i] = np.array(boxes[i]) - boxes[i][:, 0] = boxes[i][:, 0] * ratio_w - boxes[i][:, 1] = boxes[i][:, 1] * ratio_h - - boxes_batch.append(boxes) - score_batch.append(score) - else: - -# tmp_boxes, tmp_scores = self.boxes_from_bitmap( -# pred[batch_index], segmentation[batch_index], width, height) - - - tmp_boxes = cpp_boxes_from_bitmap(pred[batch_index], segmentation[batch_index],self.box_thresh,self.unclip_ratio) - boxes = [] - score = [] - for k in range(len(tmp_boxes)): -# if tmp_scores[k] > self.box_thresh: - boxes.append(tmp_boxes[k]) -# score.append(tmp_scores[k]) - if len(boxes) > 0: - boxes = np.array(boxes) - - ratio_w, ratio_h = ratio_list[batch_index] - boxes[:, :, 0] = boxes[:, :, 0] * ratio_w - boxes[:, :, 1] = boxes[:, :, 1] * ratio_h - - boxes_batch.append(boxes) - score_batch.append(score) - return boxes_batch, score_batch \ No newline at end of file diff --git a/ptocr/postprocess/PANpostprocess.py b/ptocr/postprocess/PANpostprocess.py deleted file mode 100644 index eb9dce8..0000000 --- a/ptocr/postprocess/PANpostprocess.py +++ /dev/null @@ -1,135 +0,0 @@ -#-*- coding:utf-8 _*- -""" -@author:fxw -@file: PSEpostprocess.py -@time: 2020/08/13 -""" - -import numpy as np -import cv2 -import torch -from .piexlmerge import pan - -class PANPostProcess(object): - - def __init__(self, config): - self.min_text_area = config['postprocess']['min_text_area'] - self.is_poly = config['postprocess']['is_poly'] - self.min_score = config['postprocess']['min_score'] - self.config = config - - def polygons_from_bitmap(self, pred): - - boxes = [] - scores = [] - pred,label_points,label_values = pan(pred,self.config) - - for label_value, label_point in label_points.items(): - if label_value not in label_values: - continue - score_i = label_point[0] - label_point = label_point[2:] - points = np.array(label_point, dtype=int).reshape(-1, 2) - - if points.shape[0] < self.min_text_area : - continue - - if score_i < self.min_score: - continue - - binary = np.zeros(pred.shape, dtype='uint8') - binary[pred == label_value] = 1 - - find_out = cv2.findContours(binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) - if(len(find_out)==2): - contours, _ = find_out - else: - _, contours, _ = find_out - contour = contours[0] - # epsilon = 0.01 * cv2.arcLength(contour, True) - # box = cv2.approxPolyDP(contour, epsilon, True) - box = contour - - if box.shape[0] <= 2: - continue - - box = box * self.config['postprocess']['scale'] - box = box.astype('int32') - boxes.append(box[:,0]) - scores.append(score_i) - return boxes, scores - - def boxes_from_bitmap(self, pred): - boxes = [] - scores = [] - pred,label_points,label_values = pan(pred,self.config) - - for label_value, label_point in label_points.items(): - if label_value not in label_values: - continue - score_i = label_point[0] - label_point = label_point[2:] - points = np.array(label_point, dtype=int).reshape(-1, 2) - - if points.shape[0] < self.min_text_area : - continue - - if score_i < self.min_score: - continue - box = self.get_mini_boxes(points)*self.config['postprocess']['scale'] - boxes.append(box) - scores.append(score_i) - return boxes, scores - - def get_mini_boxes(self, contour): - bounding_box = cv2.minAreaRect(contour) - points = sorted(list(cv2.boxPoints(bounding_box)), key=lambda x: x[0]) - - index_1, index_2, index_3, index_4 = 0, 1, 2, 3 - if points[1][1] > points[0][1]: - index_1 = 0 - index_4 = 1 - else: - index_1 = 1 - index_4 = 0 - if points[3][1] > points[2][1]: - index_2 = 2 - index_3 = 3 - else: - index_2 = 3 - index_3 = 2 - - box = [ - points[index_1], points[index_2], points[index_3], points[index_4] - ] - return np.array(box) - - def __call__(self, pred, ratio_list): - boxes_batch = [] - score_batch = [] - for batch_index in range(pred.shape[0]): - pred_single = pred[batch_index].unsqueeze(0) - if (self.is_poly): - boxes, score = self.polygons_from_bitmap(pred_single) - new_bboxes =[] - if len(boxes) > 0: - ratio_w, ratio_h = ratio_list[batch_index] - for bbox in boxes: - bbox[:, 0] = bbox[:, 0] * ratio_w - bbox[:, 1] = bbox[:, 1] * ratio_h - new_bboxes.append(bbox) - - boxes_batch.append(new_bboxes) - score_batch.append(score) - else: - boxes, score = self.boxes_from_bitmap(pred_single) - if len(boxes) > 0: - boxes = np.array(boxes) - ratio_w, ratio_h = ratio_list[batch_index] - boxes[:, :, 0] = boxes[:, :, 0] * ratio_w - boxes[:, :, 1] = boxes[:, :, 1] * ratio_h - - boxes_batch.append(boxes) - score_batch.append(score) - return boxes_batch, score_batch - diff --git a/ptocr/postprocess/PSEpostprocess.py b/ptocr/postprocess/PSEpostprocess.py deleted file mode 100644 index 3843ff5..0000000 --- a/ptocr/postprocess/PSEpostprocess.py +++ /dev/null @@ -1,135 +0,0 @@ -#-*- coding:utf-8 _*- -""" -@author:fxw -@file: PSEpostprocess.py -@time: 2020/08/13 -""" - -import numpy as np -import cv2 -import torch -from .piexlmerge import pse - -class PSEPostProcess(object): - - def __init__(self, config): - self.min_text_area = config['postprocess']['min_text_area'] - self.is_poly = config['postprocess']['is_poly'] - self.min_score = config['postprocess']['min_score'] - self.config = config - - def polygons_from_bitmap(self, pred): - - boxes = [] - scores = [] - pred,label_points,label_values = pse(pred,self.config) - - for label_value, label_point in label_points.items(): - if label_value not in label_values: - continue - score_i = label_point[0] - label_point = label_point[2:] - points = np.array(label_point, dtype=int).reshape(-1, 2) - - if points.shape[0] < self.min_text_area : - continue - - if score_i < self.min_score: - continue - - binary = np.zeros(pred.shape, dtype='uint8') - binary[pred == label_value] = 1 - - find_out = cv2.findContours(binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) - if(len(find_out)==2): - contours, _ = find_out - else: - _, contours, _ = find_out - contour = contours[0] - # epsilon = 0.01 * cv2.arcLength(contour, True) - # box = cv2.approxPolyDP(contour, epsilon, True) - box = contour - - if box.shape[0] <= 2: - continue - - box = box * self.config['postprocess']['scale'] - box = box.astype('int32') - boxes.append(box[:,0]) - scores.append(score_i) - return boxes, scores - - def boxes_from_bitmap(self, pred): - boxes = [] - scores = [] - pred,label_points,label_values = pse(pred,self.config) - - for label_value, label_point in label_points.items(): - if label_value not in label_values: - continue - score_i = label_point[0] - label_point = label_point[2:] - points = np.array(label_point, dtype=int).reshape(-1, 2) - - if points.shape[0] < self.min_text_area : - continue - - if score_i < self.min_score: - continue - box = self.get_mini_boxes(points)*self.config['postprocess']['scale'] - boxes.append(box) - scores.append(score_i) - return boxes, scores - - def get_mini_boxes(self, contour): - bounding_box = cv2.minAreaRect(contour) - points = sorted(list(cv2.boxPoints(bounding_box)), key=lambda x: x[0]) - - index_1, index_2, index_3, index_4 = 0, 1, 2, 3 - if points[1][1] > points[0][1]: - index_1 = 0 - index_4 = 1 - else: - index_1 = 1 - index_4 = 0 - if points[3][1] > points[2][1]: - index_2 = 2 - index_3 = 3 - else: - index_2 = 3 - index_3 = 2 - - box = [ - points[index_1], points[index_2], points[index_3], points[index_4] - ] - return np.array(box) - - def __call__(self, pred, ratio_list): - boxes_batch = [] - score_batch = [] - for batch_index in range(pred.shape[0]): - pred_single = pred[batch_index].unsqueeze(0) - if (self.is_poly): - boxes, score = self.polygons_from_bitmap(pred_single) - new_bboxes =[] - if len(boxes) > 0: - ratio_w, ratio_h = ratio_list[batch_index] - for bbox in boxes: - bbox[:, 0] = bbox[:, 0] * ratio_w - bbox[:, 1] = bbox[:, 1] * ratio_h - new_bboxes.append(bbox) - - boxes_batch.append(new_bboxes) - score_batch.append(score) - else: - boxes, score = self.boxes_from_bitmap(pred_single) - if len(boxes) > 0: - boxes = np.array(boxes) - ratio_w, ratio_h = ratio_list[batch_index] - boxes[:, :, 0] = boxes[:, :, 0] * ratio_w - boxes[:, :, 1] = boxes[:, :, 1] * ratio_h - - boxes_batch.append(boxes) - score_batch.append(score) - return boxes_batch, score_batch - diff --git a/ptocr/postprocess/SASTpostprocess.py b/ptocr/postprocess/SASTpostprocess.py deleted file mode 100644 index b10b1f4..0000000 --- a/ptocr/postprocess/SASTpostprocess.py +++ /dev/null @@ -1,294 +0,0 @@ -#-*- coding:utf-8 _*- -""" -@author:fxw -@file: 1232.py -@time: 2020/08/19 -""" - -import sys -import numpy as np -from .locality_aware_nms import nms_locality -from .lanms import merge_quadrangle_n9 -import cv2 -import time - - -class SASTPostProcess(object): - """ - The post process for SAST. - """ - - def __init__(self, config): - self.score_thresh = config['postprocess']['score_thresh'] - self.nms_thresh = config['postprocess']['nms_thresh'] - self.sample_pts_num = config['postprocess']['sample_pts_num'] - self.shrink_ratio_of_width = config['postprocess']['shrink_ratio_of_width'] - self.expand_scale = config['postprocess']['expand_scale'] - self.tcl_map_thresh = config['postprocess']['tcl_map_thresh'] - - # c++ la-nms is faster, but only support python 3.5 - self.is_python35 = True -# if sys.version_info.major == 3 and sys.version_info.minor == 6: -# self.is_python35 = True - - def point_pair2poly(self, point_pair_list): - """ - Transfer vertical point_pairs into poly point in clockwise. - """ - # constract poly - point_num = len(point_pair_list) * 2 - point_list = [0] * point_num - for idx, point_pair in enumerate(point_pair_list): - point_list[idx] = point_pair[0] - point_list[point_num - 1 - idx] = point_pair[1] - return np.array(point_list).reshape(-1, 2) - - def shrink_quad_along_width(self, quad, begin_width_ratio=0., end_width_ratio=1.): - """ - Generate shrink_quad_along_width. - """ - ratio_pair = np.array([[begin_width_ratio], [end_width_ratio]], dtype=np.float32) - p0_1 = quad[0] + (quad[1] - quad[0]) * ratio_pair - p3_2 = quad[3] + (quad[2] - quad[3]) * ratio_pair - return np.array([p0_1[0], p0_1[1], p3_2[1], p3_2[0]]) - - def expand_poly_along_width(self, poly, shrink_ratio_of_width=0.3): - """ - expand poly along width. - """ - point_num = poly.shape[0] - left_quad = np.array([poly[0], poly[1], poly[-2], poly[-1]], dtype=np.float32) - left_ratio = -shrink_ratio_of_width * np.linalg.norm(left_quad[0] - left_quad[3]) / \ - (np.linalg.norm(left_quad[0] - left_quad[1]) + 1e-6) - left_quad_expand = self.shrink_quad_along_width(left_quad, left_ratio, 1.0) - right_quad = np.array([poly[point_num // 2 - 2], poly[point_num // 2 - 1], - poly[point_num // 2], poly[point_num // 2 + 1]], dtype=np.float32) - right_ratio = 1.0 + \ - shrink_ratio_of_width * np.linalg.norm(right_quad[0] - right_quad[3]) / \ - (np.linalg.norm(right_quad[0] - right_quad[1]) + 1e-6) - right_quad_expand = self.shrink_quad_along_width(right_quad, 0.0, right_ratio) - poly[0] = left_quad_expand[0] - poly[-1] = left_quad_expand[-1] - poly[point_num // 2 - 1] = right_quad_expand[1] - poly[point_num // 2] = right_quad_expand[2] - return poly - - def restore_quad(self, tcl_map, tcl_map_thresh, tvo_map): - """Restore quad.""" - xy_text = np.argwhere(tcl_map[:, :, 0] > tcl_map_thresh) - xy_text = xy_text[:, ::-1] # (n, 2) - - # Sort the text boxes via the y axis - xy_text = xy_text[np.argsort(xy_text[:, 1])] - - scores = tcl_map[xy_text[:, 1], xy_text[:, 0], 0] - scores = scores[:, np.newaxis] - - # Restore - point_num = int(tvo_map.shape[-1] / 2) - assert point_num == 4 - tvo_map = tvo_map[xy_text[:, 1], xy_text[:, 0], :] - xy_text_tile = np.tile(xy_text, (1, point_num)) # (n, point_num * 2) - quads = xy_text_tile - tvo_map - - return scores, quads, xy_text - - def quad_area(self, quad): - """ - compute area of a quad. - """ - edge = [ - (quad[1][0] - quad[0][0]) * (quad[1][1] + quad[0][1]), - (quad[2][0] - quad[1][0]) * (quad[2][1] + quad[1][1]), - (quad[3][0] - quad[2][0]) * (quad[3][1] + quad[2][1]), - (quad[0][0] - quad[3][0]) * (quad[0][1] + quad[3][1]) - ] - return np.sum(edge) / 2. - - def nms(self, dets): - if self.is_python35: - dets = merge_quadrangle_n9(dets, self.nms_thresh) - else: - dets = nms_locality(dets, self.nms_thresh) - return dets - - def cluster_by_quads_tco(self, tcl_map, tcl_map_thresh, quads, tco_map): - """ - Cluster pixels in tcl_map based on quads. - """ - instance_count = quads.shape[0] + 1 # contain background - instance_label_map = np.zeros(tcl_map.shape[:2], dtype=np.int32) - if instance_count == 1: - return instance_count, instance_label_map - - # predict text center - xy_text = np.argwhere(tcl_map[:, :, 0] > tcl_map_thresh) - n = xy_text.shape[0] - xy_text = xy_text[:, ::-1] # (n, 2) - tco = tco_map[xy_text[:, 1], xy_text[:, 0], :] # (n, 2) - pred_tc = xy_text - tco - - # get gt text center - m = quads.shape[0] - gt_tc = np.mean(quads, axis=1) # (m, 2) - - pred_tc_tile = np.tile(pred_tc[:, np.newaxis, :], (1, m, 1)) # (n, m, 2) - gt_tc_tile = np.tile(gt_tc[np.newaxis, :, :], (n, 1, 1)) # (n, m, 2) - dist_mat = np.linalg.norm(pred_tc_tile - gt_tc_tile, axis=2) # (n, m) - xy_text_assign = np.argmin(dist_mat, axis=1) + 1 # (n,) - - instance_label_map[xy_text[:, 1], xy_text[:, 0]] = xy_text_assign - return instance_count, instance_label_map - - def estimate_sample_pts_num(self, quad, xy_text): - """ - Estimate sample points number. - """ - eh = (np.linalg.norm(quad[0] - quad[3]) + np.linalg.norm(quad[1] - quad[2])) / 2.0 - ew = (np.linalg.norm(quad[0] - quad[1]) + np.linalg.norm(quad[2] - quad[3])) / 2.0 - - dense_sample_pts_num = max(2, int(ew)) - dense_xy_center_line = xy_text[np.linspace(0, xy_text.shape[0] - 1, dense_sample_pts_num, - endpoint=True, dtype=np.float32).astype(np.int32)] - - dense_xy_center_line_diff = dense_xy_center_line[1:] - dense_xy_center_line[:-1] - estimate_arc_len = np.sum(np.linalg.norm(dense_xy_center_line_diff, axis=1)) - - sample_pts_num = max(2, int(estimate_arc_len / eh)) - return sample_pts_num - - def sort_coord(self,coord): - points = sorted(list(coord), key=lambda x: x[0]) - - index_1, index_2, index_3, index_4 = 0, 1, 2, 3 - if points[1][1] > points[0][1]: - index_1 = 0 - index_4 = 1 - else: - index_1 = 1 - index_4 = 0 - if points[3][1] > points[2][1]: - index_2 = 2 - index_3 = 3 - else: - index_2 = 3 - index_3 = 2 - - box = [ - points[index_1], points[index_2], points[index_3], points[index_4] - ] - return np.array(box) - - def detect_sast(self, tcl_map, tvo_map, tbo_map, tco_map, ratio_w, ratio_h, src_w, src_h, - shrink_ratio_of_width=0.3, tcl_map_thresh=0.5, offset_expand=1.0, out_strid=4.0): - """ - first resize the tcl_map, tvo_map and tbo_map to the input_size, then restore the polys - """ - # restore quad - scores, quads, xy_text = self.restore_quad(tcl_map, tcl_map_thresh, tvo_map) - dets = np.hstack((quads, scores)).astype(np.float32, copy=False) - dets = self.nms(dets) - if dets.shape[0] == 0: - return [] - quads = dets[:, :-1].reshape(-1, 4, 2) - - # Compute quad area - quad_areas = [] - for quad in quads: - quad_areas.append(-self.quad_area(quad)) - - # instance segmentation -# instance_count, instance_label_map = cv2.connectedComponents(tcl_map.astype(np.uint8), connectivity=8) - instance_count, instance_label_map = self.cluster_by_quads_tco(tcl_map, tcl_map_thresh, quads, tco_map) - - # restore single poly with tcl instance. - poly_list = [] - for instance_idx in range(1, instance_count): - xy_text = np.argwhere(instance_label_map == instance_idx)[:, ::-1] - quad = quads[instance_idx - 1] - q_area = quad_areas[instance_idx - 1] - if q_area < 5: - continue - - # - len1 = float(np.linalg.norm(quad[0] - quad[1])) - len2 = float(np.linalg.norm(quad[1] - quad[2])) - min_len = min(len1, len2) - if min_len < 3: - continue - - # filter small CC - if xy_text.shape[0] <= 0: - continue - - # filter low confidence instance - xy_text_scores = tcl_map[xy_text[:, 1], xy_text[:, 0], 0] - if np.sum(xy_text_scores) / quad_areas[instance_idx - 1] < 0.1: -# if np.sum(xy_text_scores) / quad_areas[instance_idx - 1] < 0.15: - continue - - # sort xy_text - left_center_pt = np.array([[(quad[0, 0] + quad[-1, 0]) / 2.0, - (quad[0, 1] + quad[-1, 1]) / 2.0]]) # (1, 2) - right_center_pt = np.array([[(quad[1, 0] + quad[2, 0]) / 2.0, - (quad[1, 1] + quad[2, 1]) / 2.0]]) # (1, 2) - proj_unit_vec = (right_center_pt - left_center_pt) / \ - (np.linalg.norm(right_center_pt - left_center_pt) + 1e-6) - proj_value = np.sum(xy_text * proj_unit_vec, axis=1) - xy_text = xy_text[np.argsort(proj_value)] - - # Sample pts in tcl map - if self.sample_pts_num == 0: - sample_pts_num = self.estimate_sample_pts_num(quad, xy_text) - else: - sample_pts_num = self.sample_pts_num - xy_center_line = xy_text[np.linspace(0, xy_text.shape[0] - 1, sample_pts_num, - endpoint=True, dtype=np.float32).astype(np.int32)] - - point_pair_list = [] - for x, y in xy_center_line: - # get corresponding offset - offset = tbo_map[y, x, :].reshape(2, 2) - if offset_expand != 1.0: - offset_length = np.linalg.norm(offset, axis=1, keepdims=True) - expand_length = np.clip(offset_length * (offset_expand - 1), a_min=0.5, a_max=3.0) - offset_detal = offset / offset_length * expand_length - offset = offset + offset_detal - # original point - ori_yx = np.array([y, x], dtype=np.float32) - point_pair = (ori_yx + offset)[:, ::-1] * out_strid / np.array([ratio_w, ratio_h]).reshape(-1, 2) - point_pair_list.append(point_pair) - - # ndarry: (x, 2), expand poly along width - detected_poly = self.point_pair2poly(point_pair_list) - detected_poly = self.expand_poly_along_width(detected_poly, shrink_ratio_of_width) - detected_poly[:, 0] = np.clip(detected_poly[:, 0], a_min=0, a_max=src_w) - detected_poly[:, 1] = np.clip(detected_poly[:, 1], a_min=0, a_max=src_h) -# detected_poly = self.sort_coord(cv2.boxPoints(cv2.minAreaRect(detected_poly.astype(np.int)))) - poly_list.append(detected_poly) - - return poly_list - - def __call__(self, outs_dict, ratio_list): - score_list = outs_dict['f_score'] - border_list = outs_dict['f_border'] - tvo_list = outs_dict['f_tvo'] - tco_list = outs_dict['f_tco'] - img_num = len(ratio_list) - poly_lists = [] - - for ino in range(img_num): - p_score = score_list[ino].transpose((1, 2, 0)) - p_border = border_list[ino].transpose((1, 2, 0)) - p_tvo = tvo_list[ino].transpose((1, 2, 0)) - p_tco = tco_list[ino].transpose((1, 2, 0)) - - ratio_h, ratio_w, src_h, src_w = ratio_list[ino] - - poly_list = self.detect_sast(p_score, p_tvo, p_border, p_tco, ratio_w, ratio_h, src_w, src_h,shrink_ratio_of_width=self.shrink_ratio_of_width,tcl_map_thresh=self.tcl_map_thresh, offset_expand=self.expand_scale) - - poly_lists.append(poly_list) - - return poly_lists,None - - diff --git a/ptocr/postprocess/__init__.py b/ptocr/postprocess/__init__.py deleted file mode 100644 index 8529416..0000000 --- a/ptocr/postprocess/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -#-*- coding:utf-8 _*- -""" -@author:fxw -@file: __init__.py.py -@time: 2020/08/11 -""" diff --git a/ptocr/postprocess/dbprocess/Makefile b/ptocr/postprocess/dbprocess/Makefile deleted file mode 100644 index 19c5e82..0000000 --- a/ptocr/postprocess/dbprocess/Makefile +++ /dev/null @@ -1,15 +0,0 @@ -CXXFLAGS = -I include -std=c++11 -O3 $(shell python3-config --cflags) - -DEPS = $(shell find include -xtype f) -CXX_SOURCES = cppdbprocess.cpp include/clipper/clipper.cpp -OPENCV = `pkg-config --cflags --libs opencv` - -LIB_SO = cppdbprocess.so - -$(LIB_SO): $(CXX_SOURCES) $(DEPS) - $(CXX) -o $@ $(CXXFLAGS) $(LDFLAGS) $(CXX_SOURCES) --shared -fPIC $(OPENCV) - -clean: - rm -rf $(LIB_SO) - - diff --git a/ptocr/postprocess/dbprocess/__init__.py b/ptocr/postprocess/dbprocess/__init__.py deleted file mode 100644 index 1125432..0000000 --- a/ptocr/postprocess/dbprocess/__init__.py +++ /dev/null @@ -1,32 +0,0 @@ - -import os -import cv2 -import torch -import time -import subprocess -import numpy as np - - - -BASE_DIR = os.path.dirname(os.path.realpath(__file__)) - -if subprocess.call(['make', '-C', BASE_DIR]) != 0: # return value - raise RuntimeError('Cannot compile pse: {}'.format(BASE_DIR)) - - -def cpp_boxes_from_bitmap(pred,bitmap,box_thresh=0.6,det_db_unclip_ratio=2.0): - - from .cppdbprocess import db_cpp - bitmap = bitmap.astype(np.uint8) - bboxes = db_cpp(pred,bitmap,box_thresh,det_db_unclip_ratio) - - return bboxes - - - - - - - - - diff --git a/ptocr/postprocess/dbprocess/cppdbprocess.cpp b/ptocr/postprocess/dbprocess/cppdbprocess.cpp deleted file mode 100644 index 56258e5..0000000 --- a/ptocr/postprocess/dbprocess/cppdbprocess.cpp +++ /dev/null @@ -1,369 +0,0 @@ -// Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "include/postprocess_op.h" -#include "include/pybind11/pybind11.h" -#include "include/pybind11/numpy.h" -#include "include/pybind11/stl.h" -#include "include/pybind11/stl_bind.h" - -#include -#include -#include -#include - -using namespace std; -using namespace cv; - -namespace py = pybind11; - - -namespace cppdbprocess { - -void PostProcessor::GetContourArea(const std::vector> &box, - float unclip_ratio, float &distance) { - int pts_num = 4; - float area = 0.0f; - float dist = 0.0f; - for (int i = 0; i < pts_num; i++) { - area += box[i][0] * box[(i + 1) % pts_num][1] - - box[i][1] * box[(i + 1) % pts_num][0]; - dist += sqrtf((box[i][0] - box[(i + 1) % pts_num][0]) * - (box[i][0] - box[(i + 1) % pts_num][0]) + - (box[i][1] - box[(i + 1) % pts_num][1]) * - (box[i][1] - box[(i + 1) % pts_num][1])); - } - area = fabs(float(area / 2.0)); - - distance = area * unclip_ratio / dist; -} - -cv::RotatedRect PostProcessor::UnClip(std::vector> box, - const float &unclip_ratio) { - float distance = 1.0; - - GetContourArea(box, unclip_ratio, distance); - - ClipperLib::ClipperOffset offset; - ClipperLib::Path p; - p << ClipperLib::IntPoint(int(box[0][0]), int(box[0][1])) - << ClipperLib::IntPoint(int(box[1][0]), int(box[1][1])) - << ClipperLib::IntPoint(int(box[2][0]), int(box[2][1])) - << ClipperLib::IntPoint(int(box[3][0]), int(box[3][1])); - offset.AddPath(p, ClipperLib::jtRound, ClipperLib::etClosedPolygon); - - ClipperLib::Paths soln; - offset.Execute(soln, distance); - std::vector points; - - for (int j = 0; j < soln.size(); j++) { - for (int i = 0; i < soln[soln.size() - 1].size(); i++) { - points.emplace_back(soln[j][i].X, soln[j][i].Y); - } - } - cv::RotatedRect res; - if (points.size() <= 0) { - res = cv::RotatedRect(cv::Point2f(0, 0), cv::Size2f(1, 1), 0); - } else { - res = cv::minAreaRect(points); - } - return res; -} - -float **PostProcessor::Mat2Vec(cv::Mat mat) { - auto **array = new float *[mat.rows]; - for (int i = 0; i < mat.rows; ++i) - array[i] = new float[mat.cols]; - for (int i = 0; i < mat.rows; ++i) { - for (int j = 0; j < mat.cols; ++j) { - array[i][j] = mat.at(i, j); - } - } - - return array; -} - -std::vector> -PostProcessor::OrderPointsClockwise(std::vector> pts) { - std::vector> box = pts; - std::sort(box.begin(), box.end(), XsortInt); - - std::vector> leftmost = {box[0], box[1]}; - std::vector> rightmost = {box[2], box[3]}; - - if (leftmost[0][1] > leftmost[1][1]) - std::swap(leftmost[0], leftmost[1]); - - if (rightmost[0][1] > rightmost[1][1]) - std::swap(rightmost[0], rightmost[1]); - - std::vector> rect = {leftmost[0], rightmost[0], rightmost[1], - leftmost[1]}; - return rect; -} - -std::vector> PostProcessor::Mat2Vector(cv::Mat mat) { - std::vector> img_vec; - std::vector tmp; - - for (int i = 0; i < mat.rows; ++i) { - tmp.clear(); - for (int j = 0; j < mat.cols; ++j) { - tmp.push_back(mat.at(i, j)); - } - img_vec.push_back(tmp); - } - return img_vec; -} - -bool PostProcessor::XsortFp32(std::vector a, std::vector b) { - if (a[0] != b[0]) - return a[0] < b[0]; - return false; -} - -bool PostProcessor::XsortInt(std::vector a, std::vector b) { - if (a[0] != b[0]) - return a[0] < b[0]; - return false; -} - -std::vector> PostProcessor::GetMiniBoxes(cv::RotatedRect box, - float &ssid) { - ssid = std::max(box.size.width, box.size.height); - - cv::Mat points; - cv::boxPoints(box, points); - - auto array = Mat2Vector(points); - std::sort(array.begin(), array.end(), XsortFp32); - - std::vector idx1 = array[0], idx2 = array[1], idx3 = array[2], - idx4 = array[3]; - if (array[3][1] <= array[2][1]) { - idx2 = array[3]; - idx3 = array[2]; - } else { - idx2 = array[2]; - idx3 = array[3]; - } - if (array[1][1] <= array[0][1]) { - idx1 = array[1]; - idx4 = array[0]; - } else { - idx1 = array[0]; - idx4 = array[1]; - } - - array[0] = idx1; - array[1] = idx2; - array[2] = idx3; - array[3] = idx4; - - return array; -} - -float PostProcessor::BoxScoreFast(std::vector> box_array, - cv::Mat pred) { - auto array = box_array; - int width = pred.cols; - int height = pred.rows; - - float box_x[4] = {array[0][0], array[1][0], array[2][0], array[3][0]}; - float box_y[4] = {array[0][1], array[1][1], array[2][1], array[3][1]}; - - int xmin = clamp(int(std::floor(*(std::min_element(box_x, box_x + 4)))), 0, - width - 1); - int xmax = clamp(int(std::ceil(*(std::max_element(box_x, box_x + 4)))), 0, - width - 1); - int ymin = clamp(int(std::floor(*(std::min_element(box_y, box_y + 4)))), 0, - height - 1); - int ymax = clamp(int(std::ceil(*(std::max_element(box_y, box_y + 4)))), 0, - height - 1); - - cv::Mat mask; - mask = cv::Mat::zeros(ymax - ymin + 1, xmax - xmin + 1, CV_8UC1); - - cv::Point root_point[4]; - root_point[0] = cv::Point(int(array[0][0]) - xmin, int(array[0][1]) - ymin); - root_point[1] = cv::Point(int(array[1][0]) - xmin, int(array[1][1]) - ymin); - root_point[2] = cv::Point(int(array[2][0]) - xmin, int(array[2][1]) - ymin); - root_point[3] = cv::Point(int(array[3][0]) - xmin, int(array[3][1]) - ymin); - const cv::Point *ppt[1] = {root_point}; - int npt[] = {4}; - cv::fillPoly(mask, ppt, npt, 1, cv::Scalar(1)); - - cv::Mat croppedImg; - pred(cv::Rect(xmin, ymin, xmax - xmin + 1, ymax - ymin + 1)) - .copyTo(croppedImg); - auto score = cv::mean(croppedImg, mask)[0]; - return score; -} - -std::vector>> -PostProcessor::BoxesFromBitmap(const cv::Mat pred, const cv::Mat bitmap, - const float &box_thresh, - const float &det_db_unclip_ratio) { - const int min_size = 3; - const int max_candidates = 1000; - - int width = bitmap.cols; - int height = bitmap.rows; - - - - std::vector> contours; - std::vector hierarchy; - - cv::findContours(bitmap, contours, hierarchy, cv::RETR_LIST, - cv::CHAIN_APPROX_SIMPLE); - - int num_contours = - contours.size() >= max_candidates ? max_candidates : contours.size(); - - std::vector>> boxes; - - for (int _i = 0; _i < num_contours; _i++) { - if (contours[_i].size() <= 2) { - continue; - } - float ssid; - cv::RotatedRect box = cv::minAreaRect(contours[_i]); - auto array = GetMiniBoxes(box, ssid); - - auto box_for_unclip = array; - // end get_mini_box - - if (ssid < min_size) { - continue; - } - - double score; - score = BoxScoreFast(array, pred); -// cout<> intcliparray; - - for (int num_pt = 0; num_pt < 4; num_pt++) { - std::vector a{int(clampf(roundf(cliparray[num_pt][0] / float(width) * - float(dest_width)), - 0, float(dest_width))), - int(clampf(roundf(cliparray[num_pt][1] / - float(height) * float(dest_height)), - 0, float(dest_height)))}; - - intcliparray.push_back(a); - } - boxes.push_back(intcliparray); - - - } // end for - - return boxes; -} - - -std::vector>> DBProcess(py::array_t pred, - py::array_t bitmap, - float box_thresh, - float det_db_unclip_ratio){ - - auto buf_pred = pred.request(); - auto buf_bitmap= bitmap.request(); - - auto ptr_pred = static_cast(buf_pred.ptr); - auto ptr_bitmap = static_cast(buf_bitmap.ptr); - - cv::Mat pred_mat; - cv::Mat bitmap_mat; - - - - vector data_shape=buf_pred.shape; - - std::vector>> boxes; - - - pred_mat = Mat::zeros(data_shape[0], data_shape[1], CV_32FC1); - for (int x = 0; x < pred_mat.rows; ++x) { - for (int y = 0; y < pred_mat.cols; ++y) { - pred_mat.at(x, y) = ptr_pred[x * data_shape[1] + y]; - - }} - bitmap_mat = Mat::zeros(data_shape[0], data_shape[1], CV_8UC1); - for (int x = 0; x < bitmap_mat.rows; ++x) { - for (int y = 0; y < bitmap_mat.cols; ++y) { - bitmap_mat.at(x, y) = ptr_bitmap[x * data_shape[1] + y]; - }} - PostProcessor *post_processor_ = new PostProcessor(); - boxes = post_processor_->BoxesFromBitmap(pred_mat,bitmap_mat,box_thresh,det_db_unclip_ratio); - delete post_processor_; - return boxes; - -} - -std::vector>> -PostProcessor::FilterTagDetRes(std::vector>> boxes, - float ratio_h, float ratio_w, cv::Mat srcimg) { - int oriimg_h = srcimg.rows; - int oriimg_w = srcimg.cols; - - std::vector>> root_points; - for (int n = 0; n < boxes.size(); n++) { - boxes[n] = OrderPointsClockwise(boxes[n]); - for (int m = 0; m < boxes[0].size(); m++) { - boxes[n][m][0] /= ratio_w; - boxes[n][m][1] /= ratio_h; - - boxes[n][m][0] = int(_min(_max(boxes[n][m][0], 0), oriimg_w - 1)); - boxes[n][m][1] = int(_min(_max(boxes[n][m][1], 0), oriimg_h - 1)); - } - } - - for (int n = 0; n < boxes.size(); n++) { - int rect_width, rect_height; - rect_width = int(sqrt(pow(boxes[n][0][0] - boxes[n][1][0], 2) + - pow(boxes[n][0][1] - boxes[n][1][1], 2))); - rect_height = int(sqrt(pow(boxes[n][0][0] - boxes[n][3][0], 2) + - pow(boxes[n][0][1] - boxes[n][3][1], 2))); - if (rect_width <= 10 || rect_height <= 10) - continue; - root_points.push_back(boxes[n]); - } - return root_points; -} - -} // namespace PaddleOCR - -PYBIND11_MODULE(cppdbprocess, m){ - m.def("db_cpp", &cppdbprocess::DBProcess, " re-implementation db algorithm(cpp)", py::arg("pred"), py::arg("bitmap"), py::arg("box_thresh"), py::arg("det_db_unclip_ratio")); -} - diff --git a/ptocr/postprocess/dbprocess/cppdbprocess.so b/ptocr/postprocess/dbprocess/cppdbprocess.so deleted file mode 100644 index 5381addd0c1041f3ae647efcefa2112aa24d9cb2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 299392 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 -#include -#include -#include -#include -#include -#include -#include -#include - -namespace ClipperLib { - -enum ClipType { ctIntersection, ctUnion, ctDifference, ctXor }; -enum PolyType { ptSubject, ptClip }; -// By far the most widely used winding rules for polygon filling are -// EvenOdd & NonZero (GDI, GDI+, XLib, OpenGL, Cairo, AGG, Quartz, SVG, Gr32) -// Others rules include Positive, Negative and ABS_GTR_EQ_TWO (only in OpenGL) -// see http://glprogramming.com/red/chapter11.html -enum PolyFillType { pftEvenOdd, pftNonZero, pftPositive, pftNegative }; - -#ifdef use_int32 -typedef int cInt; -static cInt const loRange = 0x7FFF; -static cInt const hiRange = 0x7FFF; -#else -typedef signed long long cInt; -static cInt const loRange = 0x3FFFFFFF; -static cInt const hiRange = 0x3FFFFFFFFFFFFFFFLL; -typedef signed long long long64; // used by Int128 class -typedef unsigned long long ulong64; - -#endif - -struct IntPoint { - cInt X; - cInt Y; -#ifdef use_xyz - cInt Z; - IntPoint(cInt x = 0, cInt y = 0, cInt z = 0) : X(x), Y(y), Z(z){}; -#else - IntPoint(cInt x = 0, cInt y = 0) : X(x), Y(y){}; -#endif - - friend inline bool operator==(const IntPoint &a, const IntPoint &b) { - return a.X == b.X && a.Y == b.Y; - } - friend inline bool operator!=(const IntPoint &a, const IntPoint &b) { - return a.X != b.X || a.Y != b.Y; - } -}; -//------------------------------------------------------------------------------ - -typedef std::vector Path; -typedef std::vector Paths; - -inline Path &operator<<(Path &poly, const IntPoint &p) { - poly.push_back(p); - return poly; -} -inline Paths &operator<<(Paths &polys, const Path &p) { - polys.push_back(p); - return polys; -} - -std::ostream &operator<<(std::ostream &s, const IntPoint &p); -std::ostream &operator<<(std::ostream &s, const Path &p); -std::ostream &operator<<(std::ostream &s, const Paths &p); - -struct DoublePoint { - double X; - double Y; - DoublePoint(double x = 0, double y = 0) : X(x), Y(y) {} - DoublePoint(IntPoint ip) : X((double)ip.X), Y((double)ip.Y) {} -}; -//------------------------------------------------------------------------------ - -#ifdef use_xyz -typedef void (*ZFillCallback)(IntPoint &e1bot, IntPoint &e1top, IntPoint &e2bot, - IntPoint &e2top, IntPoint &pt); -#endif - -enum InitOptions { - ioReverseSolution = 1, - ioStrictlySimple = 2, - ioPreserveCollinear = 4 -}; -enum JoinType { jtSquare, jtRound, jtMiter }; -enum EndType { - etClosedPolygon, - etClosedLine, - etOpenButt, - etOpenSquare, - etOpenRound -}; - -class PolyNode; -typedef std::vector PolyNodes; - -class PolyNode { -public: - PolyNode(); - virtual ~PolyNode(){}; - Path Contour; - PolyNodes Childs; - PolyNode *Parent; - PolyNode *GetNext() const; - bool IsHole() const; - bool IsOpen() const; - int ChildCount() const; - -private: - // PolyNode& operator =(PolyNode& other); - unsigned Index; // node index in Parent.Childs - bool m_IsOpen; - JoinType m_jointype; - EndType m_endtype; - PolyNode *GetNextSiblingUp() const; - void AddChild(PolyNode &child); - friend class Clipper; // to access Index - friend class ClipperOffset; -}; - -class PolyTree : public PolyNode { -public: - ~PolyTree() { Clear(); }; - PolyNode *GetFirst() const; - void Clear(); - int Total() const; - -private: - // PolyTree& operator =(PolyTree& other); - PolyNodes AllNodes; - friend class Clipper; // to access AllNodes -}; - -bool Orientation(const Path &poly); -double Area(const Path &poly); -int PointInPolygon(const IntPoint &pt, const Path &path); - -void SimplifyPolygon(const Path &in_poly, Paths &out_polys, - PolyFillType fillType = pftEvenOdd); -void SimplifyPolygons(const Paths &in_polys, Paths &out_polys, - PolyFillType fillType = pftEvenOdd); -void SimplifyPolygons(Paths &polys, PolyFillType fillType = pftEvenOdd); - -void CleanPolygon(const Path &in_poly, Path &out_poly, double distance = 1.415); -void CleanPolygon(Path &poly, double distance = 1.415); -void CleanPolygons(const Paths &in_polys, Paths &out_polys, - double distance = 1.415); -void CleanPolygons(Paths &polys, double distance = 1.415); - -void MinkowskiSum(const Path &pattern, const Path &path, Paths &solution, - bool pathIsClosed); -void MinkowskiSum(const Path &pattern, const Paths &paths, Paths &solution, - bool pathIsClosed); -void MinkowskiDiff(const Path &poly1, const Path &poly2, Paths &solution); - -void PolyTreeToPaths(const PolyTree &polytree, Paths &paths); -void ClosedPathsFromPolyTree(const PolyTree &polytree, Paths &paths); -void OpenPathsFromPolyTree(PolyTree &polytree, Paths &paths); - -void ReversePath(Path &p); -void ReversePaths(Paths &p); - -struct IntRect { - cInt left; - cInt top; - cInt right; - cInt bottom; -}; - -// enums that are used internally ... -enum EdgeSide { esLeft = 1, esRight = 2 }; - -// forward declarations (for stuff used internally) ... -struct TEdge; -struct IntersectNode; -struct LocalMinimum; -struct OutPt; -struct OutRec; -struct Join; - -typedef std::vector PolyOutList; -typedef std::vector EdgeList; -typedef std::vector JoinList; -typedef std::vector IntersectList; - -//------------------------------------------------------------------------------ - -// ClipperBase is the ancestor to the Clipper class. It should not be -// instantiated directly. This class simply abstracts the conversion of sets of -// polygon coordinates into edge objects that are stored in a LocalMinima list. -class ClipperBase { -public: - ClipperBase(); - virtual ~ClipperBase(); - virtual bool AddPath(const Path &pg, PolyType PolyTyp, bool Closed); - bool AddPaths(const Paths &ppg, PolyType PolyTyp, bool Closed); - virtual void Clear(); - IntRect GetBounds(); - bool PreserveCollinear() { return m_PreserveCollinear; }; - void PreserveCollinear(bool value) { m_PreserveCollinear = value; }; - -protected: - void DisposeLocalMinimaList(); - TEdge *AddBoundsToLML(TEdge *e, bool IsClosed); - virtual void Reset(); - TEdge *ProcessBound(TEdge *E, bool IsClockwise); - void InsertScanbeam(const cInt Y); - bool PopScanbeam(cInt &Y); - bool LocalMinimaPending(); - bool PopLocalMinima(cInt Y, const LocalMinimum *&locMin); - OutRec *CreateOutRec(); - void DisposeAllOutRecs(); - void DisposeOutRec(PolyOutList::size_type index); - void SwapPositionsInAEL(TEdge *edge1, TEdge *edge2); - void DeleteFromAEL(TEdge *e); - void UpdateEdgeIntoAEL(TEdge *&e); - - typedef std::vector MinimaList; - MinimaList::iterator m_CurrentLM; - MinimaList m_MinimaList; - - bool m_UseFullRange; - EdgeList m_edges; - bool m_PreserveCollinear; - bool m_HasOpenPaths; - PolyOutList m_PolyOuts; - TEdge *m_ActiveEdges; - - typedef std::priority_queue ScanbeamList; - ScanbeamList m_Scanbeam; -}; -//------------------------------------------------------------------------------ - -class Clipper : public virtual ClipperBase { -public: - Clipper(int initOptions = 0); - bool Execute(ClipType clipType, Paths &solution, - PolyFillType fillType = pftEvenOdd); - bool Execute(ClipType clipType, Paths &solution, PolyFillType subjFillType, - PolyFillType clipFillType); - bool Execute(ClipType clipType, PolyTree &polytree, - PolyFillType fillType = pftEvenOdd); - bool Execute(ClipType clipType, PolyTree &polytree, PolyFillType subjFillType, - PolyFillType clipFillType); - bool ReverseSolution() { return m_ReverseOutput; }; - void ReverseSolution(bool value) { m_ReverseOutput = value; }; - bool StrictlySimple() { return m_StrictSimple; }; - void StrictlySimple(bool value) { m_StrictSimple = value; }; -// set the callback function for z value filling on intersections (otherwise Z -// is 0) -#ifdef use_xyz - void ZFillFunction(ZFillCallback zFillFunc); -#endif -protected: - virtual bool ExecuteInternal(); - -private: - JoinList m_Joins; - JoinList m_GhostJoins; - IntersectList m_IntersectList; - ClipType m_ClipType; - typedef std::list MaximaList; - MaximaList m_Maxima; - TEdge *m_SortedEdges; - bool m_ExecuteLocked; - PolyFillType m_ClipFillType; - PolyFillType m_SubjFillType; - bool m_ReverseOutput; - bool m_UsingPolyTree; - bool m_StrictSimple; -#ifdef use_xyz - ZFillCallback m_ZFill; // custom callback -#endif - void SetWindingCount(TEdge &edge); - bool IsEvenOddFillType(const TEdge &edge) const; - bool IsEvenOddAltFillType(const TEdge &edge) const; - void InsertLocalMinimaIntoAEL(const cInt botY); - void InsertEdgeIntoAEL(TEdge *edge, TEdge *startEdge); - void AddEdgeToSEL(TEdge *edge); - bool PopEdgeFromSEL(TEdge *&edge); - void CopyAELToSEL(); - void DeleteFromSEL(TEdge *e); - void SwapPositionsInSEL(TEdge *edge1, TEdge *edge2); - bool IsContributing(const TEdge &edge) const; - bool IsTopHorz(const cInt XPos); - void DoMaxima(TEdge *e); - void ProcessHorizontals(); - void ProcessHorizontal(TEdge *horzEdge); - void AddLocalMaxPoly(TEdge *e1, TEdge *e2, const IntPoint &pt); - OutPt *AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &pt); - OutRec *GetOutRec(int idx); - void AppendPolygon(TEdge *e1, TEdge *e2); - void IntersectEdges(TEdge *e1, TEdge *e2, IntPoint &pt); - OutPt *AddOutPt(TEdge *e, const IntPoint &pt); - OutPt *GetLastOutPt(TEdge *e); - bool ProcessIntersections(const cInt topY); - void BuildIntersectList(const cInt topY); - void ProcessIntersectList(); - void ProcessEdgesAtTopOfScanbeam(const cInt topY); - void BuildResult(Paths &polys); - void BuildResult2(PolyTree &polytree); - void SetHoleState(TEdge *e, OutRec *outrec); - void DisposeIntersectNodes(); - bool FixupIntersectionOrder(); - void FixupOutPolygon(OutRec &outrec); - void FixupOutPolyline(OutRec &outrec); - bool IsHole(TEdge *e); - bool FindOwnerFromSplitRecs(OutRec &outRec, OutRec *&currOrfl); - void FixHoleLinkage(OutRec &outrec); - void AddJoin(OutPt *op1, OutPt *op2, const IntPoint offPt); - void ClearJoins(); - void ClearGhostJoins(); - void AddGhostJoin(OutPt *op, const IntPoint offPt); - bool JoinPoints(Join *j, OutRec *outRec1, OutRec *outRec2); - void JoinCommonEdges(); - void DoSimplePolygons(); - void FixupFirstLefts1(OutRec *OldOutRec, OutRec *NewOutRec); - void FixupFirstLefts2(OutRec *InnerOutRec, OutRec *OuterOutRec); - void FixupFirstLefts3(OutRec *OldOutRec, OutRec *NewOutRec); -#ifdef use_xyz - void SetZ(IntPoint &pt, TEdge &e1, TEdge &e2); -#endif -}; -//------------------------------------------------------------------------------ - -class ClipperOffset { -public: - ClipperOffset(double miterLimit = 2.0, double roundPrecision = 0.25); - ~ClipperOffset(); - void AddPath(const Path &path, JoinType joinType, EndType endType); - void AddPaths(const Paths &paths, JoinType joinType, EndType endType); - void Execute(Paths &solution, double delta); - void Execute(PolyTree &solution, double delta); - void Clear(); - double MiterLimit; - double ArcTolerance; - -private: - Paths m_destPolys; - Path m_srcPoly; - Path m_destPoly; - std::vector m_normals; - double m_delta, m_sinA, m_sin, m_cos; - double m_miterLim, m_StepsPerRad; - IntPoint m_lowest; - PolyNode m_polyNodes; - - void FixOrientations(); - void DoOffset(double delta); - void OffsetPoint(int j, int &k, JoinType jointype); - void DoSquare(int j, int k); - void DoMiter(int j, int k, double r); - void DoRound(int j, int k); -}; -//------------------------------------------------------------------------------ - -class clipperException : public std::exception { -public: - clipperException(const char *description) : m_descr(description) {} - virtual ~clipperException() throw() {} - virtual const char *what() const throw() { return m_descr.c_str(); } - -private: - std::string m_descr; -}; -//------------------------------------------------------------------------------ - -} // ClipperLib namespace - -#endif // clipper_hpp diff --git a/ptocr/postprocess/dbprocess/include/clipper/clipper.cpp b/ptocr/postprocess/dbprocess/include/clipper/clipper.cpp deleted file mode 100644 index 4993ba9..0000000 --- a/ptocr/postprocess/dbprocess/include/clipper/clipper.cpp +++ /dev/null @@ -1,4622 +0,0 @@ -/******************************************************************************* -* * -* Author : Angus Johnson * -* Version : 6.4.0 * -* Date : 2 July 2015 * -* Website : http://www.angusj.com * -* Copyright : Angus Johnson 2010-2015 * -* * -* License: * -* Use, modification & distribution is subject to Boost Software License Ver 1. * -* http://www.boost.org/LICENSE_1_0.txt * -* * -* Attributions: * -* The code in this library is an extension of Bala Vatti's clipping algorithm: * -* "A generic solution to polygon clipping" * -* Communications of the ACM, Vol 35, Issue 7 (July 1992) pp 56-63. * -* http://portal.acm.org/citation.cfm?id=129906 * -* * -* Computer graphics and geometric modeling: implementation and algorithms * -* By Max K. Agoston * -* Springer; 1 edition (January 4, 2005) * -* http://books.google.com/books?q=vatti+clipping+agoston * -* * -* See also: * -* "Polygon Offsetting by Computing Winding Numbers" * -* Paper no. DETC2005-85513 pp. 565-575 * -* ASME 2005 International Design Engineering Technical Conferences * -* and Computers and Information in Engineering Conference (IDETC/CIE2005) * -* September 24-28, 2005 , Long Beach, California, USA * -* http://www.me.berkeley.edu/~mcmains/pubs/DAC05OffsetPolygon.pdf * -* * -*******************************************************************************/ - -/******************************************************************************* -* * -* This is a translation of the Delphi Clipper library and the naming style * -* used has retained a Delphi flavour. * -* * -*******************************************************************************/ - -#include "clipper.hpp" -#include -#include -#include -#include -#include -#include -#include -#include - -namespace ClipperLib { - -static double const pi = 3.141592653589793238; -static double const two_pi = pi *2; -static double const def_arc_tolerance = 0.25; - -enum Direction { dRightToLeft, dLeftToRight }; - -static int const Unassigned = -1; //edge not currently 'owning' a solution -static int const Skip = -2; //edge that would otherwise close a path - -#define HORIZONTAL (-1.0E+40) -#define TOLERANCE (1.0e-20) -#define NEAR_ZERO(val) (((val) > -TOLERANCE) && ((val) < TOLERANCE)) - -struct TEdge { - IntPoint Bot; - IntPoint Curr; //current (updated for every new scanbeam) - IntPoint Top; - double Dx; - PolyType PolyTyp; - EdgeSide Side; //side only refers to current side of solution poly - int WindDelta; //1 or -1 depending on winding direction - int WindCnt; - int WindCnt2; //winding count of the opposite polytype - int OutIdx; - TEdge *Next; - TEdge *Prev; - TEdge *NextInLML; - TEdge *NextInAEL; - TEdge *PrevInAEL; - TEdge *NextInSEL; - TEdge *PrevInSEL; -}; - -struct IntersectNode { - TEdge *Edge1; - TEdge *Edge2; - IntPoint Pt; -}; - -struct LocalMinimum { - cInt Y; - TEdge *LeftBound; - TEdge *RightBound; -}; - -struct OutPt; - -//OutRec: contains a path in the clipping solution. Edges in the AEL will -//carry a pointer to an OutRec when they are part of the clipping solution. -struct OutRec { - int Idx; - bool IsHole; - bool IsOpen; - OutRec *FirstLeft; //see comments in clipper.pas - PolyNode *PolyNd; - OutPt *Pts; - OutPt *BottomPt; -}; - -struct OutPt { - int Idx; - IntPoint Pt; - OutPt *Next; - OutPt *Prev; -}; - -struct Join { - OutPt *OutPt1; - OutPt *OutPt2; - IntPoint OffPt; -}; - -struct LocMinSorter -{ - inline bool operator()(const LocalMinimum& locMin1, const LocalMinimum& locMin2) - { - return locMin2.Y < locMin1.Y; - } -}; - -//------------------------------------------------------------------------------ -//------------------------------------------------------------------------------ - -inline cInt Round(double val) -{ - if ((val < 0)) return static_cast(val - 0.5); - else return static_cast(val + 0.5); -} -//------------------------------------------------------------------------------ - -inline cInt Abs(cInt val) -{ - return val < 0 ? -val : val; -} - -//------------------------------------------------------------------------------ -// PolyTree methods ... -//------------------------------------------------------------------------------ - -void PolyTree::Clear() -{ - for (PolyNodes::size_type i = 0; i < AllNodes.size(); ++i) - delete AllNodes[i]; - AllNodes.resize(0); - Childs.resize(0); -} -//------------------------------------------------------------------------------ - -PolyNode* PolyTree::GetFirst() const -{ - if (!Childs.empty()) - return Childs[0]; - else - return 0; -} -//------------------------------------------------------------------------------ - -int PolyTree::Total() const -{ - int result = (int)AllNodes.size(); - //with negative offsets, ignore the hidden outer polygon ... - if (result > 0 && Childs[0] != AllNodes[0]) result--; - return result; -} - -//------------------------------------------------------------------------------ -// PolyNode methods ... -//------------------------------------------------------------------------------ - -PolyNode::PolyNode(): Childs(), Parent(0), Index(0), m_IsOpen(false) -{ -} -//------------------------------------------------------------------------------ - -int PolyNode::ChildCount() const -{ - return (int)Childs.size(); -} -//------------------------------------------------------------------------------ - -void PolyNode::AddChild(PolyNode& child) -{ - unsigned cnt = (unsigned)Childs.size(); - Childs.push_back(&child); - child.Parent = this; - child.Index = cnt; -} -//------------------------------------------------------------------------------ - -PolyNode* PolyNode::GetNext() const -{ - if (!Childs.empty()) - return Childs[0]; - else - return GetNextSiblingUp(); -} -//------------------------------------------------------------------------------ - -PolyNode* PolyNode::GetNextSiblingUp() const -{ - if (!Parent) //protects against PolyTree.GetNextSiblingUp() - return 0; - else if (Index == Parent->Childs.size() - 1) - return Parent->GetNextSiblingUp(); - else - return Parent->Childs[Index + 1]; -} -//------------------------------------------------------------------------------ - -bool PolyNode::IsHole() const -{ - bool result = true; - PolyNode* node = Parent; - while (node) - { - result = !result; - node = node->Parent; - } - return result; -} -//------------------------------------------------------------------------------ - -bool PolyNode::IsOpen() const -{ - return m_IsOpen; -} -//------------------------------------------------------------------------------ - -#ifndef use_int32 - -//------------------------------------------------------------------------------ -// Int128 class (enables safe math on signed 64bit integers) -// eg Int128 val1((long64)9223372036854775807); //ie 2^63 -1 -// Int128 val2((long64)9223372036854775807); -// Int128 val3 = val1 * val2; -// val3.AsString => "85070591730234615847396907784232501249" (8.5e+37) -//------------------------------------------------------------------------------ - -class Int128 -{ - public: - ulong64 lo; - long64 hi; - - Int128(long64 _lo = 0) - { - lo = (ulong64)_lo; - if (_lo < 0) hi = -1; else hi = 0; - } - - - Int128(const Int128 &val): lo(val.lo), hi(val.hi){} - - Int128(const long64& _hi, const ulong64& _lo): lo(_lo), hi(_hi){} - - Int128& operator = (const long64 &val) - { - lo = (ulong64)val; - if (val < 0) hi = -1; else hi = 0; - return *this; - } - - bool operator == (const Int128 &val) const - {return (hi == val.hi && lo == val.lo);} - - bool operator != (const Int128 &val) const - { return !(*this == val);} - - bool operator > (const Int128 &val) const - { - if (hi != val.hi) - return hi > val.hi; - else - return lo > val.lo; - } - - bool operator < (const Int128 &val) const - { - if (hi != val.hi) - return hi < val.hi; - else - return lo < val.lo; - } - - bool operator >= (const Int128 &val) const - { return !(*this < val);} - - bool operator <= (const Int128 &val) const - { return !(*this > val);} - - Int128& operator += (const Int128 &rhs) - { - hi += rhs.hi; - lo += rhs.lo; - if (lo < rhs.lo) hi++; - return *this; - } - - Int128 operator + (const Int128 &rhs) const - { - Int128 result(*this); - result+= rhs; - return result; - } - - Int128& operator -= (const Int128 &rhs) - { - *this += -rhs; - return *this; - } - - Int128 operator - (const Int128 &rhs) const - { - Int128 result(*this); - result -= rhs; - return result; - } - - Int128 operator-() const //unary negation - { - if (lo == 0) - return Int128(-hi, 0); - else - return Int128(~hi, ~lo + 1); - } - - operator double() const - { - const double shift64 = 18446744073709551616.0; //2^64 - if (hi < 0) - { - if (lo == 0) return (double)hi * shift64; - else return -(double)(~lo + ~hi * shift64); - } - else - return (double)(lo + hi * shift64); - } - -}; -//------------------------------------------------------------------------------ - -Int128 Int128Mul (long64 lhs, long64 rhs) -{ - bool negate = (lhs < 0) != (rhs < 0); - - if (lhs < 0) lhs = -lhs; - ulong64 int1Hi = ulong64(lhs) >> 32; - ulong64 int1Lo = ulong64(lhs & 0xFFFFFFFF); - - if (rhs < 0) rhs = -rhs; - ulong64 int2Hi = ulong64(rhs) >> 32; - ulong64 int2Lo = ulong64(rhs & 0xFFFFFFFF); - - //nb: see comments in clipper.pas - ulong64 a = int1Hi * int2Hi; - ulong64 b = int1Lo * int2Lo; - ulong64 c = int1Hi * int2Lo + int1Lo * int2Hi; - - Int128 tmp; - tmp.hi = long64(a + (c >> 32)); - tmp.lo = long64(c << 32); - tmp.lo += long64(b); - if (tmp.lo < b) tmp.hi++; - if (negate) tmp = -tmp; - return tmp; -}; -#endif - -//------------------------------------------------------------------------------ -// Miscellaneous global functions -//------------------------------------------------------------------------------ - -bool Orientation(const Path &poly) -{ - return Area(poly) >= 0; -} -//------------------------------------------------------------------------------ - -double Area(const Path &poly) -{ - int size = (int)poly.size(); - if (size < 3) return 0; - - double a = 0; - for (int i = 0, j = size -1; i < size; ++i) - { - a += ((double)poly[j].X + poly[i].X) * ((double)poly[j].Y - poly[i].Y); - j = i; - } - return -a * 0.5; -} -//------------------------------------------------------------------------------ - -double Area(const OutPt *op) -{ - const OutPt *startOp = op; - if (!op) return 0; - double a = 0; - do { - a += (double)(op->Prev->Pt.X + op->Pt.X) * (double)(op->Prev->Pt.Y - op->Pt.Y); - op = op->Next; - } while (op != startOp); - return a * 0.5; -} -//------------------------------------------------------------------------------ - -double Area(const OutRec &outRec) -{ - return Area(outRec.Pts); -} -//------------------------------------------------------------------------------ - -bool PointIsVertex(const IntPoint &Pt, OutPt *pp) -{ - OutPt *pp2 = pp; - do - { - if (pp2->Pt == Pt) return true; - pp2 = pp2->Next; - } - while (pp2 != pp); - return false; -} -//------------------------------------------------------------------------------ - -//See "The Point in Polygon Problem for Arbitrary Polygons" by Hormann & Agathos -//http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.88.5498&rep=rep1&type=pdf -int PointInPolygon(const IntPoint &pt, const Path &path) -{ - //returns 0 if false, +1 if true, -1 if pt ON polygon boundary - int result = 0; - size_t cnt = path.size(); - if (cnt < 3) return 0; - IntPoint ip = path[0]; - for(size_t i = 1; i <= cnt; ++i) - { - IntPoint ipNext = (i == cnt ? path[0] : path[i]); - if (ipNext.Y == pt.Y) - { - if ((ipNext.X == pt.X) || (ip.Y == pt.Y && - ((ipNext.X > pt.X) == (ip.X < pt.X)))) return -1; - } - if ((ip.Y < pt.Y) != (ipNext.Y < pt.Y)) - { - if (ip.X >= pt.X) - { - if (ipNext.X > pt.X) result = 1 - result; - else - { - double d = (double)(ip.X - pt.X) * (ipNext.Y - pt.Y) - - (double)(ipNext.X - pt.X) * (ip.Y - pt.Y); - if (!d) return -1; - if ((d > 0) == (ipNext.Y > ip.Y)) result = 1 - result; - } - } else - { - if (ipNext.X > pt.X) - { - double d = (double)(ip.X - pt.X) * (ipNext.Y - pt.Y) - - (double)(ipNext.X - pt.X) * (ip.Y - pt.Y); - if (!d) return -1; - if ((d > 0) == (ipNext.Y > ip.Y)) result = 1 - result; - } - } - } - ip = ipNext; - } - return result; -} -//------------------------------------------------------------------------------ - -int PointInPolygon (const IntPoint &pt, OutPt *op) -{ - //returns 0 if false, +1 if true, -1 if pt ON polygon boundary - int result = 0; - OutPt* startOp = op; - for(;;) - { - if (op->Next->Pt.Y == pt.Y) - { - if ((op->Next->Pt.X == pt.X) || (op->Pt.Y == pt.Y && - ((op->Next->Pt.X > pt.X) == (op->Pt.X < pt.X)))) return -1; - } - if ((op->Pt.Y < pt.Y) != (op->Next->Pt.Y < pt.Y)) - { - if (op->Pt.X >= pt.X) - { - if (op->Next->Pt.X > pt.X) result = 1 - result; - else - { - double d = (double)(op->Pt.X - pt.X) * (op->Next->Pt.Y - pt.Y) - - (double)(op->Next->Pt.X - pt.X) * (op->Pt.Y - pt.Y); - if (!d) return -1; - if ((d > 0) == (op->Next->Pt.Y > op->Pt.Y)) result = 1 - result; - } - } else - { - if (op->Next->Pt.X > pt.X) - { - double d = (double)(op->Pt.X - pt.X) * (op->Next->Pt.Y - pt.Y) - - (double)(op->Next->Pt.X - pt.X) * (op->Pt.Y - pt.Y); - if (!d) return -1; - if ((d > 0) == (op->Next->Pt.Y > op->Pt.Y)) result = 1 - result; - } - } - } - op = op->Next; - if (startOp == op) break; - } - return result; -} -//------------------------------------------------------------------------------ - -bool Poly2ContainsPoly1(OutPt *OutPt1, OutPt *OutPt2) -{ - OutPt* op = OutPt1; - do - { - //nb: PointInPolygon returns 0 if false, +1 if true, -1 if pt on polygon - int res = PointInPolygon(op->Pt, OutPt2); - if (res >= 0) return res > 0; - op = op->Next; - } - while (op != OutPt1); - return true; -} -//---------------------------------------------------------------------- - -bool SlopesEqual(const TEdge &e1, const TEdge &e2, bool UseFullInt64Range) -{ -#ifndef use_int32 - if (UseFullInt64Range) - return Int128Mul(e1.Top.Y - e1.Bot.Y, e2.Top.X - e2.Bot.X) == - Int128Mul(e1.Top.X - e1.Bot.X, e2.Top.Y - e2.Bot.Y); - else -#endif - return (e1.Top.Y - e1.Bot.Y) * (e2.Top.X - e2.Bot.X) == - (e1.Top.X - e1.Bot.X) * (e2.Top.Y - e2.Bot.Y); -} -//------------------------------------------------------------------------------ - -bool SlopesEqual(const IntPoint pt1, const IntPoint pt2, - const IntPoint pt3, bool UseFullInt64Range) -{ -#ifndef use_int32 - if (UseFullInt64Range) - return Int128Mul(pt1.Y-pt2.Y, pt2.X-pt3.X) == Int128Mul(pt1.X-pt2.X, pt2.Y-pt3.Y); - else -#endif - return (pt1.Y-pt2.Y)*(pt2.X-pt3.X) == (pt1.X-pt2.X)*(pt2.Y-pt3.Y); -} -//------------------------------------------------------------------------------ - -bool SlopesEqual(const IntPoint pt1, const IntPoint pt2, - const IntPoint pt3, const IntPoint pt4, bool UseFullInt64Range) -{ -#ifndef use_int32 - if (UseFullInt64Range) - return Int128Mul(pt1.Y-pt2.Y, pt3.X-pt4.X) == Int128Mul(pt1.X-pt2.X, pt3.Y-pt4.Y); - else -#endif - return (pt1.Y-pt2.Y)*(pt3.X-pt4.X) == (pt1.X-pt2.X)*(pt3.Y-pt4.Y); -} -//------------------------------------------------------------------------------ - -inline bool IsHorizontal(TEdge &e) -{ - return e.Dx == HORIZONTAL; -} -//------------------------------------------------------------------------------ - -inline double GetDx(const IntPoint pt1, const IntPoint pt2) -{ - return (pt1.Y == pt2.Y) ? - HORIZONTAL : (double)(pt2.X - pt1.X) / (pt2.Y - pt1.Y); -} -//--------------------------------------------------------------------------- - -inline void SetDx(TEdge &e) -{ - cInt dy = (e.Top.Y - e.Bot.Y); - if (dy == 0) e.Dx = HORIZONTAL; - else e.Dx = (double)(e.Top.X - e.Bot.X) / dy; -} -//--------------------------------------------------------------------------- - -inline void SwapSides(TEdge &Edge1, TEdge &Edge2) -{ - EdgeSide Side = Edge1.Side; - Edge1.Side = Edge2.Side; - Edge2.Side = Side; -} -//------------------------------------------------------------------------------ - -inline void SwapPolyIndexes(TEdge &Edge1, TEdge &Edge2) -{ - int OutIdx = Edge1.OutIdx; - Edge1.OutIdx = Edge2.OutIdx; - Edge2.OutIdx = OutIdx; -} -//------------------------------------------------------------------------------ - -inline cInt TopX(TEdge &edge, const cInt currentY) -{ - return ( currentY == edge.Top.Y ) ? - edge.Top.X : edge.Bot.X + Round(edge.Dx *(currentY - edge.Bot.Y)); -} -//------------------------------------------------------------------------------ - -void IntersectPoint(TEdge &Edge1, TEdge &Edge2, IntPoint &ip) -{ -#ifdef use_xyz - ip.Z = 0; -#endif - - double b1, b2; - if (Edge1.Dx == Edge2.Dx) - { - ip.Y = Edge1.Curr.Y; - ip.X = TopX(Edge1, ip.Y); - return; - } - else if (Edge1.Dx == 0) - { - ip.X = Edge1.Bot.X; - if (IsHorizontal(Edge2)) - ip.Y = Edge2.Bot.Y; - else - { - b2 = Edge2.Bot.Y - (Edge2.Bot.X / Edge2.Dx); - ip.Y = Round(ip.X / Edge2.Dx + b2); - } - } - else if (Edge2.Dx == 0) - { - ip.X = Edge2.Bot.X; - if (IsHorizontal(Edge1)) - ip.Y = Edge1.Bot.Y; - else - { - b1 = Edge1.Bot.Y - (Edge1.Bot.X / Edge1.Dx); - ip.Y = Round(ip.X / Edge1.Dx + b1); - } - } - else - { - b1 = Edge1.Bot.X - Edge1.Bot.Y * Edge1.Dx; - b2 = Edge2.Bot.X - Edge2.Bot.Y * Edge2.Dx; - double q = (b2-b1) / (Edge1.Dx - Edge2.Dx); - ip.Y = Round(q); - if (std::fabs(Edge1.Dx) < std::fabs(Edge2.Dx)) - ip.X = Round(Edge1.Dx * q + b1); - else - ip.X = Round(Edge2.Dx * q + b2); - } - - if (ip.Y < Edge1.Top.Y || ip.Y < Edge2.Top.Y) - { - if (Edge1.Top.Y > Edge2.Top.Y) - ip.Y = Edge1.Top.Y; - else - ip.Y = Edge2.Top.Y; - if (std::fabs(Edge1.Dx) < std::fabs(Edge2.Dx)) - ip.X = TopX(Edge1, ip.Y); - else - ip.X = TopX(Edge2, ip.Y); - } - //finally, don't allow 'ip' to be BELOW curr.Y (ie bottom of scanbeam) ... - if (ip.Y > Edge1.Curr.Y) - { - ip.Y = Edge1.Curr.Y; - //use the more vertical edge to derive X ... - if (std::fabs(Edge1.Dx) > std::fabs(Edge2.Dx)) - ip.X = TopX(Edge2, ip.Y); else - ip.X = TopX(Edge1, ip.Y); - } -} -//------------------------------------------------------------------------------ - -void ReversePolyPtLinks(OutPt *pp) -{ - if (!pp) return; - OutPt *pp1, *pp2; - pp1 = pp; - do { - pp2 = pp1->Next; - pp1->Next = pp1->Prev; - pp1->Prev = pp2; - pp1 = pp2; - } while( pp1 != pp ); -} -//------------------------------------------------------------------------------ - -void DisposeOutPts(OutPt*& pp) -{ - if (pp == 0) return; - pp->Prev->Next = 0; - while( pp ) - { - OutPt *tmpPp = pp; - pp = pp->Next; - delete tmpPp; - } -} -//------------------------------------------------------------------------------ - -inline void InitEdge(TEdge* e, TEdge* eNext, TEdge* ePrev, const IntPoint& Pt) -{ - std::memset(e, 0, sizeof(TEdge)); - e->Next = eNext; - e->Prev = ePrev; - e->Curr = Pt; - e->OutIdx = Unassigned; -} -//------------------------------------------------------------------------------ - -void InitEdge2(TEdge& e, PolyType Pt) -{ - if (e.Curr.Y >= e.Next->Curr.Y) - { - e.Bot = e.Curr; - e.Top = e.Next->Curr; - } else - { - e.Top = e.Curr; - e.Bot = e.Next->Curr; - } - SetDx(e); - e.PolyTyp = Pt; -} -//------------------------------------------------------------------------------ - -TEdge* RemoveEdge(TEdge* e) -{ - //removes e from double_linked_list (but without removing from memory) - e->Prev->Next = e->Next; - e->Next->Prev = e->Prev; - TEdge* result = e->Next; - e->Prev = 0; //flag as removed (see ClipperBase.Clear) - return result; -} -//------------------------------------------------------------------------------ - -inline void ReverseHorizontal(TEdge &e) -{ - //swap horizontal edges' Top and Bottom x's so they follow the natural - //progression of the bounds - ie so their xbots will align with the - //adjoining lower edge. [Helpful in the ProcessHorizontal() method.] - std::swap(e.Top.X, e.Bot.X); -#ifdef use_xyz - std::swap(e.Top.Z, e.Bot.Z); -#endif -} -//------------------------------------------------------------------------------ - -void SwapPoints(IntPoint &pt1, IntPoint &pt2) -{ - IntPoint tmp = pt1; - pt1 = pt2; - pt2 = tmp; -} -//------------------------------------------------------------------------------ - -bool GetOverlapSegment(IntPoint pt1a, IntPoint pt1b, IntPoint pt2a, - IntPoint pt2b, IntPoint &pt1, IntPoint &pt2) -{ - //precondition: segments are Collinear. - if (Abs(pt1a.X - pt1b.X) > Abs(pt1a.Y - pt1b.Y)) - { - if (pt1a.X > pt1b.X) SwapPoints(pt1a, pt1b); - if (pt2a.X > pt2b.X) SwapPoints(pt2a, pt2b); - if (pt1a.X > pt2a.X) pt1 = pt1a; else pt1 = pt2a; - if (pt1b.X < pt2b.X) pt2 = pt1b; else pt2 = pt2b; - return pt1.X < pt2.X; - } else - { - if (pt1a.Y < pt1b.Y) SwapPoints(pt1a, pt1b); - if (pt2a.Y < pt2b.Y) SwapPoints(pt2a, pt2b); - if (pt1a.Y < pt2a.Y) pt1 = pt1a; else pt1 = pt2a; - if (pt1b.Y > pt2b.Y) pt2 = pt1b; else pt2 = pt2b; - return pt1.Y > pt2.Y; - } -} -//------------------------------------------------------------------------------ - -bool FirstIsBottomPt(const OutPt* btmPt1, const OutPt* btmPt2) -{ - OutPt *p = btmPt1->Prev; - while ((p->Pt == btmPt1->Pt) && (p != btmPt1)) p = p->Prev; - double dx1p = std::fabs(GetDx(btmPt1->Pt, p->Pt)); - p = btmPt1->Next; - while ((p->Pt == btmPt1->Pt) && (p != btmPt1)) p = p->Next; - double dx1n = std::fabs(GetDx(btmPt1->Pt, p->Pt)); - - p = btmPt2->Prev; - while ((p->Pt == btmPt2->Pt) && (p != btmPt2)) p = p->Prev; - double dx2p = std::fabs(GetDx(btmPt2->Pt, p->Pt)); - p = btmPt2->Next; - while ((p->Pt == btmPt2->Pt) && (p != btmPt2)) p = p->Next; - double dx2n = std::fabs(GetDx(btmPt2->Pt, p->Pt)); - - if (std::max(dx1p, dx1n) == std::max(dx2p, dx2n) && - std::min(dx1p, dx1n) == std::min(dx2p, dx2n)) - return Area(btmPt1) > 0; //if otherwise identical use orientation - else - return (dx1p >= dx2p && dx1p >= dx2n) || (dx1n >= dx2p && dx1n >= dx2n); -} -//------------------------------------------------------------------------------ - -OutPt* GetBottomPt(OutPt *pp) -{ - OutPt* dups = 0; - OutPt* p = pp->Next; - while (p != pp) - { - if (p->Pt.Y > pp->Pt.Y) - { - pp = p; - dups = 0; - } - else if (p->Pt.Y == pp->Pt.Y && p->Pt.X <= pp->Pt.X) - { - if (p->Pt.X < pp->Pt.X) - { - dups = 0; - pp = p; - } else - { - if (p->Next != pp && p->Prev != pp) dups = p; - } - } - p = p->Next; - } - if (dups) - { - //there appears to be at least 2 vertices at BottomPt so ... - while (dups != p) - { - if (!FirstIsBottomPt(p, dups)) pp = dups; - dups = dups->Next; - while (dups->Pt != pp->Pt) dups = dups->Next; - } - } - return pp; -} -//------------------------------------------------------------------------------ - -bool Pt2IsBetweenPt1AndPt3(const IntPoint pt1, - const IntPoint pt2, const IntPoint pt3) -{ - if ((pt1 == pt3) || (pt1 == pt2) || (pt3 == pt2)) - return false; - else if (pt1.X != pt3.X) - return (pt2.X > pt1.X) == (pt2.X < pt3.X); - else - return (pt2.Y > pt1.Y) == (pt2.Y < pt3.Y); -} -//------------------------------------------------------------------------------ - -bool HorzSegmentsOverlap(cInt seg1a, cInt seg1b, cInt seg2a, cInt seg2b) -{ - if (seg1a > seg1b) std::swap(seg1a, seg1b); - if (seg2a > seg2b) std::swap(seg2a, seg2b); - return (seg1a < seg2b) && (seg2a < seg1b); -} - -//------------------------------------------------------------------------------ -// ClipperBase class methods ... -//------------------------------------------------------------------------------ - -ClipperBase::ClipperBase() //constructor -{ - m_CurrentLM = m_MinimaList.begin(); //begin() == end() here - m_UseFullRange = false; -} -//------------------------------------------------------------------------------ - -ClipperBase::~ClipperBase() //destructor -{ - Clear(); -} -//------------------------------------------------------------------------------ - -void RangeTest(const IntPoint& Pt, bool& useFullRange) -{ - if (useFullRange) - { - if (Pt.X > hiRange || Pt.Y > hiRange || -Pt.X > hiRange || -Pt.Y > hiRange) - throw clipperException("Coordinate outside allowed range"); - } - else if (Pt.X > loRange|| Pt.Y > loRange || -Pt.X > loRange || -Pt.Y > loRange) - { - useFullRange = true; - RangeTest(Pt, useFullRange); - } -} -//------------------------------------------------------------------------------ - -TEdge* FindNextLocMin(TEdge* E) -{ - for (;;) - { - while (E->Bot != E->Prev->Bot || E->Curr == E->Top) E = E->Next; - if (!IsHorizontal(*E) && !IsHorizontal(*E->Prev)) break; - while (IsHorizontal(*E->Prev)) E = E->Prev; - TEdge* E2 = E; - while (IsHorizontal(*E)) E = E->Next; - if (E->Top.Y == E->Prev->Bot.Y) continue; //ie just an intermediate horz. - if (E2->Prev->Bot.X < E->Bot.X) E = E2; - break; - } - return E; -} -//------------------------------------------------------------------------------ - -TEdge* ClipperBase::ProcessBound(TEdge* E, bool NextIsForward) -{ - TEdge *Result = E; - TEdge *Horz = 0; - - if (E->OutIdx == Skip) - { - //if edges still remain in the current bound beyond the skip edge then - //create another LocMin and call ProcessBound once more - if (NextIsForward) - { - while (E->Top.Y == E->Next->Bot.Y) E = E->Next; - //don't include top horizontals when parsing a bound a second time, - //they will be contained in the opposite bound ... - while (E != Result && IsHorizontal(*E)) E = E->Prev; - } - else - { - while (E->Top.Y == E->Prev->Bot.Y) E = E->Prev; - while (E != Result && IsHorizontal(*E)) E = E->Next; - } - - if (E == Result) - { - if (NextIsForward) Result = E->Next; - else Result = E->Prev; - } - else - { - //there are more edges in the bound beyond result starting with E - if (NextIsForward) - E = Result->Next; - else - E = Result->Prev; - MinimaList::value_type locMin; - locMin.Y = E->Bot.Y; - locMin.LeftBound = 0; - locMin.RightBound = E; - E->WindDelta = 0; - Result = ProcessBound(E, NextIsForward); - m_MinimaList.push_back(locMin); - } - return Result; - } - - TEdge *EStart; - - if (IsHorizontal(*E)) - { - //We need to be careful with open paths because this may not be a - //true local minima (ie E may be following a skip edge). - //Also, consecutive horz. edges may start heading left before going right. - if (NextIsForward) - EStart = E->Prev; - else - EStart = E->Next; - if (IsHorizontal(*EStart)) //ie an adjoining horizontal skip edge - { - if (EStart->Bot.X != E->Bot.X && EStart->Top.X != E->Bot.X) - ReverseHorizontal(*E); - } - else if (EStart->Bot.X != E->Bot.X) - ReverseHorizontal(*E); - } - - EStart = E; - if (NextIsForward) - { - while (Result->Top.Y == Result->Next->Bot.Y && Result->Next->OutIdx != Skip) - Result = Result->Next; - if (IsHorizontal(*Result) && Result->Next->OutIdx != Skip) - { - //nb: at the top of a bound, horizontals are added to the bound - //only when the preceding edge attaches to the horizontal's left vertex - //unless a Skip edge is encountered when that becomes the top divide - Horz = Result; - while (IsHorizontal(*Horz->Prev)) Horz = Horz->Prev; - if (Horz->Prev->Top.X > Result->Next->Top.X) Result = Horz->Prev; - } - while (E != Result) - { - E->NextInLML = E->Next; - if (IsHorizontal(*E) && E != EStart && - E->Bot.X != E->Prev->Top.X) ReverseHorizontal(*E); - E = E->Next; - } - if (IsHorizontal(*E) && E != EStart && E->Bot.X != E->Prev->Top.X) - ReverseHorizontal(*E); - Result = Result->Next; //move to the edge just beyond current bound - } else - { - while (Result->Top.Y == Result->Prev->Bot.Y && Result->Prev->OutIdx != Skip) - Result = Result->Prev; - if (IsHorizontal(*Result) && Result->Prev->OutIdx != Skip) - { - Horz = Result; - while (IsHorizontal(*Horz->Next)) Horz = Horz->Next; - if (Horz->Next->Top.X == Result->Prev->Top.X || - Horz->Next->Top.X > Result->Prev->Top.X) Result = Horz->Next; - } - - while (E != Result) - { - E->NextInLML = E->Prev; - if (IsHorizontal(*E) && E != EStart && E->Bot.X != E->Next->Top.X) - ReverseHorizontal(*E); - E = E->Prev; - } - if (IsHorizontal(*E) && E != EStart && E->Bot.X != E->Next->Top.X) - ReverseHorizontal(*E); - Result = Result->Prev; //move to the edge just beyond current bound - } - - return Result; -} -//------------------------------------------------------------------------------ - -bool ClipperBase::AddPath(const Path &pg, PolyType PolyTyp, bool Closed) -{ -#ifdef use_lines - if (!Closed && PolyTyp == ptClip) - throw clipperException("AddPath: Open paths must be subject."); -#else - if (!Closed) - throw clipperException("AddPath: Open paths have been disabled."); -#endif - - int highI = (int)pg.size() -1; - if (Closed) while (highI > 0 && (pg[highI] == pg[0])) --highI; - while (highI > 0 && (pg[highI] == pg[highI -1])) --highI; - if ((Closed && highI < 2) || (!Closed && highI < 1)) return false; - - //create a new edge array ... - TEdge *edges = new TEdge [highI +1]; - - bool IsFlat = true; - //1. Basic (first) edge initialization ... - try - { - edges[1].Curr = pg[1]; - RangeTest(pg[0], m_UseFullRange); - RangeTest(pg[highI], m_UseFullRange); - InitEdge(&edges[0], &edges[1], &edges[highI], pg[0]); - InitEdge(&edges[highI], &edges[0], &edges[highI-1], pg[highI]); - for (int i = highI - 1; i >= 1; --i) - { - RangeTest(pg[i], m_UseFullRange); - InitEdge(&edges[i], &edges[i+1], &edges[i-1], pg[i]); - } - } - catch(...) - { - delete [] edges; - throw; //range test fails - } - TEdge *eStart = &edges[0]; - - //2. Remove duplicate vertices, and (when closed) collinear edges ... - TEdge *E = eStart, *eLoopStop = eStart; - for (;;) - { - //nb: allows matching start and end points when not Closed ... - if (E->Curr == E->Next->Curr && (Closed || E->Next != eStart)) - { - if (E == E->Next) break; - if (E == eStart) eStart = E->Next; - E = RemoveEdge(E); - eLoopStop = E; - continue; - } - if (E->Prev == E->Next) - break; //only two vertices - else if (Closed && - SlopesEqual(E->Prev->Curr, E->Curr, E->Next->Curr, m_UseFullRange) && - (!m_PreserveCollinear || - !Pt2IsBetweenPt1AndPt3(E->Prev->Curr, E->Curr, E->Next->Curr))) - { - //Collinear edges are allowed for open paths but in closed paths - //the default is to merge adjacent collinear edges into a single edge. - //However, if the PreserveCollinear property is enabled, only overlapping - //collinear edges (ie spikes) will be removed from closed paths. - if (E == eStart) eStart = E->Next; - E = RemoveEdge(E); - E = E->Prev; - eLoopStop = E; - continue; - } - E = E->Next; - if ((E == eLoopStop) || (!Closed && E->Next == eStart)) break; - } - - if ((!Closed && (E == E->Next)) || (Closed && (E->Prev == E->Next))) - { - delete [] edges; - return false; - } - - if (!Closed) - { - m_HasOpenPaths = true; - eStart->Prev->OutIdx = Skip; - } - - //3. Do second stage of edge initialization ... - E = eStart; - do - { - InitEdge2(*E, PolyTyp); - E = E->Next; - if (IsFlat && E->Curr.Y != eStart->Curr.Y) IsFlat = false; - } - while (E != eStart); - - //4. Finally, add edge bounds to LocalMinima list ... - - //Totally flat paths must be handled differently when adding them - //to LocalMinima list to avoid endless loops etc ... - if (IsFlat) - { - if (Closed) - { - delete [] edges; - return false; - } - E->Prev->OutIdx = Skip; - MinimaList::value_type locMin; - locMin.Y = E->Bot.Y; - locMin.LeftBound = 0; - locMin.RightBound = E; - locMin.RightBound->Side = esRight; - locMin.RightBound->WindDelta = 0; - for (;;) - { - if (E->Bot.X != E->Prev->Top.X) ReverseHorizontal(*E); - if (E->Next->OutIdx == Skip) break; - E->NextInLML = E->Next; - E = E->Next; - } - m_MinimaList.push_back(locMin); - m_edges.push_back(edges); - return true; - } - - m_edges.push_back(edges); - bool leftBoundIsForward; - TEdge* EMin = 0; - - //workaround to avoid an endless loop in the while loop below when - //open paths have matching start and end points ... - if (E->Prev->Bot == E->Prev->Top) E = E->Next; - - for (;;) - { - E = FindNextLocMin(E); - if (E == EMin) break; - else if (!EMin) EMin = E; - - //E and E.Prev now share a local minima (left aligned if horizontal). - //Compare their slopes to find which starts which bound ... - MinimaList::value_type locMin; - locMin.Y = E->Bot.Y; - if (E->Dx < E->Prev->Dx) - { - locMin.LeftBound = E->Prev; - locMin.RightBound = E; - leftBoundIsForward = false; //Q.nextInLML = Q.prev - } else - { - locMin.LeftBound = E; - locMin.RightBound = E->Prev; - leftBoundIsForward = true; //Q.nextInLML = Q.next - } - - if (!Closed) locMin.LeftBound->WindDelta = 0; - else if (locMin.LeftBound->Next == locMin.RightBound) - locMin.LeftBound->WindDelta = -1; - else locMin.LeftBound->WindDelta = 1; - locMin.RightBound->WindDelta = -locMin.LeftBound->WindDelta; - - E = ProcessBound(locMin.LeftBound, leftBoundIsForward); - if (E->OutIdx == Skip) E = ProcessBound(E, leftBoundIsForward); - - TEdge* E2 = ProcessBound(locMin.RightBound, !leftBoundIsForward); - if (E2->OutIdx == Skip) E2 = ProcessBound(E2, !leftBoundIsForward); - - if (locMin.LeftBound->OutIdx == Skip) - locMin.LeftBound = 0; - else if (locMin.RightBound->OutIdx == Skip) - locMin.RightBound = 0; - m_MinimaList.push_back(locMin); - if (!leftBoundIsForward) E = E2; - } - return true; -} -//------------------------------------------------------------------------------ - -bool ClipperBase::AddPaths(const Paths &ppg, PolyType PolyTyp, bool Closed) -{ - bool result = false; - for (Paths::size_type i = 0; i < ppg.size(); ++i) - if (AddPath(ppg[i], PolyTyp, Closed)) result = true; - return result; -} -//------------------------------------------------------------------------------ - -void ClipperBase::Clear() -{ - DisposeLocalMinimaList(); - for (EdgeList::size_type i = 0; i < m_edges.size(); ++i) - { - TEdge* edges = m_edges[i]; - delete [] edges; - } - m_edges.clear(); - m_UseFullRange = false; - m_HasOpenPaths = false; -} -//------------------------------------------------------------------------------ - -void ClipperBase::Reset() -{ - m_CurrentLM = m_MinimaList.begin(); - if (m_CurrentLM == m_MinimaList.end()) return; //ie nothing to process - std::sort(m_MinimaList.begin(), m_MinimaList.end(), LocMinSorter()); - - m_Scanbeam = ScanbeamList(); //clears/resets priority_queue - //reset all edges ... - for (MinimaList::iterator lm = m_MinimaList.begin(); lm != m_MinimaList.end(); ++lm) - { - InsertScanbeam(lm->Y); - TEdge* e = lm->LeftBound; - if (e) - { - e->Curr = e->Bot; - e->Side = esLeft; - e->OutIdx = Unassigned; - } - - e = lm->RightBound; - if (e) - { - e->Curr = e->Bot; - e->Side = esRight; - e->OutIdx = Unassigned; - } - } - m_ActiveEdges = 0; - m_CurrentLM = m_MinimaList.begin(); -} -//------------------------------------------------------------------------------ - -void ClipperBase::DisposeLocalMinimaList() -{ - m_MinimaList.clear(); - m_CurrentLM = m_MinimaList.begin(); -} -//------------------------------------------------------------------------------ - -bool ClipperBase::PopLocalMinima(cInt Y, const LocalMinimum *&locMin) -{ - if (m_CurrentLM == m_MinimaList.end() || (*m_CurrentLM).Y != Y) return false; - locMin = &(*m_CurrentLM); - ++m_CurrentLM; - return true; -} -//------------------------------------------------------------------------------ - -IntRect ClipperBase::GetBounds() -{ - IntRect result; - MinimaList::iterator lm = m_MinimaList.begin(); - if (lm == m_MinimaList.end()) - { - result.left = result.top = result.right = result.bottom = 0; - return result; - } - result.left = lm->LeftBound->Bot.X; - result.top = lm->LeftBound->Bot.Y; - result.right = lm->LeftBound->Bot.X; - result.bottom = lm->LeftBound->Bot.Y; - while (lm != m_MinimaList.end()) - { - //todo - needs fixing for open paths - result.bottom = std::max(result.bottom, lm->LeftBound->Bot.Y); - TEdge* e = lm->LeftBound; - for (;;) { - TEdge* bottomE = e; - while (e->NextInLML) - { - if (e->Bot.X < result.left) result.left = e->Bot.X; - if (e->Bot.X > result.right) result.right = e->Bot.X; - e = e->NextInLML; - } - result.left = std::min(result.left, e->Bot.X); - result.right = std::max(result.right, e->Bot.X); - result.left = std::min(result.left, e->Top.X); - result.right = std::max(result.right, e->Top.X); - result.top = std::min(result.top, e->Top.Y); - if (bottomE == lm->LeftBound) e = lm->RightBound; - else break; - } - ++lm; - } - return result; -} -//------------------------------------------------------------------------------ - -void ClipperBase::InsertScanbeam(const cInt Y) -{ - m_Scanbeam.push(Y); -} -//------------------------------------------------------------------------------ - -bool ClipperBase::PopScanbeam(cInt &Y) -{ - if (m_Scanbeam.empty()) return false; - Y = m_Scanbeam.top(); - m_Scanbeam.pop(); - while (!m_Scanbeam.empty() && Y == m_Scanbeam.top()) { m_Scanbeam.pop(); } // Pop duplicates. - return true; -} -//------------------------------------------------------------------------------ - -void ClipperBase::DisposeAllOutRecs(){ - for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) - DisposeOutRec(i); - m_PolyOuts.clear(); -} -//------------------------------------------------------------------------------ - -void ClipperBase::DisposeOutRec(PolyOutList::size_type index) -{ - OutRec *outRec = m_PolyOuts[index]; - if (outRec->Pts) DisposeOutPts(outRec->Pts); - delete outRec; - m_PolyOuts[index] = 0; -} -//------------------------------------------------------------------------------ - -void ClipperBase::DeleteFromAEL(TEdge *e) -{ - TEdge* AelPrev = e->PrevInAEL; - TEdge* AelNext = e->NextInAEL; - if (!AelPrev && !AelNext && (e != m_ActiveEdges)) return; //already deleted - if (AelPrev) AelPrev->NextInAEL = AelNext; - else m_ActiveEdges = AelNext; - if (AelNext) AelNext->PrevInAEL = AelPrev; - e->NextInAEL = 0; - e->PrevInAEL = 0; -} -//------------------------------------------------------------------------------ - -OutRec* ClipperBase::CreateOutRec() -{ - OutRec* result = new OutRec; - result->IsHole = false; - result->IsOpen = false; - result->FirstLeft = 0; - result->Pts = 0; - result->BottomPt = 0; - result->PolyNd = 0; - m_PolyOuts.push_back(result); - result->Idx = (int)m_PolyOuts.size() - 1; - return result; -} -//------------------------------------------------------------------------------ - -void ClipperBase::SwapPositionsInAEL(TEdge *Edge1, TEdge *Edge2) -{ - //check that one or other edge hasn't already been removed from AEL ... - if (Edge1->NextInAEL == Edge1->PrevInAEL || - Edge2->NextInAEL == Edge2->PrevInAEL) return; - - if (Edge1->NextInAEL == Edge2) - { - TEdge* Next = Edge2->NextInAEL; - if (Next) Next->PrevInAEL = Edge1; - TEdge* Prev = Edge1->PrevInAEL; - if (Prev) Prev->NextInAEL = Edge2; - Edge2->PrevInAEL = Prev; - Edge2->NextInAEL = Edge1; - Edge1->PrevInAEL = Edge2; - Edge1->NextInAEL = Next; - } - else if (Edge2->NextInAEL == Edge1) - { - TEdge* Next = Edge1->NextInAEL; - if (Next) Next->PrevInAEL = Edge2; - TEdge* Prev = Edge2->PrevInAEL; - if (Prev) Prev->NextInAEL = Edge1; - Edge1->PrevInAEL = Prev; - Edge1->NextInAEL = Edge2; - Edge2->PrevInAEL = Edge1; - Edge2->NextInAEL = Next; - } - else - { - TEdge* Next = Edge1->NextInAEL; - TEdge* Prev = Edge1->PrevInAEL; - Edge1->NextInAEL = Edge2->NextInAEL; - if (Edge1->NextInAEL) Edge1->NextInAEL->PrevInAEL = Edge1; - Edge1->PrevInAEL = Edge2->PrevInAEL; - if (Edge1->PrevInAEL) Edge1->PrevInAEL->NextInAEL = Edge1; - Edge2->NextInAEL = Next; - if (Edge2->NextInAEL) Edge2->NextInAEL->PrevInAEL = Edge2; - Edge2->PrevInAEL = Prev; - if (Edge2->PrevInAEL) Edge2->PrevInAEL->NextInAEL = Edge2; - } - - if (!Edge1->PrevInAEL) m_ActiveEdges = Edge1; - else if (!Edge2->PrevInAEL) m_ActiveEdges = Edge2; -} -//------------------------------------------------------------------------------ - -void ClipperBase::UpdateEdgeIntoAEL(TEdge *&e) -{ - if (!e->NextInLML) - throw clipperException("UpdateEdgeIntoAEL: invalid call"); - - e->NextInLML->OutIdx = e->OutIdx; - TEdge* AelPrev = e->PrevInAEL; - TEdge* AelNext = e->NextInAEL; - if (AelPrev) AelPrev->NextInAEL = e->NextInLML; - else m_ActiveEdges = e->NextInLML; - if (AelNext) AelNext->PrevInAEL = e->NextInLML; - e->NextInLML->Side = e->Side; - e->NextInLML->WindDelta = e->WindDelta; - e->NextInLML->WindCnt = e->WindCnt; - e->NextInLML->WindCnt2 = e->WindCnt2; - e = e->NextInLML; - e->Curr = e->Bot; - e->PrevInAEL = AelPrev; - e->NextInAEL = AelNext; - if (!IsHorizontal(*e)) InsertScanbeam(e->Top.Y); -} -//------------------------------------------------------------------------------ - -bool ClipperBase::LocalMinimaPending() -{ - return (m_CurrentLM != m_MinimaList.end()); -} - -//------------------------------------------------------------------------------ -// TClipper methods ... -//------------------------------------------------------------------------------ - -Clipper::Clipper(int initOptions) : ClipperBase() //constructor -{ - m_ExecuteLocked = false; - m_UseFullRange = false; - m_ReverseOutput = ((initOptions & ioReverseSolution) != 0); - m_StrictSimple = ((initOptions & ioStrictlySimple) != 0); - m_PreserveCollinear = ((initOptions & ioPreserveCollinear) != 0); - m_HasOpenPaths = false; -#ifdef use_xyz - m_ZFill = 0; -#endif -} -//------------------------------------------------------------------------------ - -#ifdef use_xyz -void Clipper::ZFillFunction(ZFillCallback zFillFunc) -{ - m_ZFill = zFillFunc; -} -//------------------------------------------------------------------------------ -#endif - -bool Clipper::Execute(ClipType clipType, Paths &solution, PolyFillType fillType) -{ - return Execute(clipType, solution, fillType, fillType); -} -//------------------------------------------------------------------------------ - -bool Clipper::Execute(ClipType clipType, PolyTree &polytree, PolyFillType fillType) -{ - return Execute(clipType, polytree, fillType, fillType); -} -//------------------------------------------------------------------------------ - -bool Clipper::Execute(ClipType clipType, Paths &solution, - PolyFillType subjFillType, PolyFillType clipFillType) -{ - if( m_ExecuteLocked ) return false; - if (m_HasOpenPaths) - throw clipperException("Error: PolyTree struct is needed for open path clipping."); - m_ExecuteLocked = true; - solution.resize(0); - m_SubjFillType = subjFillType; - m_ClipFillType = clipFillType; - m_ClipType = clipType; - m_UsingPolyTree = false; - bool succeeded = ExecuteInternal(); - if (succeeded) BuildResult(solution); - DisposeAllOutRecs(); - m_ExecuteLocked = false; - return succeeded; -} -//------------------------------------------------------------------------------ - -bool Clipper::Execute(ClipType clipType, PolyTree& polytree, - PolyFillType subjFillType, PolyFillType clipFillType) -{ - if( m_ExecuteLocked ) return false; - m_ExecuteLocked = true; - m_SubjFillType = subjFillType; - m_ClipFillType = clipFillType; - m_ClipType = clipType; - m_UsingPolyTree = true; - bool succeeded = ExecuteInternal(); - if (succeeded) BuildResult2(polytree); - DisposeAllOutRecs(); - m_ExecuteLocked = false; - return succeeded; -} -//------------------------------------------------------------------------------ - -void Clipper::FixHoleLinkage(OutRec &outrec) -{ - //skip OutRecs that (a) contain outermost polygons or - //(b) already have the correct owner/child linkage ... - if (!outrec.FirstLeft || - (outrec.IsHole != outrec.FirstLeft->IsHole && - outrec.FirstLeft->Pts)) return; - - OutRec* orfl = outrec.FirstLeft; - while (orfl && ((orfl->IsHole == outrec.IsHole) || !orfl->Pts)) - orfl = orfl->FirstLeft; - outrec.FirstLeft = orfl; -} -//------------------------------------------------------------------------------ - -bool Clipper::ExecuteInternal() -{ - bool succeeded = true; - try { - Reset(); - m_Maxima = MaximaList(); - m_SortedEdges = 0; - - succeeded = true; - cInt botY, topY; - if (!PopScanbeam(botY)) return false; - InsertLocalMinimaIntoAEL(botY); - while (PopScanbeam(topY) || LocalMinimaPending()) - { - ProcessHorizontals(); - ClearGhostJoins(); - if (!ProcessIntersections(topY)) - { - succeeded = false; - break; - } - ProcessEdgesAtTopOfScanbeam(topY); - botY = topY; - InsertLocalMinimaIntoAEL(botY); - } - } - catch(...) - { - succeeded = false; - } - - if (succeeded) - { - //fix orientations ... - for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) - { - OutRec *outRec = m_PolyOuts[i]; - if (!outRec->Pts || outRec->IsOpen) continue; - if ((outRec->IsHole ^ m_ReverseOutput) == (Area(*outRec) > 0)) - ReversePolyPtLinks(outRec->Pts); - } - - if (!m_Joins.empty()) JoinCommonEdges(); - - //unfortunately FixupOutPolygon() must be done after JoinCommonEdges() - for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) - { - OutRec *outRec = m_PolyOuts[i]; - if (!outRec->Pts) continue; - if (outRec->IsOpen) - FixupOutPolyline(*outRec); - else - FixupOutPolygon(*outRec); - } - - if (m_StrictSimple) DoSimplePolygons(); - } - - ClearJoins(); - ClearGhostJoins(); - return succeeded; -} -//------------------------------------------------------------------------------ - -void Clipper::SetWindingCount(TEdge &edge) -{ - TEdge *e = edge.PrevInAEL; - //find the edge of the same polytype that immediately preceeds 'edge' in AEL - while (e && ((e->PolyTyp != edge.PolyTyp) || (e->WindDelta == 0))) e = e->PrevInAEL; - if (!e) - { - if (edge.WindDelta == 0) - { - PolyFillType pft = (edge.PolyTyp == ptSubject ? m_SubjFillType : m_ClipFillType); - edge.WindCnt = (pft == pftNegative ? -1 : 1); - } - else - edge.WindCnt = edge.WindDelta; - edge.WindCnt2 = 0; - e = m_ActiveEdges; //ie get ready to calc WindCnt2 - } - else if (edge.WindDelta == 0 && m_ClipType != ctUnion) - { - edge.WindCnt = 1; - edge.WindCnt2 = e->WindCnt2; - e = e->NextInAEL; //ie get ready to calc WindCnt2 - } - else if (IsEvenOddFillType(edge)) - { - //EvenOdd filling ... - if (edge.WindDelta == 0) - { - //are we inside a subj polygon ... - bool Inside = true; - TEdge *e2 = e->PrevInAEL; - while (e2) - { - if (e2->PolyTyp == e->PolyTyp && e2->WindDelta != 0) - Inside = !Inside; - e2 = e2->PrevInAEL; - } - edge.WindCnt = (Inside ? 0 : 1); - } - else - { - edge.WindCnt = edge.WindDelta; - } - edge.WindCnt2 = e->WindCnt2; - e = e->NextInAEL; //ie get ready to calc WindCnt2 - } - else - { - //nonZero, Positive or Negative filling ... - if (e->WindCnt * e->WindDelta < 0) - { - //prev edge is 'decreasing' WindCount (WC) toward zero - //so we're outside the previous polygon ... - if (Abs(e->WindCnt) > 1) - { - //outside prev poly but still inside another. - //when reversing direction of prev poly use the same WC - if (e->WindDelta * edge.WindDelta < 0) edge.WindCnt = e->WindCnt; - //otherwise continue to 'decrease' WC ... - else edge.WindCnt = e->WindCnt + edge.WindDelta; - } - else - //now outside all polys of same polytype so set own WC ... - edge.WindCnt = (edge.WindDelta == 0 ? 1 : edge.WindDelta); - } else - { - //prev edge is 'increasing' WindCount (WC) away from zero - //so we're inside the previous polygon ... - if (edge.WindDelta == 0) - edge.WindCnt = (e->WindCnt < 0 ? e->WindCnt - 1 : e->WindCnt + 1); - //if wind direction is reversing prev then use same WC - else if (e->WindDelta * edge.WindDelta < 0) edge.WindCnt = e->WindCnt; - //otherwise add to WC ... - else edge.WindCnt = e->WindCnt + edge.WindDelta; - } - edge.WindCnt2 = e->WindCnt2; - e = e->NextInAEL; //ie get ready to calc WindCnt2 - } - - //update WindCnt2 ... - if (IsEvenOddAltFillType(edge)) - { - //EvenOdd filling ... - while (e != &edge) - { - if (e->WindDelta != 0) - edge.WindCnt2 = (edge.WindCnt2 == 0 ? 1 : 0); - e = e->NextInAEL; - } - } else - { - //nonZero, Positive or Negative filling ... - while ( e != &edge ) - { - edge.WindCnt2 += e->WindDelta; - e = e->NextInAEL; - } - } -} -//------------------------------------------------------------------------------ - -bool Clipper::IsEvenOddFillType(const TEdge& edge) const -{ - if (edge.PolyTyp == ptSubject) - return m_SubjFillType == pftEvenOdd; else - return m_ClipFillType == pftEvenOdd; -} -//------------------------------------------------------------------------------ - -bool Clipper::IsEvenOddAltFillType(const TEdge& edge) const -{ - if (edge.PolyTyp == ptSubject) - return m_ClipFillType == pftEvenOdd; else - return m_SubjFillType == pftEvenOdd; -} -//------------------------------------------------------------------------------ - -bool Clipper::IsContributing(const TEdge& edge) const -{ - PolyFillType pft, pft2; - if (edge.PolyTyp == ptSubject) - { - pft = m_SubjFillType; - pft2 = m_ClipFillType; - } else - { - pft = m_ClipFillType; - pft2 = m_SubjFillType; - } - - switch(pft) - { - case pftEvenOdd: - //return false if a subj line has been flagged as inside a subj polygon - if (edge.WindDelta == 0 && edge.WindCnt != 1) return false; - break; - case pftNonZero: - if (Abs(edge.WindCnt) != 1) return false; - break; - case pftPositive: - if (edge.WindCnt != 1) return false; - break; - default: //pftNegative - if (edge.WindCnt != -1) return false; - } - - switch(m_ClipType) - { - case ctIntersection: - switch(pft2) - { - case pftEvenOdd: - case pftNonZero: - return (edge.WindCnt2 != 0); - case pftPositive: - return (edge.WindCnt2 > 0); - default: - return (edge.WindCnt2 < 0); - } - break; - case ctUnion: - switch(pft2) - { - case pftEvenOdd: - case pftNonZero: - return (edge.WindCnt2 == 0); - case pftPositive: - return (edge.WindCnt2 <= 0); - default: - return (edge.WindCnt2 >= 0); - } - break; - case ctDifference: - if (edge.PolyTyp == ptSubject) - switch(pft2) - { - case pftEvenOdd: - case pftNonZero: - return (edge.WindCnt2 == 0); - case pftPositive: - return (edge.WindCnt2 <= 0); - default: - return (edge.WindCnt2 >= 0); - } - else - switch(pft2) - { - case pftEvenOdd: - case pftNonZero: - return (edge.WindCnt2 != 0); - case pftPositive: - return (edge.WindCnt2 > 0); - default: - return (edge.WindCnt2 < 0); - } - break; - case ctXor: - if (edge.WindDelta == 0) //XOr always contributing unless open - switch(pft2) - { - case pftEvenOdd: - case pftNonZero: - return (edge.WindCnt2 == 0); - case pftPositive: - return (edge.WindCnt2 <= 0); - default: - return (edge.WindCnt2 >= 0); - } - else - return true; - break; - default: - return true; - } -} -//------------------------------------------------------------------------------ - -OutPt* Clipper::AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &Pt) -{ - OutPt* result; - TEdge *e, *prevE; - if (IsHorizontal(*e2) || ( e1->Dx > e2->Dx )) - { - result = AddOutPt(e1, Pt); - e2->OutIdx = e1->OutIdx; - e1->Side = esLeft; - e2->Side = esRight; - e = e1; - if (e->PrevInAEL == e2) - prevE = e2->PrevInAEL; - else - prevE = e->PrevInAEL; - } else - { - result = AddOutPt(e2, Pt); - e1->OutIdx = e2->OutIdx; - e1->Side = esRight; - e2->Side = esLeft; - e = e2; - if (e->PrevInAEL == e1) - prevE = e1->PrevInAEL; - else - prevE = e->PrevInAEL; - } - - if (prevE && prevE->OutIdx >= 0) - { - cInt xPrev = TopX(*prevE, Pt.Y); - cInt xE = TopX(*e, Pt.Y); - if (xPrev == xE && (e->WindDelta != 0) && (prevE->WindDelta != 0) && - SlopesEqual(IntPoint(xPrev, Pt.Y), prevE->Top, IntPoint(xE, Pt.Y), e->Top, m_UseFullRange)) - { - OutPt* outPt = AddOutPt(prevE, Pt); - AddJoin(result, outPt, e->Top); - } - } - return result; -} -//------------------------------------------------------------------------------ - -void Clipper::AddLocalMaxPoly(TEdge *e1, TEdge *e2, const IntPoint &Pt) -{ - AddOutPt( e1, Pt ); - if (e2->WindDelta == 0) AddOutPt(e2, Pt); - if( e1->OutIdx == e2->OutIdx ) - { - e1->OutIdx = Unassigned; - e2->OutIdx = Unassigned; - } - else if (e1->OutIdx < e2->OutIdx) - AppendPolygon(e1, e2); - else - AppendPolygon(e2, e1); -} -//------------------------------------------------------------------------------ - -void Clipper::AddEdgeToSEL(TEdge *edge) -{ - //SEL pointers in PEdge are reused to build a list of horizontal edges. - //However, we don't need to worry about order with horizontal edge processing. - if( !m_SortedEdges ) - { - m_SortedEdges = edge; - edge->PrevInSEL = 0; - edge->NextInSEL = 0; - } - else - { - edge->NextInSEL = m_SortedEdges; - edge->PrevInSEL = 0; - m_SortedEdges->PrevInSEL = edge; - m_SortedEdges = edge; - } -} -//------------------------------------------------------------------------------ - -bool Clipper::PopEdgeFromSEL(TEdge *&edge) -{ - if (!m_SortedEdges) return false; - edge = m_SortedEdges; - DeleteFromSEL(m_SortedEdges); - return true; -} -//------------------------------------------------------------------------------ - -void Clipper::CopyAELToSEL() -{ - TEdge* e = m_ActiveEdges; - m_SortedEdges = e; - while ( e ) - { - e->PrevInSEL = e->PrevInAEL; - e->NextInSEL = e->NextInAEL; - e = e->NextInAEL; - } -} -//------------------------------------------------------------------------------ - -void Clipper::AddJoin(OutPt *op1, OutPt *op2, const IntPoint OffPt) -{ - Join* j = new Join; - j->OutPt1 = op1; - j->OutPt2 = op2; - j->OffPt = OffPt; - m_Joins.push_back(j); -} -//------------------------------------------------------------------------------ - -void Clipper::ClearJoins() -{ - for (JoinList::size_type i = 0; i < m_Joins.size(); i++) - delete m_Joins[i]; - m_Joins.resize(0); -} -//------------------------------------------------------------------------------ - -void Clipper::ClearGhostJoins() -{ - for (JoinList::size_type i = 0; i < m_GhostJoins.size(); i++) - delete m_GhostJoins[i]; - m_GhostJoins.resize(0); -} -//------------------------------------------------------------------------------ - -void Clipper::AddGhostJoin(OutPt *op, const IntPoint OffPt) -{ - Join* j = new Join; - j->OutPt1 = op; - j->OutPt2 = 0; - j->OffPt = OffPt; - m_GhostJoins.push_back(j); -} -//------------------------------------------------------------------------------ - -void Clipper::InsertLocalMinimaIntoAEL(const cInt botY) -{ - const LocalMinimum *lm; - while (PopLocalMinima(botY, lm)) - { - TEdge* lb = lm->LeftBound; - TEdge* rb = lm->RightBound; - - OutPt *Op1 = 0; - if (!lb) - { - //nb: don't insert LB into either AEL or SEL - InsertEdgeIntoAEL(rb, 0); - SetWindingCount(*rb); - if (IsContributing(*rb)) - Op1 = AddOutPt(rb, rb->Bot); - } - else if (!rb) - { - InsertEdgeIntoAEL(lb, 0); - SetWindingCount(*lb); - if (IsContributing(*lb)) - Op1 = AddOutPt(lb, lb->Bot); - InsertScanbeam(lb->Top.Y); - } - else - { - InsertEdgeIntoAEL(lb, 0); - InsertEdgeIntoAEL(rb, lb); - SetWindingCount( *lb ); - rb->WindCnt = lb->WindCnt; - rb->WindCnt2 = lb->WindCnt2; - if (IsContributing(*lb)) - Op1 = AddLocalMinPoly(lb, rb, lb->Bot); - InsertScanbeam(lb->Top.Y); - } - - if (rb) - { - if (IsHorizontal(*rb)) - { - AddEdgeToSEL(rb); - if (rb->NextInLML) - InsertScanbeam(rb->NextInLML->Top.Y); - } - else InsertScanbeam( rb->Top.Y ); - } - - if (!lb || !rb) continue; - - //if any output polygons share an edge, they'll need joining later ... - if (Op1 && IsHorizontal(*rb) && - m_GhostJoins.size() > 0 && (rb->WindDelta != 0)) - { - for (JoinList::size_type i = 0; i < m_GhostJoins.size(); ++i) - { - Join* jr = m_GhostJoins[i]; - //if the horizontal Rb and a 'ghost' horizontal overlap, then convert - //the 'ghost' join to a real join ready for later ... - if (HorzSegmentsOverlap(jr->OutPt1->Pt.X, jr->OffPt.X, rb->Bot.X, rb->Top.X)) - AddJoin(jr->OutPt1, Op1, jr->OffPt); - } - } - - if (lb->OutIdx >= 0 && lb->PrevInAEL && - lb->PrevInAEL->Curr.X == lb->Bot.X && - lb->PrevInAEL->OutIdx >= 0 && - SlopesEqual(lb->PrevInAEL->Bot, lb->PrevInAEL->Top, lb->Curr, lb->Top, m_UseFullRange) && - (lb->WindDelta != 0) && (lb->PrevInAEL->WindDelta != 0)) - { - OutPt *Op2 = AddOutPt(lb->PrevInAEL, lb->Bot); - AddJoin(Op1, Op2, lb->Top); - } - - if(lb->NextInAEL != rb) - { - - if (rb->OutIdx >= 0 && rb->PrevInAEL->OutIdx >= 0 && - SlopesEqual(rb->PrevInAEL->Curr, rb->PrevInAEL->Top, rb->Curr, rb->Top, m_UseFullRange) && - (rb->WindDelta != 0) && (rb->PrevInAEL->WindDelta != 0)) - { - OutPt *Op2 = AddOutPt(rb->PrevInAEL, rb->Bot); - AddJoin(Op1, Op2, rb->Top); - } - - TEdge* e = lb->NextInAEL; - if (e) - { - while( e != rb ) - { - //nb: For calculating winding counts etc, IntersectEdges() assumes - //that param1 will be to the Right of param2 ABOVE the intersection ... - IntersectEdges(rb , e , lb->Curr); //order important here - e = e->NextInAEL; - } - } - } - - } -} -//------------------------------------------------------------------------------ - -void Clipper::DeleteFromSEL(TEdge *e) -{ - TEdge* SelPrev = e->PrevInSEL; - TEdge* SelNext = e->NextInSEL; - if( !SelPrev && !SelNext && (e != m_SortedEdges) ) return; //already deleted - if( SelPrev ) SelPrev->NextInSEL = SelNext; - else m_SortedEdges = SelNext; - if( SelNext ) SelNext->PrevInSEL = SelPrev; - e->NextInSEL = 0; - e->PrevInSEL = 0; -} -//------------------------------------------------------------------------------ - -#ifdef use_xyz -void Clipper::SetZ(IntPoint& pt, TEdge& e1, TEdge& e2) -{ - if (pt.Z != 0 || !m_ZFill) return; - else if (pt == e1.Bot) pt.Z = e1.Bot.Z; - else if (pt == e1.Top) pt.Z = e1.Top.Z; - else if (pt == e2.Bot) pt.Z = e2.Bot.Z; - else if (pt == e2.Top) pt.Z = e2.Top.Z; - else (*m_ZFill)(e1.Bot, e1.Top, e2.Bot, e2.Top, pt); -} -//------------------------------------------------------------------------------ -#endif - -void Clipper::IntersectEdges(TEdge *e1, TEdge *e2, IntPoint &Pt) -{ - bool e1Contributing = ( e1->OutIdx >= 0 ); - bool e2Contributing = ( e2->OutIdx >= 0 ); - -#ifdef use_xyz - SetZ(Pt, *e1, *e2); -#endif - -#ifdef use_lines - //if either edge is on an OPEN path ... - if (e1->WindDelta == 0 || e2->WindDelta == 0) - { - //ignore subject-subject open path intersections UNLESS they - //are both open paths, AND they are both 'contributing maximas' ... - if (e1->WindDelta == 0 && e2->WindDelta == 0) return; - - //if intersecting a subj line with a subj poly ... - else if (e1->PolyTyp == e2->PolyTyp && - e1->WindDelta != e2->WindDelta && m_ClipType == ctUnion) - { - if (e1->WindDelta == 0) - { - if (e2Contributing) - { - AddOutPt(e1, Pt); - if (e1Contributing) e1->OutIdx = Unassigned; - } - } - else - { - if (e1Contributing) - { - AddOutPt(e2, Pt); - if (e2Contributing) e2->OutIdx = Unassigned; - } - } - } - else if (e1->PolyTyp != e2->PolyTyp) - { - //toggle subj open path OutIdx on/off when Abs(clip.WndCnt) == 1 ... - if ((e1->WindDelta == 0) && abs(e2->WindCnt) == 1 && - (m_ClipType != ctUnion || e2->WindCnt2 == 0)) - { - AddOutPt(e1, Pt); - if (e1Contributing) e1->OutIdx = Unassigned; - } - else if ((e2->WindDelta == 0) && (abs(e1->WindCnt) == 1) && - (m_ClipType != ctUnion || e1->WindCnt2 == 0)) - { - AddOutPt(e2, Pt); - if (e2Contributing) e2->OutIdx = Unassigned; - } - } - return; - } -#endif - - //update winding counts... - //assumes that e1 will be to the Right of e2 ABOVE the intersection - if ( e1->PolyTyp == e2->PolyTyp ) - { - if ( IsEvenOddFillType( *e1) ) - { - int oldE1WindCnt = e1->WindCnt; - e1->WindCnt = e2->WindCnt; - e2->WindCnt = oldE1WindCnt; - } else - { - if (e1->WindCnt + e2->WindDelta == 0 ) e1->WindCnt = -e1->WindCnt; - else e1->WindCnt += e2->WindDelta; - if ( e2->WindCnt - e1->WindDelta == 0 ) e2->WindCnt = -e2->WindCnt; - else e2->WindCnt -= e1->WindDelta; - } - } else - { - if (!IsEvenOddFillType(*e2)) e1->WindCnt2 += e2->WindDelta; - else e1->WindCnt2 = ( e1->WindCnt2 == 0 ) ? 1 : 0; - if (!IsEvenOddFillType(*e1)) e2->WindCnt2 -= e1->WindDelta; - else e2->WindCnt2 = ( e2->WindCnt2 == 0 ) ? 1 : 0; - } - - PolyFillType e1FillType, e2FillType, e1FillType2, e2FillType2; - if (e1->PolyTyp == ptSubject) - { - e1FillType = m_SubjFillType; - e1FillType2 = m_ClipFillType; - } else - { - e1FillType = m_ClipFillType; - e1FillType2 = m_SubjFillType; - } - if (e2->PolyTyp == ptSubject) - { - e2FillType = m_SubjFillType; - e2FillType2 = m_ClipFillType; - } else - { - e2FillType = m_ClipFillType; - e2FillType2 = m_SubjFillType; - } - - cInt e1Wc, e2Wc; - switch (e1FillType) - { - case pftPositive: e1Wc = e1->WindCnt; break; - case pftNegative: e1Wc = -e1->WindCnt; break; - default: e1Wc = Abs(e1->WindCnt); - } - switch(e2FillType) - { - case pftPositive: e2Wc = e2->WindCnt; break; - case pftNegative: e2Wc = -e2->WindCnt; break; - default: e2Wc = Abs(e2->WindCnt); - } - - if ( e1Contributing && e2Contributing ) - { - if ((e1Wc != 0 && e1Wc != 1) || (e2Wc != 0 && e2Wc != 1) || - (e1->PolyTyp != e2->PolyTyp && m_ClipType != ctXor) ) - { - AddLocalMaxPoly(e1, e2, Pt); - } - else - { - AddOutPt(e1, Pt); - AddOutPt(e2, Pt); - SwapSides( *e1 , *e2 ); - SwapPolyIndexes( *e1 , *e2 ); - } - } - else if ( e1Contributing ) - { - if (e2Wc == 0 || e2Wc == 1) - { - AddOutPt(e1, Pt); - SwapSides(*e1, *e2); - SwapPolyIndexes(*e1, *e2); - } - } - else if ( e2Contributing ) - { - if (e1Wc == 0 || e1Wc == 1) - { - AddOutPt(e2, Pt); - SwapSides(*e1, *e2); - SwapPolyIndexes(*e1, *e2); - } - } - else if ( (e1Wc == 0 || e1Wc == 1) && (e2Wc == 0 || e2Wc == 1)) - { - //neither edge is currently contributing ... - - cInt e1Wc2, e2Wc2; - switch (e1FillType2) - { - case pftPositive: e1Wc2 = e1->WindCnt2; break; - case pftNegative : e1Wc2 = -e1->WindCnt2; break; - default: e1Wc2 = Abs(e1->WindCnt2); - } - switch (e2FillType2) - { - case pftPositive: e2Wc2 = e2->WindCnt2; break; - case pftNegative: e2Wc2 = -e2->WindCnt2; break; - default: e2Wc2 = Abs(e2->WindCnt2); - } - - if (e1->PolyTyp != e2->PolyTyp) - { - AddLocalMinPoly(e1, e2, Pt); - } - else if (e1Wc == 1 && e2Wc == 1) - switch( m_ClipType ) { - case ctIntersection: - if (e1Wc2 > 0 && e2Wc2 > 0) - AddLocalMinPoly(e1, e2, Pt); - break; - case ctUnion: - if ( e1Wc2 <= 0 && e2Wc2 <= 0 ) - AddLocalMinPoly(e1, e2, Pt); - break; - case ctDifference: - if (((e1->PolyTyp == ptClip) && (e1Wc2 > 0) && (e2Wc2 > 0)) || - ((e1->PolyTyp == ptSubject) && (e1Wc2 <= 0) && (e2Wc2 <= 0))) - AddLocalMinPoly(e1, e2, Pt); - break; - case ctXor: - AddLocalMinPoly(e1, e2, Pt); - } - else - SwapSides( *e1, *e2 ); - } -} -//------------------------------------------------------------------------------ - -void Clipper::SetHoleState(TEdge *e, OutRec *outrec) -{ - TEdge *e2 = e->PrevInAEL; - TEdge *eTmp = 0; - while (e2) - { - if (e2->OutIdx >= 0 && e2->WindDelta != 0) - { - if (!eTmp) eTmp = e2; - else if (eTmp->OutIdx == e2->OutIdx) eTmp = 0; - } - e2 = e2->PrevInAEL; - } - if (!eTmp) - { - outrec->FirstLeft = 0; - outrec->IsHole = false; - } - else - { - outrec->FirstLeft = m_PolyOuts[eTmp->OutIdx]; - outrec->IsHole = !outrec->FirstLeft->IsHole; - } -} -//------------------------------------------------------------------------------ - -OutRec* GetLowermostRec(OutRec *outRec1, OutRec *outRec2) -{ - //work out which polygon fragment has the correct hole state ... - if (!outRec1->BottomPt) - outRec1->BottomPt = GetBottomPt(outRec1->Pts); - if (!outRec2->BottomPt) - outRec2->BottomPt = GetBottomPt(outRec2->Pts); - OutPt *OutPt1 = outRec1->BottomPt; - OutPt *OutPt2 = outRec2->BottomPt; - if (OutPt1->Pt.Y > OutPt2->Pt.Y) return outRec1; - else if (OutPt1->Pt.Y < OutPt2->Pt.Y) return outRec2; - else if (OutPt1->Pt.X < OutPt2->Pt.X) return outRec1; - else if (OutPt1->Pt.X > OutPt2->Pt.X) return outRec2; - else if (OutPt1->Next == OutPt1) return outRec2; - else if (OutPt2->Next == OutPt2) return outRec1; - else if (FirstIsBottomPt(OutPt1, OutPt2)) return outRec1; - else return outRec2; -} -//------------------------------------------------------------------------------ - -bool OutRec1RightOfOutRec2(OutRec* outRec1, OutRec* outRec2) -{ - do - { - outRec1 = outRec1->FirstLeft; - if (outRec1 == outRec2) return true; - } while (outRec1); - return false; -} -//------------------------------------------------------------------------------ - -OutRec* Clipper::GetOutRec(int Idx) -{ - OutRec* outrec = m_PolyOuts[Idx]; - while (outrec != m_PolyOuts[outrec->Idx]) - outrec = m_PolyOuts[outrec->Idx]; - return outrec; -} -//------------------------------------------------------------------------------ - -void Clipper::AppendPolygon(TEdge *e1, TEdge *e2) -{ - //get the start and ends of both output polygons ... - OutRec *outRec1 = m_PolyOuts[e1->OutIdx]; - OutRec *outRec2 = m_PolyOuts[e2->OutIdx]; - - OutRec *holeStateRec; - if (OutRec1RightOfOutRec2(outRec1, outRec2)) - holeStateRec = outRec2; - else if (OutRec1RightOfOutRec2(outRec2, outRec1)) - holeStateRec = outRec1; - else - holeStateRec = GetLowermostRec(outRec1, outRec2); - - //get the start and ends of both output polygons and - //join e2 poly onto e1 poly and delete pointers to e2 ... - - OutPt* p1_lft = outRec1->Pts; - OutPt* p1_rt = p1_lft->Prev; - OutPt* p2_lft = outRec2->Pts; - OutPt* p2_rt = p2_lft->Prev; - - //join e2 poly onto e1 poly and delete pointers to e2 ... - if( e1->Side == esLeft ) - { - if( e2->Side == esLeft ) - { - //z y x a b c - ReversePolyPtLinks(p2_lft); - p2_lft->Next = p1_lft; - p1_lft->Prev = p2_lft; - p1_rt->Next = p2_rt; - p2_rt->Prev = p1_rt; - outRec1->Pts = p2_rt; - } else - { - //x y z a b c - p2_rt->Next = p1_lft; - p1_lft->Prev = p2_rt; - p2_lft->Prev = p1_rt; - p1_rt->Next = p2_lft; - outRec1->Pts = p2_lft; - } - } else - { - if( e2->Side == esRight ) - { - //a b c z y x - ReversePolyPtLinks(p2_lft); - p1_rt->Next = p2_rt; - p2_rt->Prev = p1_rt; - p2_lft->Next = p1_lft; - p1_lft->Prev = p2_lft; - } else - { - //a b c x y z - p1_rt->Next = p2_lft; - p2_lft->Prev = p1_rt; - p1_lft->Prev = p2_rt; - p2_rt->Next = p1_lft; - } - } - - outRec1->BottomPt = 0; - if (holeStateRec == outRec2) - { - if (outRec2->FirstLeft != outRec1) - outRec1->FirstLeft = outRec2->FirstLeft; - outRec1->IsHole = outRec2->IsHole; - } - outRec2->Pts = 0; - outRec2->BottomPt = 0; - outRec2->FirstLeft = outRec1; - - int OKIdx = e1->OutIdx; - int ObsoleteIdx = e2->OutIdx; - - e1->OutIdx = Unassigned; //nb: safe because we only get here via AddLocalMaxPoly - e2->OutIdx = Unassigned; - - TEdge* e = m_ActiveEdges; - while( e ) - { - if( e->OutIdx == ObsoleteIdx ) - { - e->OutIdx = OKIdx; - e->Side = e1->Side; - break; - } - e = e->NextInAEL; - } - - outRec2->Idx = outRec1->Idx; -} -//------------------------------------------------------------------------------ - -OutPt* Clipper::AddOutPt(TEdge *e, const IntPoint &pt) -{ - if( e->OutIdx < 0 ) - { - OutRec *outRec = CreateOutRec(); - outRec->IsOpen = (e->WindDelta == 0); - OutPt* newOp = new OutPt; - outRec->Pts = newOp; - newOp->Idx = outRec->Idx; - newOp->Pt = pt; - newOp->Next = newOp; - newOp->Prev = newOp; - if (!outRec->IsOpen) - SetHoleState(e, outRec); - e->OutIdx = outRec->Idx; - return newOp; - } else - { - OutRec *outRec = m_PolyOuts[e->OutIdx]; - //OutRec.Pts is the 'Left-most' point & OutRec.Pts.Prev is the 'Right-most' - OutPt* op = outRec->Pts; - - bool ToFront = (e->Side == esLeft); - if (ToFront && (pt == op->Pt)) return op; - else if (!ToFront && (pt == op->Prev->Pt)) return op->Prev; - - OutPt* newOp = new OutPt; - newOp->Idx = outRec->Idx; - newOp->Pt = pt; - newOp->Next = op; - newOp->Prev = op->Prev; - newOp->Prev->Next = newOp; - op->Prev = newOp; - if (ToFront) outRec->Pts = newOp; - return newOp; - } -} -//------------------------------------------------------------------------------ - -OutPt* Clipper::GetLastOutPt(TEdge *e) -{ - OutRec *outRec = m_PolyOuts[e->OutIdx]; - if (e->Side == esLeft) - return outRec->Pts; - else - return outRec->Pts->Prev; -} -//------------------------------------------------------------------------------ - -void Clipper::ProcessHorizontals() -{ - TEdge* horzEdge; - while (PopEdgeFromSEL(horzEdge)) - ProcessHorizontal(horzEdge); -} -//------------------------------------------------------------------------------ - -inline bool IsMinima(TEdge *e) -{ - return e && (e->Prev->NextInLML != e) && (e->Next->NextInLML != e); -} -//------------------------------------------------------------------------------ - -inline bool IsMaxima(TEdge *e, const cInt Y) -{ - return e && e->Top.Y == Y && !e->NextInLML; -} -//------------------------------------------------------------------------------ - -inline bool IsIntermediate(TEdge *e, const cInt Y) -{ - return e->Top.Y == Y && e->NextInLML; -} -//------------------------------------------------------------------------------ - -TEdge *GetMaximaPair(TEdge *e) -{ - if ((e->Next->Top == e->Top) && !e->Next->NextInLML) - return e->Next; - else if ((e->Prev->Top == e->Top) && !e->Prev->NextInLML) - return e->Prev; - else return 0; -} -//------------------------------------------------------------------------------ - -TEdge *GetMaximaPairEx(TEdge *e) -{ - //as GetMaximaPair() but returns 0 if MaxPair isn't in AEL (unless it's horizontal) - TEdge* result = GetMaximaPair(e); - if (result && (result->OutIdx == Skip || - (result->NextInAEL == result->PrevInAEL && !IsHorizontal(*result)))) return 0; - return result; -} -//------------------------------------------------------------------------------ - -void Clipper::SwapPositionsInSEL(TEdge *Edge1, TEdge *Edge2) -{ - if( !( Edge1->NextInSEL ) && !( Edge1->PrevInSEL ) ) return; - if( !( Edge2->NextInSEL ) && !( Edge2->PrevInSEL ) ) return; - - if( Edge1->NextInSEL == Edge2 ) - { - TEdge* Next = Edge2->NextInSEL; - if( Next ) Next->PrevInSEL = Edge1; - TEdge* Prev = Edge1->PrevInSEL; - if( Prev ) Prev->NextInSEL = Edge2; - Edge2->PrevInSEL = Prev; - Edge2->NextInSEL = Edge1; - Edge1->PrevInSEL = Edge2; - Edge1->NextInSEL = Next; - } - else if( Edge2->NextInSEL == Edge1 ) - { - TEdge* Next = Edge1->NextInSEL; - if( Next ) Next->PrevInSEL = Edge2; - TEdge* Prev = Edge2->PrevInSEL; - if( Prev ) Prev->NextInSEL = Edge1; - Edge1->PrevInSEL = Prev; - Edge1->NextInSEL = Edge2; - Edge2->PrevInSEL = Edge1; - Edge2->NextInSEL = Next; - } - else - { - TEdge* Next = Edge1->NextInSEL; - TEdge* Prev = Edge1->PrevInSEL; - Edge1->NextInSEL = Edge2->NextInSEL; - if( Edge1->NextInSEL ) Edge1->NextInSEL->PrevInSEL = Edge1; - Edge1->PrevInSEL = Edge2->PrevInSEL; - if( Edge1->PrevInSEL ) Edge1->PrevInSEL->NextInSEL = Edge1; - Edge2->NextInSEL = Next; - if( Edge2->NextInSEL ) Edge2->NextInSEL->PrevInSEL = Edge2; - Edge2->PrevInSEL = Prev; - if( Edge2->PrevInSEL ) Edge2->PrevInSEL->NextInSEL = Edge2; - } - - if( !Edge1->PrevInSEL ) m_SortedEdges = Edge1; - else if( !Edge2->PrevInSEL ) m_SortedEdges = Edge2; -} -//------------------------------------------------------------------------------ - -TEdge* GetNextInAEL(TEdge *e, Direction dir) -{ - return dir == dLeftToRight ? e->NextInAEL : e->PrevInAEL; -} -//------------------------------------------------------------------------------ - -void GetHorzDirection(TEdge& HorzEdge, Direction& Dir, cInt& Left, cInt& Right) -{ - if (HorzEdge.Bot.X < HorzEdge.Top.X) - { - Left = HorzEdge.Bot.X; - Right = HorzEdge.Top.X; - Dir = dLeftToRight; - } else - { - Left = HorzEdge.Top.X; - Right = HorzEdge.Bot.X; - Dir = dRightToLeft; - } -} -//------------------------------------------------------------------------ - -/******************************************************************************* -* Notes: Horizontal edges (HEs) at scanline intersections (ie at the Top or * -* Bottom of a scanbeam) are processed as if layered. The order in which HEs * -* are processed doesn't matter. HEs intersect with other HE Bot.Xs only [#] * -* (or they could intersect with Top.Xs only, ie EITHER Bot.Xs OR Top.Xs), * -* and with other non-horizontal edges [*]. Once these intersections are * -* processed, intermediate HEs then 'promote' the Edge above (NextInLML) into * -* the AEL. These 'promoted' edges may in turn intersect [%] with other HEs. * -*******************************************************************************/ - -void Clipper::ProcessHorizontal(TEdge *horzEdge) -{ - Direction dir; - cInt horzLeft, horzRight; - bool IsOpen = (horzEdge->WindDelta == 0); - - GetHorzDirection(*horzEdge, dir, horzLeft, horzRight); - - TEdge* eLastHorz = horzEdge, *eMaxPair = 0; - while (eLastHorz->NextInLML && IsHorizontal(*eLastHorz->NextInLML)) - eLastHorz = eLastHorz->NextInLML; - if (!eLastHorz->NextInLML) - eMaxPair = GetMaximaPair(eLastHorz); - - MaximaList::const_iterator maxIt; - MaximaList::const_reverse_iterator maxRit; - if (m_Maxima.size() > 0) - { - //get the first maxima in range (X) ... - if (dir == dLeftToRight) - { - maxIt = m_Maxima.begin(); - while (maxIt != m_Maxima.end() && *maxIt <= horzEdge->Bot.X) maxIt++; - if (maxIt != m_Maxima.end() && *maxIt >= eLastHorz->Top.X) - maxIt = m_Maxima.end(); - } - else - { - maxRit = m_Maxima.rbegin(); - while (maxRit != m_Maxima.rend() && *maxRit > horzEdge->Bot.X) maxRit++; - if (maxRit != m_Maxima.rend() && *maxRit <= eLastHorz->Top.X) - maxRit = m_Maxima.rend(); - } - } - - OutPt* op1 = 0; - - for (;;) //loop through consec. horizontal edges - { - - bool IsLastHorz = (horzEdge == eLastHorz); - TEdge* e = GetNextInAEL(horzEdge, dir); - while(e) - { - - //this code block inserts extra coords into horizontal edges (in output - //polygons) whereever maxima touch these horizontal edges. This helps - //'simplifying' polygons (ie if the Simplify property is set). - if (m_Maxima.size() > 0) - { - if (dir == dLeftToRight) - { - while (maxIt != m_Maxima.end() && *maxIt < e->Curr.X) - { - if (horzEdge->OutIdx >= 0 && !IsOpen) - AddOutPt(horzEdge, IntPoint(*maxIt, horzEdge->Bot.Y)); - maxIt++; - } - } - else - { - while (maxRit != m_Maxima.rend() && *maxRit > e->Curr.X) - { - if (horzEdge->OutIdx >= 0 && !IsOpen) - AddOutPt(horzEdge, IntPoint(*maxRit, horzEdge->Bot.Y)); - maxRit++; - } - } - }; - - if ((dir == dLeftToRight && e->Curr.X > horzRight) || - (dir == dRightToLeft && e->Curr.X < horzLeft)) break; - - //Also break if we've got to the end of an intermediate horizontal edge ... - //nb: Smaller Dx's are to the right of larger Dx's ABOVE the horizontal. - if (e->Curr.X == horzEdge->Top.X && horzEdge->NextInLML && - e->Dx < horzEdge->NextInLML->Dx) break; - - if (horzEdge->OutIdx >= 0 && !IsOpen) //note: may be done multiple times - { - op1 = AddOutPt(horzEdge, e->Curr); - TEdge* eNextHorz = m_SortedEdges; - while (eNextHorz) - { - if (eNextHorz->OutIdx >= 0 && - HorzSegmentsOverlap(horzEdge->Bot.X, - horzEdge->Top.X, eNextHorz->Bot.X, eNextHorz->Top.X)) - { - OutPt* op2 = GetLastOutPt(eNextHorz); - AddJoin(op2, op1, eNextHorz->Top); - } - eNextHorz = eNextHorz->NextInSEL; - } - AddGhostJoin(op1, horzEdge->Bot); - } - - //OK, so far we're still in range of the horizontal Edge but make sure - //we're at the last of consec. horizontals when matching with eMaxPair - if(e == eMaxPair && IsLastHorz) - { - if (horzEdge->OutIdx >= 0) - AddLocalMaxPoly(horzEdge, eMaxPair, horzEdge->Top); - DeleteFromAEL(horzEdge); - DeleteFromAEL(eMaxPair); - return; - } - - if(dir == dLeftToRight) - { - IntPoint Pt = IntPoint(e->Curr.X, horzEdge->Curr.Y); - IntersectEdges(horzEdge, e, Pt); - } - else - { - IntPoint Pt = IntPoint(e->Curr.X, horzEdge->Curr.Y); - IntersectEdges( e, horzEdge, Pt); - } - TEdge* eNext = GetNextInAEL(e, dir); - SwapPositionsInAEL( horzEdge, e ); - e = eNext; - } //end while(e) - - //Break out of loop if HorzEdge.NextInLML is not also horizontal ... - if (!horzEdge->NextInLML || !IsHorizontal(*horzEdge->NextInLML)) break; - - UpdateEdgeIntoAEL(horzEdge); - if (horzEdge->OutIdx >= 0) AddOutPt(horzEdge, horzEdge->Bot); - GetHorzDirection(*horzEdge, dir, horzLeft, horzRight); - - } //end for (;;) - - if (horzEdge->OutIdx >= 0 && !op1) - { - op1 = GetLastOutPt(horzEdge); - TEdge* eNextHorz = m_SortedEdges; - while (eNextHorz) - { - if (eNextHorz->OutIdx >= 0 && - HorzSegmentsOverlap(horzEdge->Bot.X, - horzEdge->Top.X, eNextHorz->Bot.X, eNextHorz->Top.X)) - { - OutPt* op2 = GetLastOutPt(eNextHorz); - AddJoin(op2, op1, eNextHorz->Top); - } - eNextHorz = eNextHorz->NextInSEL; - } - AddGhostJoin(op1, horzEdge->Top); - } - - if (horzEdge->NextInLML) - { - if(horzEdge->OutIdx >= 0) - { - op1 = AddOutPt( horzEdge, horzEdge->Top); - UpdateEdgeIntoAEL(horzEdge); - if (horzEdge->WindDelta == 0) return; - //nb: HorzEdge is no longer horizontal here - TEdge* ePrev = horzEdge->PrevInAEL; - TEdge* eNext = horzEdge->NextInAEL; - if (ePrev && ePrev->Curr.X == horzEdge->Bot.X && - ePrev->Curr.Y == horzEdge->Bot.Y && ePrev->WindDelta != 0 && - (ePrev->OutIdx >= 0 && ePrev->Curr.Y > ePrev->Top.Y && - SlopesEqual(*horzEdge, *ePrev, m_UseFullRange))) - { - OutPt* op2 = AddOutPt(ePrev, horzEdge->Bot); - AddJoin(op1, op2, horzEdge->Top); - } - else if (eNext && eNext->Curr.X == horzEdge->Bot.X && - eNext->Curr.Y == horzEdge->Bot.Y && eNext->WindDelta != 0 && - eNext->OutIdx >= 0 && eNext->Curr.Y > eNext->Top.Y && - SlopesEqual(*horzEdge, *eNext, m_UseFullRange)) - { - OutPt* op2 = AddOutPt(eNext, horzEdge->Bot); - AddJoin(op1, op2, horzEdge->Top); - } - } - else - UpdateEdgeIntoAEL(horzEdge); - } - else - { - if (horzEdge->OutIdx >= 0) AddOutPt(horzEdge, horzEdge->Top); - DeleteFromAEL(horzEdge); - } -} -//------------------------------------------------------------------------------ - -bool Clipper::ProcessIntersections(const cInt topY) -{ - if( !m_ActiveEdges ) return true; - try { - BuildIntersectList(topY); - size_t IlSize = m_IntersectList.size(); - if (IlSize == 0) return true; - if (IlSize == 1 || FixupIntersectionOrder()) ProcessIntersectList(); - else return false; - } - catch(...) - { - m_SortedEdges = 0; - DisposeIntersectNodes(); - throw clipperException("ProcessIntersections error"); - } - m_SortedEdges = 0; - return true; -} -//------------------------------------------------------------------------------ - -void Clipper::DisposeIntersectNodes() -{ - for (size_t i = 0; i < m_IntersectList.size(); ++i ) - delete m_IntersectList[i]; - m_IntersectList.clear(); -} -//------------------------------------------------------------------------------ - -void Clipper::BuildIntersectList(const cInt topY) -{ - if ( !m_ActiveEdges ) return; - - //prepare for sorting ... - TEdge* e = m_ActiveEdges; - m_SortedEdges = e; - while( e ) - { - e->PrevInSEL = e->PrevInAEL; - e->NextInSEL = e->NextInAEL; - e->Curr.X = TopX( *e, topY ); - e = e->NextInAEL; - } - - //bubblesort ... - bool isModified; - do - { - isModified = false; - e = m_SortedEdges; - while( e->NextInSEL ) - { - TEdge *eNext = e->NextInSEL; - IntPoint Pt; - if(e->Curr.X > eNext->Curr.X) - { - IntersectPoint(*e, *eNext, Pt); - if (Pt.Y < topY) Pt = IntPoint(TopX(*e, topY), topY); - IntersectNode * newNode = new IntersectNode; - newNode->Edge1 = e; - newNode->Edge2 = eNext; - newNode->Pt = Pt; - m_IntersectList.push_back(newNode); - - SwapPositionsInSEL(e, eNext); - isModified = true; - } - else - e = eNext; - } - if( e->PrevInSEL ) e->PrevInSEL->NextInSEL = 0; - else break; - } - while ( isModified ); - m_SortedEdges = 0; //important -} -//------------------------------------------------------------------------------ - - -void Clipper::ProcessIntersectList() -{ - for (size_t i = 0; i < m_IntersectList.size(); ++i) - { - IntersectNode* iNode = m_IntersectList[i]; - { - IntersectEdges( iNode->Edge1, iNode->Edge2, iNode->Pt); - SwapPositionsInAEL( iNode->Edge1 , iNode->Edge2 ); - } - delete iNode; - } - m_IntersectList.clear(); -} -//------------------------------------------------------------------------------ - -bool IntersectListSort(IntersectNode* node1, IntersectNode* node2) -{ - return node2->Pt.Y < node1->Pt.Y; -} -//------------------------------------------------------------------------------ - -inline bool EdgesAdjacent(const IntersectNode &inode) -{ - return (inode.Edge1->NextInSEL == inode.Edge2) || - (inode.Edge1->PrevInSEL == inode.Edge2); -} -//------------------------------------------------------------------------------ - -bool Clipper::FixupIntersectionOrder() -{ - //pre-condition: intersections are sorted Bottom-most first. - //Now it's crucial that intersections are made only between adjacent edges, - //so to ensure this the order of intersections may need adjusting ... - CopyAELToSEL(); - std::sort(m_IntersectList.begin(), m_IntersectList.end(), IntersectListSort); - size_t cnt = m_IntersectList.size(); - for (size_t i = 0; i < cnt; ++i) - { - if (!EdgesAdjacent(*m_IntersectList[i])) - { - size_t j = i + 1; - while (j < cnt && !EdgesAdjacent(*m_IntersectList[j])) j++; - if (j == cnt) return false; - std::swap(m_IntersectList[i], m_IntersectList[j]); - } - SwapPositionsInSEL(m_IntersectList[i]->Edge1, m_IntersectList[i]->Edge2); - } - return true; -} -//------------------------------------------------------------------------------ - -void Clipper::DoMaxima(TEdge *e) -{ - TEdge* eMaxPair = GetMaximaPairEx(e); - if (!eMaxPair) - { - if (e->OutIdx >= 0) - AddOutPt(e, e->Top); - DeleteFromAEL(e); - return; - } - - TEdge* eNext = e->NextInAEL; - while(eNext && eNext != eMaxPair) - { - IntersectEdges(e, eNext, e->Top); - SwapPositionsInAEL(e, eNext); - eNext = e->NextInAEL; - } - - if(e->OutIdx == Unassigned && eMaxPair->OutIdx == Unassigned) - { - DeleteFromAEL(e); - DeleteFromAEL(eMaxPair); - } - else if( e->OutIdx >= 0 && eMaxPair->OutIdx >= 0 ) - { - if (e->OutIdx >= 0) AddLocalMaxPoly(e, eMaxPair, e->Top); - DeleteFromAEL(e); - DeleteFromAEL(eMaxPair); - } -#ifdef use_lines - else if (e->WindDelta == 0) - { - if (e->OutIdx >= 0) - { - AddOutPt(e, e->Top); - e->OutIdx = Unassigned; - } - DeleteFromAEL(e); - - if (eMaxPair->OutIdx >= 0) - { - AddOutPt(eMaxPair, e->Top); - eMaxPair->OutIdx = Unassigned; - } - DeleteFromAEL(eMaxPair); - } -#endif - else throw clipperException("DoMaxima error"); -} -//------------------------------------------------------------------------------ - -void Clipper::ProcessEdgesAtTopOfScanbeam(const cInt topY) -{ - TEdge* e = m_ActiveEdges; - while( e ) - { - //1. process maxima, treating them as if they're 'bent' horizontal edges, - // but exclude maxima with horizontal edges. nb: e can't be a horizontal. - bool IsMaximaEdge = IsMaxima(e, topY); - - if(IsMaximaEdge) - { - TEdge* eMaxPair = GetMaximaPairEx(e); - IsMaximaEdge = (!eMaxPair || !IsHorizontal(*eMaxPair)); - } - - if(IsMaximaEdge) - { - if (m_StrictSimple) m_Maxima.push_back(e->Top.X); - TEdge* ePrev = e->PrevInAEL; - DoMaxima(e); - if( !ePrev ) e = m_ActiveEdges; - else e = ePrev->NextInAEL; - } - else - { - //2. promote horizontal edges, otherwise update Curr.X and Curr.Y ... - if (IsIntermediate(e, topY) && IsHorizontal(*e->NextInLML)) - { - UpdateEdgeIntoAEL(e); - if (e->OutIdx >= 0) - AddOutPt(e, e->Bot); - AddEdgeToSEL(e); - } - else - { - e->Curr.X = TopX( *e, topY ); - e->Curr.Y = topY; - } - - //When StrictlySimple and 'e' is being touched by another edge, then - //make sure both edges have a vertex here ... - if (m_StrictSimple) - { - TEdge* ePrev = e->PrevInAEL; - if ((e->OutIdx >= 0) && (e->WindDelta != 0) && ePrev && (ePrev->OutIdx >= 0) && - (ePrev->Curr.X == e->Curr.X) && (ePrev->WindDelta != 0)) - { - IntPoint pt = e->Curr; -#ifdef use_xyz - SetZ(pt, *ePrev, *e); -#endif - OutPt* op = AddOutPt(ePrev, pt); - OutPt* op2 = AddOutPt(e, pt); - AddJoin(op, op2, pt); //StrictlySimple (type-3) join - } - } - - e = e->NextInAEL; - } - } - - //3. Process horizontals at the Top of the scanbeam ... - m_Maxima.sort(); - ProcessHorizontals(); - m_Maxima.clear(); - - //4. Promote intermediate vertices ... - e = m_ActiveEdges; - while(e) - { - if(IsIntermediate(e, topY)) - { - OutPt* op = 0; - if( e->OutIdx >= 0 ) - op = AddOutPt(e, e->Top); - UpdateEdgeIntoAEL(e); - - //if output polygons share an edge, they'll need joining later ... - TEdge* ePrev = e->PrevInAEL; - TEdge* eNext = e->NextInAEL; - if (ePrev && ePrev->Curr.X == e->Bot.X && - ePrev->Curr.Y == e->Bot.Y && op && - ePrev->OutIdx >= 0 && ePrev->Curr.Y > ePrev->Top.Y && - SlopesEqual(e->Curr, e->Top, ePrev->Curr, ePrev->Top, m_UseFullRange) && - (e->WindDelta != 0) && (ePrev->WindDelta != 0)) - { - OutPt* op2 = AddOutPt(ePrev, e->Bot); - AddJoin(op, op2, e->Top); - } - else if (eNext && eNext->Curr.X == e->Bot.X && - eNext->Curr.Y == e->Bot.Y && op && - eNext->OutIdx >= 0 && eNext->Curr.Y > eNext->Top.Y && - SlopesEqual(e->Curr, e->Top, eNext->Curr, eNext->Top, m_UseFullRange) && - (e->WindDelta != 0) && (eNext->WindDelta != 0)) - { - OutPt* op2 = AddOutPt(eNext, e->Bot); - AddJoin(op, op2, e->Top); - } - } - e = e->NextInAEL; - } -} -//------------------------------------------------------------------------------ - -void Clipper::FixupOutPolyline(OutRec &outrec) -{ - OutPt *pp = outrec.Pts; - OutPt *lastPP = pp->Prev; - while (pp != lastPP) - { - pp = pp->Next; - if (pp->Pt == pp->Prev->Pt) - { - if (pp == lastPP) lastPP = pp->Prev; - OutPt *tmpPP = pp->Prev; - tmpPP->Next = pp->Next; - pp->Next->Prev = tmpPP; - delete pp; - pp = tmpPP; - } - } - - if (pp == pp->Prev) - { - DisposeOutPts(pp); - outrec.Pts = 0; - return; - } -} -//------------------------------------------------------------------------------ - -void Clipper::FixupOutPolygon(OutRec &outrec) -{ - //FixupOutPolygon() - removes duplicate points and simplifies consecutive - //parallel edges by removing the middle vertex. - OutPt *lastOK = 0; - outrec.BottomPt = 0; - OutPt *pp = outrec.Pts; - bool preserveCol = m_PreserveCollinear || m_StrictSimple; - - for (;;) - { - if (pp->Prev == pp || pp->Prev == pp->Next) - { - DisposeOutPts(pp); - outrec.Pts = 0; - return; - } - - //test for duplicate points and collinear edges ... - if ((pp->Pt == pp->Next->Pt) || (pp->Pt == pp->Prev->Pt) || - (SlopesEqual(pp->Prev->Pt, pp->Pt, pp->Next->Pt, m_UseFullRange) && - (!preserveCol || !Pt2IsBetweenPt1AndPt3(pp->Prev->Pt, pp->Pt, pp->Next->Pt)))) - { - lastOK = 0; - OutPt *tmp = pp; - pp->Prev->Next = pp->Next; - pp->Next->Prev = pp->Prev; - pp = pp->Prev; - delete tmp; - } - else if (pp == lastOK) break; - else - { - if (!lastOK) lastOK = pp; - pp = pp->Next; - } - } - outrec.Pts = pp; -} -//------------------------------------------------------------------------------ - -int PointCount(OutPt *Pts) -{ - if (!Pts) return 0; - int result = 0; - OutPt* p = Pts; - do - { - result++; - p = p->Next; - } - while (p != Pts); - return result; -} -//------------------------------------------------------------------------------ - -void Clipper::BuildResult(Paths &polys) -{ - polys.reserve(m_PolyOuts.size()); - for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) - { - if (!m_PolyOuts[i]->Pts) continue; - Path pg; - OutPt* p = m_PolyOuts[i]->Pts->Prev; - int cnt = PointCount(p); - if (cnt < 2) continue; - pg.reserve(cnt); - for (int i = 0; i < cnt; ++i) - { - pg.push_back(p->Pt); - p = p->Prev; - } - polys.push_back(pg); - } -} -//------------------------------------------------------------------------------ - -void Clipper::BuildResult2(PolyTree& polytree) -{ - polytree.Clear(); - polytree.AllNodes.reserve(m_PolyOuts.size()); - //add each output polygon/contour to polytree ... - for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); i++) - { - OutRec* outRec = m_PolyOuts[i]; - int cnt = PointCount(outRec->Pts); - if ((outRec->IsOpen && cnt < 2) || (!outRec->IsOpen && cnt < 3)) continue; - FixHoleLinkage(*outRec); - PolyNode* pn = new PolyNode(); - //nb: polytree takes ownership of all the PolyNodes - polytree.AllNodes.push_back(pn); - outRec->PolyNd = pn; - pn->Parent = 0; - pn->Index = 0; - pn->Contour.reserve(cnt); - OutPt *op = outRec->Pts->Prev; - for (int j = 0; j < cnt; j++) - { - pn->Contour.push_back(op->Pt); - op = op->Prev; - } - } - - //fixup PolyNode links etc ... - polytree.Childs.reserve(m_PolyOuts.size()); - for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); i++) - { - OutRec* outRec = m_PolyOuts[i]; - if (!outRec->PolyNd) continue; - if (outRec->IsOpen) - { - outRec->PolyNd->m_IsOpen = true; - polytree.AddChild(*outRec->PolyNd); - } - else if (outRec->FirstLeft && outRec->FirstLeft->PolyNd) - outRec->FirstLeft->PolyNd->AddChild(*outRec->PolyNd); - else - polytree.AddChild(*outRec->PolyNd); - } -} -//------------------------------------------------------------------------------ - -void SwapIntersectNodes(IntersectNode &int1, IntersectNode &int2) -{ - //just swap the contents (because fIntersectNodes is a single-linked-list) - IntersectNode inode = int1; //gets a copy of Int1 - int1.Edge1 = int2.Edge1; - int1.Edge2 = int2.Edge2; - int1.Pt = int2.Pt; - int2.Edge1 = inode.Edge1; - int2.Edge2 = inode.Edge2; - int2.Pt = inode.Pt; -} -//------------------------------------------------------------------------------ - -inline bool E2InsertsBeforeE1(TEdge &e1, TEdge &e2) -{ - if (e2.Curr.X == e1.Curr.X) - { - if (e2.Top.Y > e1.Top.Y) - return e2.Top.X < TopX(e1, e2.Top.Y); - else return e1.Top.X > TopX(e2, e1.Top.Y); - } - else return e2.Curr.X < e1.Curr.X; -} -//------------------------------------------------------------------------------ - -bool GetOverlap(const cInt a1, const cInt a2, const cInt b1, const cInt b2, - cInt& Left, cInt& Right) -{ - if (a1 < a2) - { - if (b1 < b2) {Left = std::max(a1,b1); Right = std::min(a2,b2);} - else {Left = std::max(a1,b2); Right = std::min(a2,b1);} - } - else - { - if (b1 < b2) {Left = std::max(a2,b1); Right = std::min(a1,b2);} - else {Left = std::max(a2,b2); Right = std::min(a1,b1);} - } - return Left < Right; -} -//------------------------------------------------------------------------------ - -inline void UpdateOutPtIdxs(OutRec& outrec) -{ - OutPt* op = outrec.Pts; - do - { - op->Idx = outrec.Idx; - op = op->Prev; - } - while(op != outrec.Pts); -} -//------------------------------------------------------------------------------ - -void Clipper::InsertEdgeIntoAEL(TEdge *edge, TEdge* startEdge) -{ - if(!m_ActiveEdges) - { - edge->PrevInAEL = 0; - edge->NextInAEL = 0; - m_ActiveEdges = edge; - } - else if(!startEdge && E2InsertsBeforeE1(*m_ActiveEdges, *edge)) - { - edge->PrevInAEL = 0; - edge->NextInAEL = m_ActiveEdges; - m_ActiveEdges->PrevInAEL = edge; - m_ActiveEdges = edge; - } - else - { - if(!startEdge) startEdge = m_ActiveEdges; - while(startEdge->NextInAEL && - !E2InsertsBeforeE1(*startEdge->NextInAEL , *edge)) - startEdge = startEdge->NextInAEL; - edge->NextInAEL = startEdge->NextInAEL; - if(startEdge->NextInAEL) startEdge->NextInAEL->PrevInAEL = edge; - edge->PrevInAEL = startEdge; - startEdge->NextInAEL = edge; - } -} -//---------------------------------------------------------------------- - -OutPt* DupOutPt(OutPt* outPt, bool InsertAfter) -{ - OutPt* result = new OutPt; - result->Pt = outPt->Pt; - result->Idx = outPt->Idx; - if (InsertAfter) - { - result->Next = outPt->Next; - result->Prev = outPt; - outPt->Next->Prev = result; - outPt->Next = result; - } - else - { - result->Prev = outPt->Prev; - result->Next = outPt; - outPt->Prev->Next = result; - outPt->Prev = result; - } - return result; -} -//------------------------------------------------------------------------------ - -bool JoinHorz(OutPt* op1, OutPt* op1b, OutPt* op2, OutPt* op2b, - const IntPoint Pt, bool DiscardLeft) -{ - Direction Dir1 = (op1->Pt.X > op1b->Pt.X ? dRightToLeft : dLeftToRight); - Direction Dir2 = (op2->Pt.X > op2b->Pt.X ? dRightToLeft : dLeftToRight); - if (Dir1 == Dir2) return false; - - //When DiscardLeft, we want Op1b to be on the Left of Op1, otherwise we - //want Op1b to be on the Right. (And likewise with Op2 and Op2b.) - //So, to facilitate this while inserting Op1b and Op2b ... - //when DiscardLeft, make sure we're AT or RIGHT of Pt before adding Op1b, - //otherwise make sure we're AT or LEFT of Pt. (Likewise with Op2b.) - if (Dir1 == dLeftToRight) - { - while (op1->Next->Pt.X <= Pt.X && - op1->Next->Pt.X >= op1->Pt.X && op1->Next->Pt.Y == Pt.Y) - op1 = op1->Next; - if (DiscardLeft && (op1->Pt.X != Pt.X)) op1 = op1->Next; - op1b = DupOutPt(op1, !DiscardLeft); - if (op1b->Pt != Pt) - { - op1 = op1b; - op1->Pt = Pt; - op1b = DupOutPt(op1, !DiscardLeft); - } - } - else - { - while (op1->Next->Pt.X >= Pt.X && - op1->Next->Pt.X <= op1->Pt.X && op1->Next->Pt.Y == Pt.Y) - op1 = op1->Next; - if (!DiscardLeft && (op1->Pt.X != Pt.X)) op1 = op1->Next; - op1b = DupOutPt(op1, DiscardLeft); - if (op1b->Pt != Pt) - { - op1 = op1b; - op1->Pt = Pt; - op1b = DupOutPt(op1, DiscardLeft); - } - } - - if (Dir2 == dLeftToRight) - { - while (op2->Next->Pt.X <= Pt.X && - op2->Next->Pt.X >= op2->Pt.X && op2->Next->Pt.Y == Pt.Y) - op2 = op2->Next; - if (DiscardLeft && (op2->Pt.X != Pt.X)) op2 = op2->Next; - op2b = DupOutPt(op2, !DiscardLeft); - if (op2b->Pt != Pt) - { - op2 = op2b; - op2->Pt = Pt; - op2b = DupOutPt(op2, !DiscardLeft); - }; - } else - { - while (op2->Next->Pt.X >= Pt.X && - op2->Next->Pt.X <= op2->Pt.X && op2->Next->Pt.Y == Pt.Y) - op2 = op2->Next; - if (!DiscardLeft && (op2->Pt.X != Pt.X)) op2 = op2->Next; - op2b = DupOutPt(op2, DiscardLeft); - if (op2b->Pt != Pt) - { - op2 = op2b; - op2->Pt = Pt; - op2b = DupOutPt(op2, DiscardLeft); - }; - }; - - if ((Dir1 == dLeftToRight) == DiscardLeft) - { - op1->Prev = op2; - op2->Next = op1; - op1b->Next = op2b; - op2b->Prev = op1b; - } - else - { - op1->Next = op2; - op2->Prev = op1; - op1b->Prev = op2b; - op2b->Next = op1b; - } - return true; -} -//------------------------------------------------------------------------------ - -bool Clipper::JoinPoints(Join *j, OutRec* outRec1, OutRec* outRec2) -{ - OutPt *op1 = j->OutPt1, *op1b; - OutPt *op2 = j->OutPt2, *op2b; - - //There are 3 kinds of joins for output polygons ... - //1. Horizontal joins where Join.OutPt1 & Join.OutPt2 are vertices anywhere - //along (horizontal) collinear edges (& Join.OffPt is on the same horizontal). - //2. Non-horizontal joins where Join.OutPt1 & Join.OutPt2 are at the same - //location at the Bottom of the overlapping segment (& Join.OffPt is above). - //3. StrictSimple joins where edges touch but are not collinear and where - //Join.OutPt1, Join.OutPt2 & Join.OffPt all share the same point. - bool isHorizontal = (j->OutPt1->Pt.Y == j->OffPt.Y); - - if (isHorizontal && (j->OffPt == j->OutPt1->Pt) && - (j->OffPt == j->OutPt2->Pt)) - { - //Strictly Simple join ... - if (outRec1 != outRec2) return false; - op1b = j->OutPt1->Next; - while (op1b != op1 && (op1b->Pt == j->OffPt)) - op1b = op1b->Next; - bool reverse1 = (op1b->Pt.Y > j->OffPt.Y); - op2b = j->OutPt2->Next; - while (op2b != op2 && (op2b->Pt == j->OffPt)) - op2b = op2b->Next; - bool reverse2 = (op2b->Pt.Y > j->OffPt.Y); - if (reverse1 == reverse2) return false; - if (reverse1) - { - op1b = DupOutPt(op1, false); - op2b = DupOutPt(op2, true); - op1->Prev = op2; - op2->Next = op1; - op1b->Next = op2b; - op2b->Prev = op1b; - j->OutPt1 = op1; - j->OutPt2 = op1b; - return true; - } else - { - op1b = DupOutPt(op1, true); - op2b = DupOutPt(op2, false); - op1->Next = op2; - op2->Prev = op1; - op1b->Prev = op2b; - op2b->Next = op1b; - j->OutPt1 = op1; - j->OutPt2 = op1b; - return true; - } - } - else if (isHorizontal) - { - //treat horizontal joins differently to non-horizontal joins since with - //them we're not yet sure where the overlapping is. OutPt1.Pt & OutPt2.Pt - //may be anywhere along the horizontal edge. - op1b = op1; - while (op1->Prev->Pt.Y == op1->Pt.Y && op1->Prev != op1b && op1->Prev != op2) - op1 = op1->Prev; - while (op1b->Next->Pt.Y == op1b->Pt.Y && op1b->Next != op1 && op1b->Next != op2) - op1b = op1b->Next; - if (op1b->Next == op1 || op1b->Next == op2) return false; //a flat 'polygon' - - op2b = op2; - while (op2->Prev->Pt.Y == op2->Pt.Y && op2->Prev != op2b && op2->Prev != op1b) - op2 = op2->Prev; - while (op2b->Next->Pt.Y == op2b->Pt.Y && op2b->Next != op2 && op2b->Next != op1) - op2b = op2b->Next; - if (op2b->Next == op2 || op2b->Next == op1) return false; //a flat 'polygon' - - cInt Left, Right; - //Op1 --> Op1b & Op2 --> Op2b are the extremites of the horizontal edges - if (!GetOverlap(op1->Pt.X, op1b->Pt.X, op2->Pt.X, op2b->Pt.X, Left, Right)) - return false; - - //DiscardLeftSide: when overlapping edges are joined, a spike will created - //which needs to be cleaned up. However, we don't want Op1 or Op2 caught up - //on the discard Side as either may still be needed for other joins ... - IntPoint Pt; - bool DiscardLeftSide; - if (op1->Pt.X >= Left && op1->Pt.X <= Right) - { - Pt = op1->Pt; DiscardLeftSide = (op1->Pt.X > op1b->Pt.X); - } - else if (op2->Pt.X >= Left&& op2->Pt.X <= Right) - { - Pt = op2->Pt; DiscardLeftSide = (op2->Pt.X > op2b->Pt.X); - } - else if (op1b->Pt.X >= Left && op1b->Pt.X <= Right) - { - Pt = op1b->Pt; DiscardLeftSide = op1b->Pt.X > op1->Pt.X; - } - else - { - Pt = op2b->Pt; DiscardLeftSide = (op2b->Pt.X > op2->Pt.X); - } - j->OutPt1 = op1; j->OutPt2 = op2; - return JoinHorz(op1, op1b, op2, op2b, Pt, DiscardLeftSide); - } else - { - //nb: For non-horizontal joins ... - // 1. Jr.OutPt1.Pt.Y == Jr.OutPt2.Pt.Y - // 2. Jr.OutPt1.Pt > Jr.OffPt.Y - - //make sure the polygons are correctly oriented ... - op1b = op1->Next; - while ((op1b->Pt == op1->Pt) && (op1b != op1)) op1b = op1b->Next; - bool Reverse1 = ((op1b->Pt.Y > op1->Pt.Y) || - !SlopesEqual(op1->Pt, op1b->Pt, j->OffPt, m_UseFullRange)); - if (Reverse1) - { - op1b = op1->Prev; - while ((op1b->Pt == op1->Pt) && (op1b != op1)) op1b = op1b->Prev; - if ((op1b->Pt.Y > op1->Pt.Y) || - !SlopesEqual(op1->Pt, op1b->Pt, j->OffPt, m_UseFullRange)) return false; - }; - op2b = op2->Next; - while ((op2b->Pt == op2->Pt) && (op2b != op2))op2b = op2b->Next; - bool Reverse2 = ((op2b->Pt.Y > op2->Pt.Y) || - !SlopesEqual(op2->Pt, op2b->Pt, j->OffPt, m_UseFullRange)); - if (Reverse2) - { - op2b = op2->Prev; - while ((op2b->Pt == op2->Pt) && (op2b != op2)) op2b = op2b->Prev; - if ((op2b->Pt.Y > op2->Pt.Y) || - !SlopesEqual(op2->Pt, op2b->Pt, j->OffPt, m_UseFullRange)) return false; - } - - if ((op1b == op1) || (op2b == op2) || (op1b == op2b) || - ((outRec1 == outRec2) && (Reverse1 == Reverse2))) return false; - - if (Reverse1) - { - op1b = DupOutPt(op1, false); - op2b = DupOutPt(op2, true); - op1->Prev = op2; - op2->Next = op1; - op1b->Next = op2b; - op2b->Prev = op1b; - j->OutPt1 = op1; - j->OutPt2 = op1b; - return true; - } else - { - op1b = DupOutPt(op1, true); - op2b = DupOutPt(op2, false); - op1->Next = op2; - op2->Prev = op1; - op1b->Prev = op2b; - op2b->Next = op1b; - j->OutPt1 = op1; - j->OutPt2 = op1b; - return true; - } - } -} -//---------------------------------------------------------------------- - -static OutRec* ParseFirstLeft(OutRec* FirstLeft) -{ - while (FirstLeft && !FirstLeft->Pts) - FirstLeft = FirstLeft->FirstLeft; - return FirstLeft; -} -//------------------------------------------------------------------------------ - -void Clipper::FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec) -{ - //tests if NewOutRec contains the polygon before reassigning FirstLeft - for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) - { - OutRec* outRec = m_PolyOuts[i]; - OutRec* firstLeft = ParseFirstLeft(outRec->FirstLeft); - if (outRec->Pts && firstLeft == OldOutRec) - { - if (Poly2ContainsPoly1(outRec->Pts, NewOutRec->Pts)) - outRec->FirstLeft = NewOutRec; - } - } -} -//---------------------------------------------------------------------- - -void Clipper::FixupFirstLefts2(OutRec* InnerOutRec, OutRec* OuterOutRec) -{ - //A polygon has split into two such that one is now the inner of the other. - //It's possible that these polygons now wrap around other polygons, so check - //every polygon that's also contained by OuterOutRec's FirstLeft container - //(including 0) to see if they've become inner to the new inner polygon ... - OutRec* orfl = OuterOutRec->FirstLeft; - for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) - { - OutRec* outRec = m_PolyOuts[i]; - - if (!outRec->Pts || outRec == OuterOutRec || outRec == InnerOutRec) - continue; - OutRec* firstLeft = ParseFirstLeft(outRec->FirstLeft); - if (firstLeft != orfl && firstLeft != InnerOutRec && firstLeft != OuterOutRec) - continue; - if (Poly2ContainsPoly1(outRec->Pts, InnerOutRec->Pts)) - outRec->FirstLeft = InnerOutRec; - else if (Poly2ContainsPoly1(outRec->Pts, OuterOutRec->Pts)) - outRec->FirstLeft = OuterOutRec; - else if (outRec->FirstLeft == InnerOutRec || outRec->FirstLeft == OuterOutRec) - outRec->FirstLeft = orfl; - } -} -//---------------------------------------------------------------------- -void Clipper::FixupFirstLefts3(OutRec* OldOutRec, OutRec* NewOutRec) -{ - //reassigns FirstLeft WITHOUT testing if NewOutRec contains the polygon - for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) - { - OutRec* outRec = m_PolyOuts[i]; - OutRec* firstLeft = ParseFirstLeft(outRec->FirstLeft); - if (outRec->Pts && outRec->FirstLeft == OldOutRec) - outRec->FirstLeft = NewOutRec; - } -} -//---------------------------------------------------------------------- - -void Clipper::JoinCommonEdges() -{ - for (JoinList::size_type i = 0; i < m_Joins.size(); i++) - { - Join* join = m_Joins[i]; - - OutRec *outRec1 = GetOutRec(join->OutPt1->Idx); - OutRec *outRec2 = GetOutRec(join->OutPt2->Idx); - - if (!outRec1->Pts || !outRec2->Pts) continue; - if (outRec1->IsOpen || outRec2->IsOpen) continue; - - //get the polygon fragment with the correct hole state (FirstLeft) - //before calling JoinPoints() ... - OutRec *holeStateRec; - if (outRec1 == outRec2) holeStateRec = outRec1; - else if (OutRec1RightOfOutRec2(outRec1, outRec2)) holeStateRec = outRec2; - else if (OutRec1RightOfOutRec2(outRec2, outRec1)) holeStateRec = outRec1; - else holeStateRec = GetLowermostRec(outRec1, outRec2); - - if (!JoinPoints(join, outRec1, outRec2)) continue; - - if (outRec1 == outRec2) - { - //instead of joining two polygons, we've just created a new one by - //splitting one polygon into two. - outRec1->Pts = join->OutPt1; - outRec1->BottomPt = 0; - outRec2 = CreateOutRec(); - outRec2->Pts = join->OutPt2; - - //update all OutRec2.Pts Idx's ... - UpdateOutPtIdxs(*outRec2); - - if (Poly2ContainsPoly1(outRec2->Pts, outRec1->Pts)) - { - //outRec1 contains outRec2 ... - outRec2->IsHole = !outRec1->IsHole; - outRec2->FirstLeft = outRec1; - - if (m_UsingPolyTree) FixupFirstLefts2(outRec2, outRec1); - - if ((outRec2->IsHole ^ m_ReverseOutput) == (Area(*outRec2) > 0)) - ReversePolyPtLinks(outRec2->Pts); - - } else if (Poly2ContainsPoly1(outRec1->Pts, outRec2->Pts)) - { - //outRec2 contains outRec1 ... - outRec2->IsHole = outRec1->IsHole; - outRec1->IsHole = !outRec2->IsHole; - outRec2->FirstLeft = outRec1->FirstLeft; - outRec1->FirstLeft = outRec2; - - if (m_UsingPolyTree) FixupFirstLefts2(outRec1, outRec2); - - if ((outRec1->IsHole ^ m_ReverseOutput) == (Area(*outRec1) > 0)) - ReversePolyPtLinks(outRec1->Pts); - } - else - { - //the 2 polygons are completely separate ... - outRec2->IsHole = outRec1->IsHole; - outRec2->FirstLeft = outRec1->FirstLeft; - - //fixup FirstLeft pointers that may need reassigning to OutRec2 - if (m_UsingPolyTree) FixupFirstLefts1(outRec1, outRec2); - } - - } else - { - //joined 2 polygons together ... - - outRec2->Pts = 0; - outRec2->BottomPt = 0; - outRec2->Idx = outRec1->Idx; - - outRec1->IsHole = holeStateRec->IsHole; - if (holeStateRec == outRec2) - outRec1->FirstLeft = outRec2->FirstLeft; - outRec2->FirstLeft = outRec1; - - if (m_UsingPolyTree) FixupFirstLefts3(outRec2, outRec1); - } - } -} - -//------------------------------------------------------------------------------ -// ClipperOffset support functions ... -//------------------------------------------------------------------------------ - -DoublePoint GetUnitNormal(const IntPoint &pt1, const IntPoint &pt2) -{ - if(pt2.X == pt1.X && pt2.Y == pt1.Y) - return DoublePoint(0, 0); - - double Dx = (double)(pt2.X - pt1.X); - double dy = (double)(pt2.Y - pt1.Y); - double f = 1 *1.0/ std::sqrt( Dx*Dx + dy*dy ); - Dx *= f; - dy *= f; - return DoublePoint(dy, -Dx); -} - -//------------------------------------------------------------------------------ -// ClipperOffset class -//------------------------------------------------------------------------------ - -ClipperOffset::ClipperOffset(double miterLimit, double arcTolerance) -{ - this->MiterLimit = miterLimit; - this->ArcTolerance = arcTolerance; - m_lowest.X = -1; -} -//------------------------------------------------------------------------------ - -ClipperOffset::~ClipperOffset() -{ - Clear(); -} -//------------------------------------------------------------------------------ - -void ClipperOffset::Clear() -{ - for (int i = 0; i < m_polyNodes.ChildCount(); ++i) - delete m_polyNodes.Childs[i]; - m_polyNodes.Childs.clear(); - m_lowest.X = -1; -} -//------------------------------------------------------------------------------ - -void ClipperOffset::AddPath(const Path& path, JoinType joinType, EndType endType) -{ - int highI = (int)path.size() - 1; - if (highI < 0) return; - PolyNode* newNode = new PolyNode(); - newNode->m_jointype = joinType; - newNode->m_endtype = endType; - - //strip duplicate points from path and also get index to the lowest point ... - if (endType == etClosedLine || endType == etClosedPolygon) - while (highI > 0 && path[0] == path[highI]) highI--; - newNode->Contour.reserve(highI + 1); - newNode->Contour.push_back(path[0]); - int j = 0, k = 0; - for (int i = 1; i <= highI; i++) - if (newNode->Contour[j] != path[i]) - { - j++; - newNode->Contour.push_back(path[i]); - if (path[i].Y > newNode->Contour[k].Y || - (path[i].Y == newNode->Contour[k].Y && - path[i].X < newNode->Contour[k].X)) k = j; - } - if (endType == etClosedPolygon && j < 2) - { - delete newNode; - return; - } - m_polyNodes.AddChild(*newNode); - - //if this path's lowest pt is lower than all the others then update m_lowest - if (endType != etClosedPolygon) return; - if (m_lowest.X < 0) - m_lowest = IntPoint(m_polyNodes.ChildCount() - 1, k); - else - { - IntPoint ip = m_polyNodes.Childs[(int)m_lowest.X]->Contour[(int)m_lowest.Y]; - if (newNode->Contour[k].Y > ip.Y || - (newNode->Contour[k].Y == ip.Y && - newNode->Contour[k].X < ip.X)) - m_lowest = IntPoint(m_polyNodes.ChildCount() - 1, k); - } -} -//------------------------------------------------------------------------------ - -void ClipperOffset::AddPaths(const Paths& paths, JoinType joinType, EndType endType) -{ - for (Paths::size_type i = 0; i < paths.size(); ++i) - AddPath(paths[i], joinType, endType); -} -//------------------------------------------------------------------------------ - -void ClipperOffset::FixOrientations() -{ - //fixup orientations of all closed paths if the orientation of the - //closed path with the lowermost vertex is wrong ... - if (m_lowest.X >= 0 && - !Orientation(m_polyNodes.Childs[(int)m_lowest.X]->Contour)) - { - for (int i = 0; i < m_polyNodes.ChildCount(); ++i) - { - PolyNode& node = *m_polyNodes.Childs[i]; - if (node.m_endtype == etClosedPolygon || - (node.m_endtype == etClosedLine && Orientation(node.Contour))) - ReversePath(node.Contour); - } - } else - { - for (int i = 0; i < m_polyNodes.ChildCount(); ++i) - { - PolyNode& node = *m_polyNodes.Childs[i]; - if (node.m_endtype == etClosedLine && !Orientation(node.Contour)) - ReversePath(node.Contour); - } - } -} -//------------------------------------------------------------------------------ - -void ClipperOffset::Execute(Paths& solution, double delta) -{ - solution.clear(); - FixOrientations(); - DoOffset(delta); - - //now clean up 'corners' ... - Clipper clpr; - clpr.AddPaths(m_destPolys, ptSubject, true); - if (delta > 0) - { - clpr.Execute(ctUnion, solution, pftPositive, pftPositive); - } - else - { - IntRect r = clpr.GetBounds(); - Path outer(4); - outer[0] = IntPoint(r.left - 10, r.bottom + 10); - outer[1] = IntPoint(r.right + 10, r.bottom + 10); - outer[2] = IntPoint(r.right + 10, r.top - 10); - outer[3] = IntPoint(r.left - 10, r.top - 10); - - clpr.AddPath(outer, ptSubject, true); - clpr.ReverseSolution(true); - clpr.Execute(ctUnion, solution, pftNegative, pftNegative); - if (solution.size() > 0) solution.erase(solution.begin()); - } -} -//------------------------------------------------------------------------------ - -void ClipperOffset::Execute(PolyTree& solution, double delta) -{ - solution.Clear(); - FixOrientations(); - DoOffset(delta); - - //now clean up 'corners' ... - Clipper clpr; - clpr.AddPaths(m_destPolys, ptSubject, true); - if (delta > 0) - { - clpr.Execute(ctUnion, solution, pftPositive, pftPositive); - } - else - { - IntRect r = clpr.GetBounds(); - Path outer(4); - outer[0] = IntPoint(r.left - 10, r.bottom + 10); - outer[1] = IntPoint(r.right + 10, r.bottom + 10); - outer[2] = IntPoint(r.right + 10, r.top - 10); - outer[3] = IntPoint(r.left - 10, r.top - 10); - - clpr.AddPath(outer, ptSubject, true); - clpr.ReverseSolution(true); - clpr.Execute(ctUnion, solution, pftNegative, pftNegative); - //remove the outer PolyNode rectangle ... - if (solution.ChildCount() == 1 && solution.Childs[0]->ChildCount() > 0) - { - PolyNode* outerNode = solution.Childs[0]; - solution.Childs.reserve(outerNode->ChildCount()); - solution.Childs[0] = outerNode->Childs[0]; - solution.Childs[0]->Parent = outerNode->Parent; - for (int i = 1; i < outerNode->ChildCount(); ++i) - solution.AddChild(*outerNode->Childs[i]); - } - else - solution.Clear(); - } -} -//------------------------------------------------------------------------------ - -void ClipperOffset::DoOffset(double delta) -{ - m_destPolys.clear(); - m_delta = delta; - - //if Zero offset, just copy any CLOSED polygons to m_p and return ... - if (NEAR_ZERO(delta)) - { - m_destPolys.reserve(m_polyNodes.ChildCount()); - for (int i = 0; i < m_polyNodes.ChildCount(); i++) - { - PolyNode& node = *m_polyNodes.Childs[i]; - if (node.m_endtype == etClosedPolygon) - m_destPolys.push_back(node.Contour); - } - return; - } - - //see offset_triginometry3.svg in the documentation folder ... - if (MiterLimit > 2) m_miterLim = 2/(MiterLimit * MiterLimit); - else m_miterLim = 0.5; - - double y; - if (ArcTolerance <= 0.0) y = def_arc_tolerance; - else if (ArcTolerance > std::fabs(delta) * def_arc_tolerance) - y = std::fabs(delta) * def_arc_tolerance; - else y = ArcTolerance; - //see offset_triginometry2.svg in the documentation folder ... - double steps = pi / std::acos(1 - y / std::fabs(delta)); - if (steps > std::fabs(delta) * pi) - steps = std::fabs(delta) * pi; //ie excessive precision check - m_sin = std::sin(two_pi / steps); - m_cos = std::cos(two_pi / steps); - m_StepsPerRad = steps / two_pi; - if (delta < 0.0) m_sin = -m_sin; - - m_destPolys.reserve(m_polyNodes.ChildCount() * 2); - for (int i = 0; i < m_polyNodes.ChildCount(); i++) - { - PolyNode& node = *m_polyNodes.Childs[i]; - m_srcPoly = node.Contour; - - int len = (int)m_srcPoly.size(); - if (len == 0 || (delta <= 0 && (len < 3 || node.m_endtype != etClosedPolygon))) - continue; - - m_destPoly.clear(); - if (len == 1) - { - if (node.m_jointype == jtRound) - { - double X = 1.0, Y = 0.0; - for (cInt j = 1; j <= steps; j++) - { - m_destPoly.push_back(IntPoint( - Round(m_srcPoly[0].X + X * delta), - Round(m_srcPoly[0].Y + Y * delta))); - double X2 = X; - X = X * m_cos - m_sin * Y; - Y = X2 * m_sin + Y * m_cos; - } - } - else - { - double X = -1.0, Y = -1.0; - for (int j = 0; j < 4; ++j) - { - m_destPoly.push_back(IntPoint( - Round(m_srcPoly[0].X + X * delta), - Round(m_srcPoly[0].Y + Y * delta))); - if (X < 0) X = 1; - else if (Y < 0) Y = 1; - else X = -1; - } - } - m_destPolys.push_back(m_destPoly); - continue; - } - //build m_normals ... - m_normals.clear(); - m_normals.reserve(len); - for (int j = 0; j < len - 1; ++j) - m_normals.push_back(GetUnitNormal(m_srcPoly[j], m_srcPoly[j + 1])); - if (node.m_endtype == etClosedLine || node.m_endtype == etClosedPolygon) - m_normals.push_back(GetUnitNormal(m_srcPoly[len - 1], m_srcPoly[0])); - else - m_normals.push_back(DoublePoint(m_normals[len - 2])); - - if (node.m_endtype == etClosedPolygon) - { - int k = len - 1; - for (int j = 0; j < len; ++j) - OffsetPoint(j, k, node.m_jointype); - m_destPolys.push_back(m_destPoly); - } - else if (node.m_endtype == etClosedLine) - { - int k = len - 1; - for (int j = 0; j < len; ++j) - OffsetPoint(j, k, node.m_jointype); - m_destPolys.push_back(m_destPoly); - m_destPoly.clear(); - //re-build m_normals ... - DoublePoint n = m_normals[len -1]; - for (int j = len - 1; j > 0; j--) - m_normals[j] = DoublePoint(-m_normals[j - 1].X, -m_normals[j - 1].Y); - m_normals[0] = DoublePoint(-n.X, -n.Y); - k = 0; - for (int j = len - 1; j >= 0; j--) - OffsetPoint(j, k, node.m_jointype); - m_destPolys.push_back(m_destPoly); - } - else - { - int k = 0; - for (int j = 1; j < len - 1; ++j) - OffsetPoint(j, k, node.m_jointype); - - IntPoint pt1; - if (node.m_endtype == etOpenButt) - { - int j = len - 1; - pt1 = IntPoint((cInt)Round(m_srcPoly[j].X + m_normals[j].X * - delta), (cInt)Round(m_srcPoly[j].Y + m_normals[j].Y * delta)); - m_destPoly.push_back(pt1); - pt1 = IntPoint((cInt)Round(m_srcPoly[j].X - m_normals[j].X * - delta), (cInt)Round(m_srcPoly[j].Y - m_normals[j].Y * delta)); - m_destPoly.push_back(pt1); - } - else - { - int j = len - 1; - k = len - 2; - m_sinA = 0; - m_normals[j] = DoublePoint(-m_normals[j].X, -m_normals[j].Y); - if (node.m_endtype == etOpenSquare) - DoSquare(j, k); - else - DoRound(j, k); - } - - //re-build m_normals ... - for (int j = len - 1; j > 0; j--) - m_normals[j] = DoublePoint(-m_normals[j - 1].X, -m_normals[j - 1].Y); - m_normals[0] = DoublePoint(-m_normals[1].X, -m_normals[1].Y); - - k = len - 1; - for (int j = k - 1; j > 0; --j) OffsetPoint(j, k, node.m_jointype); - - if (node.m_endtype == etOpenButt) - { - pt1 = IntPoint((cInt)Round(m_srcPoly[0].X - m_normals[0].X * delta), - (cInt)Round(m_srcPoly[0].Y - m_normals[0].Y * delta)); - m_destPoly.push_back(pt1); - pt1 = IntPoint((cInt)Round(m_srcPoly[0].X + m_normals[0].X * delta), - (cInt)Round(m_srcPoly[0].Y + m_normals[0].Y * delta)); - m_destPoly.push_back(pt1); - } - else - { - k = 1; - m_sinA = 0; - if (node.m_endtype == etOpenSquare) - DoSquare(0, 1); - else - DoRound(0, 1); - } - m_destPolys.push_back(m_destPoly); - } - } -} -//------------------------------------------------------------------------------ - -void ClipperOffset::OffsetPoint(int j, int& k, JoinType jointype) -{ - //cross product ... - m_sinA = (m_normals[k].X * m_normals[j].Y - m_normals[j].X * m_normals[k].Y); - if (std::fabs(m_sinA * m_delta) < 1.0) - { - //dot product ... - double cosA = (m_normals[k].X * m_normals[j].X + m_normals[j].Y * m_normals[k].Y ); - if (cosA > 0) // angle => 0 degrees - { - m_destPoly.push_back(IntPoint(Round(m_srcPoly[j].X + m_normals[k].X * m_delta), - Round(m_srcPoly[j].Y + m_normals[k].Y * m_delta))); - return; - } - //else angle => 180 degrees - } - else if (m_sinA > 1.0) m_sinA = 1.0; - else if (m_sinA < -1.0) m_sinA = -1.0; - - if (m_sinA * m_delta < 0) - { - m_destPoly.push_back(IntPoint(Round(m_srcPoly[j].X + m_normals[k].X * m_delta), - Round(m_srcPoly[j].Y + m_normals[k].Y * m_delta))); - m_destPoly.push_back(m_srcPoly[j]); - m_destPoly.push_back(IntPoint(Round(m_srcPoly[j].X + m_normals[j].X * m_delta), - Round(m_srcPoly[j].Y + m_normals[j].Y * m_delta))); - } - else - switch (jointype) - { - case jtMiter: - { - double r = 1 + (m_normals[j].X * m_normals[k].X + - m_normals[j].Y * m_normals[k].Y); - if (r >= m_miterLim) DoMiter(j, k, r); else DoSquare(j, k); - break; - } - case jtSquare: DoSquare(j, k); break; - case jtRound: DoRound(j, k); break; - } - k = j; -} -//------------------------------------------------------------------------------ - -void ClipperOffset::DoSquare(int j, int k) -{ - double dx = std::tan(std::atan2(m_sinA, - m_normals[k].X * m_normals[j].X + m_normals[k].Y * m_normals[j].Y) / 4); - m_destPoly.push_back(IntPoint( - Round(m_srcPoly[j].X + m_delta * (m_normals[k].X - m_normals[k].Y * dx)), - Round(m_srcPoly[j].Y + m_delta * (m_normals[k].Y + m_normals[k].X * dx)))); - m_destPoly.push_back(IntPoint( - Round(m_srcPoly[j].X + m_delta * (m_normals[j].X + m_normals[j].Y * dx)), - Round(m_srcPoly[j].Y + m_delta * (m_normals[j].Y - m_normals[j].X * dx)))); -} -//------------------------------------------------------------------------------ - -void ClipperOffset::DoMiter(int j, int k, double r) -{ - double q = m_delta / r; - m_destPoly.push_back(IntPoint(Round(m_srcPoly[j].X + (m_normals[k].X + m_normals[j].X) * q), - Round(m_srcPoly[j].Y + (m_normals[k].Y + m_normals[j].Y) * q))); -} -//------------------------------------------------------------------------------ - -void ClipperOffset::DoRound(int j, int k) -{ - double a = std::atan2(m_sinA, - m_normals[k].X * m_normals[j].X + m_normals[k].Y * m_normals[j].Y); - int steps = std::max((int)Round(m_StepsPerRad * std::fabs(a)), 1); - - double X = m_normals[k].X, Y = m_normals[k].Y, X2; - for (int i = 0; i < steps; ++i) - { - m_destPoly.push_back(IntPoint( - Round(m_srcPoly[j].X + X * m_delta), - Round(m_srcPoly[j].Y + Y * m_delta))); - X2 = X; - X = X * m_cos - m_sin * Y; - Y = X2 * m_sin + Y * m_cos; - } - m_destPoly.push_back(IntPoint( - Round(m_srcPoly[j].X + m_normals[j].X * m_delta), - Round(m_srcPoly[j].Y + m_normals[j].Y * m_delta))); -} - -//------------------------------------------------------------------------------ -// Miscellaneous public functions -//------------------------------------------------------------------------------ - -void Clipper::DoSimplePolygons() -{ - PolyOutList::size_type i = 0; - while (i < m_PolyOuts.size()) - { - OutRec* outrec = m_PolyOuts[i++]; - OutPt* op = outrec->Pts; - if (!op || outrec->IsOpen) continue; - do //for each Pt in Polygon until duplicate found do ... - { - OutPt* op2 = op->Next; - while (op2 != outrec->Pts) - { - if ((op->Pt == op2->Pt) && op2->Next != op && op2->Prev != op) - { - //split the polygon into two ... - OutPt* op3 = op->Prev; - OutPt* op4 = op2->Prev; - op->Prev = op4; - op4->Next = op; - op2->Prev = op3; - op3->Next = op2; - - outrec->Pts = op; - OutRec* outrec2 = CreateOutRec(); - outrec2->Pts = op2; - UpdateOutPtIdxs(*outrec2); - if (Poly2ContainsPoly1(outrec2->Pts, outrec->Pts)) - { - //OutRec2 is contained by OutRec1 ... - outrec2->IsHole = !outrec->IsHole; - outrec2->FirstLeft = outrec; - if (m_UsingPolyTree) FixupFirstLefts2(outrec2, outrec); - } - else - if (Poly2ContainsPoly1(outrec->Pts, outrec2->Pts)) - { - //OutRec1 is contained by OutRec2 ... - outrec2->IsHole = outrec->IsHole; - outrec->IsHole = !outrec2->IsHole; - outrec2->FirstLeft = outrec->FirstLeft; - outrec->FirstLeft = outrec2; - if (m_UsingPolyTree) FixupFirstLefts2(outrec, outrec2); - } - else - { - //the 2 polygons are separate ... - outrec2->IsHole = outrec->IsHole; - outrec2->FirstLeft = outrec->FirstLeft; - if (m_UsingPolyTree) FixupFirstLefts1(outrec, outrec2); - } - op2 = op; //ie get ready for the Next iteration - } - op2 = op2->Next; - } - op = op->Next; - } - while (op != outrec->Pts); - } -} -//------------------------------------------------------------------------------ - -void ReversePath(Path& p) -{ - std::reverse(p.begin(), p.end()); -} -//------------------------------------------------------------------------------ - -void ReversePaths(Paths& p) -{ - for (Paths::size_type i = 0; i < p.size(); ++i) - ReversePath(p[i]); -} -//------------------------------------------------------------------------------ - -void SimplifyPolygon(const Path &in_poly, Paths &out_polys, PolyFillType fillType) -{ - Clipper c; - c.StrictlySimple(true); - c.AddPath(in_poly, ptSubject, true); - c.Execute(ctUnion, out_polys, fillType, fillType); -} -//------------------------------------------------------------------------------ - -void SimplifyPolygons(const Paths &in_polys, Paths &out_polys, PolyFillType fillType) -{ - Clipper c; - c.StrictlySimple(true); - c.AddPaths(in_polys, ptSubject, true); - c.Execute(ctUnion, out_polys, fillType, fillType); -} -//------------------------------------------------------------------------------ - -void SimplifyPolygons(Paths &polys, PolyFillType fillType) -{ - SimplifyPolygons(polys, polys, fillType); -} -//------------------------------------------------------------------------------ - -inline double DistanceSqrd(const IntPoint& pt1, const IntPoint& pt2) -{ - double Dx = ((double)pt1.X - pt2.X); - double dy = ((double)pt1.Y - pt2.Y); - return (Dx*Dx + dy*dy); -} -//------------------------------------------------------------------------------ - -double DistanceFromLineSqrd( - const IntPoint& pt, const IntPoint& ln1, const IntPoint& ln2) -{ - //The equation of a line in general form (Ax + By + C = 0) - //given 2 points (x,y) & (x,y) is ... - //(y - y)x + (x - x)y + (y - y)x - (x - x)y = 0 - //A = (y - y); B = (x - x); C = (y - y)x - (x - x)y - //perpendicular distance of point (x,y) = (Ax + By + C)/Sqrt(A + B) - //see http://en.wikipedia.org/wiki/Perpendicular_distance - double A = double(ln1.Y - ln2.Y); - double B = double(ln2.X - ln1.X); - double C = A * ln1.X + B * ln1.Y; - C = A * pt.X + B * pt.Y - C; - return (C * C) / (A * A + B * B); -} -//--------------------------------------------------------------------------- - -bool SlopesNearCollinear(const IntPoint& pt1, - const IntPoint& pt2, const IntPoint& pt3, double distSqrd) -{ - //this function is more accurate when the point that's geometrically - //between the other 2 points is the one that's tested for distance. - //ie makes it more likely to pick up 'spikes' ... - if (Abs(pt1.X - pt2.X) > Abs(pt1.Y - pt2.Y)) - { - if ((pt1.X > pt2.X) == (pt1.X < pt3.X)) - return DistanceFromLineSqrd(pt1, pt2, pt3) < distSqrd; - else if ((pt2.X > pt1.X) == (pt2.X < pt3.X)) - return DistanceFromLineSqrd(pt2, pt1, pt3) < distSqrd; - else - return DistanceFromLineSqrd(pt3, pt1, pt2) < distSqrd; - } - else - { - if ((pt1.Y > pt2.Y) == (pt1.Y < pt3.Y)) - return DistanceFromLineSqrd(pt1, pt2, pt3) < distSqrd; - else if ((pt2.Y > pt1.Y) == (pt2.Y < pt3.Y)) - return DistanceFromLineSqrd(pt2, pt1, pt3) < distSqrd; - else - return DistanceFromLineSqrd(pt3, pt1, pt2) < distSqrd; - } -} -//------------------------------------------------------------------------------ - -bool PointsAreClose(IntPoint pt1, IntPoint pt2, double distSqrd) -{ - double Dx = (double)pt1.X - pt2.X; - double dy = (double)pt1.Y - pt2.Y; - return ((Dx * Dx) + (dy * dy) <= distSqrd); -} -//------------------------------------------------------------------------------ - -OutPt* ExcludeOp(OutPt* op) -{ - OutPt* result = op->Prev; - result->Next = op->Next; - op->Next->Prev = result; - result->Idx = 0; - return result; -} -//------------------------------------------------------------------------------ - -void CleanPolygon(const Path& in_poly, Path& out_poly, double distance) -{ - //distance = proximity in units/pixels below which vertices - //will be stripped. Default ~= sqrt(2). - - size_t size = in_poly.size(); - - if (size == 0) - { - out_poly.clear(); - return; - } - - OutPt* outPts = new OutPt[size]; - for (size_t i = 0; i < size; ++i) - { - outPts[i].Pt = in_poly[i]; - outPts[i].Next = &outPts[(i + 1) % size]; - outPts[i].Next->Prev = &outPts[i]; - outPts[i].Idx = 0; - } - - double distSqrd = distance * distance; - OutPt* op = &outPts[0]; - while (op->Idx == 0 && op->Next != op->Prev) - { - if (PointsAreClose(op->Pt, op->Prev->Pt, distSqrd)) - { - op = ExcludeOp(op); - size--; - } - else if (PointsAreClose(op->Prev->Pt, op->Next->Pt, distSqrd)) - { - ExcludeOp(op->Next); - op = ExcludeOp(op); - size -= 2; - } - else if (SlopesNearCollinear(op->Prev->Pt, op->Pt, op->Next->Pt, distSqrd)) - { - op = ExcludeOp(op); - size--; - } - else - { - op->Idx = 1; - op = op->Next; - } - } - - if (size < 3) size = 0; - out_poly.resize(size); - for (size_t i = 0; i < size; ++i) - { - out_poly[i] = op->Pt; - op = op->Next; - } - delete [] outPts; -} -//------------------------------------------------------------------------------ - -void CleanPolygon(Path& poly, double distance) -{ - CleanPolygon(poly, poly, distance); -} -//------------------------------------------------------------------------------ - -void CleanPolygons(const Paths& in_polys, Paths& out_polys, double distance) -{ - out_polys.resize(in_polys.size()); - for (Paths::size_type i = 0; i < in_polys.size(); ++i) - CleanPolygon(in_polys[i], out_polys[i], distance); -} -//------------------------------------------------------------------------------ - -void CleanPolygons(Paths& polys, double distance) -{ - CleanPolygons(polys, polys, distance); -} -//------------------------------------------------------------------------------ - -void Minkowski(const Path& poly, const Path& path, - Paths& solution, bool isSum, bool isClosed) -{ - int delta = (isClosed ? 1 : 0); - size_t polyCnt = poly.size(); - size_t pathCnt = path.size(); - Paths pp; - pp.reserve(pathCnt); - if (isSum) - for (size_t i = 0; i < pathCnt; ++i) - { - Path p; - p.reserve(polyCnt); - for (size_t j = 0; j < poly.size(); ++j) - p.push_back(IntPoint(path[i].X + poly[j].X, path[i].Y + poly[j].Y)); - pp.push_back(p); - } - else - for (size_t i = 0; i < pathCnt; ++i) - { - Path p; - p.reserve(polyCnt); - for (size_t j = 0; j < poly.size(); ++j) - p.push_back(IntPoint(path[i].X - poly[j].X, path[i].Y - poly[j].Y)); - pp.push_back(p); - } - - solution.clear(); - solution.reserve((pathCnt + delta) * (polyCnt + 1)); - for (size_t i = 0; i < pathCnt - 1 + delta; ++i) - for (size_t j = 0; j < polyCnt; ++j) - { - Path quad; - quad.reserve(4); - quad.push_back(pp[i % pathCnt][j % polyCnt]); - quad.push_back(pp[(i + 1) % pathCnt][j % polyCnt]); - quad.push_back(pp[(i + 1) % pathCnt][(j + 1) % polyCnt]); - quad.push_back(pp[i % pathCnt][(j + 1) % polyCnt]); - if (!Orientation(quad)) ReversePath(quad); - solution.push_back(quad); - } -} -//------------------------------------------------------------------------------ - -void MinkowskiSum(const Path& pattern, const Path& path, Paths& solution, bool pathIsClosed) -{ - Minkowski(pattern, path, solution, true, pathIsClosed); - Clipper c; - c.AddPaths(solution, ptSubject, true); - c.Execute(ctUnion, solution, pftNonZero, pftNonZero); -} -//------------------------------------------------------------------------------ - -void TranslatePath(const Path& input, Path& output, const IntPoint delta) -{ - //precondition: input != output - output.resize(input.size()); - for (size_t i = 0; i < input.size(); ++i) - output[i] = IntPoint(input[i].X + delta.X, input[i].Y + delta.Y); -} -//------------------------------------------------------------------------------ - -void MinkowskiSum(const Path& pattern, const Paths& paths, Paths& solution, bool pathIsClosed) -{ - Clipper c; - for (size_t i = 0; i < paths.size(); ++i) - { - Paths tmp; - Minkowski(pattern, paths[i], tmp, true, pathIsClosed); - c.AddPaths(tmp, ptSubject, true); - if (pathIsClosed) - { - Path tmp2; - TranslatePath(paths[i], tmp2, pattern[0]); - c.AddPath(tmp2, ptClip, true); - } - } - c.Execute(ctUnion, solution, pftNonZero, pftNonZero); -} -//------------------------------------------------------------------------------ - -void MinkowskiDiff(const Path& poly1, const Path& poly2, Paths& solution) -{ - Minkowski(poly1, poly2, solution, false, true); - Clipper c; - c.AddPaths(solution, ptSubject, true); - c.Execute(ctUnion, solution, pftNonZero, pftNonZero); -} -//------------------------------------------------------------------------------ - -enum NodeType {ntAny, ntOpen, ntClosed}; - -void AddPolyNodeToPaths(const PolyNode& polynode, NodeType nodetype, Paths& paths) -{ - bool match = true; - if (nodetype == ntClosed) match = !polynode.IsOpen(); - else if (nodetype == ntOpen) return; - - if (!polynode.Contour.empty() && match) - paths.push_back(polynode.Contour); - for (int i = 0; i < polynode.ChildCount(); ++i) - AddPolyNodeToPaths(*polynode.Childs[i], nodetype, paths); -} -//------------------------------------------------------------------------------ - -void PolyTreeToPaths(const PolyTree& polytree, Paths& paths) -{ - paths.resize(0); - paths.reserve(polytree.Total()); - AddPolyNodeToPaths(polytree, ntAny, paths); -} -//------------------------------------------------------------------------------ - -void ClosedPathsFromPolyTree(const PolyTree& polytree, Paths& paths) -{ - paths.resize(0); - paths.reserve(polytree.Total()); - AddPolyNodeToPaths(polytree, ntClosed, paths); -} -//------------------------------------------------------------------------------ - -void OpenPathsFromPolyTree(PolyTree& polytree, Paths& paths) -{ - paths.resize(0); - paths.reserve(polytree.Total()); - //Open paths are top level only, so ... - for (int i = 0; i < polytree.ChildCount(); ++i) - if (polytree.Childs[i]->IsOpen()) - paths.push_back(polytree.Childs[i]->Contour); -} -//------------------------------------------------------------------------------ - -std::ostream& operator <<(std::ostream &s, const IntPoint &p) -{ - s << "(" << p.X << "," << p.Y << ")"; - return s; -} -//------------------------------------------------------------------------------ - -std::ostream& operator <<(std::ostream &s, const Path &p) -{ - if (p.empty()) return s; - Path::size_type last = p.size() -1; - for (Path::size_type i = 0; i < last; i++) - s << "(" << p[i].X << "," << p[i].Y << "), "; - s << "(" << p[last].X << "," << p[last].Y << ")\n"; - return s; -} -//------------------------------------------------------------------------------ - -std::ostream& operator <<(std::ostream &s, const Paths &p) -{ - for (Paths::size_type i = 0; i < p.size(); i++) - s << p[i]; - s << "\n"; - return s; -} -//------------------------------------------------------------------------------ - -} //ClipperLib namespace diff --git a/ptocr/postprocess/dbprocess/include/clipper/clipper.hpp b/ptocr/postprocess/dbprocess/include/clipper/clipper.hpp deleted file mode 100644 index 1c38371..0000000 --- a/ptocr/postprocess/dbprocess/include/clipper/clipper.hpp +++ /dev/null @@ -1,404 +0,0 @@ -/******************************************************************************* -* * -* Author : Angus Johnson * -* Version : 6.4.0 * -* Date : 2 July 2015 * -* Website : http://www.angusj.com * -* Copyright : Angus Johnson 2010-2015 * -* * -* License: * -* Use, modification & distribution is subject to Boost Software License Ver 1. * -* http://www.boost.org/LICENSE_1_0.txt * -* * -* Attributions: * -* The code in this library is an extension of Bala Vatti's clipping algorithm: * -* "A generic solution to polygon clipping" * -* Communications of the ACM, Vol 35, Issue 7 (July 1992) pp 56-63. * -* http://portal.acm.org/citation.cfm?id=129906 * -* * -* Computer graphics and geometric modeling: implementation and algorithms * -* By Max K. Agoston * -* Springer; 1 edition (January 4, 2005) * -* http://books.google.com/books?q=vatti+clipping+agoston * -* * -* See also: * -* "Polygon Offsetting by Computing Winding Numbers" * -* Paper no. DETC2005-85513 pp. 565-575 * -* ASME 2005 International Design Engineering Technical Conferences * -* and Computers and Information in Engineering Conference (IDETC/CIE2005) * -* September 24-28, 2005 , Long Beach, California, USA * -* http://www.me.berkeley.edu/~mcmains/pubs/DAC05OffsetPolygon.pdf * -* * -*******************************************************************************/ - -#ifndef clipper_hpp -#define clipper_hpp - -#define CLIPPER_VERSION "6.2.6" - -//use_int32: When enabled 32bit ints are used instead of 64bit ints. This -//improve performance but coordinate values are limited to the range +/- 46340 -//#define use_int32 - -//use_xyz: adds a Z member to IntPoint. Adds a minor cost to perfomance. -//#define use_xyz - -//use_lines: Enables line clipping. Adds a very minor cost to performance. -#define use_lines - -//use_deprecated: Enables temporary support for the obsolete functions -//#define use_deprecated - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace ClipperLib { - -enum ClipType { ctIntersection, ctUnion, ctDifference, ctXor }; -enum PolyType { ptSubject, ptClip }; -//By far the most widely used winding rules for polygon filling are -//EvenOdd & NonZero (GDI, GDI+, XLib, OpenGL, Cairo, AGG, Quartz, SVG, Gr32) -//Others rules include Positive, Negative and ABS_GTR_EQ_TWO (only in OpenGL) -//see http://glprogramming.com/red/chapter11.html -enum PolyFillType { pftEvenOdd, pftNonZero, pftPositive, pftNegative }; - -#ifdef use_int32 - typedef int cInt; - static cInt const loRange = 0x7FFF; - static cInt const hiRange = 0x7FFF; -#else - typedef signed long long cInt; - static cInt const loRange = 0x3FFFFFFF; - static cInt const hiRange = 0x3FFFFFFFFFFFFFFFLL; - typedef signed long long long64; //used by Int128 class - typedef unsigned long long ulong64; - -#endif - -struct IntPoint { - cInt X; - cInt Y; -#ifdef use_xyz - cInt Z; - IntPoint(cInt x = 0, cInt y = 0, cInt z = 0): X(x), Y(y), Z(z) {}; -#else - IntPoint(cInt x = 0, cInt y = 0): X(x), Y(y) {}; -#endif - - friend inline bool operator== (const IntPoint& a, const IntPoint& b) - { - return a.X == b.X && a.Y == b.Y; - } - friend inline bool operator!= (const IntPoint& a, const IntPoint& b) - { - return a.X != b.X || a.Y != b.Y; - } -}; -//------------------------------------------------------------------------------ - -typedef std::vector< IntPoint > Path; -typedef std::vector< Path > Paths; - -inline Path& operator <<(Path& poly, const IntPoint& p) {poly.push_back(p); return poly;} -inline Paths& operator <<(Paths& polys, const Path& p) {polys.push_back(p); return polys;} - -std::ostream& operator <<(std::ostream &s, const IntPoint &p); -std::ostream& operator <<(std::ostream &s, const Path &p); -std::ostream& operator <<(std::ostream &s, const Paths &p); - -struct DoublePoint -{ - double X; - double Y; - DoublePoint(double x = 0, double y = 0) : X(x), Y(y) {} - DoublePoint(IntPoint ip) : X((double)ip.X), Y((double)ip.Y) {} -}; -//------------------------------------------------------------------------------ - -#ifdef use_xyz -typedef void (*ZFillCallback)(IntPoint& e1bot, IntPoint& e1top, IntPoint& e2bot, IntPoint& e2top, IntPoint& pt); -#endif - -enum InitOptions {ioReverseSolution = 1, ioStrictlySimple = 2, ioPreserveCollinear = 4}; -enum JoinType {jtSquare, jtRound, jtMiter}; -enum EndType {etClosedPolygon, etClosedLine, etOpenButt, etOpenSquare, etOpenRound}; - -class PolyNode; -typedef std::vector< PolyNode* > PolyNodes; - -class PolyNode -{ -public: - PolyNode(); - virtual ~PolyNode(){}; - Path Contour; - PolyNodes Childs; - PolyNode* Parent; - PolyNode* GetNext() const; - bool IsHole() const; - bool IsOpen() const; - int ChildCount() const; -private: - unsigned Index; //node index in Parent.Childs - bool m_IsOpen; - JoinType m_jointype; - EndType m_endtype; - PolyNode* GetNextSiblingUp() const; - void AddChild(PolyNode& child); - friend class Clipper; //to access Index - friend class ClipperOffset; -}; - -class PolyTree: public PolyNode -{ -public: - ~PolyTree(){Clear();}; - PolyNode* GetFirst() const; - void Clear(); - int Total() const; -private: - PolyNodes AllNodes; - friend class Clipper; //to access AllNodes -}; - -bool Orientation(const Path &poly); -double Area(const Path &poly); -int PointInPolygon(const IntPoint &pt, const Path &path); - -void SimplifyPolygon(const Path &in_poly, Paths &out_polys, PolyFillType fillType = pftEvenOdd); -void SimplifyPolygons(const Paths &in_polys, Paths &out_polys, PolyFillType fillType = pftEvenOdd); -void SimplifyPolygons(Paths &polys, PolyFillType fillType = pftEvenOdd); - -void CleanPolygon(const Path& in_poly, Path& out_poly, double distance = 1.415); -void CleanPolygon(Path& poly, double distance = 1.415); -void CleanPolygons(const Paths& in_polys, Paths& out_polys, double distance = 1.415); -void CleanPolygons(Paths& polys, double distance = 1.415); - -void MinkowskiSum(const Path& pattern, const Path& path, Paths& solution, bool pathIsClosed); -void MinkowskiSum(const Path& pattern, const Paths& paths, Paths& solution, bool pathIsClosed); -void MinkowskiDiff(const Path& poly1, const Path& poly2, Paths& solution); - -void PolyTreeToPaths(const PolyTree& polytree, Paths& paths); -void ClosedPathsFromPolyTree(const PolyTree& polytree, Paths& paths); -void OpenPathsFromPolyTree(PolyTree& polytree, Paths& paths); - -void ReversePath(Path& p); -void ReversePaths(Paths& p); - -struct IntRect { cInt left; cInt top; cInt right; cInt bottom; }; - -//enums that are used internally ... -enum EdgeSide { esLeft = 1, esRight = 2}; - -//forward declarations (for stuff used internally) ... -struct TEdge; -struct IntersectNode; -struct LocalMinimum; -struct OutPt; -struct OutRec; -struct Join; - -typedef std::vector < OutRec* > PolyOutList; -typedef std::vector < TEdge* > EdgeList; -typedef std::vector < Join* > JoinList; -typedef std::vector < IntersectNode* > IntersectList; - -//------------------------------------------------------------------------------ - -//ClipperBase is the ancestor to the Clipper class. It should not be -//instantiated directly. This class simply abstracts the conversion of sets of -//polygon coordinates into edge objects that are stored in a LocalMinima list. -class ClipperBase -{ -public: - ClipperBase(); - virtual ~ClipperBase(); - virtual bool AddPath(const Path &pg, PolyType PolyTyp, bool Closed); - bool AddPaths(const Paths &ppg, PolyType PolyTyp, bool Closed); - virtual void Clear(); - IntRect GetBounds(); - bool PreserveCollinear() {return m_PreserveCollinear;}; - void PreserveCollinear(bool value) {m_PreserveCollinear = value;}; -protected: - void DisposeLocalMinimaList(); - TEdge* AddBoundsToLML(TEdge *e, bool IsClosed); - virtual void Reset(); - TEdge* ProcessBound(TEdge* E, bool IsClockwise); - void InsertScanbeam(const cInt Y); - bool PopScanbeam(cInt &Y); - bool LocalMinimaPending(); - bool PopLocalMinima(cInt Y, const LocalMinimum *&locMin); - OutRec* CreateOutRec(); - void DisposeAllOutRecs(); - void DisposeOutRec(PolyOutList::size_type index); - void SwapPositionsInAEL(TEdge *edge1, TEdge *edge2); - void DeleteFromAEL(TEdge *e); - void UpdateEdgeIntoAEL(TEdge *&e); - - typedef std::vector MinimaList; - MinimaList::iterator m_CurrentLM; - MinimaList m_MinimaList; - - bool m_UseFullRange; - EdgeList m_edges; - bool m_PreserveCollinear; - bool m_HasOpenPaths; - PolyOutList m_PolyOuts; - TEdge *m_ActiveEdges; - - typedef std::priority_queue ScanbeamList; - ScanbeamList m_Scanbeam; -}; -//------------------------------------------------------------------------------ - -class Clipper : public virtual ClipperBase -{ -public: - Clipper(int initOptions = 0); - bool Execute(ClipType clipType, - Paths &solution, - PolyFillType fillType = pftEvenOdd); - bool Execute(ClipType clipType, - Paths &solution, - PolyFillType subjFillType, - PolyFillType clipFillType); - bool Execute(ClipType clipType, - PolyTree &polytree, - PolyFillType fillType = pftEvenOdd); - bool Execute(ClipType clipType, - PolyTree &polytree, - PolyFillType subjFillType, - PolyFillType clipFillType); - bool ReverseSolution() { return m_ReverseOutput; }; - void ReverseSolution(bool value) {m_ReverseOutput = value;}; - bool StrictlySimple() {return m_StrictSimple;}; - void StrictlySimple(bool value) {m_StrictSimple = value;}; - //set the callback function for z value filling on intersections (otherwise Z is 0) -#ifdef use_xyz - void ZFillFunction(ZFillCallback zFillFunc); -#endif -protected: - virtual bool ExecuteInternal(); -private: - JoinList m_Joins; - JoinList m_GhostJoins; - IntersectList m_IntersectList; - ClipType m_ClipType; - typedef std::list MaximaList; - MaximaList m_Maxima; - TEdge *m_SortedEdges; - bool m_ExecuteLocked; - PolyFillType m_ClipFillType; - PolyFillType m_SubjFillType; - bool m_ReverseOutput; - bool m_UsingPolyTree; - bool m_StrictSimple; -#ifdef use_xyz - ZFillCallback m_ZFill; //custom callback -#endif - void SetWindingCount(TEdge& edge); - bool IsEvenOddFillType(const TEdge& edge) const; - bool IsEvenOddAltFillType(const TEdge& edge) const; - void InsertLocalMinimaIntoAEL(const cInt botY); - void InsertEdgeIntoAEL(TEdge *edge, TEdge* startEdge); - void AddEdgeToSEL(TEdge *edge); - bool PopEdgeFromSEL(TEdge *&edge); - void CopyAELToSEL(); - void DeleteFromSEL(TEdge *e); - void SwapPositionsInSEL(TEdge *edge1, TEdge *edge2); - bool IsContributing(const TEdge& edge) const; - bool IsTopHorz(const cInt XPos); - void DoMaxima(TEdge *e); - void ProcessHorizontals(); - void ProcessHorizontal(TEdge *horzEdge); - void AddLocalMaxPoly(TEdge *e1, TEdge *e2, const IntPoint &pt); - OutPt* AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &pt); - OutRec* GetOutRec(int idx); - void AppendPolygon(TEdge *e1, TEdge *e2); - void IntersectEdges(TEdge *e1, TEdge *e2, IntPoint &pt); - OutPt* AddOutPt(TEdge *e, const IntPoint &pt); - OutPt* GetLastOutPt(TEdge *e); - bool ProcessIntersections(const cInt topY); - void BuildIntersectList(const cInt topY); - void ProcessIntersectList(); - void ProcessEdgesAtTopOfScanbeam(const cInt topY); - void BuildResult(Paths& polys); - void BuildResult2(PolyTree& polytree); - void SetHoleState(TEdge *e, OutRec *outrec); - void DisposeIntersectNodes(); - bool FixupIntersectionOrder(); - void FixupOutPolygon(OutRec &outrec); - void FixupOutPolyline(OutRec &outrec); - bool IsHole(TEdge *e); - bool FindOwnerFromSplitRecs(OutRec &outRec, OutRec *&currOrfl); - void FixHoleLinkage(OutRec &outrec); - void AddJoin(OutPt *op1, OutPt *op2, const IntPoint offPt); - void ClearJoins(); - void ClearGhostJoins(); - void AddGhostJoin(OutPt *op, const IntPoint offPt); - bool JoinPoints(Join *j, OutRec* outRec1, OutRec* outRec2); - void JoinCommonEdges(); - void DoSimplePolygons(); - void FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec); - void FixupFirstLefts2(OutRec* InnerOutRec, OutRec* OuterOutRec); - void FixupFirstLefts3(OutRec* OldOutRec, OutRec* NewOutRec); -#ifdef use_xyz - void SetZ(IntPoint& pt, TEdge& e1, TEdge& e2); -#endif -}; -//------------------------------------------------------------------------------ - -class ClipperOffset -{ -public: - ClipperOffset(double miterLimit = 2.0, double roundPrecision = 0.25); - ~ClipperOffset(); - void AddPath(const Path& path, JoinType joinType, EndType endType); - void AddPaths(const Paths& paths, JoinType joinType, EndType endType); - void Execute(Paths& solution, double delta); - void Execute(PolyTree& solution, double delta); - void Clear(); - double MiterLimit; - double ArcTolerance; -private: - Paths m_destPolys; - Path m_srcPoly; - Path m_destPoly; - std::vector m_normals; - double m_delta, m_sinA, m_sin, m_cos; - double m_miterLim, m_StepsPerRad; - IntPoint m_lowest; - PolyNode m_polyNodes; - - void FixOrientations(); - void DoOffset(double delta); - void OffsetPoint(int j, int& k, JoinType jointype); - void DoSquare(int j, int k); - void DoMiter(int j, int k, double r); - void DoRound(int j, int k); -}; -//------------------------------------------------------------------------------ - -class clipperException : public std::exception -{ - public: - clipperException(const char* description): m_descr(description) {} - virtual ~clipperException() throw() {} - virtual const char* what() const throw() {return m_descr.c_str();} - private: - std::string m_descr; -}; -//------------------------------------------------------------------------------ - -} //ClipperLib namespace - -#endif //clipper_hpp - - diff --git a/ptocr/postprocess/dbprocess/include/postprocess_op.h b/ptocr/postprocess/dbprocess/include/postprocess_op.h deleted file mode 100644 index 3eee75d..0000000 --- a/ptocr/postprocess/dbprocess/include/postprocess_op.h +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#pragma once - -#include "opencv2/core.hpp" -#include "opencv2/imgcodecs.hpp" -#include "opencv2/imgproc.hpp" -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "clipper.h" -// #include "utility.h" - -using namespace std; - -namespace cppdbprocess { - -class PostProcessor { -public: - void GetContourArea(const std::vector> &box, - float unclip_ratio, float &distance); - - cv::RotatedRect UnClip(std::vector> box, - const float &unclip_ratio); - - float **Mat2Vec(cv::Mat mat); - - std::vector> - OrderPointsClockwise(std::vector> pts); - - std::vector> GetMiniBoxes(cv::RotatedRect box, - float &ssid); - - float BoxScoreFast(std::vector> box_array, cv::Mat pred); - - std::vector>> - BoxesFromBitmap(const cv::Mat pred, const cv::Mat bitmap, - const float &box_thresh, const float &det_db_unclip_ratio); - - std::vector>> - FilterTagDetRes(std::vector>> boxes, - float ratio_h, float ratio_w, cv::Mat srcimg); - -private: - static bool XsortInt(std::vector a, std::vector b); - - static bool XsortFp32(std::vector a, std::vector b); - - std::vector> Mat2Vector(cv::Mat mat); - - inline int _max(int a, int b) { return a >= b ? a : b; } - - inline int _min(int a, int b) { return a >= b ? b : a; } - - template inline T clamp(T x, T min, T max) { - if (x > max) - return max; - if (x < min) - return min; - return x; - } - - inline float clampf(float x, float min, float max) { - if (x > max) - return max; - if (x < min) - return min; - return x; - } -}; - -} // namespace PaddleOCR diff --git a/ptocr/postprocess/dbprocess/include/pybind11/attr.h b/ptocr/postprocess/dbprocess/include/pybind11/attr.h deleted file mode 100644 index 8732cfe..0000000 --- a/ptocr/postprocess/dbprocess/include/pybind11/attr.h +++ /dev/null @@ -1,492 +0,0 @@ -/* - pybind11/attr.h: Infrastructure for processing custom - type and function attributes - - Copyright (c) 2016 Wenzel Jakob - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#include "cast.h" - -NAMESPACE_BEGIN(PYBIND11_NAMESPACE) - -/// \addtogroup annotations -/// @{ - -/// Annotation for methods -struct is_method { handle class_; is_method(const handle &c) : class_(c) { } }; - -/// Annotation for operators -struct is_operator { }; - -/// Annotation for parent scope -struct scope { handle value; scope(const handle &s) : value(s) { } }; - -/// Annotation for documentation -struct doc { const char *value; doc(const char *value) : value(value) { } }; - -/// Annotation for function names -struct name { const char *value; name(const char *value) : value(value) { } }; - -/// Annotation indicating that a function is an overload associated with a given "sibling" -struct sibling { handle value; sibling(const handle &value) : value(value.ptr()) { } }; - -/// Annotation indicating that a class derives from another given type -template struct base { - PYBIND11_DEPRECATED("base() was deprecated in favor of specifying 'T' as a template argument to class_") - base() { } -}; - -/// Keep patient alive while nurse lives -template struct keep_alive { }; - -/// Annotation indicating that a class is involved in a multiple inheritance relationship -struct multiple_inheritance { }; - -/// Annotation which enables dynamic attributes, i.e. adds `__dict__` to a class -struct dynamic_attr { }; - -/// Annotation which enables the buffer protocol for a type -struct buffer_protocol { }; - -/// Annotation which requests that a special metaclass is created for a type -struct metaclass { - handle value; - - PYBIND11_DEPRECATED("py::metaclass() is no longer required. It's turned on by default now.") - metaclass() {} - - /// Override pybind11's default metaclass - explicit metaclass(handle value) : value(value) { } -}; - -/// Annotation that marks a class as local to the module: -struct module_local { const bool value; constexpr module_local(bool v = true) : value(v) { } }; - -/// Annotation to mark enums as an arithmetic type -struct arithmetic { }; - -/** \rst - A call policy which places one or more guard variables (``Ts...``) around the function call. - - For example, this definition: - - .. code-block:: cpp - - m.def("foo", foo, py::call_guard()); - - is equivalent to the following pseudocode: - - .. code-block:: cpp - - m.def("foo", [](args...) { - T scope_guard; - return foo(args...); // forwarded arguments - }); - \endrst */ -template struct call_guard; - -template <> struct call_guard<> { using type = detail::void_type; }; - -template -struct call_guard { - static_assert(std::is_default_constructible::value, - "The guard type must be default constructible"); - - using type = T; -}; - -template -struct call_guard { - struct type { - T guard{}; // Compose multiple guard types with left-to-right default-constructor order - typename call_guard::type next{}; - }; -}; - -/// @} annotations - -NAMESPACE_BEGIN(detail) -/* Forward declarations */ -enum op_id : int; -enum op_type : int; -struct undefined_t; -template struct op_; -inline void keep_alive_impl(size_t Nurse, size_t Patient, function_call &call, handle ret); - -/// Internal data structure which holds metadata about a keyword argument -struct argument_record { - const char *name; ///< Argument name - const char *descr; ///< Human-readable version of the argument value - handle value; ///< Associated Python object - bool convert : 1; ///< True if the argument is allowed to convert when loading - bool none : 1; ///< True if None is allowed when loading - - argument_record(const char *name, const char *descr, handle value, bool convert, bool none) - : name(name), descr(descr), value(value), convert(convert), none(none) { } -}; - -/// Internal data structure which holds metadata about a bound function (signature, overloads, etc.) -struct function_record { - function_record() - : is_constructor(false), is_new_style_constructor(false), is_stateless(false), - is_operator(false), has_args(false), has_kwargs(false), is_method(false) { } - - /// Function name - char *name = nullptr; /* why no C++ strings? They generate heavier code.. */ - - // User-specified documentation string - char *doc = nullptr; - - /// Human-readable version of the function signature - char *signature = nullptr; - - /// List of registered keyword arguments - std::vector args; - - /// Pointer to lambda function which converts arguments and performs the actual call - handle (*impl) (function_call &) = nullptr; - - /// Storage for the wrapped function pointer and captured data, if any - void *data[3] = { }; - - /// Pointer to custom destructor for 'data' (if needed) - void (*free_data) (function_record *ptr) = nullptr; - - /// Return value policy associated with this function - return_value_policy policy = return_value_policy::automatic; - - /// True if name == '__init__' - bool is_constructor : 1; - - /// True if this is a new-style `__init__` defined in `detail/init.h` - bool is_new_style_constructor : 1; - - /// True if this is a stateless function pointer - bool is_stateless : 1; - - /// True if this is an operator (__add__), etc. - bool is_operator : 1; - - /// True if the function has a '*args' argument - bool has_args : 1; - - /// True if the function has a '**kwargs' argument - bool has_kwargs : 1; - - /// True if this is a method - bool is_method : 1; - - /// Number of arguments (including py::args and/or py::kwargs, if present) - std::uint16_t nargs; - - /// Python method object - PyMethodDef *def = nullptr; - - /// Python handle to the parent scope (a class or a module) - handle scope; - - /// Python handle to the sibling function representing an overload chain - handle sibling; - - /// Pointer to next overload - function_record *next = nullptr; -}; - -/// Special data structure which (temporarily) holds metadata about a bound class -struct type_record { - PYBIND11_NOINLINE type_record() - : multiple_inheritance(false), dynamic_attr(false), buffer_protocol(false), module_local(false) { } - - /// Handle to the parent scope - handle scope; - - /// Name of the class - const char *name = nullptr; - - // Pointer to RTTI type_info data structure - const std::type_info *type = nullptr; - - /// How large is the underlying C++ type? - size_t type_size = 0; - - /// What is the alignment of the underlying C++ type? - size_t type_align = 0; - - /// How large is the type's holder? - size_t holder_size = 0; - - /// The global operator new can be overridden with a class-specific variant - void *(*operator_new)(size_t) = nullptr; - - /// Function pointer to class_<..>::init_instance - void (*init_instance)(instance *, const void *) = nullptr; - - /// Function pointer to class_<..>::dealloc - void (*dealloc)(detail::value_and_holder &) = nullptr; - - /// List of base classes of the newly created type - list bases; - - /// Optional docstring - const char *doc = nullptr; - - /// Custom metaclass (optional) - handle metaclass; - - /// Multiple inheritance marker - bool multiple_inheritance : 1; - - /// Does the class manage a __dict__? - bool dynamic_attr : 1; - - /// Does the class implement the buffer protocol? - bool buffer_protocol : 1; - - /// Is the default (unique_ptr) holder type used? - bool default_holder : 1; - - /// Is the class definition local to the module shared object? - bool module_local : 1; - - PYBIND11_NOINLINE void add_base(const std::type_info &base, void *(*caster)(void *)) { - auto base_info = detail::get_type_info(base, false); - if (!base_info) { - std::string tname(base.name()); - detail::clean_type_id(tname); - pybind11_fail("generic_type: type \"" + std::string(name) + - "\" referenced unknown base type \"" + tname + "\""); - } - - if (default_holder != base_info->default_holder) { - std::string tname(base.name()); - detail::clean_type_id(tname); - pybind11_fail("generic_type: type \"" + std::string(name) + "\" " + - (default_holder ? "does not have" : "has") + - " a non-default holder type while its base \"" + tname + "\" " + - (base_info->default_holder ? "does not" : "does")); - } - - bases.append((PyObject *) base_info->type); - - if (base_info->type->tp_dictoffset != 0) - dynamic_attr = true; - - if (caster) - base_info->implicit_casts.emplace_back(type, caster); - } -}; - -inline function_call::function_call(const function_record &f, handle p) : - func(f), parent(p) { - args.reserve(f.nargs); - args_convert.reserve(f.nargs); -} - -/// Tag for a new-style `__init__` defined in `detail/init.h` -struct is_new_style_constructor { }; - -/** - * Partial template specializations to process custom attributes provided to - * cpp_function_ and class_. These are either used to initialize the respective - * fields in the type_record and function_record data structures or executed at - * runtime to deal with custom call policies (e.g. keep_alive). - */ -template struct process_attribute; - -template struct process_attribute_default { - /// Default implementation: do nothing - static void init(const T &, function_record *) { } - static void init(const T &, type_record *) { } - static void precall(function_call &) { } - static void postcall(function_call &, handle) { } -}; - -/// Process an attribute specifying the function's name -template <> struct process_attribute : process_attribute_default { - static void init(const name &n, function_record *r) { r->name = const_cast(n.value); } -}; - -/// Process an attribute specifying the function's docstring -template <> struct process_attribute : process_attribute_default { - static void init(const doc &n, function_record *r) { r->doc = const_cast(n.value); } -}; - -/// Process an attribute specifying the function's docstring (provided as a C-style string) -template <> struct process_attribute : process_attribute_default { - static void init(const char *d, function_record *r) { r->doc = const_cast(d); } - static void init(const char *d, type_record *r) { r->doc = const_cast(d); } -}; -template <> struct process_attribute : process_attribute { }; - -/// Process an attribute indicating the function's return value policy -template <> struct process_attribute : process_attribute_default { - static void init(const return_value_policy &p, function_record *r) { r->policy = p; } -}; - -/// Process an attribute which indicates that this is an overloaded function associated with a given sibling -template <> struct process_attribute : process_attribute_default { - static void init(const sibling &s, function_record *r) { r->sibling = s.value; } -}; - -/// Process an attribute which indicates that this function is a method -template <> struct process_attribute : process_attribute_default { - static void init(const is_method &s, function_record *r) { r->is_method = true; r->scope = s.class_; } -}; - -/// Process an attribute which indicates the parent scope of a method -template <> struct process_attribute : process_attribute_default { - static void init(const scope &s, function_record *r) { r->scope = s.value; } -}; - -/// Process an attribute which indicates that this function is an operator -template <> struct process_attribute : process_attribute_default { - static void init(const is_operator &, function_record *r) { r->is_operator = true; } -}; - -template <> struct process_attribute : process_attribute_default { - static void init(const is_new_style_constructor &, function_record *r) { r->is_new_style_constructor = true; } -}; - -/// Process a keyword argument attribute (*without* a default value) -template <> struct process_attribute : process_attribute_default { - static void init(const arg &a, function_record *r) { - if (r->is_method && r->args.empty()) - r->args.emplace_back("self", nullptr, handle(), true /*convert*/, false /*none not allowed*/); - r->args.emplace_back(a.name, nullptr, handle(), !a.flag_noconvert, a.flag_none); - } -}; - -/// Process a keyword argument attribute (*with* a default value) -template <> struct process_attribute : process_attribute_default { - static void init(const arg_v &a, function_record *r) { - if (r->is_method && r->args.empty()) - r->args.emplace_back("self", nullptr /*descr*/, handle() /*parent*/, true /*convert*/, false /*none not allowed*/); - - if (!a.value) { -#if !defined(NDEBUG) - std::string descr("'"); - if (a.name) descr += std::string(a.name) + ": "; - descr += a.type + "'"; - if (r->is_method) { - if (r->name) - descr += " in method '" + (std::string) str(r->scope) + "." + (std::string) r->name + "'"; - else - descr += " in method of '" + (std::string) str(r->scope) + "'"; - } else if (r->name) { - descr += " in function '" + (std::string) r->name + "'"; - } - pybind11_fail("arg(): could not convert default argument " - + descr + " into a Python object (type not registered yet?)"); -#else - pybind11_fail("arg(): could not convert default argument " - "into a Python object (type not registered yet?). " - "Compile in debug mode for more information."); -#endif - } - r->args.emplace_back(a.name, a.descr, a.value.inc_ref(), !a.flag_noconvert, a.flag_none); - } -}; - -/// Process a parent class attribute. Single inheritance only (class_ itself already guarantees that) -template -struct process_attribute::value>> : process_attribute_default { - static void init(const handle &h, type_record *r) { r->bases.append(h); } -}; - -/// Process a parent class attribute (deprecated, does not support multiple inheritance) -template -struct process_attribute> : process_attribute_default> { - static void init(const base &, type_record *r) { r->add_base(typeid(T), nullptr); } -}; - -/// Process a multiple inheritance attribute -template <> -struct process_attribute : process_attribute_default { - static void init(const multiple_inheritance &, type_record *r) { r->multiple_inheritance = true; } -}; - -template <> -struct process_attribute : process_attribute_default { - static void init(const dynamic_attr &, type_record *r) { r->dynamic_attr = true; } -}; - -template <> -struct process_attribute : process_attribute_default { - static void init(const buffer_protocol &, type_record *r) { r->buffer_protocol = true; } -}; - -template <> -struct process_attribute : process_attribute_default { - static void init(const metaclass &m, type_record *r) { r->metaclass = m.value; } -}; - -template <> -struct process_attribute : process_attribute_default { - static void init(const module_local &l, type_record *r) { r->module_local = l.value; } -}; - -/// Process an 'arithmetic' attribute for enums (does nothing here) -template <> -struct process_attribute : process_attribute_default {}; - -template -struct process_attribute> : process_attribute_default> { }; - -/** - * Process a keep_alive call policy -- invokes keep_alive_impl during the - * pre-call handler if both Nurse, Patient != 0 and use the post-call handler - * otherwise - */ -template struct process_attribute> : public process_attribute_default> { - template = 0> - static void precall(function_call &call) { keep_alive_impl(Nurse, Patient, call, handle()); } - template = 0> - static void postcall(function_call &, handle) { } - template = 0> - static void precall(function_call &) { } - template = 0> - static void postcall(function_call &call, handle ret) { keep_alive_impl(Nurse, Patient, call, ret); } -}; - -/// Recursively iterate over variadic template arguments -template struct process_attributes { - static void init(const Args&... args, function_record *r) { - int unused[] = { 0, (process_attribute::type>::init(args, r), 0) ... }; - ignore_unused(unused); - } - static void init(const Args&... args, type_record *r) { - int unused[] = { 0, (process_attribute::type>::init(args, r), 0) ... }; - ignore_unused(unused); - } - static void precall(function_call &call) { - int unused[] = { 0, (process_attribute::type>::precall(call), 0) ... }; - ignore_unused(unused); - } - static void postcall(function_call &call, handle fn_ret) { - int unused[] = { 0, (process_attribute::type>::postcall(call, fn_ret), 0) ... }; - ignore_unused(unused); - } -}; - -template -using is_call_guard = is_instantiation; - -/// Extract the ``type`` from the first `call_guard` in `Extras...` (or `void_type` if none found) -template -using extract_guard_t = typename exactly_one_t, Extra...>::type; - -/// Check the number of named arguments at compile time -template ::value...), - size_t self = constexpr_sum(std::is_same::value...)> -constexpr bool expected_num_args(size_t nargs, bool has_args, bool has_kwargs) { - return named == 0 || (self + named + has_args + has_kwargs) == nargs; -} - -NAMESPACE_END(detail) -NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/ptocr/postprocess/dbprocess/include/pybind11/buffer_info.h b/ptocr/postprocess/dbprocess/include/pybind11/buffer_info.h deleted file mode 100644 index 9f072fa..0000000 --- a/ptocr/postprocess/dbprocess/include/pybind11/buffer_info.h +++ /dev/null @@ -1,108 +0,0 @@ -/* - pybind11/buffer_info.h: Python buffer object interface - - Copyright (c) 2016 Wenzel Jakob - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#include "detail/common.h" - -NAMESPACE_BEGIN(PYBIND11_NAMESPACE) - -/// Information record describing a Python buffer object -struct buffer_info { - void *ptr = nullptr; // Pointer to the underlying storage - ssize_t itemsize = 0; // Size of individual items in bytes - ssize_t size = 0; // Total number of entries - std::string format; // For homogeneous buffers, this should be set to format_descriptor::format() - ssize_t ndim = 0; // Number of dimensions - std::vector shape; // Shape of the tensor (1 entry per dimension) - std::vector strides; // Number of entries between adjacent entries (for each per dimension) - - buffer_info() { } - - buffer_info(void *ptr, ssize_t itemsize, const std::string &format, ssize_t ndim, - detail::any_container shape_in, detail::any_container strides_in) - : ptr(ptr), itemsize(itemsize), size(1), format(format), ndim(ndim), - shape(std::move(shape_in)), strides(std::move(strides_in)) { - if (ndim != (ssize_t) shape.size() || ndim != (ssize_t) strides.size()) - pybind11_fail("buffer_info: ndim doesn't match shape and/or strides length"); - for (size_t i = 0; i < (size_t) ndim; ++i) - size *= shape[i]; - } - - template - buffer_info(T *ptr, detail::any_container shape_in, detail::any_container strides_in) - : buffer_info(private_ctr_tag(), ptr, sizeof(T), format_descriptor::format(), static_cast(shape_in->size()), std::move(shape_in), std::move(strides_in)) { } - - buffer_info(void *ptr, ssize_t itemsize, const std::string &format, ssize_t size) - : buffer_info(ptr, itemsize, format, 1, {size}, {itemsize}) { } - - template - buffer_info(T *ptr, ssize_t size) - : buffer_info(ptr, sizeof(T), format_descriptor::format(), size) { } - - explicit buffer_info(Py_buffer *view, bool ownview = true) - : buffer_info(view->buf, view->itemsize, view->format, view->ndim, - {view->shape, view->shape + view->ndim}, {view->strides, view->strides + view->ndim}) { - this->view = view; - this->ownview = ownview; - } - - buffer_info(const buffer_info &) = delete; - buffer_info& operator=(const buffer_info &) = delete; - - buffer_info(buffer_info &&other) { - (*this) = std::move(other); - } - - buffer_info& operator=(buffer_info &&rhs) { - ptr = rhs.ptr; - itemsize = rhs.itemsize; - size = rhs.size; - format = std::move(rhs.format); - ndim = rhs.ndim; - shape = std::move(rhs.shape); - strides = std::move(rhs.strides); - std::swap(view, rhs.view); - std::swap(ownview, rhs.ownview); - return *this; - } - - ~buffer_info() { - if (view && ownview) { PyBuffer_Release(view); delete view; } - } - -private: - struct private_ctr_tag { }; - - buffer_info(private_ctr_tag, void *ptr, ssize_t itemsize, const std::string &format, ssize_t ndim, - detail::any_container &&shape_in, detail::any_container &&strides_in) - : buffer_info(ptr, itemsize, format, ndim, std::move(shape_in), std::move(strides_in)) { } - - Py_buffer *view = nullptr; - bool ownview = false; -}; - -NAMESPACE_BEGIN(detail) - -template struct compare_buffer_info { - static bool compare(const buffer_info& b) { - return b.format == format_descriptor::format() && b.itemsize == (ssize_t) sizeof(T); - } -}; - -template struct compare_buffer_info::value>> { - static bool compare(const buffer_info& b) { - return (size_t) b.itemsize == sizeof(T) && (b.format == format_descriptor::value || - ((sizeof(T) == sizeof(long)) && b.format == (std::is_unsigned::value ? "L" : "l")) || - ((sizeof(T) == sizeof(size_t)) && b.format == (std::is_unsigned::value ? "N" : "n"))); - } -}; - -NAMESPACE_END(detail) -NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/ptocr/postprocess/dbprocess/include/pybind11/cast.h b/ptocr/postprocess/dbprocess/include/pybind11/cast.h deleted file mode 100644 index 80abb2b..0000000 --- a/ptocr/postprocess/dbprocess/include/pybind11/cast.h +++ /dev/null @@ -1,2128 +0,0 @@ -/* - pybind11/cast.h: Partial template specializations to cast between - C++ and Python types - - Copyright (c) 2016 Wenzel Jakob - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#include "pytypes.h" -#include "detail/typeid.h" -#include "detail/descr.h" -#include "detail/internals.h" -#include -#include -#include -#include - -#if defined(PYBIND11_CPP17) -# if defined(__has_include) -# if __has_include() -# define PYBIND11_HAS_STRING_VIEW -# endif -# elif defined(_MSC_VER) -# define PYBIND11_HAS_STRING_VIEW -# endif -#endif -#ifdef PYBIND11_HAS_STRING_VIEW -#include -#endif - -NAMESPACE_BEGIN(PYBIND11_NAMESPACE) -NAMESPACE_BEGIN(detail) - -/// A life support system for temporary objects created by `type_caster::load()`. -/// Adding a patient will keep it alive up until the enclosing function returns. -class loader_life_support { -public: - /// A new patient frame is created when a function is entered - loader_life_support() { - get_internals().loader_patient_stack.push_back(nullptr); - } - - /// ... and destroyed after it returns - ~loader_life_support() { - auto &stack = get_internals().loader_patient_stack; - if (stack.empty()) - pybind11_fail("loader_life_support: internal error"); - - auto ptr = stack.back(); - stack.pop_back(); - Py_CLEAR(ptr); - - // A heuristic to reduce the stack's capacity (e.g. after long recursive calls) - if (stack.capacity() > 16 && stack.size() != 0 && stack.capacity() / stack.size() > 2) - stack.shrink_to_fit(); - } - - /// This can only be used inside a pybind11-bound function, either by `argument_loader` - /// at argument preparation time or by `py::cast()` at execution time. - PYBIND11_NOINLINE static void add_patient(handle h) { - auto &stack = get_internals().loader_patient_stack; - if (stack.empty()) - throw cast_error("When called outside a bound function, py::cast() cannot " - "do Python -> C++ conversions which require the creation " - "of temporary values"); - - auto &list_ptr = stack.back(); - if (list_ptr == nullptr) { - list_ptr = PyList_New(1); - if (!list_ptr) - pybind11_fail("loader_life_support: error allocating list"); - PyList_SET_ITEM(list_ptr, 0, h.inc_ref().ptr()); - } else { - auto result = PyList_Append(list_ptr, h.ptr()); - if (result == -1) - pybind11_fail("loader_life_support: error adding patient"); - } - } -}; - -// Gets the cache entry for the given type, creating it if necessary. The return value is the pair -// returned by emplace, i.e. an iterator for the entry and a bool set to `true` if the entry was -// just created. -inline std::pair all_type_info_get_cache(PyTypeObject *type); - -// Populates a just-created cache entry. -PYBIND11_NOINLINE inline void all_type_info_populate(PyTypeObject *t, std::vector &bases) { - std::vector check; - for (handle parent : reinterpret_borrow(t->tp_bases)) - check.push_back((PyTypeObject *) parent.ptr()); - - auto const &type_dict = get_internals().registered_types_py; - for (size_t i = 0; i < check.size(); i++) { - auto type = check[i]; - // Ignore Python2 old-style class super type: - if (!PyType_Check((PyObject *) type)) continue; - - // Check `type` in the current set of registered python types: - auto it = type_dict.find(type); - if (it != type_dict.end()) { - // We found a cache entry for it, so it's either pybind-registered or has pre-computed - // pybind bases, but we have to make sure we haven't already seen the type(s) before: we - // want to follow Python/virtual C++ rules that there should only be one instance of a - // common base. - for (auto *tinfo : it->second) { - // NB: Could use a second set here, rather than doing a linear search, but since - // having a large number of immediate pybind11-registered types seems fairly - // unlikely, that probably isn't worthwhile. - bool found = false; - for (auto *known : bases) { - if (known == tinfo) { found = true; break; } - } - if (!found) bases.push_back(tinfo); - } - } - else if (type->tp_bases) { - // It's some python type, so keep follow its bases classes to look for one or more - // registered types - if (i + 1 == check.size()) { - // When we're at the end, we can pop off the current element to avoid growing - // `check` when adding just one base (which is typical--i.e. when there is no - // multiple inheritance) - check.pop_back(); - i--; - } - for (handle parent : reinterpret_borrow(type->tp_bases)) - check.push_back((PyTypeObject *) parent.ptr()); - } - } -} - -/** - * Extracts vector of type_info pointers of pybind-registered roots of the given Python type. Will - * be just 1 pybind type for the Python type of a pybind-registered class, or for any Python-side - * derived class that uses single inheritance. Will contain as many types as required for a Python - * class that uses multiple inheritance to inherit (directly or indirectly) from multiple - * pybind-registered classes. Will be empty if neither the type nor any base classes are - * pybind-registered. - * - * The value is cached for the lifetime of the Python type. - */ -inline const std::vector &all_type_info(PyTypeObject *type) { - auto ins = all_type_info_get_cache(type); - if (ins.second) - // New cache entry: populate it - all_type_info_populate(type, ins.first->second); - - return ins.first->second; -} - -/** - * Gets a single pybind11 type info for a python type. Returns nullptr if neither the type nor any - * ancestors are pybind11-registered. Throws an exception if there are multiple bases--use - * `all_type_info` instead if you want to support multiple bases. - */ -PYBIND11_NOINLINE inline detail::type_info* get_type_info(PyTypeObject *type) { - auto &bases = all_type_info(type); - if (bases.size() == 0) - return nullptr; - if (bases.size() > 1) - pybind11_fail("pybind11::detail::get_type_info: type has multiple pybind11-registered bases"); - return bases.front(); -} - -inline detail::type_info *get_local_type_info(const std::type_index &tp) { - auto &locals = registered_local_types_cpp(); - auto it = locals.find(tp); - if (it != locals.end()) - return it->second; - return nullptr; -} - -inline detail::type_info *get_global_type_info(const std::type_index &tp) { - auto &types = get_internals().registered_types_cpp; - auto it = types.find(tp); - if (it != types.end()) - return it->second; - return nullptr; -} - -/// Return the type info for a given C++ type; on lookup failure can either throw or return nullptr. -PYBIND11_NOINLINE inline detail::type_info *get_type_info(const std::type_index &tp, - bool throw_if_missing = false) { - if (auto ltype = get_local_type_info(tp)) - return ltype; - if (auto gtype = get_global_type_info(tp)) - return gtype; - - if (throw_if_missing) { - std::string tname = tp.name(); - detail::clean_type_id(tname); - pybind11_fail("pybind11::detail::get_type_info: unable to find type info for \"" + tname + "\""); - } - return nullptr; -} - -PYBIND11_NOINLINE inline handle get_type_handle(const std::type_info &tp, bool throw_if_missing) { - detail::type_info *type_info = get_type_info(tp, throw_if_missing); - return handle(type_info ? ((PyObject *) type_info->type) : nullptr); -} - -struct value_and_holder { - instance *inst; - size_t index; - const detail::type_info *type; - void **vh; - - // Main constructor for a found value/holder: - value_and_holder(instance *i, const detail::type_info *type, size_t vpos, size_t index) : - inst{i}, index{index}, type{type}, - vh{inst->simple_layout ? inst->simple_value_holder : &inst->nonsimple.values_and_holders[vpos]} - {} - - // Default constructor (used to signal a value-and-holder not found by get_value_and_holder()) - value_and_holder() : inst{nullptr} {} - - // Used for past-the-end iterator - value_and_holder(size_t index) : index{index} {} - - template V *&value_ptr() const { - return reinterpret_cast(vh[0]); - } - // True if this `value_and_holder` has a non-null value pointer - explicit operator bool() const { return value_ptr(); } - - template H &holder() const { - return reinterpret_cast(vh[1]); - } - bool holder_constructed() const { - return inst->simple_layout - ? inst->simple_holder_constructed - : inst->nonsimple.status[index] & instance::status_holder_constructed; - } - void set_holder_constructed(bool v = true) { - if (inst->simple_layout) - inst->simple_holder_constructed = v; - else if (v) - inst->nonsimple.status[index] |= instance::status_holder_constructed; - else - inst->nonsimple.status[index] &= (uint8_t) ~instance::status_holder_constructed; - } - bool instance_registered() const { - return inst->simple_layout - ? inst->simple_instance_registered - : inst->nonsimple.status[index] & instance::status_instance_registered; - } - void set_instance_registered(bool v = true) { - if (inst->simple_layout) - inst->simple_instance_registered = v; - else if (v) - inst->nonsimple.status[index] |= instance::status_instance_registered; - else - inst->nonsimple.status[index] &= (uint8_t) ~instance::status_instance_registered; - } -}; - -// Container for accessing and iterating over an instance's values/holders -struct values_and_holders { -private: - instance *inst; - using type_vec = std::vector; - const type_vec &tinfo; - -public: - values_and_holders(instance *inst) : inst{inst}, tinfo(all_type_info(Py_TYPE(inst))) {} - - struct iterator { - private: - instance *inst; - const type_vec *types; - value_and_holder curr; - friend struct values_and_holders; - iterator(instance *inst, const type_vec *tinfo) - : inst{inst}, types{tinfo}, - curr(inst /* instance */, - types->empty() ? nullptr : (*types)[0] /* type info */, - 0, /* vpos: (non-simple types only): the first vptr comes first */ - 0 /* index */) - {} - // Past-the-end iterator: - iterator(size_t end) : curr(end) {} - public: - bool operator==(const iterator &other) { return curr.index == other.curr.index; } - bool operator!=(const iterator &other) { return curr.index != other.curr.index; } - iterator &operator++() { - if (!inst->simple_layout) - curr.vh += 1 + (*types)[curr.index]->holder_size_in_ptrs; - ++curr.index; - curr.type = curr.index < types->size() ? (*types)[curr.index] : nullptr; - return *this; - } - value_and_holder &operator*() { return curr; } - value_and_holder *operator->() { return &curr; } - }; - - iterator begin() { return iterator(inst, &tinfo); } - iterator end() { return iterator(tinfo.size()); } - - iterator find(const type_info *find_type) { - auto it = begin(), endit = end(); - while (it != endit && it->type != find_type) ++it; - return it; - } - - size_t size() { return tinfo.size(); } -}; - -/** - * Extracts C++ value and holder pointer references from an instance (which may contain multiple - * values/holders for python-side multiple inheritance) that match the given type. Throws an error - * if the given type (or ValueType, if omitted) is not a pybind11 base of the given instance. If - * `find_type` is omitted (or explicitly specified as nullptr) the first value/holder are returned, - * regardless of type (and the resulting .type will be nullptr). - * - * The returned object should be short-lived: in particular, it must not outlive the called-upon - * instance. - */ -PYBIND11_NOINLINE inline value_and_holder instance::get_value_and_holder(const type_info *find_type /*= nullptr default in common.h*/, bool throw_if_missing /*= true in common.h*/) { - // Optimize common case: - if (!find_type || Py_TYPE(this) == find_type->type) - return value_and_holder(this, find_type, 0, 0); - - detail::values_and_holders vhs(this); - auto it = vhs.find(find_type); - if (it != vhs.end()) - return *it; - - if (!throw_if_missing) - return value_and_holder(); - -#if defined(NDEBUG) - pybind11_fail("pybind11::detail::instance::get_value_and_holder: " - "type is not a pybind11 base of the given instance " - "(compile in debug mode for type details)"); -#else - pybind11_fail("pybind11::detail::instance::get_value_and_holder: `" + - std::string(find_type->type->tp_name) + "' is not a pybind11 base of the given `" + - std::string(Py_TYPE(this)->tp_name) + "' instance"); -#endif -} - -PYBIND11_NOINLINE inline void instance::allocate_layout() { - auto &tinfo = all_type_info(Py_TYPE(this)); - - const size_t n_types = tinfo.size(); - - if (n_types == 0) - pybind11_fail("instance allocation failed: new instance has no pybind11-registered base types"); - - simple_layout = - n_types == 1 && tinfo.front()->holder_size_in_ptrs <= instance_simple_holder_in_ptrs(); - - // Simple path: no python-side multiple inheritance, and a small-enough holder - if (simple_layout) { - simple_value_holder[0] = nullptr; - simple_holder_constructed = false; - simple_instance_registered = false; - } - else { // multiple base types or a too-large holder - // Allocate space to hold: [v1*][h1][v2*][h2]...[bb...] where [vN*] is a value pointer, - // [hN] is the (uninitialized) holder instance for value N, and [bb...] is a set of bool - // values that tracks whether each associated holder has been initialized. Each [block] is - // padded, if necessary, to an integer multiple of sizeof(void *). - size_t space = 0; - for (auto t : tinfo) { - space += 1; // value pointer - space += t->holder_size_in_ptrs; // holder instance - } - size_t flags_at = space; - space += size_in_ptrs(n_types); // status bytes (holder_constructed and instance_registered) - - // Allocate space for flags, values, and holders, and initialize it to 0 (flags and values, - // in particular, need to be 0). Use Python's memory allocation functions: in Python 3.6 - // they default to using pymalloc, which is designed to be efficient for small allocations - // like the one we're doing here; in earlier versions (and for larger allocations) they are - // just wrappers around malloc. -#if PY_VERSION_HEX >= 0x03050000 - nonsimple.values_and_holders = (void **) PyMem_Calloc(space, sizeof(void *)); - if (!nonsimple.values_and_holders) throw std::bad_alloc(); -#else - nonsimple.values_and_holders = (void **) PyMem_New(void *, space); - if (!nonsimple.values_and_holders) throw std::bad_alloc(); - std::memset(nonsimple.values_and_holders, 0, space * sizeof(void *)); -#endif - nonsimple.status = reinterpret_cast(&nonsimple.values_and_holders[flags_at]); - } - owned = true; -} - -PYBIND11_NOINLINE inline void instance::deallocate_layout() { - if (!simple_layout) - PyMem_Free(nonsimple.values_and_holders); -} - -PYBIND11_NOINLINE inline bool isinstance_generic(handle obj, const std::type_info &tp) { - handle type = detail::get_type_handle(tp, false); - if (!type) - return false; - return isinstance(obj, type); -} - -PYBIND11_NOINLINE inline std::string error_string() { - if (!PyErr_Occurred()) { - PyErr_SetString(PyExc_RuntimeError, "Unknown internal error occurred"); - return "Unknown internal error occurred"; - } - - error_scope scope; // Preserve error state - - std::string errorString; - if (scope.type) { - errorString += handle(scope.type).attr("__name__").cast(); - errorString += ": "; - } - if (scope.value) - errorString += (std::string) str(scope.value); - - PyErr_NormalizeException(&scope.type, &scope.value, &scope.trace); - -#if PY_MAJOR_VERSION >= 3 - if (scope.trace != nullptr) - PyException_SetTraceback(scope.value, scope.trace); -#endif - -#if !defined(PYPY_VERSION) - if (scope.trace) { - PyTracebackObject *trace = (PyTracebackObject *) scope.trace; - - /* Get the deepest trace possible */ - while (trace->tb_next) - trace = trace->tb_next; - - PyFrameObject *frame = trace->tb_frame; - errorString += "\n\nAt:\n"; - while (frame) { - int lineno = PyFrame_GetLineNumber(frame); - errorString += - " " + handle(frame->f_code->co_filename).cast() + - "(" + std::to_string(lineno) + "): " + - handle(frame->f_code->co_name).cast() + "\n"; - frame = frame->f_back; - } - } -#endif - - return errorString; -} - -PYBIND11_NOINLINE inline handle get_object_handle(const void *ptr, const detail::type_info *type ) { - auto &instances = get_internals().registered_instances; - auto range = instances.equal_range(ptr); - for (auto it = range.first; it != range.second; ++it) { - for (auto vh : values_and_holders(it->second)) { - if (vh.type == type) - return handle((PyObject *) it->second); - } - } - return handle(); -} - -inline PyThreadState *get_thread_state_unchecked() { -#if defined(PYPY_VERSION) - return PyThreadState_GET(); -#elif PY_VERSION_HEX < 0x03000000 - return _PyThreadState_Current; -#elif PY_VERSION_HEX < 0x03050000 - return (PyThreadState*) _Py_atomic_load_relaxed(&_PyThreadState_Current); -#elif PY_VERSION_HEX < 0x03050200 - return (PyThreadState*) _PyThreadState_Current.value; -#else - return _PyThreadState_UncheckedGet(); -#endif -} - -// Forward declarations -inline void keep_alive_impl(handle nurse, handle patient); -inline PyObject *make_new_instance(PyTypeObject *type); - -class type_caster_generic { -public: - PYBIND11_NOINLINE type_caster_generic(const std::type_info &type_info) - : typeinfo(get_type_info(type_info)), cpptype(&type_info) { } - - type_caster_generic(const type_info *typeinfo) - : typeinfo(typeinfo), cpptype(typeinfo ? typeinfo->cpptype : nullptr) { } - - bool load(handle src, bool convert) { - return load_impl(src, convert); - } - - PYBIND11_NOINLINE static handle cast(const void *_src, return_value_policy policy, handle parent, - const detail::type_info *tinfo, - void *(*copy_constructor)(const void *), - void *(*move_constructor)(const void *), - const void *existing_holder = nullptr) { - if (!tinfo) // no type info: error will be set already - return handle(); - - void *src = const_cast(_src); - if (src == nullptr) - return none().release(); - - auto it_instances = get_internals().registered_instances.equal_range(src); - for (auto it_i = it_instances.first; it_i != it_instances.second; ++it_i) { - for (auto instance_type : detail::all_type_info(Py_TYPE(it_i->second))) { - if (instance_type && same_type(*instance_type->cpptype, *tinfo->cpptype)) - return handle((PyObject *) it_i->second).inc_ref(); - } - } - - auto inst = reinterpret_steal(make_new_instance(tinfo->type)); - auto wrapper = reinterpret_cast(inst.ptr()); - wrapper->owned = false; - void *&valueptr = values_and_holders(wrapper).begin()->value_ptr(); - - switch (policy) { - case return_value_policy::automatic: - case return_value_policy::take_ownership: - valueptr = src; - wrapper->owned = true; - break; - - case return_value_policy::automatic_reference: - case return_value_policy::reference: - valueptr = src; - wrapper->owned = false; - break; - - case return_value_policy::copy: - if (copy_constructor) - valueptr = copy_constructor(src); - else - throw cast_error("return_value_policy = copy, but the " - "object is non-copyable!"); - wrapper->owned = true; - break; - - case return_value_policy::move: - if (move_constructor) - valueptr = move_constructor(src); - else if (copy_constructor) - valueptr = copy_constructor(src); - else - throw cast_error("return_value_policy = move, but the " - "object is neither movable nor copyable!"); - wrapper->owned = true; - break; - - case return_value_policy::reference_internal: - valueptr = src; - wrapper->owned = false; - keep_alive_impl(inst, parent); - break; - - default: - throw cast_error("unhandled return_value_policy: should not happen!"); - } - - tinfo->init_instance(wrapper, existing_holder); - - return inst.release(); - } - - // Base methods for generic caster; there are overridden in copyable_holder_caster - void load_value(value_and_holder &&v_h) { - auto *&vptr = v_h.value_ptr(); - // Lazy allocation for unallocated values: - if (vptr == nullptr) { - auto *type = v_h.type ? v_h.type : typeinfo; - if (type->operator_new) { - vptr = type->operator_new(type->type_size); - } else { - #if defined(PYBIND11_CPP17) - if (type->type_align > __STDCPP_DEFAULT_NEW_ALIGNMENT__) - vptr = ::operator new(type->type_size, - (std::align_val_t) type->type_align); - else - #endif - vptr = ::operator new(type->type_size); - } - } - value = vptr; - } - bool try_implicit_casts(handle src, bool convert) { - for (auto &cast : typeinfo->implicit_casts) { - type_caster_generic sub_caster(*cast.first); - if (sub_caster.load(src, convert)) { - value = cast.second(sub_caster.value); - return true; - } - } - return false; - } - bool try_direct_conversions(handle src) { - for (auto &converter : *typeinfo->direct_conversions) { - if (converter(src.ptr(), value)) - return true; - } - return false; - } - void check_holder_compat() {} - - PYBIND11_NOINLINE static void *local_load(PyObject *src, const type_info *ti) { - auto caster = type_caster_generic(ti); - if (caster.load(src, false)) - return caster.value; - return nullptr; - } - - /// Try to load with foreign typeinfo, if available. Used when there is no - /// native typeinfo, or when the native one wasn't able to produce a value. - PYBIND11_NOINLINE bool try_load_foreign_module_local(handle src) { - constexpr auto *local_key = PYBIND11_MODULE_LOCAL_ID; - const auto pytype = src.get_type(); - if (!hasattr(pytype, local_key)) - return false; - - type_info *foreign_typeinfo = reinterpret_borrow(getattr(pytype, local_key)); - // Only consider this foreign loader if actually foreign and is a loader of the correct cpp type - if (foreign_typeinfo->module_local_load == &local_load - || (cpptype && !same_type(*cpptype, *foreign_typeinfo->cpptype))) - return false; - - if (auto result = foreign_typeinfo->module_local_load(src.ptr(), foreign_typeinfo)) { - value = result; - return true; - } - return false; - } - - // Implementation of `load`; this takes the type of `this` so that it can dispatch the relevant - // bits of code between here and copyable_holder_caster where the two classes need different - // logic (without having to resort to virtual inheritance). - template - PYBIND11_NOINLINE bool load_impl(handle src, bool convert) { - if (!src) return false; - if (!typeinfo) return try_load_foreign_module_local(src); - if (src.is_none()) { - // Defer accepting None to other overloads (if we aren't in convert mode): - if (!convert) return false; - value = nullptr; - return true; - } - - auto &this_ = static_cast(*this); - this_.check_holder_compat(); - - PyTypeObject *srctype = Py_TYPE(src.ptr()); - - // Case 1: If src is an exact type match for the target type then we can reinterpret_cast - // the instance's value pointer to the target type: - if (srctype == typeinfo->type) { - this_.load_value(reinterpret_cast(src.ptr())->get_value_and_holder()); - return true; - } - // Case 2: We have a derived class - else if (PyType_IsSubtype(srctype, typeinfo->type)) { - auto &bases = all_type_info(srctype); - bool no_cpp_mi = typeinfo->simple_type; - - // Case 2a: the python type is a Python-inherited derived class that inherits from just - // one simple (no MI) pybind11 class, or is an exact match, so the C++ instance is of - // the right type and we can use reinterpret_cast. - // (This is essentially the same as case 2b, but because not using multiple inheritance - // is extremely common, we handle it specially to avoid the loop iterator and type - // pointer lookup overhead) - if (bases.size() == 1 && (no_cpp_mi || bases.front()->type == typeinfo->type)) { - this_.load_value(reinterpret_cast(src.ptr())->get_value_and_holder()); - return true; - } - // Case 2b: the python type inherits from multiple C++ bases. Check the bases to see if - // we can find an exact match (or, for a simple C++ type, an inherited match); if so, we - // can safely reinterpret_cast to the relevant pointer. - else if (bases.size() > 1) { - for (auto base : bases) { - if (no_cpp_mi ? PyType_IsSubtype(base->type, typeinfo->type) : base->type == typeinfo->type) { - this_.load_value(reinterpret_cast(src.ptr())->get_value_and_holder(base)); - return true; - } - } - } - - // Case 2c: C++ multiple inheritance is involved and we couldn't find an exact type match - // in the registered bases, above, so try implicit casting (needed for proper C++ casting - // when MI is involved). - if (this_.try_implicit_casts(src, convert)) - return true; - } - - // Perform an implicit conversion - if (convert) { - for (auto &converter : typeinfo->implicit_conversions) { - auto temp = reinterpret_steal(converter(src.ptr(), typeinfo->type)); - if (load_impl(temp, false)) { - loader_life_support::add_patient(temp); - return true; - } - } - if (this_.try_direct_conversions(src)) - return true; - } - - // Failed to match local typeinfo. Try again with global. - if (typeinfo->module_local) { - if (auto gtype = get_global_type_info(*typeinfo->cpptype)) { - typeinfo = gtype; - return load(src, false); - } - } - - // Global typeinfo has precedence over foreign module_local - return try_load_foreign_module_local(src); - } - - - // Called to do type lookup and wrap the pointer and type in a pair when a dynamic_cast - // isn't needed or can't be used. If the type is unknown, sets the error and returns a pair - // with .second = nullptr. (p.first = nullptr is not an error: it becomes None). - PYBIND11_NOINLINE static std::pair src_and_type( - const void *src, const std::type_info &cast_type, const std::type_info *rtti_type = nullptr) { - if (auto *tpi = get_type_info(cast_type)) - return {src, const_cast(tpi)}; - - // Not found, set error: - std::string tname = rtti_type ? rtti_type->name() : cast_type.name(); - detail::clean_type_id(tname); - std::string msg = "Unregistered type : " + tname; - PyErr_SetString(PyExc_TypeError, msg.c_str()); - return {nullptr, nullptr}; - } - - const type_info *typeinfo = nullptr; - const std::type_info *cpptype = nullptr; - void *value = nullptr; -}; - -/** - * Determine suitable casting operator for pointer-or-lvalue-casting type casters. The type caster - * needs to provide `operator T*()` and `operator T&()` operators. - * - * If the type supports moving the value away via an `operator T&&() &&` method, it should use - * `movable_cast_op_type` instead. - */ -template -using cast_op_type = - conditional_t>::value, - typename std::add_pointer>::type, - typename std::add_lvalue_reference>::type>; - -/** - * Determine suitable casting operator for a type caster with a movable value. Such a type caster - * needs to provide `operator T*()`, `operator T&()`, and `operator T&&() &&`. The latter will be - * called in appropriate contexts where the value can be moved rather than copied. - * - * These operator are automatically provided when using the PYBIND11_TYPE_CASTER macro. - */ -template -using movable_cast_op_type = - conditional_t::type>::value, - typename std::add_pointer>::type, - conditional_t::value, - typename std::add_rvalue_reference>::type, - typename std::add_lvalue_reference>::type>>; - -// std::is_copy_constructible isn't quite enough: it lets std::vector (and similar) through when -// T is non-copyable, but code containing such a copy constructor fails to actually compile. -template struct is_copy_constructible : std::is_copy_constructible {}; - -// Specialization for types that appear to be copy constructible but also look like stl containers -// (we specifically check for: has `value_type` and `reference` with `reference = value_type&`): if -// so, copy constructability depends on whether the value_type is copy constructible. -template struct is_copy_constructible, - std::is_same - >::value>> : is_copy_constructible {}; - -#if !defined(PYBIND11_CPP17) -// Likewise for std::pair before C++17 (which mandates that the copy constructor not exist when the -// two types aren't themselves copy constructible). -template struct is_copy_constructible> - : all_of, is_copy_constructible> {}; -#endif - -NAMESPACE_END(detail) - -// polymorphic_type_hook::get(src, tinfo) determines whether the object pointed -// to by `src` actually is an instance of some class derived from `itype`. -// If so, it sets `tinfo` to point to the std::type_info representing that derived -// type, and returns a pointer to the start of the most-derived object of that type -// (in which `src` is a subobject; this will be the same address as `src` in most -// single inheritance cases). If not, or if `src` is nullptr, it simply returns `src` -// and leaves `tinfo` at its default value of nullptr. -// -// The default polymorphic_type_hook just returns src. A specialization for polymorphic -// types determines the runtime type of the passed object and adjusts the this-pointer -// appropriately via dynamic_cast. This is what enables a C++ Animal* to appear -// to Python as a Dog (if Dog inherits from Animal, Animal is polymorphic, Dog is -// registered with pybind11, and this Animal is in fact a Dog). -// -// You may specialize polymorphic_type_hook yourself for types that want to appear -// polymorphic to Python but do not use C++ RTTI. (This is a not uncommon pattern -// in performance-sensitive applications, used most notably in LLVM.) -template -struct polymorphic_type_hook -{ - static const void *get(const itype *src, const std::type_info*&) { return src; } -}; -template -struct polymorphic_type_hook::value>> -{ - static const void *get(const itype *src, const std::type_info*& type) { - type = src ? &typeid(*src) : nullptr; - return dynamic_cast(src); - } -}; - -NAMESPACE_BEGIN(detail) - -/// Generic type caster for objects stored on the heap -template class type_caster_base : public type_caster_generic { - using itype = intrinsic_t; - -public: - static constexpr auto name = _(); - - type_caster_base() : type_caster_base(typeid(type)) { } - explicit type_caster_base(const std::type_info &info) : type_caster_generic(info) { } - - static handle cast(const itype &src, return_value_policy policy, handle parent) { - if (policy == return_value_policy::automatic || policy == return_value_policy::automatic_reference) - policy = return_value_policy::copy; - return cast(&src, policy, parent); - } - - static handle cast(itype &&src, return_value_policy, handle parent) { - return cast(&src, return_value_policy::move, parent); - } - - // Returns a (pointer, type_info) pair taking care of necessary type lookup for a - // polymorphic type (using RTTI by default, but can be overridden by specializing - // polymorphic_type_hook). If the instance isn't derived, returns the base version. - static std::pair src_and_type(const itype *src) { - auto &cast_type = typeid(itype); - const std::type_info *instance_type = nullptr; - const void *vsrc = polymorphic_type_hook::get(src, instance_type); - if (instance_type && !same_type(cast_type, *instance_type)) { - // This is a base pointer to a derived type. If the derived type is registered - // with pybind11, we want to make the full derived object available. - // In the typical case where itype is polymorphic, we get the correct - // derived pointer (which may be != base pointer) by a dynamic_cast to - // most derived type. If itype is not polymorphic, we won't get here - // except via a user-provided specialization of polymorphic_type_hook, - // and the user has promised that no this-pointer adjustment is - // required in that case, so it's OK to use static_cast. - if (const auto *tpi = get_type_info(*instance_type)) - return {vsrc, tpi}; - } - // Otherwise we have either a nullptr, an `itype` pointer, or an unknown derived pointer, so - // don't do a cast - return type_caster_generic::src_and_type(src, cast_type, instance_type); - } - - static handle cast(const itype *src, return_value_policy policy, handle parent) { - auto st = src_and_type(src); - return type_caster_generic::cast( - st.first, policy, parent, st.second, - make_copy_constructor(src), make_move_constructor(src)); - } - - static handle cast_holder(const itype *src, const void *holder) { - auto st = src_and_type(src); - return type_caster_generic::cast( - st.first, return_value_policy::take_ownership, {}, st.second, - nullptr, nullptr, holder); - } - - template using cast_op_type = detail::cast_op_type; - - operator itype*() { return (type *) value; } - operator itype&() { if (!value) throw reference_cast_error(); return *((itype *) value); } - -protected: - using Constructor = void *(*)(const void *); - - /* Only enabled when the types are {copy,move}-constructible *and* when the type - does not have a private operator new implementation. */ - template ::value>> - static auto make_copy_constructor(const T *x) -> decltype(new T(*x), Constructor{}) { - return [](const void *arg) -> void * { - return new T(*reinterpret_cast(arg)); - }; - } - - template ::value>> - static auto make_move_constructor(const T *x) -> decltype(new T(std::move(*const_cast(x))), Constructor{}) { - return [](const void *arg) -> void * { - return new T(std::move(*const_cast(reinterpret_cast(arg)))); - }; - } - - static Constructor make_copy_constructor(...) { return nullptr; } - static Constructor make_move_constructor(...) { return nullptr; } -}; - -template class type_caster : public type_caster_base { }; -template using make_caster = type_caster>; - -// Shortcut for calling a caster's `cast_op_type` cast operator for casting a type_caster to a T -template typename make_caster::template cast_op_type cast_op(make_caster &caster) { - return caster.operator typename make_caster::template cast_op_type(); -} -template typename make_caster::template cast_op_type::type> -cast_op(make_caster &&caster) { - return std::move(caster).operator - typename make_caster::template cast_op_type::type>(); -} - -template class type_caster> { -private: - using caster_t = make_caster; - caster_t subcaster; - using subcaster_cast_op_type = typename caster_t::template cast_op_type; - static_assert(std::is_same::type &, subcaster_cast_op_type>::value, - "std::reference_wrapper caster requires T to have a caster with an `T &` operator"); -public: - bool load(handle src, bool convert) { return subcaster.load(src, convert); } - static constexpr auto name = caster_t::name; - static handle cast(const std::reference_wrapper &src, return_value_policy policy, handle parent) { - // It is definitely wrong to take ownership of this pointer, so mask that rvp - if (policy == return_value_policy::take_ownership || policy == return_value_policy::automatic) - policy = return_value_policy::automatic_reference; - return caster_t::cast(&src.get(), policy, parent); - } - template using cast_op_type = std::reference_wrapper; - operator std::reference_wrapper() { return subcaster.operator subcaster_cast_op_type&(); } -}; - -#define PYBIND11_TYPE_CASTER(type, py_name) \ - protected: \ - type value; \ - public: \ - static constexpr auto name = py_name; \ - template >::value, int> = 0> \ - static handle cast(T_ *src, return_value_policy policy, handle parent) { \ - if (!src) return none().release(); \ - if (policy == return_value_policy::take_ownership) { \ - auto h = cast(std::move(*src), policy, parent); delete src; return h; \ - } else { \ - return cast(*src, policy, parent); \ - } \ - } \ - operator type*() { return &value; } \ - operator type&() { return value; } \ - operator type&&() && { return std::move(value); } \ - template using cast_op_type = pybind11::detail::movable_cast_op_type - - -template using is_std_char_type = any_of< - std::is_same, /* std::string */ - std::is_same, /* std::u16string */ - std::is_same, /* std::u32string */ - std::is_same /* std::wstring */ ->; - -template -struct type_caster::value && !is_std_char_type::value>> { - using _py_type_0 = conditional_t; - using _py_type_1 = conditional_t::value, _py_type_0, typename std::make_unsigned<_py_type_0>::type>; - using py_type = conditional_t::value, double, _py_type_1>; -public: - - bool load(handle src, bool convert) { - py_type py_value; - - if (!src) - return false; - - if (std::is_floating_point::value) { - if (convert || PyFloat_Check(src.ptr())) - py_value = (py_type) PyFloat_AsDouble(src.ptr()); - else - return false; - } else if (PyFloat_Check(src.ptr())) { - return false; - } else if (std::is_unsigned::value) { - py_value = as_unsigned(src.ptr()); - } else { // signed integer: - py_value = sizeof(T) <= sizeof(long) - ? (py_type) PyLong_AsLong(src.ptr()) - : (py_type) PYBIND11_LONG_AS_LONGLONG(src.ptr()); - } - - bool py_err = py_value == (py_type) -1 && PyErr_Occurred(); - if (py_err || (std::is_integral::value && sizeof(py_type) != sizeof(T) && - (py_value < (py_type) std::numeric_limits::min() || - py_value > (py_type) std::numeric_limits::max()))) { - bool type_error = py_err && PyErr_ExceptionMatches( -#if PY_VERSION_HEX < 0x03000000 && !defined(PYPY_VERSION) - PyExc_SystemError -#else - PyExc_TypeError -#endif - ); - PyErr_Clear(); - if (type_error && convert && PyNumber_Check(src.ptr())) { - auto tmp = reinterpret_steal(std::is_floating_point::value - ? PyNumber_Float(src.ptr()) - : PyNumber_Long(src.ptr())); - PyErr_Clear(); - return load(tmp, false); - } - return false; - } - - value = (T) py_value; - return true; - } - - template - static typename std::enable_if::value, handle>::type - cast(U src, return_value_policy /* policy */, handle /* parent */) { - return PyFloat_FromDouble((double) src); - } - - template - static typename std::enable_if::value && std::is_signed::value && (sizeof(U) <= sizeof(long)), handle>::type - cast(U src, return_value_policy /* policy */, handle /* parent */) { - return PYBIND11_LONG_FROM_SIGNED((long) src); - } - - template - static typename std::enable_if::value && std::is_unsigned::value && (sizeof(U) <= sizeof(unsigned long)), handle>::type - cast(U src, return_value_policy /* policy */, handle /* parent */) { - return PYBIND11_LONG_FROM_UNSIGNED((unsigned long) src); - } - - template - static typename std::enable_if::value && std::is_signed::value && (sizeof(U) > sizeof(long)), handle>::type - cast(U src, return_value_policy /* policy */, handle /* parent */) { - return PyLong_FromLongLong((long long) src); - } - - template - static typename std::enable_if::value && std::is_unsigned::value && (sizeof(U) > sizeof(unsigned long)), handle>::type - cast(U src, return_value_policy /* policy */, handle /* parent */) { - return PyLong_FromUnsignedLongLong((unsigned long long) src); - } - - PYBIND11_TYPE_CASTER(T, _::value>("int", "float")); -}; - -template struct void_caster { -public: - bool load(handle src, bool) { - if (src && src.is_none()) - return true; - return false; - } - static handle cast(T, return_value_policy /* policy */, handle /* parent */) { - return none().inc_ref(); - } - PYBIND11_TYPE_CASTER(T, _("None")); -}; - -template <> class type_caster : public void_caster {}; - -template <> class type_caster : public type_caster { -public: - using type_caster::cast; - - bool load(handle h, bool) { - if (!h) { - return false; - } else if (h.is_none()) { - value = nullptr; - return true; - } - - /* Check if this is a capsule */ - if (isinstance(h)) { - value = reinterpret_borrow(h); - return true; - } - - /* Check if this is a C++ type */ - auto &bases = all_type_info((PyTypeObject *) h.get_type().ptr()); - if (bases.size() == 1) { // Only allowing loading from a single-value type - value = values_and_holders(reinterpret_cast(h.ptr())).begin()->value_ptr(); - return true; - } - - /* Fail */ - return false; - } - - static handle cast(const void *ptr, return_value_policy /* policy */, handle /* parent */) { - if (ptr) - return capsule(ptr).release(); - else - return none().inc_ref(); - } - - template using cast_op_type = void*&; - operator void *&() { return value; } - static constexpr auto name = _("capsule"); -private: - void *value = nullptr; -}; - -template <> class type_caster : public void_caster { }; - -template <> class type_caster { -public: - bool load(handle src, bool convert) { - if (!src) return false; - else if (src.ptr() == Py_True) { value = true; return true; } - else if (src.ptr() == Py_False) { value = false; return true; } - else if (convert || !strcmp("numpy.bool_", Py_TYPE(src.ptr())->tp_name)) { - // (allow non-implicit conversion for numpy booleans) - - Py_ssize_t res = -1; - if (src.is_none()) { - res = 0; // None is implicitly converted to False - } - #if defined(PYPY_VERSION) - // On PyPy, check that "__bool__" (or "__nonzero__" on Python 2.7) attr exists - else if (hasattr(src, PYBIND11_BOOL_ATTR)) { - res = PyObject_IsTrue(src.ptr()); - } - #else - // Alternate approach for CPython: this does the same as the above, but optimized - // using the CPython API so as to avoid an unneeded attribute lookup. - else if (auto tp_as_number = src.ptr()->ob_type->tp_as_number) { - if (PYBIND11_NB_BOOL(tp_as_number)) { - res = (*PYBIND11_NB_BOOL(tp_as_number))(src.ptr()); - } - } - #endif - if (res == 0 || res == 1) { - value = (bool) res; - return true; - } - } - return false; - } - static handle cast(bool src, return_value_policy /* policy */, handle /* parent */) { - return handle(src ? Py_True : Py_False).inc_ref(); - } - PYBIND11_TYPE_CASTER(bool, _("bool")); -}; - -// Helper class for UTF-{8,16,32} C++ stl strings: -template struct string_caster { - using CharT = typename StringType::value_type; - - // Simplify life by being able to assume standard char sizes (the standard only guarantees - // minimums, but Python requires exact sizes) - static_assert(!std::is_same::value || sizeof(CharT) == 1, "Unsupported char size != 1"); - static_assert(!std::is_same::value || sizeof(CharT) == 2, "Unsupported char16_t size != 2"); - static_assert(!std::is_same::value || sizeof(CharT) == 4, "Unsupported char32_t size != 4"); - // wchar_t can be either 16 bits (Windows) or 32 (everywhere else) - static_assert(!std::is_same::value || sizeof(CharT) == 2 || sizeof(CharT) == 4, - "Unsupported wchar_t size != 2/4"); - static constexpr size_t UTF_N = 8 * sizeof(CharT); - - bool load(handle src, bool) { -#if PY_MAJOR_VERSION < 3 - object temp; -#endif - handle load_src = src; - if (!src) { - return false; - } else if (!PyUnicode_Check(load_src.ptr())) { -#if PY_MAJOR_VERSION >= 3 - return load_bytes(load_src); -#else - if (sizeof(CharT) == 1) { - return load_bytes(load_src); - } - - // The below is a guaranteed failure in Python 3 when PyUnicode_Check returns false - if (!PYBIND11_BYTES_CHECK(load_src.ptr())) - return false; - - temp = reinterpret_steal(PyUnicode_FromObject(load_src.ptr())); - if (!temp) { PyErr_Clear(); return false; } - load_src = temp; -#endif - } - - object utfNbytes = reinterpret_steal(PyUnicode_AsEncodedString( - load_src.ptr(), UTF_N == 8 ? "utf-8" : UTF_N == 16 ? "utf-16" : "utf-32", nullptr)); - if (!utfNbytes) { PyErr_Clear(); return false; } - - const CharT *buffer = reinterpret_cast(PYBIND11_BYTES_AS_STRING(utfNbytes.ptr())); - size_t length = (size_t) PYBIND11_BYTES_SIZE(utfNbytes.ptr()) / sizeof(CharT); - if (UTF_N > 8) { buffer++; length--; } // Skip BOM for UTF-16/32 - value = StringType(buffer, length); - - // If we're loading a string_view we need to keep the encoded Python object alive: - if (IsView) - loader_life_support::add_patient(utfNbytes); - - return true; - } - - static handle cast(const StringType &src, return_value_policy /* policy */, handle /* parent */) { - const char *buffer = reinterpret_cast(src.data()); - ssize_t nbytes = ssize_t(src.size() * sizeof(CharT)); - handle s = decode_utfN(buffer, nbytes); - if (!s) throw error_already_set(); - return s; - } - - PYBIND11_TYPE_CASTER(StringType, _(PYBIND11_STRING_NAME)); - -private: - static handle decode_utfN(const char *buffer, ssize_t nbytes) { -#if !defined(PYPY_VERSION) - return - UTF_N == 8 ? PyUnicode_DecodeUTF8(buffer, nbytes, nullptr) : - UTF_N == 16 ? PyUnicode_DecodeUTF16(buffer, nbytes, nullptr, nullptr) : - PyUnicode_DecodeUTF32(buffer, nbytes, nullptr, nullptr); -#else - // PyPy seems to have multiple problems related to PyUnicode_UTF*: the UTF8 version - // sometimes segfaults for unknown reasons, while the UTF16 and 32 versions require a - // non-const char * arguments, which is also a nuisance, so bypass the whole thing by just - // passing the encoding as a string value, which works properly: - return PyUnicode_Decode(buffer, nbytes, UTF_N == 8 ? "utf-8" : UTF_N == 16 ? "utf-16" : "utf-32", nullptr); -#endif - } - - // When loading into a std::string or char*, accept a bytes object as-is (i.e. - // without any encoding/decoding attempt). For other C++ char sizes this is a no-op. - // which supports loading a unicode from a str, doesn't take this path. - template - bool load_bytes(enable_if_t src) { - if (PYBIND11_BYTES_CHECK(src.ptr())) { - // We were passed a Python 3 raw bytes; accept it into a std::string or char* - // without any encoding attempt. - const char *bytes = PYBIND11_BYTES_AS_STRING(src.ptr()); - if (bytes) { - value = StringType(bytes, (size_t) PYBIND11_BYTES_SIZE(src.ptr())); - return true; - } - } - - return false; - } - - template - bool load_bytes(enable_if_t) { return false; } -}; - -template -struct type_caster, enable_if_t::value>> - : string_caster> {}; - -#ifdef PYBIND11_HAS_STRING_VIEW -template -struct type_caster, enable_if_t::value>> - : string_caster, true> {}; -#endif - -// Type caster for C-style strings. We basically use a std::string type caster, but also add the -// ability to use None as a nullptr char* (which the string caster doesn't allow). -template struct type_caster::value>> { - using StringType = std::basic_string; - using StringCaster = type_caster; - StringCaster str_caster; - bool none = false; - CharT one_char = 0; -public: - bool load(handle src, bool convert) { - if (!src) return false; - if (src.is_none()) { - // Defer accepting None to other overloads (if we aren't in convert mode): - if (!convert) return false; - none = true; - return true; - } - return str_caster.load(src, convert); - } - - static handle cast(const CharT *src, return_value_policy policy, handle parent) { - if (src == nullptr) return pybind11::none().inc_ref(); - return StringCaster::cast(StringType(src), policy, parent); - } - - static handle cast(CharT src, return_value_policy policy, handle parent) { - if (std::is_same::value) { - handle s = PyUnicode_DecodeLatin1((const char *) &src, 1, nullptr); - if (!s) throw error_already_set(); - return s; - } - return StringCaster::cast(StringType(1, src), policy, parent); - } - - operator CharT*() { return none ? nullptr : const_cast(static_cast(str_caster).c_str()); } - operator CharT&() { - if (none) - throw value_error("Cannot convert None to a character"); - - auto &value = static_cast(str_caster); - size_t str_len = value.size(); - if (str_len == 0) - throw value_error("Cannot convert empty string to a character"); - - // If we're in UTF-8 mode, we have two possible failures: one for a unicode character that - // is too high, and one for multiple unicode characters (caught later), so we need to figure - // out how long the first encoded character is in bytes to distinguish between these two - // errors. We also allow want to allow unicode characters U+0080 through U+00FF, as those - // can fit into a single char value. - if (StringCaster::UTF_N == 8 && str_len > 1 && str_len <= 4) { - unsigned char v0 = static_cast(value[0]); - size_t char0_bytes = !(v0 & 0x80) ? 1 : // low bits only: 0-127 - (v0 & 0xE0) == 0xC0 ? 2 : // 0b110xxxxx - start of 2-byte sequence - (v0 & 0xF0) == 0xE0 ? 3 : // 0b1110xxxx - start of 3-byte sequence - 4; // 0b11110xxx - start of 4-byte sequence - - if (char0_bytes == str_len) { - // If we have a 128-255 value, we can decode it into a single char: - if (char0_bytes == 2 && (v0 & 0xFC) == 0xC0) { // 0x110000xx 0x10xxxxxx - one_char = static_cast(((v0 & 3) << 6) + (static_cast(value[1]) & 0x3F)); - return one_char; - } - // Otherwise we have a single character, but it's > U+00FF - throw value_error("Character code point not in range(0x100)"); - } - } - - // UTF-16 is much easier: we can only have a surrogate pair for values above U+FFFF, thus a - // surrogate pair with total length 2 instantly indicates a range error (but not a "your - // string was too long" error). - else if (StringCaster::UTF_N == 16 && str_len == 2) { - one_char = static_cast(value[0]); - if (one_char >= 0xD800 && one_char < 0xE000) - throw value_error("Character code point not in range(0x10000)"); - } - - if (str_len != 1) - throw value_error("Expected a character, but multi-character string found"); - - one_char = value[0]; - return one_char; - } - - static constexpr auto name = _(PYBIND11_STRING_NAME); - template using cast_op_type = pybind11::detail::cast_op_type<_T>; -}; - -// Base implementation for std::tuple and std::pair -template class Tuple, typename... Ts> class tuple_caster { - using type = Tuple; - static constexpr auto size = sizeof...(Ts); - using indices = make_index_sequence; -public: - - bool load(handle src, bool convert) { - if (!isinstance(src)) - return false; - const auto seq = reinterpret_borrow(src); - if (seq.size() != size) - return false; - return load_impl(seq, convert, indices{}); - } - - template - static handle cast(T &&src, return_value_policy policy, handle parent) { - return cast_impl(std::forward(src), policy, parent, indices{}); - } - - static constexpr auto name = _("Tuple[") + concat(make_caster::name...) + _("]"); - - template using cast_op_type = type; - - operator type() & { return implicit_cast(indices{}); } - operator type() && { return std::move(*this).implicit_cast(indices{}); } - -protected: - template - type implicit_cast(index_sequence) & { return type(cast_op(std::get(subcasters))...); } - template - type implicit_cast(index_sequence) && { return type(cast_op(std::move(std::get(subcasters)))...); } - - static constexpr bool load_impl(const sequence &, bool, index_sequence<>) { return true; } - - template - bool load_impl(const sequence &seq, bool convert, index_sequence) { - for (bool r : {std::get(subcasters).load(seq[Is], convert)...}) - if (!r) - return false; - return true; - } - - /* Implementation: Convert a C++ tuple into a Python tuple */ - template - static handle cast_impl(T &&src, return_value_policy policy, handle parent, index_sequence) { - std::array entries{{ - reinterpret_steal(make_caster::cast(std::get(std::forward(src)), policy, parent))... - }}; - for (const auto &entry: entries) - if (!entry) - return handle(); - tuple result(size); - int counter = 0; - for (auto & entry: entries) - PyTuple_SET_ITEM(result.ptr(), counter++, entry.release().ptr()); - return result.release(); - } - - Tuple...> subcasters; -}; - -template class type_caster> - : public tuple_caster {}; - -template class type_caster> - : public tuple_caster {}; - -/// Helper class which abstracts away certain actions. Users can provide specializations for -/// custom holders, but it's only necessary if the type has a non-standard interface. -template -struct holder_helper { - static auto get(const T &p) -> decltype(p.get()) { return p.get(); } -}; - -/// Type caster for holder types like std::shared_ptr, etc. -template -struct copyable_holder_caster : public type_caster_base { -public: - using base = type_caster_base; - static_assert(std::is_base_of>::value, - "Holder classes are only supported for custom types"); - using base::base; - using base::cast; - using base::typeinfo; - using base::value; - - bool load(handle src, bool convert) { - return base::template load_impl>(src, convert); - } - - explicit operator type*() { return this->value; } - explicit operator type&() { return *(this->value); } - explicit operator holder_type*() { return std::addressof(holder); } - - // Workaround for Intel compiler bug - // see pybind11 issue 94 - #if defined(__ICC) || defined(__INTEL_COMPILER) - operator holder_type&() { return holder; } - #else - explicit operator holder_type&() { return holder; } - #endif - - static handle cast(const holder_type &src, return_value_policy, handle) { - const auto *ptr = holder_helper::get(src); - return type_caster_base::cast_holder(ptr, &src); - } - -protected: - friend class type_caster_generic; - void check_holder_compat() { - if (typeinfo->default_holder) - throw cast_error("Unable to load a custom holder type from a default-holder instance"); - } - - bool load_value(value_and_holder &&v_h) { - if (v_h.holder_constructed()) { - value = v_h.value_ptr(); - holder = v_h.template holder(); - return true; - } else { - throw cast_error("Unable to cast from non-held to held instance (T& to Holder) " -#if defined(NDEBUG) - "(compile in debug mode for type information)"); -#else - "of type '" + type_id() + "''"); -#endif - } - } - - template ::value, int> = 0> - bool try_implicit_casts(handle, bool) { return false; } - - template ::value, int> = 0> - bool try_implicit_casts(handle src, bool convert) { - for (auto &cast : typeinfo->implicit_casts) { - copyable_holder_caster sub_caster(*cast.first); - if (sub_caster.load(src, convert)) { - value = cast.second(sub_caster.value); - holder = holder_type(sub_caster.holder, (type *) value); - return true; - } - } - return false; - } - - static bool try_direct_conversions(handle) { return false; } - - - holder_type holder; -}; - -/// Specialize for the common std::shared_ptr, so users don't need to -template -class type_caster> : public copyable_holder_caster> { }; - -template -struct move_only_holder_caster { - static_assert(std::is_base_of, type_caster>::value, - "Holder classes are only supported for custom types"); - - static handle cast(holder_type &&src, return_value_policy, handle) { - auto *ptr = holder_helper::get(src); - return type_caster_base::cast_holder(ptr, std::addressof(src)); - } - static constexpr auto name = type_caster_base::name; -}; - -template -class type_caster> - : public move_only_holder_caster> { }; - -template -using type_caster_holder = conditional_t::value, - copyable_holder_caster, - move_only_holder_caster>; - -template struct always_construct_holder { static constexpr bool value = Value; }; - -/// Create a specialization for custom holder types (silently ignores std::shared_ptr) -#define PYBIND11_DECLARE_HOLDER_TYPE(type, holder_type, ...) \ - namespace pybind11 { namespace detail { \ - template \ - struct always_construct_holder : always_construct_holder { }; \ - template \ - class type_caster::value>> \ - : public type_caster_holder { }; \ - }} - -// PYBIND11_DECLARE_HOLDER_TYPE holder types: -template struct is_holder_type : - std::is_base_of, detail::type_caster> {}; -// Specialization for always-supported unique_ptr holders: -template struct is_holder_type> : - std::true_type {}; - -template struct handle_type_name { static constexpr auto name = _(); }; -template <> struct handle_type_name { static constexpr auto name = _(PYBIND11_BYTES_NAME); }; -template <> struct handle_type_name { static constexpr auto name = _("*args"); }; -template <> struct handle_type_name { static constexpr auto name = _("**kwargs"); }; - -template -struct pyobject_caster { - template ::value, int> = 0> - bool load(handle src, bool /* convert */) { value = src; return static_cast(value); } - - template ::value, int> = 0> - bool load(handle src, bool /* convert */) { - if (!isinstance(src)) - return false; - value = reinterpret_borrow(src); - return true; - } - - static handle cast(const handle &src, return_value_policy /* policy */, handle /* parent */) { - return src.inc_ref(); - } - PYBIND11_TYPE_CASTER(type, handle_type_name::name); -}; - -template -class type_caster::value>> : public pyobject_caster { }; - -// Our conditions for enabling moving are quite restrictive: -// At compile time: -// - T needs to be a non-const, non-pointer, non-reference type -// - type_caster::operator T&() must exist -// - the type must be move constructible (obviously) -// At run-time: -// - if the type is non-copy-constructible, the object must be the sole owner of the type (i.e. it -// must have ref_count() == 1)h -// If any of the above are not satisfied, we fall back to copying. -template using move_is_plain_type = satisfies_none_of; -template struct move_always : std::false_type {}; -template struct move_always, - negation>, - std::is_move_constructible, - std::is_same>().operator T&()), T&> ->::value>> : std::true_type {}; -template struct move_if_unreferenced : std::false_type {}; -template struct move_if_unreferenced, - negation>, - std::is_move_constructible, - std::is_same>().operator T&()), T&> ->::value>> : std::true_type {}; -template using move_never = none_of, move_if_unreferenced>; - -// Detect whether returning a `type` from a cast on type's type_caster is going to result in a -// reference or pointer to a local variable of the type_caster. Basically, only -// non-reference/pointer `type`s and reference/pointers from a type_caster_generic are safe; -// everything else returns a reference/pointer to a local variable. -template using cast_is_temporary_value_reference = bool_constant< - (std::is_reference::value || std::is_pointer::value) && - !std::is_base_of>::value && - !std::is_same, void>::value ->; - -// When a value returned from a C++ function is being cast back to Python, we almost always want to -// force `policy = move`, regardless of the return value policy the function/method was declared -// with. -template struct return_value_policy_override { - static return_value_policy policy(return_value_policy p) { return p; } -}; - -template struct return_value_policy_override>::value, void>> { - static return_value_policy policy(return_value_policy p) { - return !std::is_lvalue_reference::value && - !std::is_pointer::value - ? return_value_policy::move : p; - } -}; - -// Basic python -> C++ casting; throws if casting fails -template type_caster &load_type(type_caster &conv, const handle &handle) { - if (!conv.load(handle, true)) { -#if defined(NDEBUG) - throw cast_error("Unable to cast Python instance to C++ type (compile in debug mode for details)"); -#else - throw cast_error("Unable to cast Python instance of type " + - (std::string) str(handle.get_type()) + " to C++ type '" + type_id() + "'"); -#endif - } - return conv; -} -// Wrapper around the above that also constructs and returns a type_caster -template make_caster load_type(const handle &handle) { - make_caster conv; - load_type(conv, handle); - return conv; -} - -NAMESPACE_END(detail) - -// pytype -> C++ type -template ::value, int> = 0> -T cast(const handle &handle) { - using namespace detail; - static_assert(!cast_is_temporary_value_reference::value, - "Unable to cast type to reference: value is local to type caster"); - return cast_op(load_type(handle)); -} - -// pytype -> pytype (calls converting constructor) -template ::value, int> = 0> -T cast(const handle &handle) { return T(reinterpret_borrow(handle)); } - -// C++ type -> py::object -template ::value, int> = 0> -object cast(const T &value, return_value_policy policy = return_value_policy::automatic_reference, - handle parent = handle()) { - if (policy == return_value_policy::automatic) - policy = std::is_pointer::value ? return_value_policy::take_ownership : return_value_policy::copy; - else if (policy == return_value_policy::automatic_reference) - policy = std::is_pointer::value ? return_value_policy::reference : return_value_policy::copy; - return reinterpret_steal(detail::make_caster::cast(value, policy, parent)); -} - -template T handle::cast() const { return pybind11::cast(*this); } -template <> inline void handle::cast() const { return; } - -template -detail::enable_if_t::value, T> move(object &&obj) { - if (obj.ref_count() > 1) -#if defined(NDEBUG) - throw cast_error("Unable to cast Python instance to C++ rvalue: instance has multiple references" - " (compile in debug mode for details)"); -#else - throw cast_error("Unable to move from Python " + (std::string) str(obj.get_type()) + - " instance to C++ " + type_id() + " instance: instance has multiple references"); -#endif - - // Move into a temporary and return that, because the reference may be a local value of `conv` - T ret = std::move(detail::load_type(obj).operator T&()); - return ret; -} - -// Calling cast() on an rvalue calls pybind::cast with the object rvalue, which does: -// - If we have to move (because T has no copy constructor), do it. This will fail if the moved -// object has multiple references, but trying to copy will fail to compile. -// - If both movable and copyable, check ref count: if 1, move; otherwise copy -// - Otherwise (not movable), copy. -template detail::enable_if_t::value, T> cast(object &&object) { - return move(std::move(object)); -} -template detail::enable_if_t::value, T> cast(object &&object) { - if (object.ref_count() > 1) - return cast(object); - else - return move(std::move(object)); -} -template detail::enable_if_t::value, T> cast(object &&object) { - return cast(object); -} - -template T object::cast() const & { return pybind11::cast(*this); } -template T object::cast() && { return pybind11::cast(std::move(*this)); } -template <> inline void object::cast() const & { return; } -template <> inline void object::cast() && { return; } - -NAMESPACE_BEGIN(detail) - -// Declared in pytypes.h: -template ::value, int>> -object object_or_cast(T &&o) { return pybind11::cast(std::forward(o)); } - -struct overload_unused {}; // Placeholder type for the unneeded (and dead code) static variable in the OVERLOAD_INT macro -template using overload_caster_t = conditional_t< - cast_is_temporary_value_reference::value, make_caster, overload_unused>; - -// Trampoline use: for reference/pointer types to value-converted values, we do a value cast, then -// store the result in the given variable. For other types, this is a no-op. -template enable_if_t::value, T> cast_ref(object &&o, make_caster &caster) { - return cast_op(load_type(caster, o)); -} -template enable_if_t::value, T> cast_ref(object &&, overload_unused &) { - pybind11_fail("Internal error: cast_ref fallback invoked"); } - -// Trampoline use: Having a pybind11::cast with an invalid reference type is going to static_assert, even -// though if it's in dead code, so we provide a "trampoline" to pybind11::cast that only does anything in -// cases where pybind11::cast is valid. -template enable_if_t::value, T> cast_safe(object &&o) { - return pybind11::cast(std::move(o)); } -template enable_if_t::value, T> cast_safe(object &&) { - pybind11_fail("Internal error: cast_safe fallback invoked"); } -template <> inline void cast_safe(object &&) {} - -NAMESPACE_END(detail) - -template -tuple make_tuple() { return tuple(0); } - -template tuple make_tuple(Args&&... args_) { - constexpr size_t size = sizeof...(Args); - std::array args { - { reinterpret_steal(detail::make_caster::cast( - std::forward(args_), policy, nullptr))... } - }; - for (size_t i = 0; i < args.size(); i++) { - if (!args[i]) { -#if defined(NDEBUG) - throw cast_error("make_tuple(): unable to convert arguments to Python object (compile in debug mode for details)"); -#else - std::array argtypes { {type_id()...} }; - throw cast_error("make_tuple(): unable to convert argument of type '" + - argtypes[i] + "' to Python object"); -#endif - } - } - tuple result(size); - int counter = 0; - for (auto &arg_value : args) - PyTuple_SET_ITEM(result.ptr(), counter++, arg_value.release().ptr()); - return result; -} - -/// \ingroup annotations -/// Annotation for arguments -struct arg { - /// Constructs an argument with the name of the argument; if null or omitted, this is a positional argument. - constexpr explicit arg(const char *name = nullptr) : name(name), flag_noconvert(false), flag_none(true) { } - /// Assign a value to this argument - template arg_v operator=(T &&value) const; - /// Indicate that the type should not be converted in the type caster - arg &noconvert(bool flag = true) { flag_noconvert = flag; return *this; } - /// Indicates that the argument should/shouldn't allow None (e.g. for nullable pointer args) - arg &none(bool flag = true) { flag_none = flag; return *this; } - - const char *name; ///< If non-null, this is a named kwargs argument - bool flag_noconvert : 1; ///< If set, do not allow conversion (requires a supporting type caster!) - bool flag_none : 1; ///< If set (the default), allow None to be passed to this argument -}; - -/// \ingroup annotations -/// Annotation for arguments with values -struct arg_v : arg { -private: - template - arg_v(arg &&base, T &&x, const char *descr = nullptr) - : arg(base), - value(reinterpret_steal( - detail::make_caster::cast(x, return_value_policy::automatic, {}) - )), - descr(descr) -#if !defined(NDEBUG) - , type(type_id()) -#endif - { } - -public: - /// Direct construction with name, default, and description - template - arg_v(const char *name, T &&x, const char *descr = nullptr) - : arg_v(arg(name), std::forward(x), descr) { } - - /// Called internally when invoking `py::arg("a") = value` - template - arg_v(const arg &base, T &&x, const char *descr = nullptr) - : arg_v(arg(base), std::forward(x), descr) { } - - /// Same as `arg::noconvert()`, but returns *this as arg_v&, not arg& - arg_v &noconvert(bool flag = true) { arg::noconvert(flag); return *this; } - - /// Same as `arg::nonone()`, but returns *this as arg_v&, not arg& - arg_v &none(bool flag = true) { arg::none(flag); return *this; } - - /// The default value - object value; - /// The (optional) description of the default value - const char *descr; -#if !defined(NDEBUG) - /// The C++ type name of the default value (only available when compiled in debug mode) - std::string type; -#endif -}; - -template -arg_v arg::operator=(T &&value) const { return {std::move(*this), std::forward(value)}; } - -/// Alias for backward compatibility -- to be removed in version 2.0 -template using arg_t = arg_v; - -inline namespace literals { -/** \rst - String literal version of `arg` - \endrst */ -constexpr arg operator"" _a(const char *name, size_t) { return arg(name); } -} - -NAMESPACE_BEGIN(detail) - -// forward declaration (definition in attr.h) -struct function_record; - -/// Internal data associated with a single function call -struct function_call { - function_call(const function_record &f, handle p); // Implementation in attr.h - - /// The function data: - const function_record &func; - - /// Arguments passed to the function: - std::vector args; - - /// The `convert` value the arguments should be loaded with - std::vector args_convert; - - /// Extra references for the optional `py::args` and/or `py::kwargs` arguments (which, if - /// present, are also in `args` but without a reference). - object args_ref, kwargs_ref; - - /// The parent, if any - handle parent; - - /// If this is a call to an initializer, this argument contains `self` - handle init_self; -}; - - -/// Helper class which loads arguments for C++ functions called from Python -template -class argument_loader { - using indices = make_index_sequence; - - template using argument_is_args = std::is_same, args>; - template using argument_is_kwargs = std::is_same, kwargs>; - // Get args/kwargs argument positions relative to the end of the argument list: - static constexpr auto args_pos = constexpr_first() - (int) sizeof...(Args), - kwargs_pos = constexpr_first() - (int) sizeof...(Args); - - static constexpr bool args_kwargs_are_last = kwargs_pos >= - 1 && args_pos >= kwargs_pos - 1; - - static_assert(args_kwargs_are_last, "py::args/py::kwargs are only permitted as the last argument(s) of a function"); - -public: - static constexpr bool has_kwargs = kwargs_pos < 0; - static constexpr bool has_args = args_pos < 0; - - static constexpr auto arg_names = concat(type_descr(make_caster::name)...); - - bool load_args(function_call &call) { - return load_impl_sequence(call, indices{}); - } - - template - enable_if_t::value, Return> call(Func &&f) && { - return std::move(*this).template call_impl(std::forward(f), indices{}, Guard{}); - } - - template - enable_if_t::value, void_type> call(Func &&f) && { - std::move(*this).template call_impl(std::forward(f), indices{}, Guard{}); - return void_type(); - } - -private: - - static bool load_impl_sequence(function_call &, index_sequence<>) { return true; } - - template - bool load_impl_sequence(function_call &call, index_sequence) { - for (bool r : {std::get(argcasters).load(call.args[Is], call.args_convert[Is])...}) - if (!r) - return false; - return true; - } - - template - Return call_impl(Func &&f, index_sequence, Guard &&) { - return std::forward(f)(cast_op(std::move(std::get(argcasters)))...); - } - - std::tuple...> argcasters; -}; - -/// Helper class which collects only positional arguments for a Python function call. -/// A fancier version below can collect any argument, but this one is optimal for simple calls. -template -class simple_collector { -public: - template - explicit simple_collector(Ts &&...values) - : m_args(pybind11::make_tuple(std::forward(values)...)) { } - - const tuple &args() const & { return m_args; } - dict kwargs() const { return {}; } - - tuple args() && { return std::move(m_args); } - - /// Call a Python function and pass the collected arguments - object call(PyObject *ptr) const { - PyObject *result = PyObject_CallObject(ptr, m_args.ptr()); - if (!result) - throw error_already_set(); - return reinterpret_steal(result); - } - -private: - tuple m_args; -}; - -/// Helper class which collects positional, keyword, * and ** arguments for a Python function call -template -class unpacking_collector { -public: - template - explicit unpacking_collector(Ts &&...values) { - // Tuples aren't (easily) resizable so a list is needed for collection, - // but the actual function call strictly requires a tuple. - auto args_list = list(); - int _[] = { 0, (process(args_list, std::forward(values)), 0)... }; - ignore_unused(_); - - m_args = std::move(args_list); - } - - const tuple &args() const & { return m_args; } - const dict &kwargs() const & { return m_kwargs; } - - tuple args() && { return std::move(m_args); } - dict kwargs() && { return std::move(m_kwargs); } - - /// Call a Python function and pass the collected arguments - object call(PyObject *ptr) const { - PyObject *result = PyObject_Call(ptr, m_args.ptr(), m_kwargs.ptr()); - if (!result) - throw error_already_set(); - return reinterpret_steal(result); - } - -private: - template - void process(list &args_list, T &&x) { - auto o = reinterpret_steal(detail::make_caster::cast(std::forward(x), policy, {})); - if (!o) { -#if defined(NDEBUG) - argument_cast_error(); -#else - argument_cast_error(std::to_string(args_list.size()), type_id()); -#endif - } - args_list.append(o); - } - - void process(list &args_list, detail::args_proxy ap) { - for (const auto &a : ap) - args_list.append(a); - } - - void process(list &/*args_list*/, arg_v a) { - if (!a.name) -#if defined(NDEBUG) - nameless_argument_error(); -#else - nameless_argument_error(a.type); -#endif - - if (m_kwargs.contains(a.name)) { -#if defined(NDEBUG) - multiple_values_error(); -#else - multiple_values_error(a.name); -#endif - } - if (!a.value) { -#if defined(NDEBUG) - argument_cast_error(); -#else - argument_cast_error(a.name, a.type); -#endif - } - m_kwargs[a.name] = a.value; - } - - void process(list &/*args_list*/, detail::kwargs_proxy kp) { - if (!kp) - return; - for (const auto &k : reinterpret_borrow(kp)) { - if (m_kwargs.contains(k.first)) { -#if defined(NDEBUG) - multiple_values_error(); -#else - multiple_values_error(str(k.first)); -#endif - } - m_kwargs[k.first] = k.second; - } - } - - [[noreturn]] static void nameless_argument_error() { - throw type_error("Got kwargs without a name; only named arguments " - "may be passed via py::arg() to a python function call. " - "(compile in debug mode for details)"); - } - [[noreturn]] static void nameless_argument_error(std::string type) { - throw type_error("Got kwargs without a name of type '" + type + "'; only named " - "arguments may be passed via py::arg() to a python function call. "); - } - [[noreturn]] static void multiple_values_error() { - throw type_error("Got multiple values for keyword argument " - "(compile in debug mode for details)"); - } - - [[noreturn]] static void multiple_values_error(std::string name) { - throw type_error("Got multiple values for keyword argument '" + name + "'"); - } - - [[noreturn]] static void argument_cast_error() { - throw cast_error("Unable to convert call argument to Python object " - "(compile in debug mode for details)"); - } - - [[noreturn]] static void argument_cast_error(std::string name, std::string type) { - throw cast_error("Unable to convert call argument '" + name - + "' of type '" + type + "' to Python object"); - } - -private: - tuple m_args; - dict m_kwargs; -}; - -/// Collect only positional arguments for a Python function call -template ...>::value>> -simple_collector collect_arguments(Args &&...args) { - return simple_collector(std::forward(args)...); -} - -/// Collect all arguments, including keywords and unpacking (only instantiated when needed) -template ...>::value>> -unpacking_collector collect_arguments(Args &&...args) { - // Following argument order rules for generalized unpacking according to PEP 448 - static_assert( - constexpr_last() < constexpr_first() - && constexpr_last() < constexpr_first(), - "Invalid function call: positional args must precede keywords and ** unpacking; " - "* unpacking must precede ** unpacking" - ); - return unpacking_collector(std::forward(args)...); -} - -template -template -object object_api::operator()(Args &&...args) const { - return detail::collect_arguments(std::forward(args)...).call(derived().ptr()); -} - -template -template -object object_api::call(Args &&...args) const { - return operator()(std::forward(args)...); -} - -NAMESPACE_END(detail) - -#define PYBIND11_MAKE_OPAQUE(...) \ - namespace pybind11 { namespace detail { \ - template<> class type_caster<__VA_ARGS__> : public type_caster_base<__VA_ARGS__> { }; \ - }} - -/// Lets you pass a type containing a `,` through a macro parameter without needing a separate -/// typedef, e.g.: `PYBIND11_OVERLOAD(PYBIND11_TYPE(ReturnType), PYBIND11_TYPE(Parent), f, arg)` -#define PYBIND11_TYPE(...) __VA_ARGS__ - -NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/ptocr/postprocess/dbprocess/include/pybind11/chrono.h b/ptocr/postprocess/dbprocess/include/pybind11/chrono.h deleted file mode 100644 index 95ada76..0000000 --- a/ptocr/postprocess/dbprocess/include/pybind11/chrono.h +++ /dev/null @@ -1,162 +0,0 @@ -/* - pybind11/chrono.h: Transparent conversion between std::chrono and python's datetime - - Copyright (c) 2016 Trent Houliston and - Wenzel Jakob - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#include "pybind11.h" -#include -#include -#include -#include - -// Backport the PyDateTime_DELTA functions from Python3.3 if required -#ifndef PyDateTime_DELTA_GET_DAYS -#define PyDateTime_DELTA_GET_DAYS(o) (((PyDateTime_Delta*)o)->days) -#endif -#ifndef PyDateTime_DELTA_GET_SECONDS -#define PyDateTime_DELTA_GET_SECONDS(o) (((PyDateTime_Delta*)o)->seconds) -#endif -#ifndef PyDateTime_DELTA_GET_MICROSECONDS -#define PyDateTime_DELTA_GET_MICROSECONDS(o) (((PyDateTime_Delta*)o)->microseconds) -#endif - -NAMESPACE_BEGIN(PYBIND11_NAMESPACE) -NAMESPACE_BEGIN(detail) - -template class duration_caster { -public: - typedef typename type::rep rep; - typedef typename type::period period; - - typedef std::chrono::duration> days; - - bool load(handle src, bool) { - using namespace std::chrono; - - // Lazy initialise the PyDateTime import - if (!PyDateTimeAPI) { PyDateTime_IMPORT; } - - if (!src) return false; - // If invoked with datetime.delta object - if (PyDelta_Check(src.ptr())) { - value = type(duration_cast>( - days(PyDateTime_DELTA_GET_DAYS(src.ptr())) - + seconds(PyDateTime_DELTA_GET_SECONDS(src.ptr())) - + microseconds(PyDateTime_DELTA_GET_MICROSECONDS(src.ptr())))); - return true; - } - // If invoked with a float we assume it is seconds and convert - else if (PyFloat_Check(src.ptr())) { - value = type(duration_cast>(duration(PyFloat_AsDouble(src.ptr())))); - return true; - } - else return false; - } - - // If this is a duration just return it back - static const std::chrono::duration& get_duration(const std::chrono::duration &src) { - return src; - } - - // If this is a time_point get the time_since_epoch - template static std::chrono::duration get_duration(const std::chrono::time_point> &src) { - return src.time_since_epoch(); - } - - static handle cast(const type &src, return_value_policy /* policy */, handle /* parent */) { - using namespace std::chrono; - - // Use overloaded function to get our duration from our source - // Works out if it is a duration or time_point and get the duration - auto d = get_duration(src); - - // Lazy initialise the PyDateTime import - if (!PyDateTimeAPI) { PyDateTime_IMPORT; } - - // Declare these special duration types so the conversions happen with the correct primitive types (int) - using dd_t = duration>; - using ss_t = duration>; - using us_t = duration; - - auto dd = duration_cast(d); - auto subd = d - dd; - auto ss = duration_cast(subd); - auto us = duration_cast(subd - ss); - return PyDelta_FromDSU(dd.count(), ss.count(), us.count()); - } - - PYBIND11_TYPE_CASTER(type, _("datetime.timedelta")); -}; - -// This is for casting times on the system clock into datetime.datetime instances -template class type_caster> { -public: - typedef std::chrono::time_point type; - bool load(handle src, bool) { - using namespace std::chrono; - - // Lazy initialise the PyDateTime import - if (!PyDateTimeAPI) { PyDateTime_IMPORT; } - - if (!src) return false; - if (PyDateTime_Check(src.ptr())) { - std::tm cal; - cal.tm_sec = PyDateTime_DATE_GET_SECOND(src.ptr()); - cal.tm_min = PyDateTime_DATE_GET_MINUTE(src.ptr()); - cal.tm_hour = PyDateTime_DATE_GET_HOUR(src.ptr()); - cal.tm_mday = PyDateTime_GET_DAY(src.ptr()); - cal.tm_mon = PyDateTime_GET_MONTH(src.ptr()) - 1; - cal.tm_year = PyDateTime_GET_YEAR(src.ptr()) - 1900; - cal.tm_isdst = -1; - - value = system_clock::from_time_t(std::mktime(&cal)) + microseconds(PyDateTime_DATE_GET_MICROSECOND(src.ptr())); - return true; - } - else return false; - } - - static handle cast(const std::chrono::time_point &src, return_value_policy /* policy */, handle /* parent */) { - using namespace std::chrono; - - // Lazy initialise the PyDateTime import - if (!PyDateTimeAPI) { PyDateTime_IMPORT; } - - std::time_t tt = system_clock::to_time_t(src); - // this function uses static memory so it's best to copy it out asap just in case - // otherwise other code that is using localtime may break this (not just python code) - std::tm localtime = *std::localtime(&tt); - - // Declare these special duration types so the conversions happen with the correct primitive types (int) - using us_t = duration; - - return PyDateTime_FromDateAndTime(localtime.tm_year + 1900, - localtime.tm_mon + 1, - localtime.tm_mday, - localtime.tm_hour, - localtime.tm_min, - localtime.tm_sec, - (duration_cast(src.time_since_epoch() % seconds(1))).count()); - } - PYBIND11_TYPE_CASTER(type, _("datetime.datetime")); -}; - -// Other clocks that are not the system clock are not measured as datetime.datetime objects -// since they are not measured on calendar time. So instead we just make them timedeltas -// Or if they have passed us a time as a float we convert that -template class type_caster> -: public duration_caster> { -}; - -template class type_caster> -: public duration_caster> { -}; - -NAMESPACE_END(detail) -NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/ptocr/postprocess/dbprocess/include/pybind11/class_support.h b/ptocr/postprocess/dbprocess/include/pybind11/class_support.h deleted file mode 100644 index 8e18c4c..0000000 --- a/ptocr/postprocess/dbprocess/include/pybind11/class_support.h +++ /dev/null @@ -1,603 +0,0 @@ -/* - pybind11/class_support.h: Python C API implementation details for py::class_ - - Copyright (c) 2017 Wenzel Jakob - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#include "attr.h" - -NAMESPACE_BEGIN(pybind11) -NAMESPACE_BEGIN(detail) - -inline PyTypeObject *type_incref(PyTypeObject *type) { - Py_INCREF(type); - return type; -} - -#if !defined(PYPY_VERSION) - -/// `pybind11_static_property.__get__()`: Always pass the class instead of the instance. -extern "C" inline PyObject *pybind11_static_get(PyObject *self, PyObject * /*ob*/, PyObject *cls) { - return PyProperty_Type.tp_descr_get(self, cls, cls); -} - -/// `pybind11_static_property.__set__()`: Just like the above `__get__()`. -extern "C" inline int pybind11_static_set(PyObject *self, PyObject *obj, PyObject *value) { - PyObject *cls = PyType_Check(obj) ? obj : (PyObject *) Py_TYPE(obj); - return PyProperty_Type.tp_descr_set(self, cls, value); -} - -/** A `static_property` is the same as a `property` but the `__get__()` and `__set__()` - methods are modified to always use the object type instead of a concrete instance. - Return value: New reference. */ -inline PyTypeObject *make_static_property_type() { - constexpr auto *name = "pybind11_static_property"; - auto name_obj = reinterpret_steal(PYBIND11_FROM_STRING(name)); - - /* Danger zone: from now (and until PyType_Ready), make sure to - issue no Python C API calls which could potentially invoke the - garbage collector (the GC will call type_traverse(), which will in - turn find the newly constructed type in an invalid state) */ - auto heap_type = (PyHeapTypeObject *) PyType_Type.tp_alloc(&PyType_Type, 0); - if (!heap_type) - pybind11_fail("make_static_property_type(): error allocating type!"); - - heap_type->ht_name = name_obj.inc_ref().ptr(); -#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 3 - heap_type->ht_qualname = name_obj.inc_ref().ptr(); -#endif - - auto type = &heap_type->ht_type; - type->tp_name = name; - type->tp_base = type_incref(&PyProperty_Type); - type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE; - type->tp_descr_get = pybind11_static_get; - type->tp_descr_set = pybind11_static_set; - - if (PyType_Ready(type) < 0) - pybind11_fail("make_static_property_type(): failure in PyType_Ready()!"); - - setattr((PyObject *) type, "__module__", str("pybind11_builtins")); - - return type; -} - -#else // PYPY - -/** PyPy has some issues with the above C API, so we evaluate Python code instead. - This function will only be called once so performance isn't really a concern. - Return value: New reference. */ -inline PyTypeObject *make_static_property_type() { - auto d = dict(); - PyObject *result = PyRun_String(R"(\ - class pybind11_static_property(property): - def __get__(self, obj, cls): - return property.__get__(self, cls, cls) - - def __set__(self, obj, value): - cls = obj if isinstance(obj, type) else type(obj) - property.__set__(self, cls, value) - )", Py_file_input, d.ptr(), d.ptr() - ); - if (result == nullptr) - throw error_already_set(); - Py_DECREF(result); - return (PyTypeObject *) d["pybind11_static_property"].cast().release().ptr(); -} - -#endif // PYPY - -/** Types with static properties need to handle `Type.static_prop = x` in a specific way. - By default, Python replaces the `static_property` itself, but for wrapped C++ types - we need to call `static_property.__set__()` in order to propagate the new value to - the underlying C++ data structure. */ -extern "C" inline int pybind11_meta_setattro(PyObject* obj, PyObject* name, PyObject* value) { - // Use `_PyType_Lookup()` instead of `PyObject_GetAttr()` in order to get the raw - // descriptor (`property`) instead of calling `tp_descr_get` (`property.__get__()`). - PyObject *descr = _PyType_Lookup((PyTypeObject *) obj, name); - - // The following assignment combinations are possible: - // 1. `Type.static_prop = value` --> descr_set: `Type.static_prop.__set__(value)` - // 2. `Type.static_prop = other_static_prop` --> setattro: replace existing `static_prop` - // 3. `Type.regular_attribute = value` --> setattro: regular attribute assignment - const auto static_prop = (PyObject *) get_internals().static_property_type; - const auto call_descr_set = descr && PyObject_IsInstance(descr, static_prop) - && !PyObject_IsInstance(value, static_prop); - if (call_descr_set) { - // Call `static_property.__set__()` instead of replacing the `static_property`. -#if !defined(PYPY_VERSION) - return Py_TYPE(descr)->tp_descr_set(descr, obj, value); -#else - if (PyObject *result = PyObject_CallMethod(descr, "__set__", "OO", obj, value)) { - Py_DECREF(result); - return 0; - } else { - return -1; - } -#endif - } else { - // Replace existing attribute. - return PyType_Type.tp_setattro(obj, name, value); - } -} - -#if PY_MAJOR_VERSION >= 3 -/** - * Python 3's PyInstanceMethod_Type hides itself via its tp_descr_get, which prevents aliasing - * methods via cls.attr("m2") = cls.attr("m1"): instead the tp_descr_get returns a plain function, - * when called on a class, or a PyMethod, when called on an instance. Override that behaviour here - * to do a special case bypass for PyInstanceMethod_Types. - */ -extern "C" inline PyObject *pybind11_meta_getattro(PyObject *obj, PyObject *name) { - PyObject *descr = _PyType_Lookup((PyTypeObject *) obj, name); - if (descr && PyInstanceMethod_Check(descr)) { - Py_INCREF(descr); - return descr; - } - else { - return PyType_Type.tp_getattro(obj, name); - } -} -#endif - -/** This metaclass is assigned by default to all pybind11 types and is required in order - for static properties to function correctly. Users may override this using `py::metaclass`. - Return value: New reference. */ -inline PyTypeObject* make_default_metaclass() { - constexpr auto *name = "pybind11_type"; - auto name_obj = reinterpret_steal(PYBIND11_FROM_STRING(name)); - - /* Danger zone: from now (and until PyType_Ready), make sure to - issue no Python C API calls which could potentially invoke the - garbage collector (the GC will call type_traverse(), which will in - turn find the newly constructed type in an invalid state) */ - auto heap_type = (PyHeapTypeObject *) PyType_Type.tp_alloc(&PyType_Type, 0); - if (!heap_type) - pybind11_fail("make_default_metaclass(): error allocating metaclass!"); - - heap_type->ht_name = name_obj.inc_ref().ptr(); -#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 3 - heap_type->ht_qualname = name_obj.inc_ref().ptr(); -#endif - - auto type = &heap_type->ht_type; - type->tp_name = name; - type->tp_base = type_incref(&PyType_Type); - type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE; - - type->tp_setattro = pybind11_meta_setattro; -#if PY_MAJOR_VERSION >= 3 - type->tp_getattro = pybind11_meta_getattro; -#endif - - if (PyType_Ready(type) < 0) - pybind11_fail("make_default_metaclass(): failure in PyType_Ready()!"); - - setattr((PyObject *) type, "__module__", str("pybind11_builtins")); - - return type; -} - -/// For multiple inheritance types we need to recursively register/deregister base pointers for any -/// base classes with pointers that are difference from the instance value pointer so that we can -/// correctly recognize an offset base class pointer. This calls a function with any offset base ptrs. -inline void traverse_offset_bases(void *valueptr, const detail::type_info *tinfo, instance *self, - bool (*f)(void * /*parentptr*/, instance * /*self*/)) { - for (handle h : reinterpret_borrow(tinfo->type->tp_bases)) { - if (auto parent_tinfo = get_type_info((PyTypeObject *) h.ptr())) { - for (auto &c : parent_tinfo->implicit_casts) { - if (c.first == tinfo->cpptype) { - auto *parentptr = c.second(valueptr); - if (parentptr != valueptr) - f(parentptr, self); - traverse_offset_bases(parentptr, parent_tinfo, self, f); - break; - } - } - } - } -} - -inline bool register_instance_impl(void *ptr, instance *self) { - get_internals().registered_instances.emplace(ptr, self); - return true; // unused, but gives the same signature as the deregister func -} -inline bool deregister_instance_impl(void *ptr, instance *self) { - auto ®istered_instances = get_internals().registered_instances; - auto range = registered_instances.equal_range(ptr); - for (auto it = range.first; it != range.second; ++it) { - if (Py_TYPE(self) == Py_TYPE(it->second)) { - registered_instances.erase(it); - return true; - } - } - return false; -} - -inline void register_instance(instance *self, void *valptr, const type_info *tinfo) { - register_instance_impl(valptr, self); - if (!tinfo->simple_ancestors) - traverse_offset_bases(valptr, tinfo, self, register_instance_impl); -} - -inline bool deregister_instance(instance *self, void *valptr, const type_info *tinfo) { - bool ret = deregister_instance_impl(valptr, self); - if (!tinfo->simple_ancestors) - traverse_offset_bases(valptr, tinfo, self, deregister_instance_impl); - return ret; -} - -/// Instance creation function for all pybind11 types. It only allocates space for the C++ object -/// (or multiple objects, for Python-side inheritance from multiple pybind11 types), but doesn't -/// call the constructor -- an `__init__` function must do that (followed by an `init_instance` -/// to set up the holder and register the instance). -inline PyObject *make_new_instance(PyTypeObject *type, bool allocate_value /*= true (in cast.h)*/) { -#if defined(PYPY_VERSION) - // PyPy gets tp_basicsize wrong (issue 2482) under multiple inheritance when the first inherited - // object is a a plain Python type (i.e. not derived from an extension type). Fix it. - ssize_t instance_size = static_cast(sizeof(instance)); - if (type->tp_basicsize < instance_size) { - type->tp_basicsize = instance_size; - } -#endif - PyObject *self = type->tp_alloc(type, 0); - auto inst = reinterpret_cast(self); - // Allocate the value/holder internals: - inst->allocate_layout(); - - inst->owned = true; - // Allocate (if requested) the value pointers; otherwise leave them as nullptr - if (allocate_value) { - for (auto &v_h : values_and_holders(inst)) { - void *&vptr = v_h.value_ptr(); - vptr = v_h.type->operator_new(v_h.type->type_size); - } - } - - return self; -} - -/// Instance creation function for all pybind11 types. It only allocates space for the -/// C++ object, but doesn't call the constructor -- an `__init__` function must do that. -extern "C" inline PyObject *pybind11_object_new(PyTypeObject *type, PyObject *, PyObject *) { - return make_new_instance(type); -} - -/// An `__init__` function constructs the C++ object. Users should provide at least one -/// of these using `py::init` or directly with `.def(__init__, ...)`. Otherwise, the -/// following default function will be used which simply throws an exception. -extern "C" inline int pybind11_object_init(PyObject *self, PyObject *, PyObject *) { - PyTypeObject *type = Py_TYPE(self); - std::string msg; -#if defined(PYPY_VERSION) - msg += handle((PyObject *) type).attr("__module__").cast() + "."; -#endif - msg += type->tp_name; - msg += ": No constructor defined!"; - PyErr_SetString(PyExc_TypeError, msg.c_str()); - return -1; -} - -inline void add_patient(PyObject *nurse, PyObject *patient) { - auto &internals = get_internals(); - auto instance = reinterpret_cast(nurse); - instance->has_patients = true; - Py_INCREF(patient); - internals.patients[nurse].push_back(patient); -} - -inline void clear_patients(PyObject *self) { - auto instance = reinterpret_cast(self); - auto &internals = get_internals(); - auto pos = internals.patients.find(self); - assert(pos != internals.patients.end()); - // Clearing the patients can cause more Python code to run, which - // can invalidate the iterator. Extract the vector of patients - // from the unordered_map first. - auto patients = std::move(pos->second); - internals.patients.erase(pos); - instance->has_patients = false; - for (PyObject *&patient : patients) - Py_CLEAR(patient); -} - -/// Clears all internal data from the instance and removes it from registered instances in -/// preparation for deallocation. -inline void clear_instance(PyObject *self) { - auto instance = reinterpret_cast(self); - - // Deallocate any values/holders, if present: - for (auto &v_h : values_and_holders(instance)) { - if (v_h) { - - // We have to deregister before we call dealloc because, for virtual MI types, we still - // need to be able to get the parent pointers. - if (v_h.instance_registered() && !deregister_instance(instance, v_h.value_ptr(), v_h.type)) - pybind11_fail("pybind11_object_dealloc(): Tried to deallocate unregistered instance!"); - - if (instance->owned || v_h.holder_constructed()) - v_h.type->dealloc(v_h); - } - } - // Deallocate the value/holder layout internals: - instance->deallocate_layout(); - - if (instance->weakrefs) - PyObject_ClearWeakRefs(self); - - PyObject **dict_ptr = _PyObject_GetDictPtr(self); - if (dict_ptr) - Py_CLEAR(*dict_ptr); - - if (instance->has_patients) - clear_patients(self); -} - -/// Instance destructor function for all pybind11 types. It calls `type_info.dealloc` -/// to destroy the C++ object itself, while the rest is Python bookkeeping. -extern "C" inline void pybind11_object_dealloc(PyObject *self) { - clear_instance(self); - Py_TYPE(self)->tp_free(self); -} - -/** Create the type which can be used as a common base for all classes. This is - needed in order to satisfy Python's requirements for multiple inheritance. - Return value: New reference. */ -inline PyObject *make_object_base_type(PyTypeObject *metaclass) { - constexpr auto *name = "pybind11_object"; - auto name_obj = reinterpret_steal(PYBIND11_FROM_STRING(name)); - - /* Danger zone: from now (and until PyType_Ready), make sure to - issue no Python C API calls which could potentially invoke the - garbage collector (the GC will call type_traverse(), which will in - turn find the newly constructed type in an invalid state) */ - auto heap_type = (PyHeapTypeObject *) metaclass->tp_alloc(metaclass, 0); - if (!heap_type) - pybind11_fail("make_object_base_type(): error allocating type!"); - - heap_type->ht_name = name_obj.inc_ref().ptr(); -#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 3 - heap_type->ht_qualname = name_obj.inc_ref().ptr(); -#endif - - auto type = &heap_type->ht_type; - type->tp_name = name; - type->tp_base = type_incref(&PyBaseObject_Type); - type->tp_basicsize = static_cast(sizeof(instance)); - type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE; - - type->tp_new = pybind11_object_new; - type->tp_init = pybind11_object_init; - type->tp_dealloc = pybind11_object_dealloc; - - /* Support weak references (needed for the keep_alive feature) */ - type->tp_weaklistoffset = offsetof(instance, weakrefs); - - if (PyType_Ready(type) < 0) - pybind11_fail("PyType_Ready failed in make_object_base_type():" + error_string()); - - setattr((PyObject *) type, "__module__", str("pybind11_builtins")); - - assert(!PyType_HasFeature(type, Py_TPFLAGS_HAVE_GC)); - return (PyObject *) heap_type; -} - -/// dynamic_attr: Support for `d = instance.__dict__`. -extern "C" inline PyObject *pybind11_get_dict(PyObject *self, void *) { - PyObject *&dict = *_PyObject_GetDictPtr(self); - if (!dict) - dict = PyDict_New(); - Py_XINCREF(dict); - return dict; -} - -/// dynamic_attr: Support for `instance.__dict__ = dict()`. -extern "C" inline int pybind11_set_dict(PyObject *self, PyObject *new_dict, void *) { - if (!PyDict_Check(new_dict)) { - PyErr_Format(PyExc_TypeError, "__dict__ must be set to a dictionary, not a '%.200s'", - Py_TYPE(new_dict)->tp_name); - return -1; - } - PyObject *&dict = *_PyObject_GetDictPtr(self); - Py_INCREF(new_dict); - Py_CLEAR(dict); - dict = new_dict; - return 0; -} - -/// dynamic_attr: Allow the garbage collector to traverse the internal instance `__dict__`. -extern "C" inline int pybind11_traverse(PyObject *self, visitproc visit, void *arg) { - PyObject *&dict = *_PyObject_GetDictPtr(self); - Py_VISIT(dict); - return 0; -} - -/// dynamic_attr: Allow the GC to clear the dictionary. -extern "C" inline int pybind11_clear(PyObject *self) { - PyObject *&dict = *_PyObject_GetDictPtr(self); - Py_CLEAR(dict); - return 0; -} - -/// Give instances of this type a `__dict__` and opt into garbage collection. -inline void enable_dynamic_attributes(PyHeapTypeObject *heap_type) { - auto type = &heap_type->ht_type; -#if defined(PYPY_VERSION) - pybind11_fail(std::string(type->tp_name) + ": dynamic attributes are " - "currently not supported in " - "conjunction with PyPy!"); -#endif - type->tp_flags |= Py_TPFLAGS_HAVE_GC; - type->tp_dictoffset = type->tp_basicsize; // place dict at the end - type->tp_basicsize += (ssize_t)sizeof(PyObject *); // and allocate enough space for it - type->tp_traverse = pybind11_traverse; - type->tp_clear = pybind11_clear; - - static PyGetSetDef getset[] = { - {const_cast("__dict__"), pybind11_get_dict, pybind11_set_dict, nullptr, nullptr}, - {nullptr, nullptr, nullptr, nullptr, nullptr} - }; - type->tp_getset = getset; -} - -/// buffer_protocol: Fill in the view as specified by flags. -extern "C" inline int pybind11_getbuffer(PyObject *obj, Py_buffer *view, int flags) { - // Look for a `get_buffer` implementation in this type's info or any bases (following MRO). - type_info *tinfo = nullptr; - for (auto type : reinterpret_borrow(Py_TYPE(obj)->tp_mro)) { - tinfo = get_type_info((PyTypeObject *) type.ptr()); - if (tinfo && tinfo->get_buffer) - break; - } - if (view == nullptr || obj == nullptr || !tinfo || !tinfo->get_buffer) { - if (view) - view->obj = nullptr; - PyErr_SetString(PyExc_BufferError, "pybind11_getbuffer(): Internal error"); - return -1; - } - std::memset(view, 0, sizeof(Py_buffer)); - buffer_info *info = tinfo->get_buffer(obj, tinfo->get_buffer_data); - view->obj = obj; - view->ndim = 1; - view->internal = info; - view->buf = info->ptr; - view->itemsize = info->itemsize; - view->len = view->itemsize; - for (auto s : info->shape) - view->len *= s; - if ((flags & PyBUF_FORMAT) == PyBUF_FORMAT) - view->format = const_cast(info->format.c_str()); - if ((flags & PyBUF_STRIDES) == PyBUF_STRIDES) { - view->ndim = (int) info->ndim; - view->strides = &info->strides[0]; - view->shape = &info->shape[0]; - } - Py_INCREF(view->obj); - return 0; -} - -/// buffer_protocol: Release the resources of the buffer. -extern "C" inline void pybind11_releasebuffer(PyObject *, Py_buffer *view) { - delete (buffer_info *) view->internal; -} - -/// Give this type a buffer interface. -inline void enable_buffer_protocol(PyHeapTypeObject *heap_type) { - heap_type->ht_type.tp_as_buffer = &heap_type->as_buffer; -#if PY_MAJOR_VERSION < 3 - heap_type->ht_type.tp_flags |= Py_TPFLAGS_HAVE_NEWBUFFER; -#endif - - heap_type->as_buffer.bf_getbuffer = pybind11_getbuffer; - heap_type->as_buffer.bf_releasebuffer = pybind11_releasebuffer; -} - -/** Create a brand new Python type according to the `type_record` specification. - Return value: New reference. */ -inline PyObject* make_new_python_type(const type_record &rec) { - auto name = reinterpret_steal(PYBIND11_FROM_STRING(rec.name)); - -#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 3 - auto ht_qualname = name; - if (rec.scope && hasattr(rec.scope, "__qualname__")) { - ht_qualname = reinterpret_steal( - PyUnicode_FromFormat("%U.%U", rec.scope.attr("__qualname__").ptr(), name.ptr())); - } -#endif - - object module; - if (rec.scope) { - if (hasattr(rec.scope, "__module__")) - module = rec.scope.attr("__module__"); - else if (hasattr(rec.scope, "__name__")) - module = rec.scope.attr("__name__"); - } - -#if !defined(PYPY_VERSION) - const auto full_name = module ? str(module).cast() + "." + rec.name - : std::string(rec.name); -#else - const auto full_name = std::string(rec.name); -#endif - - char *tp_doc = nullptr; - if (rec.doc && options::show_user_defined_docstrings()) { - /* Allocate memory for docstring (using PyObject_MALLOC, since - Python will free this later on) */ - size_t size = strlen(rec.doc) + 1; - tp_doc = (char *) PyObject_MALLOC(size); - memcpy((void *) tp_doc, rec.doc, size); - } - - auto &internals = get_internals(); - auto bases = tuple(rec.bases); - auto base = (bases.size() == 0) ? internals.instance_base - : bases[0].ptr(); - - /* Danger zone: from now (and until PyType_Ready), make sure to - issue no Python C API calls which could potentially invoke the - garbage collector (the GC will call type_traverse(), which will in - turn find the newly constructed type in an invalid state) */ - auto metaclass = rec.metaclass.ptr() ? (PyTypeObject *) rec.metaclass.ptr() - : internals.default_metaclass; - - auto heap_type = (PyHeapTypeObject *) metaclass->tp_alloc(metaclass, 0); - if (!heap_type) - pybind11_fail(std::string(rec.name) + ": Unable to create type object!"); - - heap_type->ht_name = name.release().ptr(); -#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 3 - heap_type->ht_qualname = ht_qualname.release().ptr(); -#endif - - auto type = &heap_type->ht_type; - type->tp_name = strdup(full_name.c_str()); - type->tp_doc = tp_doc; - type->tp_base = type_incref((PyTypeObject *)base); - type->tp_basicsize = static_cast(sizeof(instance)); - if (bases.size() > 0) - type->tp_bases = bases.release().ptr(); - - /* Don't inherit base __init__ */ - type->tp_init = pybind11_object_init; - - /* Supported protocols */ - type->tp_as_number = &heap_type->as_number; - type->tp_as_sequence = &heap_type->as_sequence; - type->tp_as_mapping = &heap_type->as_mapping; - - /* Flags */ - type->tp_flags |= Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE; -#if PY_MAJOR_VERSION < 3 - type->tp_flags |= Py_TPFLAGS_CHECKTYPES; -#endif - - if (rec.dynamic_attr) - enable_dynamic_attributes(heap_type); - - if (rec.buffer_protocol) - enable_buffer_protocol(heap_type); - - if (PyType_Ready(type) < 0) - pybind11_fail(std::string(rec.name) + ": PyType_Ready failed (" + error_string() + ")!"); - - assert(rec.dynamic_attr ? PyType_HasFeature(type, Py_TPFLAGS_HAVE_GC) - : !PyType_HasFeature(type, Py_TPFLAGS_HAVE_GC)); - - /* Register type with the parent scope */ - if (rec.scope) - setattr(rec.scope, rec.name, (PyObject *) type); - - if (module) // Needed by pydoc - setattr((PyObject *) type, "__module__", module); - - return (PyObject *) type; -} - -NAMESPACE_END(detail) -NAMESPACE_END(pybind11) diff --git a/ptocr/postprocess/dbprocess/include/pybind11/common.h b/ptocr/postprocess/dbprocess/include/pybind11/common.h deleted file mode 100644 index 6c8a4f1..0000000 --- a/ptocr/postprocess/dbprocess/include/pybind11/common.h +++ /dev/null @@ -1,2 +0,0 @@ -#include "detail/common.h" -#warning "Including 'common.h' is deprecated. It will be removed in v3.0. Use 'pybind11.h'." diff --git a/ptocr/postprocess/dbprocess/include/pybind11/complex.h b/ptocr/postprocess/dbprocess/include/pybind11/complex.h deleted file mode 100644 index 3f89638..0000000 --- a/ptocr/postprocess/dbprocess/include/pybind11/complex.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - pybind11/complex.h: Complex number support - - Copyright (c) 2016 Wenzel Jakob - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#include "pybind11.h" -#include - -/// glibc defines I as a macro which breaks things, e.g., boost template names -#ifdef I -# undef I -#endif - -NAMESPACE_BEGIN(PYBIND11_NAMESPACE) - -template struct format_descriptor, detail::enable_if_t::value>> { - static constexpr const char c = format_descriptor::c; - static constexpr const char value[3] = { 'Z', c, '\0' }; - static std::string format() { return std::string(value); } -}; - -#ifndef PYBIND11_CPP17 - -template constexpr const char format_descriptor< - std::complex, detail::enable_if_t::value>>::value[3]; - -#endif - -NAMESPACE_BEGIN(detail) - -template struct is_fmt_numeric, detail::enable_if_t::value>> { - static constexpr bool value = true; - static constexpr int index = is_fmt_numeric::index + 3; -}; - -template class type_caster> { -public: - bool load(handle src, bool convert) { - if (!src) - return false; - if (!convert && !PyComplex_Check(src.ptr())) - return false; - Py_complex result = PyComplex_AsCComplex(src.ptr()); - if (result.real == -1.0 && PyErr_Occurred()) { - PyErr_Clear(); - return false; - } - value = std::complex((T) result.real, (T) result.imag); - return true; - } - - static handle cast(const std::complex &src, return_value_policy /* policy */, handle /* parent */) { - return PyComplex_FromDoubles((double) src.real(), (double) src.imag()); - } - - PYBIND11_TYPE_CASTER(std::complex, _("complex")); -}; -NAMESPACE_END(detail) -NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/ptocr/postprocess/dbprocess/include/pybind11/descr.h b/ptocr/postprocess/dbprocess/include/pybind11/descr.h deleted file mode 100644 index 23a099c..0000000 --- a/ptocr/postprocess/dbprocess/include/pybind11/descr.h +++ /dev/null @@ -1,185 +0,0 @@ -/* - pybind11/descr.h: Helper type for concatenating type signatures - either at runtime (C++11) or compile time (C++14) - - Copyright (c) 2016 Wenzel Jakob - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#include "common.h" - -NAMESPACE_BEGIN(pybind11) -NAMESPACE_BEGIN(detail) - -/* Concatenate type signatures at compile time using C++14 */ -#if defined(PYBIND11_CPP14) && !defined(_MSC_VER) -#define PYBIND11_CONSTEXPR_DESCR - -template class descr { - template friend class descr; -public: - constexpr descr(char const (&text) [Size1+1], const std::type_info * const (&types)[Size2+1]) - : descr(text, types, - make_index_sequence(), - make_index_sequence()) { } - - constexpr const char *text() const { return m_text; } - constexpr const std::type_info * const * types() const { return m_types; } - - template - constexpr descr operator+(const descr &other) const { - return concat(other, - make_index_sequence(), - make_index_sequence(), - make_index_sequence(), - make_index_sequence()); - } - -protected: - template - constexpr descr( - char const (&text) [Size1+1], - const std::type_info * const (&types) [Size2+1], - index_sequence, index_sequence) - : m_text{text[Indices1]..., '\0'}, - m_types{types[Indices2]..., nullptr } {} - - template - constexpr descr - concat(const descr &other, - index_sequence, index_sequence, - index_sequence, index_sequence) const { - return descr( - { m_text[Indices1]..., other.m_text[OtherIndices1]..., '\0' }, - { m_types[Indices2]..., other.m_types[OtherIndices2]..., nullptr } - ); - } - -protected: - char m_text[Size1 + 1]; - const std::type_info * m_types[Size2 + 1]; -}; - -template constexpr descr _(char const(&text)[Size]) { - return descr(text, { nullptr }); -} - -template struct int_to_str : int_to_str { }; -template struct int_to_str<0, Digits...> { - static constexpr auto digits = descr({ ('0' + Digits)..., '\0' }, { nullptr }); -}; - -// Ternary description (like std::conditional) -template -constexpr enable_if_t> _(char const(&text1)[Size1], char const(&)[Size2]) { - return _(text1); -} -template -constexpr enable_if_t> _(char const(&)[Size1], char const(&text2)[Size2]) { - return _(text2); -} -template -constexpr enable_if_t> _(descr d, descr) { return d; } -template -constexpr enable_if_t> _(descr, descr d) { return d; } - -template auto constexpr _() -> decltype(int_to_str::digits) { - return int_to_str::digits; -} - -template constexpr descr<1, 1> _() { - return descr<1, 1>({ '%', '\0' }, { &typeid(Type), nullptr }); -} - -inline constexpr descr<0, 0> concat() { return _(""); } -template auto constexpr concat(descr descr) { return descr; } -template auto constexpr concat(descr descr, Args&&... args) { return descr + _(", ") + concat(args...); } -template auto constexpr type_descr(descr descr) { return _("{") + descr + _("}"); } - -#define PYBIND11_DESCR constexpr auto - -#else /* Simpler C++11 implementation based on run-time memory allocation and copying */ - -class descr { -public: - PYBIND11_NOINLINE descr(const char *text, const std::type_info * const * types) { - size_t nChars = len(text), nTypes = len(types); - m_text = new char[nChars]; - m_types = new const std::type_info *[nTypes]; - memcpy(m_text, text, nChars * sizeof(char)); - memcpy(m_types, types, nTypes * sizeof(const std::type_info *)); - } - - PYBIND11_NOINLINE descr operator+(descr &&d2) && { - descr r; - - size_t nChars1 = len(m_text), nTypes1 = len(m_types); - size_t nChars2 = len(d2.m_text), nTypes2 = len(d2.m_types); - - r.m_text = new char[nChars1 + nChars2 - 1]; - r.m_types = new const std::type_info *[nTypes1 + nTypes2 - 1]; - memcpy(r.m_text, m_text, (nChars1-1) * sizeof(char)); - memcpy(r.m_text + nChars1 - 1, d2.m_text, nChars2 * sizeof(char)); - memcpy(r.m_types, m_types, (nTypes1-1) * sizeof(std::type_info *)); - memcpy(r.m_types + nTypes1 - 1, d2.m_types, nTypes2 * sizeof(std::type_info *)); - - delete[] m_text; delete[] m_types; - delete[] d2.m_text; delete[] d2.m_types; - - return r; - } - - char *text() { return m_text; } - const std::type_info * * types() { return m_types; } - -protected: - PYBIND11_NOINLINE descr() { } - - template static size_t len(const T *ptr) { // return length including null termination - const T *it = ptr; - while (*it++ != (T) 0) - ; - return static_cast(it - ptr); - } - - const std::type_info **m_types = nullptr; - char *m_text = nullptr; -}; - -/* The 'PYBIND11_NOINLINE inline' combinations below are intentional to get the desired linkage while producing as little object code as possible */ - -PYBIND11_NOINLINE inline descr _(const char *text) { - const std::type_info *types[1] = { nullptr }; - return descr(text, types); -} - -template PYBIND11_NOINLINE enable_if_t _(const char *text1, const char *) { return _(text1); } -template PYBIND11_NOINLINE enable_if_t _(char const *, const char *text2) { return _(text2); } -template PYBIND11_NOINLINE enable_if_t _(descr d, descr) { return d; } -template PYBIND11_NOINLINE enable_if_t _(descr, descr d) { return d; } - -template PYBIND11_NOINLINE descr _() { - const std::type_info *types[2] = { &typeid(Type), nullptr }; - return descr("%", types); -} - -template PYBIND11_NOINLINE descr _() { - const std::type_info *types[1] = { nullptr }; - return descr(std::to_string(Size).c_str(), types); -} - -PYBIND11_NOINLINE inline descr concat() { return _(""); } -PYBIND11_NOINLINE inline descr concat(descr &&d) { return d; } -template PYBIND11_NOINLINE descr concat(descr &&d, Args&&... args) { return std::move(d) + _(", ") + concat(std::forward(args)...); } -PYBIND11_NOINLINE inline descr type_descr(descr&& d) { return _("{") + std::move(d) + _("}"); } - -#define PYBIND11_DESCR ::pybind11::detail::descr -#endif - -NAMESPACE_END(detail) -NAMESPACE_END(pybind11) diff --git a/ptocr/postprocess/dbprocess/include/pybind11/detail/class.h b/ptocr/postprocess/dbprocess/include/pybind11/detail/class.h deleted file mode 100644 index 7a5dd01..0000000 --- a/ptocr/postprocess/dbprocess/include/pybind11/detail/class.h +++ /dev/null @@ -1,622 +0,0 @@ -/* - pybind11/detail/class.h: Python C API implementation details for py::class_ - - Copyright (c) 2017 Wenzel Jakob - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#include "../attr.h" - -NAMESPACE_BEGIN(PYBIND11_NAMESPACE) -NAMESPACE_BEGIN(detail) - -#if PY_VERSION_HEX >= 0x03030000 -# define PYBIND11_BUILTIN_QUALNAME -# define PYBIND11_SET_OLDPY_QUALNAME(obj, nameobj) -#else -// In pre-3.3 Python, we still set __qualname__ so that we can produce reliable function type -// signatures; in 3.3+ this macro expands to nothing: -# define PYBIND11_SET_OLDPY_QUALNAME(obj, nameobj) setattr((PyObject *) obj, "__qualname__", nameobj) -#endif - -inline PyTypeObject *type_incref(PyTypeObject *type) { - Py_INCREF(type); - return type; -} - -#if !defined(PYPY_VERSION) - -/// `pybind11_static_property.__get__()`: Always pass the class instead of the instance. -extern "C" inline PyObject *pybind11_static_get(PyObject *self, PyObject * /*ob*/, PyObject *cls) { - return PyProperty_Type.tp_descr_get(self, cls, cls); -} - -/// `pybind11_static_property.__set__()`: Just like the above `__get__()`. -extern "C" inline int pybind11_static_set(PyObject *self, PyObject *obj, PyObject *value) { - PyObject *cls = PyType_Check(obj) ? obj : (PyObject *) Py_TYPE(obj); - return PyProperty_Type.tp_descr_set(self, cls, value); -} - -/** A `static_property` is the same as a `property` but the `__get__()` and `__set__()` - methods are modified to always use the object type instead of a concrete instance. - Return value: New reference. */ -inline PyTypeObject *make_static_property_type() { - constexpr auto *name = "pybind11_static_property"; - auto name_obj = reinterpret_steal(PYBIND11_FROM_STRING(name)); - - /* Danger zone: from now (and until PyType_Ready), make sure to - issue no Python C API calls which could potentially invoke the - garbage collector (the GC will call type_traverse(), which will in - turn find the newly constructed type in an invalid state) */ - auto heap_type = (PyHeapTypeObject *) PyType_Type.tp_alloc(&PyType_Type, 0); - if (!heap_type) - pybind11_fail("make_static_property_type(): error allocating type!"); - - heap_type->ht_name = name_obj.inc_ref().ptr(); -#ifdef PYBIND11_BUILTIN_QUALNAME - heap_type->ht_qualname = name_obj.inc_ref().ptr(); -#endif - - auto type = &heap_type->ht_type; - type->tp_name = name; - type->tp_base = type_incref(&PyProperty_Type); - type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE; - type->tp_descr_get = pybind11_static_get; - type->tp_descr_set = pybind11_static_set; - - if (PyType_Ready(type) < 0) - pybind11_fail("make_static_property_type(): failure in PyType_Ready()!"); - - setattr((PyObject *) type, "__module__", str("pybind11_builtins")); - PYBIND11_SET_OLDPY_QUALNAME(type, name_obj); - - return type; -} - -#else // PYPY - -/** PyPy has some issues with the above C API, so we evaluate Python code instead. - This function will only be called once so performance isn't really a concern. - Return value: New reference. */ -inline PyTypeObject *make_static_property_type() { - auto d = dict(); - PyObject *result = PyRun_String(R"(\ - class pybind11_static_property(property): - def __get__(self, obj, cls): - return property.__get__(self, cls, cls) - - def __set__(self, obj, value): - cls = obj if isinstance(obj, type) else type(obj) - property.__set__(self, cls, value) - )", Py_file_input, d.ptr(), d.ptr() - ); - if (result == nullptr) - throw error_already_set(); - Py_DECREF(result); - return (PyTypeObject *) d["pybind11_static_property"].cast().release().ptr(); -} - -#endif // PYPY - -/** Types with static properties need to handle `Type.static_prop = x` in a specific way. - By default, Python replaces the `static_property` itself, but for wrapped C++ types - we need to call `static_property.__set__()` in order to propagate the new value to - the underlying C++ data structure. */ -extern "C" inline int pybind11_meta_setattro(PyObject* obj, PyObject* name, PyObject* value) { - // Use `_PyType_Lookup()` instead of `PyObject_GetAttr()` in order to get the raw - // descriptor (`property`) instead of calling `tp_descr_get` (`property.__get__()`). - PyObject *descr = _PyType_Lookup((PyTypeObject *) obj, name); - - // The following assignment combinations are possible: - // 1. `Type.static_prop = value` --> descr_set: `Type.static_prop.__set__(value)` - // 2. `Type.static_prop = other_static_prop` --> setattro: replace existing `static_prop` - // 3. `Type.regular_attribute = value` --> setattro: regular attribute assignment - const auto static_prop = (PyObject *) get_internals().static_property_type; - const auto call_descr_set = descr && PyObject_IsInstance(descr, static_prop) - && !PyObject_IsInstance(value, static_prop); - if (call_descr_set) { - // Call `static_property.__set__()` instead of replacing the `static_property`. -#if !defined(PYPY_VERSION) - return Py_TYPE(descr)->tp_descr_set(descr, obj, value); -#else - if (PyObject *result = PyObject_CallMethod(descr, "__set__", "OO", obj, value)) { - Py_DECREF(result); - return 0; - } else { - return -1; - } -#endif - } else { - // Replace existing attribute. - return PyType_Type.tp_setattro(obj, name, value); - } -} - -#if PY_MAJOR_VERSION >= 3 -/** - * Python 3's PyInstanceMethod_Type hides itself via its tp_descr_get, which prevents aliasing - * methods via cls.attr("m2") = cls.attr("m1"): instead the tp_descr_get returns a plain function, - * when called on a class, or a PyMethod, when called on an instance. Override that behaviour here - * to do a special case bypass for PyInstanceMethod_Types. - */ -extern "C" inline PyObject *pybind11_meta_getattro(PyObject *obj, PyObject *name) { - PyObject *descr = _PyType_Lookup((PyTypeObject *) obj, name); - if (descr && PyInstanceMethod_Check(descr)) { - Py_INCREF(descr); - return descr; - } - else { - return PyType_Type.tp_getattro(obj, name); - } -} -#endif - -/** This metaclass is assigned by default to all pybind11 types and is required in order - for static properties to function correctly. Users may override this using `py::metaclass`. - Return value: New reference. */ -inline PyTypeObject* make_default_metaclass() { - constexpr auto *name = "pybind11_type"; - auto name_obj = reinterpret_steal(PYBIND11_FROM_STRING(name)); - - /* Danger zone: from now (and until PyType_Ready), make sure to - issue no Python C API calls which could potentially invoke the - garbage collector (the GC will call type_traverse(), which will in - turn find the newly constructed type in an invalid state) */ - auto heap_type = (PyHeapTypeObject *) PyType_Type.tp_alloc(&PyType_Type, 0); - if (!heap_type) - pybind11_fail("make_default_metaclass(): error allocating metaclass!"); - - heap_type->ht_name = name_obj.inc_ref().ptr(); -#ifdef PYBIND11_BUILTIN_QUALNAME - heap_type->ht_qualname = name_obj.inc_ref().ptr(); -#endif - - auto type = &heap_type->ht_type; - type->tp_name = name; - type->tp_base = type_incref(&PyType_Type); - type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE; - - type->tp_setattro = pybind11_meta_setattro; -#if PY_MAJOR_VERSION >= 3 - type->tp_getattro = pybind11_meta_getattro; -#endif - - if (PyType_Ready(type) < 0) - pybind11_fail("make_default_metaclass(): failure in PyType_Ready()!"); - - setattr((PyObject *) type, "__module__", str("pybind11_builtins")); - PYBIND11_SET_OLDPY_QUALNAME(type, name_obj); - - return type; -} - -/// For multiple inheritance types we need to recursively register/deregister base pointers for any -/// base classes with pointers that are difference from the instance value pointer so that we can -/// correctly recognize an offset base class pointer. This calls a function with any offset base ptrs. -inline void traverse_offset_bases(void *valueptr, const detail::type_info *tinfo, instance *self, - bool (*f)(void * /*parentptr*/, instance * /*self*/)) { - for (handle h : reinterpret_borrow(tinfo->type->tp_bases)) { - if (auto parent_tinfo = get_type_info((PyTypeObject *) h.ptr())) { - for (auto &c : parent_tinfo->implicit_casts) { - if (c.first == tinfo->cpptype) { - auto *parentptr = c.second(valueptr); - if (parentptr != valueptr) - f(parentptr, self); - traverse_offset_bases(parentptr, parent_tinfo, self, f); - break; - } - } - } - } -} - -inline bool register_instance_impl(void *ptr, instance *self) { - get_internals().registered_instances.emplace(ptr, self); - return true; // unused, but gives the same signature as the deregister func -} -inline bool deregister_instance_impl(void *ptr, instance *self) { - auto ®istered_instances = get_internals().registered_instances; - auto range = registered_instances.equal_range(ptr); - for (auto it = range.first; it != range.second; ++it) { - if (Py_TYPE(self) == Py_TYPE(it->second)) { - registered_instances.erase(it); - return true; - } - } - return false; -} - -inline void register_instance(instance *self, void *valptr, const type_info *tinfo) { - register_instance_impl(valptr, self); - if (!tinfo->simple_ancestors) - traverse_offset_bases(valptr, tinfo, self, register_instance_impl); -} - -inline bool deregister_instance(instance *self, void *valptr, const type_info *tinfo) { - bool ret = deregister_instance_impl(valptr, self); - if (!tinfo->simple_ancestors) - traverse_offset_bases(valptr, tinfo, self, deregister_instance_impl); - return ret; -} - -/// Instance creation function for all pybind11 types. It allocates the internal instance layout for -/// holding C++ objects and holders. Allocation is done lazily (the first time the instance is cast -/// to a reference or pointer), and initialization is done by an `__init__` function. -inline PyObject *make_new_instance(PyTypeObject *type) { -#if defined(PYPY_VERSION) - // PyPy gets tp_basicsize wrong (issue 2482) under multiple inheritance when the first inherited - // object is a a plain Python type (i.e. not derived from an extension type). Fix it. - ssize_t instance_size = static_cast(sizeof(instance)); - if (type->tp_basicsize < instance_size) { - type->tp_basicsize = instance_size; - } -#endif - PyObject *self = type->tp_alloc(type, 0); - auto inst = reinterpret_cast(self); - // Allocate the value/holder internals: - inst->allocate_layout(); - - inst->owned = true; - - return self; -} - -/// Instance creation function for all pybind11 types. It only allocates space for the -/// C++ object, but doesn't call the constructor -- an `__init__` function must do that. -extern "C" inline PyObject *pybind11_object_new(PyTypeObject *type, PyObject *, PyObject *) { - return make_new_instance(type); -} - -/// An `__init__` function constructs the C++ object. Users should provide at least one -/// of these using `py::init` or directly with `.def(__init__, ...)`. Otherwise, the -/// following default function will be used which simply throws an exception. -extern "C" inline int pybind11_object_init(PyObject *self, PyObject *, PyObject *) { - PyTypeObject *type = Py_TYPE(self); - std::string msg; -#if defined(PYPY_VERSION) - msg += handle((PyObject *) type).attr("__module__").cast() + "."; -#endif - msg += type->tp_name; - msg += ": No constructor defined!"; - PyErr_SetString(PyExc_TypeError, msg.c_str()); - return -1; -} - -inline void add_patient(PyObject *nurse, PyObject *patient) { - auto &internals = get_internals(); - auto instance = reinterpret_cast(nurse); - instance->has_patients = true; - Py_INCREF(patient); - internals.patients[nurse].push_back(patient); -} - -inline void clear_patients(PyObject *self) { - auto instance = reinterpret_cast(self); - auto &internals = get_internals(); - auto pos = internals.patients.find(self); - assert(pos != internals.patients.end()); - // Clearing the patients can cause more Python code to run, which - // can invalidate the iterator. Extract the vector of patients - // from the unordered_map first. - auto patients = std::move(pos->second); - internals.patients.erase(pos); - instance->has_patients = false; - for (PyObject *&patient : patients) - Py_CLEAR(patient); -} - -/// Clears all internal data from the instance and removes it from registered instances in -/// preparation for deallocation. -inline void clear_instance(PyObject *self) { - auto instance = reinterpret_cast(self); - - // Deallocate any values/holders, if present: - for (auto &v_h : values_and_holders(instance)) { - if (v_h) { - - // We have to deregister before we call dealloc because, for virtual MI types, we still - // need to be able to get the parent pointers. - if (v_h.instance_registered() && !deregister_instance(instance, v_h.value_ptr(), v_h.type)) - pybind11_fail("pybind11_object_dealloc(): Tried to deallocate unregistered instance!"); - - if (instance->owned || v_h.holder_constructed()) - v_h.type->dealloc(v_h); - } - } - // Deallocate the value/holder layout internals: - instance->deallocate_layout(); - - if (instance->weakrefs) - PyObject_ClearWeakRefs(self); - - PyObject **dict_ptr = _PyObject_GetDictPtr(self); - if (dict_ptr) - Py_CLEAR(*dict_ptr); - - if (instance->has_patients) - clear_patients(self); -} - -/// Instance destructor function for all pybind11 types. It calls `type_info.dealloc` -/// to destroy the C++ object itself, while the rest is Python bookkeeping. -extern "C" inline void pybind11_object_dealloc(PyObject *self) { - clear_instance(self); - - auto type = Py_TYPE(self); - type->tp_free(self); - - // `type->tp_dealloc != pybind11_object_dealloc` means that we're being called - // as part of a derived type's dealloc, in which case we're not allowed to decref - // the type here. For cross-module compatibility, we shouldn't compare directly - // with `pybind11_object_dealloc`, but with the common one stashed in internals. - auto pybind11_object_type = (PyTypeObject *) get_internals().instance_base; - if (type->tp_dealloc == pybind11_object_type->tp_dealloc) - Py_DECREF(type); -} - -/** Create the type which can be used as a common base for all classes. This is - needed in order to satisfy Python's requirements for multiple inheritance. - Return value: New reference. */ -inline PyObject *make_object_base_type(PyTypeObject *metaclass) { - constexpr auto *name = "pybind11_object"; - auto name_obj = reinterpret_steal(PYBIND11_FROM_STRING(name)); - - /* Danger zone: from now (and until PyType_Ready), make sure to - issue no Python C API calls which could potentially invoke the - garbage collector (the GC will call type_traverse(), which will in - turn find the newly constructed type in an invalid state) */ - auto heap_type = (PyHeapTypeObject *) metaclass->tp_alloc(metaclass, 0); - if (!heap_type) - pybind11_fail("make_object_base_type(): error allocating type!"); - - heap_type->ht_name = name_obj.inc_ref().ptr(); -#ifdef PYBIND11_BUILTIN_QUALNAME - heap_type->ht_qualname = name_obj.inc_ref().ptr(); -#endif - - auto type = &heap_type->ht_type; - type->tp_name = name; - type->tp_base = type_incref(&PyBaseObject_Type); - type->tp_basicsize = static_cast(sizeof(instance)); - type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE; - - type->tp_new = pybind11_object_new; - type->tp_init = pybind11_object_init; - type->tp_dealloc = pybind11_object_dealloc; - - /* Support weak references (needed for the keep_alive feature) */ - type->tp_weaklistoffset = offsetof(instance, weakrefs); - - if (PyType_Ready(type) < 0) - pybind11_fail("PyType_Ready failed in make_object_base_type():" + error_string()); - - setattr((PyObject *) type, "__module__", str("pybind11_builtins")); - PYBIND11_SET_OLDPY_QUALNAME(type, name_obj); - - assert(!PyType_HasFeature(type, Py_TPFLAGS_HAVE_GC)); - return (PyObject *) heap_type; -} - -/// dynamic_attr: Support for `d = instance.__dict__`. -extern "C" inline PyObject *pybind11_get_dict(PyObject *self, void *) { - PyObject *&dict = *_PyObject_GetDictPtr(self); - if (!dict) - dict = PyDict_New(); - Py_XINCREF(dict); - return dict; -} - -/// dynamic_attr: Support for `instance.__dict__ = dict()`. -extern "C" inline int pybind11_set_dict(PyObject *self, PyObject *new_dict, void *) { - if (!PyDict_Check(new_dict)) { - PyErr_Format(PyExc_TypeError, "__dict__ must be set to a dictionary, not a '%.200s'", - Py_TYPE(new_dict)->tp_name); - return -1; - } - PyObject *&dict = *_PyObject_GetDictPtr(self); - Py_INCREF(new_dict); - Py_CLEAR(dict); - dict = new_dict; - return 0; -} - -/// dynamic_attr: Allow the garbage collector to traverse the internal instance `__dict__`. -extern "C" inline int pybind11_traverse(PyObject *self, visitproc visit, void *arg) { - PyObject *&dict = *_PyObject_GetDictPtr(self); - Py_VISIT(dict); - return 0; -} - -/// dynamic_attr: Allow the GC to clear the dictionary. -extern "C" inline int pybind11_clear(PyObject *self) { - PyObject *&dict = *_PyObject_GetDictPtr(self); - Py_CLEAR(dict); - return 0; -} - -/// Give instances of this type a `__dict__` and opt into garbage collection. -inline void enable_dynamic_attributes(PyHeapTypeObject *heap_type) { - auto type = &heap_type->ht_type; -#if defined(PYPY_VERSION) - pybind11_fail(std::string(type->tp_name) + ": dynamic attributes are " - "currently not supported in " - "conjunction with PyPy!"); -#endif - type->tp_flags |= Py_TPFLAGS_HAVE_GC; - type->tp_dictoffset = type->tp_basicsize; // place dict at the end - type->tp_basicsize += (ssize_t)sizeof(PyObject *); // and allocate enough space for it - type->tp_traverse = pybind11_traverse; - type->tp_clear = pybind11_clear; - - static PyGetSetDef getset[] = { - {const_cast("__dict__"), pybind11_get_dict, pybind11_set_dict, nullptr, nullptr}, - {nullptr, nullptr, nullptr, nullptr, nullptr} - }; - type->tp_getset = getset; -} - -/// buffer_protocol: Fill in the view as specified by flags. -extern "C" inline int pybind11_getbuffer(PyObject *obj, Py_buffer *view, int flags) { - // Look for a `get_buffer` implementation in this type's info or any bases (following MRO). - type_info *tinfo = nullptr; - for (auto type : reinterpret_borrow(Py_TYPE(obj)->tp_mro)) { - tinfo = get_type_info((PyTypeObject *) type.ptr()); - if (tinfo && tinfo->get_buffer) - break; - } - if (view == nullptr || obj == nullptr || !tinfo || !tinfo->get_buffer) { - if (view) - view->obj = nullptr; - PyErr_SetString(PyExc_BufferError, "pybind11_getbuffer(): Internal error"); - return -1; - } - std::memset(view, 0, sizeof(Py_buffer)); - buffer_info *info = tinfo->get_buffer(obj, tinfo->get_buffer_data); - view->obj = obj; - view->ndim = 1; - view->internal = info; - view->buf = info->ptr; - view->itemsize = info->itemsize; - view->len = view->itemsize; - for (auto s : info->shape) - view->len *= s; - if ((flags & PyBUF_FORMAT) == PyBUF_FORMAT) - view->format = const_cast(info->format.c_str()); - if ((flags & PyBUF_STRIDES) == PyBUF_STRIDES) { - view->ndim = (int) info->ndim; - view->strides = &info->strides[0]; - view->shape = &info->shape[0]; - } - Py_INCREF(view->obj); - return 0; -} - -/// buffer_protocol: Release the resources of the buffer. -extern "C" inline void pybind11_releasebuffer(PyObject *, Py_buffer *view) { - delete (buffer_info *) view->internal; -} - -/// Give this type a buffer interface. -inline void enable_buffer_protocol(PyHeapTypeObject *heap_type) { - heap_type->ht_type.tp_as_buffer = &heap_type->as_buffer; -#if PY_MAJOR_VERSION < 3 - heap_type->ht_type.tp_flags |= Py_TPFLAGS_HAVE_NEWBUFFER; -#endif - - heap_type->as_buffer.bf_getbuffer = pybind11_getbuffer; - heap_type->as_buffer.bf_releasebuffer = pybind11_releasebuffer; -} - -/** Create a brand new Python type according to the `type_record` specification. - Return value: New reference. */ -inline PyObject* make_new_python_type(const type_record &rec) { - auto name = reinterpret_steal(PYBIND11_FROM_STRING(rec.name)); - - auto qualname = name; - if (rec.scope && !PyModule_Check(rec.scope.ptr()) && hasattr(rec.scope, "__qualname__")) { -#if PY_MAJOR_VERSION >= 3 - qualname = reinterpret_steal( - PyUnicode_FromFormat("%U.%U", rec.scope.attr("__qualname__").ptr(), name.ptr())); -#else - qualname = str(rec.scope.attr("__qualname__").cast() + "." + rec.name); -#endif - } - - object module; - if (rec.scope) { - if (hasattr(rec.scope, "__module__")) - module = rec.scope.attr("__module__"); - else if (hasattr(rec.scope, "__name__")) - module = rec.scope.attr("__name__"); - } - - auto full_name = c_str( -#if !defined(PYPY_VERSION) - module ? str(module).cast() + "." + rec.name : -#endif - rec.name); - - char *tp_doc = nullptr; - if (rec.doc && options::show_user_defined_docstrings()) { - /* Allocate memory for docstring (using PyObject_MALLOC, since - Python will free this later on) */ - size_t size = strlen(rec.doc) + 1; - tp_doc = (char *) PyObject_MALLOC(size); - memcpy((void *) tp_doc, rec.doc, size); - } - - auto &internals = get_internals(); - auto bases = tuple(rec.bases); - auto base = (bases.size() == 0) ? internals.instance_base - : bases[0].ptr(); - - /* Danger zone: from now (and until PyType_Ready), make sure to - issue no Python C API calls which could potentially invoke the - garbage collector (the GC will call type_traverse(), which will in - turn find the newly constructed type in an invalid state) */ - auto metaclass = rec.metaclass.ptr() ? (PyTypeObject *) rec.metaclass.ptr() - : internals.default_metaclass; - - auto heap_type = (PyHeapTypeObject *) metaclass->tp_alloc(metaclass, 0); - if (!heap_type) - pybind11_fail(std::string(rec.name) + ": Unable to create type object!"); - - heap_type->ht_name = name.release().ptr(); -#ifdef PYBIND11_BUILTIN_QUALNAME - heap_type->ht_qualname = qualname.inc_ref().ptr(); -#endif - - auto type = &heap_type->ht_type; - type->tp_name = full_name; - type->tp_doc = tp_doc; - type->tp_base = type_incref((PyTypeObject *)base); - type->tp_basicsize = static_cast(sizeof(instance)); - if (bases.size() > 0) - type->tp_bases = bases.release().ptr(); - - /* Don't inherit base __init__ */ - type->tp_init = pybind11_object_init; - - /* Supported protocols */ - type->tp_as_number = &heap_type->as_number; - type->tp_as_sequence = &heap_type->as_sequence; - type->tp_as_mapping = &heap_type->as_mapping; - - /* Flags */ - type->tp_flags |= Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE; -#if PY_MAJOR_VERSION < 3 - type->tp_flags |= Py_TPFLAGS_CHECKTYPES; -#endif - - if (rec.dynamic_attr) - enable_dynamic_attributes(heap_type); - - if (rec.buffer_protocol) - enable_buffer_protocol(heap_type); - - if (PyType_Ready(type) < 0) - pybind11_fail(std::string(rec.name) + ": PyType_Ready failed (" + error_string() + ")!"); - - assert(rec.dynamic_attr ? PyType_HasFeature(type, Py_TPFLAGS_HAVE_GC) - : !PyType_HasFeature(type, Py_TPFLAGS_HAVE_GC)); - - /* Register type with the parent scope */ - if (rec.scope) - setattr(rec.scope, rec.name, (PyObject *) type); - else - Py_INCREF(type); // Keep it alive forever (reference leak) - - if (module) // Needed by pydoc - setattr((PyObject *) type, "__module__", module); - - PYBIND11_SET_OLDPY_QUALNAME(type, qualname); - - return (PyObject *) type; -} - -NAMESPACE_END(detail) -NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/ptocr/postprocess/dbprocess/include/pybind11/detail/common.h b/ptocr/postprocess/dbprocess/include/pybind11/detail/common.h deleted file mode 100644 index 5ff7485..0000000 --- a/ptocr/postprocess/dbprocess/include/pybind11/detail/common.h +++ /dev/null @@ -1,807 +0,0 @@ -/* - pybind11/detail/common.h -- Basic macros - - Copyright (c) 2016 Wenzel Jakob - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#if !defined(NAMESPACE_BEGIN) -# define NAMESPACE_BEGIN(name) namespace name { -#endif -#if !defined(NAMESPACE_END) -# define NAMESPACE_END(name) } -#endif - -// Robust support for some features and loading modules compiled against different pybind versions -// requires forcing hidden visibility on pybind code, so we enforce this by setting the attribute on -// the main `pybind11` namespace. -#if !defined(PYBIND11_NAMESPACE) -# ifdef __GNUG__ -# define PYBIND11_NAMESPACE pybind11 __attribute__((visibility("hidden"))) -# else -# define PYBIND11_NAMESPACE pybind11 -# endif -#endif - -#if !(defined(_MSC_VER) && __cplusplus == 199711L) && !defined(__INTEL_COMPILER) -# if __cplusplus >= 201402L -# define PYBIND11_CPP14 -# if __cplusplus >= 201703L -# define PYBIND11_CPP17 -# endif -# endif -#elif defined(_MSC_VER) && __cplusplus == 199711L -// MSVC sets _MSVC_LANG rather than __cplusplus (supposedly until the standard is fully implemented) -// Unless you use the /Zc:__cplusplus flag on Visual Studio 2017 15.7 Preview 3 or newer -# if _MSVC_LANG >= 201402L -# define PYBIND11_CPP14 -# if _MSVC_LANG > 201402L && _MSC_VER >= 1910 -# define PYBIND11_CPP17 -# endif -# endif -#endif - -// Compiler version assertions -#if defined(__INTEL_COMPILER) -# if __INTEL_COMPILER < 1700 -# error pybind11 requires Intel C++ compiler v17 or newer -# endif -#elif defined(__clang__) && !defined(__apple_build_version__) -# if __clang_major__ < 3 || (__clang_major__ == 3 && __clang_minor__ < 3) -# error pybind11 requires clang 3.3 or newer -# endif -#elif defined(__clang__) -// Apple changes clang version macros to its Xcode version; the first Xcode release based on -// (upstream) clang 3.3 was Xcode 5: -# if __clang_major__ < 5 -# error pybind11 requires Xcode/clang 5.0 or newer -# endif -#elif defined(__GNUG__) -# if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 8) -# error pybind11 requires gcc 4.8 or newer -# endif -#elif defined(_MSC_VER) -// Pybind hits various compiler bugs in 2015u2 and earlier, and also makes use of some stl features -// (e.g. std::negation) added in 2015u3: -# if _MSC_FULL_VER < 190024210 -# error pybind11 requires MSVC 2015 update 3 or newer -# endif -#endif - -#if !defined(PYBIND11_EXPORT) -# if defined(WIN32) || defined(_WIN32) -# define PYBIND11_EXPORT __declspec(dllexport) -# else -# define PYBIND11_EXPORT __attribute__ ((visibility("default"))) -# endif -#endif - -#if defined(_MSC_VER) -# define PYBIND11_NOINLINE __declspec(noinline) -#else -# define PYBIND11_NOINLINE __attribute__ ((noinline)) -#endif - -#if defined(PYBIND11_CPP14) -# define PYBIND11_DEPRECATED(reason) [[deprecated(reason)]] -#else -# define PYBIND11_DEPRECATED(reason) __attribute__((deprecated(reason))) -#endif - -#define PYBIND11_VERSION_MAJOR 2 -#define PYBIND11_VERSION_MINOR 3 -#define PYBIND11_VERSION_PATCH dev0 - -/// Include Python header, disable linking to pythonX_d.lib on Windows in debug mode -#if defined(_MSC_VER) -# if (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION < 4) -# define HAVE_ROUND 1 -# endif -# pragma warning(push) -# pragma warning(disable: 4510 4610 4512 4005) -# if defined(_DEBUG) -# define PYBIND11_DEBUG_MARKER -# undef _DEBUG -# endif -#endif - -#include -#include -#include - -#if defined(_WIN32) && (defined(min) || defined(max)) -# error Macro clash with min and max -- define NOMINMAX when compiling your program on Windows -#endif - -#if defined(isalnum) -# undef isalnum -# undef isalpha -# undef islower -# undef isspace -# undef isupper -# undef tolower -# undef toupper -#endif - -#if defined(_MSC_VER) -# if defined(PYBIND11_DEBUG_MARKER) -# define _DEBUG -# undef PYBIND11_DEBUG_MARKER -# endif -# pragma warning(pop) -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if PY_MAJOR_VERSION >= 3 /// Compatibility macros for various Python versions -#define PYBIND11_INSTANCE_METHOD_NEW(ptr, class_) PyInstanceMethod_New(ptr) -#define PYBIND11_INSTANCE_METHOD_CHECK PyInstanceMethod_Check -#define PYBIND11_INSTANCE_METHOD_GET_FUNCTION PyInstanceMethod_GET_FUNCTION -#define PYBIND11_BYTES_CHECK PyBytes_Check -#define PYBIND11_BYTES_FROM_STRING PyBytes_FromString -#define PYBIND11_BYTES_FROM_STRING_AND_SIZE PyBytes_FromStringAndSize -#define PYBIND11_BYTES_AS_STRING_AND_SIZE PyBytes_AsStringAndSize -#define PYBIND11_BYTES_AS_STRING PyBytes_AsString -#define PYBIND11_BYTES_SIZE PyBytes_Size -#define PYBIND11_LONG_CHECK(o) PyLong_Check(o) -#define PYBIND11_LONG_AS_LONGLONG(o) PyLong_AsLongLong(o) -#define PYBIND11_LONG_FROM_SIGNED(o) PyLong_FromSsize_t((ssize_t) o) -#define PYBIND11_LONG_FROM_UNSIGNED(o) PyLong_FromSize_t((size_t) o) -#define PYBIND11_BYTES_NAME "bytes" -#define PYBIND11_STRING_NAME "str" -#define PYBIND11_SLICE_OBJECT PyObject -#define PYBIND11_FROM_STRING PyUnicode_FromString -#define PYBIND11_STR_TYPE ::pybind11::str -#define PYBIND11_BOOL_ATTR "__bool__" -#define PYBIND11_NB_BOOL(ptr) ((ptr)->nb_bool) -#define PYBIND11_PLUGIN_IMPL(name) \ - extern "C" PYBIND11_EXPORT PyObject *PyInit_##name() - -#else -#define PYBIND11_INSTANCE_METHOD_NEW(ptr, class_) PyMethod_New(ptr, nullptr, class_) -#define PYBIND11_INSTANCE_METHOD_CHECK PyMethod_Check -#define PYBIND11_INSTANCE_METHOD_GET_FUNCTION PyMethod_GET_FUNCTION -#define PYBIND11_BYTES_CHECK PyString_Check -#define PYBIND11_BYTES_FROM_STRING PyString_FromString -#define PYBIND11_BYTES_FROM_STRING_AND_SIZE PyString_FromStringAndSize -#define PYBIND11_BYTES_AS_STRING_AND_SIZE PyString_AsStringAndSize -#define PYBIND11_BYTES_AS_STRING PyString_AsString -#define PYBIND11_BYTES_SIZE PyString_Size -#define PYBIND11_LONG_CHECK(o) (PyInt_Check(o) || PyLong_Check(o)) -#define PYBIND11_LONG_AS_LONGLONG(o) (PyInt_Check(o) ? (long long) PyLong_AsLong(o) : PyLong_AsLongLong(o)) -#define PYBIND11_LONG_FROM_SIGNED(o) PyInt_FromSsize_t((ssize_t) o) // Returns long if needed. -#define PYBIND11_LONG_FROM_UNSIGNED(o) PyInt_FromSize_t((size_t) o) // Returns long if needed. -#define PYBIND11_BYTES_NAME "str" -#define PYBIND11_STRING_NAME "unicode" -#define PYBIND11_SLICE_OBJECT PySliceObject -#define PYBIND11_FROM_STRING PyString_FromString -#define PYBIND11_STR_TYPE ::pybind11::bytes -#define PYBIND11_BOOL_ATTR "__nonzero__" -#define PYBIND11_NB_BOOL(ptr) ((ptr)->nb_nonzero) -#define PYBIND11_PLUGIN_IMPL(name) \ - static PyObject *pybind11_init_wrapper(); \ - extern "C" PYBIND11_EXPORT void init##name() { \ - (void)pybind11_init_wrapper(); \ - } \ - PyObject *pybind11_init_wrapper() -#endif - -#if PY_VERSION_HEX >= 0x03050000 && PY_VERSION_HEX < 0x03050200 -extern "C" { - struct _Py_atomic_address { void *value; }; - PyAPI_DATA(_Py_atomic_address) _PyThreadState_Current; -} -#endif - -#define PYBIND11_TRY_NEXT_OVERLOAD ((PyObject *) 1) // special failure return code -#define PYBIND11_STRINGIFY(x) #x -#define PYBIND11_TOSTRING(x) PYBIND11_STRINGIFY(x) -#define PYBIND11_CONCAT(first, second) first##second - -#define PYBIND11_CHECK_PYTHON_VERSION \ - { \ - const char *compiled_ver = PYBIND11_TOSTRING(PY_MAJOR_VERSION) \ - "." PYBIND11_TOSTRING(PY_MINOR_VERSION); \ - const char *runtime_ver = Py_GetVersion(); \ - size_t len = std::strlen(compiled_ver); \ - if (std::strncmp(runtime_ver, compiled_ver, len) != 0 \ - || (runtime_ver[len] >= '0' && runtime_ver[len] <= '9')) { \ - PyErr_Format(PyExc_ImportError, \ - "Python version mismatch: module was compiled for Python %s, " \ - "but the interpreter version is incompatible: %s.", \ - compiled_ver, runtime_ver); \ - return nullptr; \ - } \ - } - -#define PYBIND11_CATCH_INIT_EXCEPTIONS \ - catch (pybind11::error_already_set &e) { \ - PyErr_SetString(PyExc_ImportError, e.what()); \ - return nullptr; \ - } catch (const std::exception &e) { \ - PyErr_SetString(PyExc_ImportError, e.what()); \ - return nullptr; \ - } \ - -/** \rst - ***Deprecated in favor of PYBIND11_MODULE*** - - This macro creates the entry point that will be invoked when the Python interpreter - imports a plugin library. Please create a `module` in the function body and return - the pointer to its underlying Python object at the end. - - .. code-block:: cpp - - PYBIND11_PLUGIN(example) { - pybind11::module m("example", "pybind11 example plugin"); - /// Set up bindings here - return m.ptr(); - } -\endrst */ -#define PYBIND11_PLUGIN(name) \ - PYBIND11_DEPRECATED("PYBIND11_PLUGIN is deprecated, use PYBIND11_MODULE") \ - static PyObject *pybind11_init(); \ - PYBIND11_PLUGIN_IMPL(name) { \ - PYBIND11_CHECK_PYTHON_VERSION \ - try { \ - return pybind11_init(); \ - } PYBIND11_CATCH_INIT_EXCEPTIONS \ - } \ - PyObject *pybind11_init() - -/** \rst - This macro creates the entry point that will be invoked when the Python interpreter - imports an extension module. The module name is given as the fist argument and it - should not be in quotes. The second macro argument defines a variable of type - `py::module` which can be used to initialize the module. - - .. code-block:: cpp - - PYBIND11_MODULE(example, m) { - m.doc() = "pybind11 example module"; - - // Add bindings here - m.def("foo", []() { - return "Hello, World!"; - }); - } -\endrst */ -#define PYBIND11_MODULE(name, variable) \ - static void PYBIND11_CONCAT(pybind11_init_, name)(pybind11::module &); \ - PYBIND11_PLUGIN_IMPL(name) { \ - PYBIND11_CHECK_PYTHON_VERSION \ - auto m = pybind11::module(PYBIND11_TOSTRING(name)); \ - try { \ - PYBIND11_CONCAT(pybind11_init_, name)(m); \ - return m.ptr(); \ - } PYBIND11_CATCH_INIT_EXCEPTIONS \ - } \ - void PYBIND11_CONCAT(pybind11_init_, name)(pybind11::module &variable) - - -NAMESPACE_BEGIN(PYBIND11_NAMESPACE) - -using ssize_t = Py_ssize_t; -using size_t = std::size_t; - -/// Approach used to cast a previously unknown C++ instance into a Python object -enum class return_value_policy : uint8_t { - /** This is the default return value policy, which falls back to the policy - return_value_policy::take_ownership when the return value is a pointer. - Otherwise, it uses return_value::move or return_value::copy for rvalue - and lvalue references, respectively. See below for a description of what - all of these different policies do. */ - automatic = 0, - - /** As above, but use policy return_value_policy::reference when the return - value is a pointer. This is the default conversion policy for function - arguments when calling Python functions manually from C++ code (i.e. via - handle::operator()). You probably won't need to use this. */ - automatic_reference, - - /** Reference an existing object (i.e. do not create a new copy) and take - ownership. Python will call the destructor and delete operator when the - object’s reference count reaches zero. Undefined behavior ensues when - the C++ side does the same.. */ - take_ownership, - - /** Create a new copy of the returned object, which will be owned by - Python. This policy is comparably safe because the lifetimes of the two - instances are decoupled. */ - copy, - - /** Use std::move to move the return value contents into a new instance - that will be owned by Python. This policy is comparably safe because the - lifetimes of the two instances (move source and destination) are - decoupled. */ - move, - - /** Reference an existing object, but do not take ownership. The C++ side - is responsible for managing the object’s lifetime and deallocating it - when it is no longer used. Warning: undefined behavior will ensue when - the C++ side deletes an object that is still referenced and used by - Python. */ - reference, - - /** This policy only applies to methods and properties. It references the - object without taking ownership similar to the above - return_value_policy::reference policy. In contrast to that policy, the - function or property’s implicit this argument (called the parent) is - considered to be the the owner of the return value (the child). - pybind11 then couples the lifetime of the parent to the child via a - reference relationship that ensures that the parent cannot be garbage - collected while Python is still using the child. More advanced - variations of this scheme are also possible using combinations of - return_value_policy::reference and the keep_alive call policy */ - reference_internal -}; - -NAMESPACE_BEGIN(detail) - -inline static constexpr int log2(size_t n, int k = 0) { return (n <= 1) ? k : log2(n >> 1, k + 1); } - -// Returns the size as a multiple of sizeof(void *), rounded up. -inline static constexpr size_t size_in_ptrs(size_t s) { return 1 + ((s - 1) >> log2(sizeof(void *))); } - -/** - * The space to allocate for simple layout instance holders (see below) in multiple of the size of - * a pointer (e.g. 2 means 16 bytes on 64-bit architectures). The default is the minimum required - * to holder either a std::unique_ptr or std::shared_ptr (which is almost always - * sizeof(std::shared_ptr)). - */ -constexpr size_t instance_simple_holder_in_ptrs() { - static_assert(sizeof(std::shared_ptr) >= sizeof(std::unique_ptr), - "pybind assumes std::shared_ptrs are at least as big as std::unique_ptrs"); - return size_in_ptrs(sizeof(std::shared_ptr)); -} - -// Forward declarations -struct type_info; -struct value_and_holder; - -struct nonsimple_values_and_holders { - void **values_and_holders; - uint8_t *status; -}; - -/// The 'instance' type which needs to be standard layout (need to be able to use 'offsetof') -struct instance { - PyObject_HEAD - /// Storage for pointers and holder; see simple_layout, below, for a description - union { - void *simple_value_holder[1 + instance_simple_holder_in_ptrs()]; - nonsimple_values_and_holders nonsimple; - }; - /// Weak references - PyObject *weakrefs; - /// If true, the pointer is owned which means we're free to manage it with a holder. - bool owned : 1; - /** - * An instance has two possible value/holder layouts. - * - * Simple layout (when this flag is true), means the `simple_value_holder` is set with a pointer - * and the holder object governing that pointer, i.e. [val1*][holder]. This layout is applied - * whenever there is no python-side multiple inheritance of bound C++ types *and* the type's - * holder will fit in the default space (which is large enough to hold either a std::unique_ptr - * or std::shared_ptr). - * - * Non-simple layout applies when using custom holders that require more space than `shared_ptr` - * (which is typically the size of two pointers), or when multiple inheritance is used on the - * python side. Non-simple layout allocates the required amount of memory to have multiple - * bound C++ classes as parents. Under this layout, `nonsimple.values_and_holders` is set to a - * pointer to allocated space of the required space to hold a sequence of value pointers and - * holders followed `status`, a set of bit flags (1 byte each), i.e. - * [val1*][holder1][val2*][holder2]...[bb...] where each [block] is rounded up to a multiple of - * `sizeof(void *)`. `nonsimple.status` is, for convenience, a pointer to the - * beginning of the [bb...] block (but not independently allocated). - * - * Status bits indicate whether the associated holder is constructed (& - * status_holder_constructed) and whether the value pointer is registered (& - * status_instance_registered) in `registered_instances`. - */ - bool simple_layout : 1; - /// For simple layout, tracks whether the holder has been constructed - bool simple_holder_constructed : 1; - /// For simple layout, tracks whether the instance is registered in `registered_instances` - bool simple_instance_registered : 1; - /// If true, get_internals().patients has an entry for this object - bool has_patients : 1; - - /// Initializes all of the above type/values/holders data (but not the instance values themselves) - void allocate_layout(); - - /// Destroys/deallocates all of the above - void deallocate_layout(); - - /// Returns the value_and_holder wrapper for the given type (or the first, if `find_type` - /// omitted). Returns a default-constructed (with `.inst = nullptr`) object on failure if - /// `throw_if_missing` is false. - value_and_holder get_value_and_holder(const type_info *find_type = nullptr, bool throw_if_missing = true); - - /// Bit values for the non-simple status flags - static constexpr uint8_t status_holder_constructed = 1; - static constexpr uint8_t status_instance_registered = 2; -}; - -static_assert(std::is_standard_layout::value, "Internal error: `pybind11::detail::instance` is not standard layout!"); - -/// from __cpp_future__ import (convenient aliases from C++14/17) -#if defined(PYBIND11_CPP14) && (!defined(_MSC_VER) || _MSC_VER >= 1910) -using std::enable_if_t; -using std::conditional_t; -using std::remove_cv_t; -using std::remove_reference_t; -#else -template using enable_if_t = typename std::enable_if::type; -template using conditional_t = typename std::conditional::type; -template using remove_cv_t = typename std::remove_cv::type; -template using remove_reference_t = typename std::remove_reference::type; -#endif - -/// Index sequences -#if defined(PYBIND11_CPP14) -using std::index_sequence; -using std::make_index_sequence; -#else -template struct index_sequence { }; -template struct make_index_sequence_impl : make_index_sequence_impl { }; -template struct make_index_sequence_impl <0, S...> { typedef index_sequence type; }; -template using make_index_sequence = typename make_index_sequence_impl::type; -#endif - -/// Make an index sequence of the indices of true arguments -template struct select_indices_impl { using type = ISeq; }; -template struct select_indices_impl, I, B, Bs...> - : select_indices_impl, index_sequence>, I + 1, Bs...> {}; -template using select_indices = typename select_indices_impl, 0, Bs...>::type; - -/// Backports of std::bool_constant and std::negation to accommodate older compilers -template using bool_constant = std::integral_constant; -template struct negation : bool_constant { }; - -template struct void_t_impl { using type = void; }; -template using void_t = typename void_t_impl::type; - -/// Compile-time all/any/none of that check the boolean value of all template types -#if defined(__cpp_fold_expressions) && !(defined(_MSC_VER) && (_MSC_VER < 1916)) -template using all_of = bool_constant<(Ts::value && ...)>; -template using any_of = bool_constant<(Ts::value || ...)>; -#elif !defined(_MSC_VER) -template struct bools {}; -template using all_of = std::is_same< - bools, - bools>; -template using any_of = negation...>>; -#else -// MSVC has trouble with the above, but supports std::conjunction, which we can use instead (albeit -// at a slight loss of compilation efficiency). -template using all_of = std::conjunction; -template using any_of = std::disjunction; -#endif -template using none_of = negation>; - -template class... Predicates> using satisfies_all_of = all_of...>; -template class... Predicates> using satisfies_any_of = any_of...>; -template class... Predicates> using satisfies_none_of = none_of...>; - -/// Strip the class from a method type -template struct remove_class { }; -template struct remove_class { typedef R type(A...); }; -template struct remove_class { typedef R type(A...); }; - -/// Helper template to strip away type modifiers -template struct intrinsic_type { typedef T type; }; -template struct intrinsic_type { typedef typename intrinsic_type::type type; }; -template struct intrinsic_type { typedef typename intrinsic_type::type type; }; -template struct intrinsic_type { typedef typename intrinsic_type::type type; }; -template struct intrinsic_type { typedef typename intrinsic_type::type type; }; -template struct intrinsic_type { typedef typename intrinsic_type::type type; }; -template struct intrinsic_type { typedef typename intrinsic_type::type type; }; -template using intrinsic_t = typename intrinsic_type::type; - -/// Helper type to replace 'void' in some expressions -struct void_type { }; - -/// Helper template which holds a list of types -template struct type_list { }; - -/// Compile-time integer sum -#ifdef __cpp_fold_expressions -template constexpr size_t constexpr_sum(Ts... ns) { return (0 + ... + size_t{ns}); } -#else -constexpr size_t constexpr_sum() { return 0; } -template -constexpr size_t constexpr_sum(T n, Ts... ns) { return size_t{n} + constexpr_sum(ns...); } -#endif - -NAMESPACE_BEGIN(constexpr_impl) -/// Implementation details for constexpr functions -constexpr int first(int i) { return i; } -template -constexpr int first(int i, T v, Ts... vs) { return v ? i : first(i + 1, vs...); } - -constexpr int last(int /*i*/, int result) { return result; } -template -constexpr int last(int i, int result, T v, Ts... vs) { return last(i + 1, v ? i : result, vs...); } -NAMESPACE_END(constexpr_impl) - -/// Return the index of the first type in Ts which satisfies Predicate. Returns sizeof...(Ts) if -/// none match. -template class Predicate, typename... Ts> -constexpr int constexpr_first() { return constexpr_impl::first(0, Predicate::value...); } - -/// Return the index of the last type in Ts which satisfies Predicate, or -1 if none match. -template class Predicate, typename... Ts> -constexpr int constexpr_last() { return constexpr_impl::last(0, -1, Predicate::value...); } - -/// Return the Nth element from the parameter pack -template -struct pack_element { using type = typename pack_element::type; }; -template -struct pack_element<0, T, Ts...> { using type = T; }; - -/// Return the one and only type which matches the predicate, or Default if none match. -/// If more than one type matches the predicate, fail at compile-time. -template class Predicate, typename Default, typename... Ts> -struct exactly_one { - static constexpr auto found = constexpr_sum(Predicate::value...); - static_assert(found <= 1, "Found more than one type matching the predicate"); - - static constexpr auto index = found ? constexpr_first() : 0; - using type = conditional_t::type, Default>; -}; -template class P, typename Default> -struct exactly_one { using type = Default; }; - -template class Predicate, typename Default, typename... Ts> -using exactly_one_t = typename exactly_one::type; - -/// Defer the evaluation of type T until types Us are instantiated -template struct deferred_type { using type = T; }; -template using deferred_t = typename deferred_type::type; - -/// Like is_base_of, but requires a strict base (i.e. `is_strict_base_of::value == false`, -/// unlike `std::is_base_of`) -template using is_strict_base_of = bool_constant< - std::is_base_of::value && !std::is_same::value>; - -/// Like is_base_of, but also requires that the base type is accessible (i.e. that a Derived pointer -/// can be converted to a Base pointer) -template using is_accessible_base_of = bool_constant< - std::is_base_of::value && std::is_convertible::value>; - -template class Base> -struct is_template_base_of_impl { - template static std::true_type check(Base *); - static std::false_type check(...); -}; - -/// Check if a template is the base of a type. For example: -/// `is_template_base_of` is true if `struct T : Base {}` where U can be anything -template class Base, typename T> -#if !defined(_MSC_VER) -using is_template_base_of = decltype(is_template_base_of_impl::check((intrinsic_t*)nullptr)); -#else // MSVC2015 has trouble with decltype in template aliases -struct is_template_base_of : decltype(is_template_base_of_impl::check((intrinsic_t*)nullptr)) { }; -#endif - -/// Check if T is an instantiation of the template `Class`. For example: -/// `is_instantiation` is true if `T == shared_ptr` where U can be anything. -template class Class, typename T> -struct is_instantiation : std::false_type { }; -template class Class, typename... Us> -struct is_instantiation> : std::true_type { }; - -/// Check if T is std::shared_ptr where U can be anything -template using is_shared_ptr = is_instantiation; - -/// Check if T looks like an input iterator -template struct is_input_iterator : std::false_type {}; -template -struct is_input_iterator()), decltype(++std::declval())>> - : std::true_type {}; - -template using is_function_pointer = bool_constant< - std::is_pointer::value && std::is_function::type>::value>; - -template struct strip_function_object { - using type = typename remove_class::type; -}; - -// Extracts the function signature from a function, function pointer or lambda. -template > -using function_signature_t = conditional_t< - std::is_function::value, - F, - typename conditional_t< - std::is_pointer::value || std::is_member_pointer::value, - std::remove_pointer, - strip_function_object - >::type ->; - -/// Returns true if the type looks like a lambda: that is, isn't a function, pointer or member -/// pointer. Note that this can catch all sorts of other things, too; this is intended to be used -/// in a place where passing a lambda makes sense. -template using is_lambda = satisfies_none_of, - std::is_function, std::is_pointer, std::is_member_pointer>; - -/// Ignore that a variable is unused in compiler warnings -inline void ignore_unused(const int *) { } - -/// Apply a function over each element of a parameter pack -#ifdef __cpp_fold_expressions -#define PYBIND11_EXPAND_SIDE_EFFECTS(PATTERN) (((PATTERN), void()), ...) -#else -using expand_side_effects = bool[]; -#define PYBIND11_EXPAND_SIDE_EFFECTS(PATTERN) pybind11::detail::expand_side_effects{ ((PATTERN), void(), false)..., false } -#endif - -NAMESPACE_END(detail) - -/// C++ bindings of builtin Python exceptions -class builtin_exception : public std::runtime_error { -public: - using std::runtime_error::runtime_error; - /// Set the error using the Python C API - virtual void set_error() const = 0; -}; - -#define PYBIND11_RUNTIME_EXCEPTION(name, type) \ - class name : public builtin_exception { public: \ - using builtin_exception::builtin_exception; \ - name() : name("") { } \ - void set_error() const override { PyErr_SetString(type, what()); } \ - }; - -PYBIND11_RUNTIME_EXCEPTION(stop_iteration, PyExc_StopIteration) -PYBIND11_RUNTIME_EXCEPTION(index_error, PyExc_IndexError) -PYBIND11_RUNTIME_EXCEPTION(key_error, PyExc_KeyError) -PYBIND11_RUNTIME_EXCEPTION(value_error, PyExc_ValueError) -PYBIND11_RUNTIME_EXCEPTION(type_error, PyExc_TypeError) -PYBIND11_RUNTIME_EXCEPTION(cast_error, PyExc_RuntimeError) /// Thrown when pybind11::cast or handle::call fail due to a type casting error -PYBIND11_RUNTIME_EXCEPTION(reference_cast_error, PyExc_RuntimeError) /// Used internally - -[[noreturn]] PYBIND11_NOINLINE inline void pybind11_fail(const char *reason) { throw std::runtime_error(reason); } -[[noreturn]] PYBIND11_NOINLINE inline void pybind11_fail(const std::string &reason) { throw std::runtime_error(reason); } - -template struct format_descriptor { }; - -NAMESPACE_BEGIN(detail) -// Returns the index of the given type in the type char array below, and in the list in numpy.h -// The order here is: bool; 8 ints ((signed,unsigned)x(8,16,32,64)bits); float,double,long double; -// complex float,double,long double. Note that the long double types only participate when long -// double is actually longer than double (it isn't under MSVC). -// NB: not only the string below but also complex.h and numpy.h rely on this order. -template struct is_fmt_numeric { static constexpr bool value = false; }; -template struct is_fmt_numeric::value>> { - static constexpr bool value = true; - static constexpr int index = std::is_same::value ? 0 : 1 + ( - std::is_integral::value ? detail::log2(sizeof(T))*2 + std::is_unsigned::value : 8 + ( - std::is_same::value ? 1 : std::is_same::value ? 2 : 0)); -}; -NAMESPACE_END(detail) - -template struct format_descriptor::value>> { - static constexpr const char c = "?bBhHiIqQfdg"[detail::is_fmt_numeric::index]; - static constexpr const char value[2] = { c, '\0' }; - static std::string format() { return std::string(1, c); } -}; - -#if !defined(PYBIND11_CPP17) - -template constexpr const char format_descriptor< - T, detail::enable_if_t::value>>::value[2]; - -#endif - -/// RAII wrapper that temporarily clears any Python error state -struct error_scope { - PyObject *type, *value, *trace; - error_scope() { PyErr_Fetch(&type, &value, &trace); } - ~error_scope() { PyErr_Restore(type, value, trace); } -}; - -/// Dummy destructor wrapper that can be used to expose classes with a private destructor -struct nodelete { template void operator()(T*) { } }; - -// overload_cast requires variable templates: C++14 -#if defined(PYBIND11_CPP14) -#define PYBIND11_OVERLOAD_CAST 1 - -NAMESPACE_BEGIN(detail) -template -struct overload_cast_impl { - constexpr overload_cast_impl() {} // MSVC 2015 needs this - - template - constexpr auto operator()(Return (*pf)(Args...)) const noexcept - -> decltype(pf) { return pf; } - - template - constexpr auto operator()(Return (Class::*pmf)(Args...), std::false_type = {}) const noexcept - -> decltype(pmf) { return pmf; } - - template - constexpr auto operator()(Return (Class::*pmf)(Args...) const, std::true_type) const noexcept - -> decltype(pmf) { return pmf; } -}; -NAMESPACE_END(detail) - -/// Syntax sugar for resolving overloaded function pointers: -/// - regular: static_cast(&Class::func) -/// - sweet: overload_cast(&Class::func) -template -static constexpr detail::overload_cast_impl overload_cast = {}; -// MSVC 2015 only accepts this particular initialization syntax for this variable template. - -/// Const member function selector for overload_cast -/// - regular: static_cast(&Class::func) -/// - sweet: overload_cast(&Class::func, const_) -static constexpr auto const_ = std::true_type{}; - -#else // no overload_cast: providing something that static_assert-fails: -template struct overload_cast { - static_assert(detail::deferred_t::value, - "pybind11::overload_cast<...> requires compiling in C++14 mode"); -}; -#endif // overload_cast - -NAMESPACE_BEGIN(detail) - -// Adaptor for converting arbitrary container arguments into a vector; implicitly convertible from -// any standard container (or C-style array) supporting std::begin/std::end, any singleton -// arithmetic type (if T is arithmetic), or explicitly constructible from an iterator pair. -template -class any_container { - std::vector v; -public: - any_container() = default; - - // Can construct from a pair of iterators - template ::value>> - any_container(It first, It last) : v(first, last) { } - - // Implicit conversion constructor from any arbitrary container type with values convertible to T - template ())), T>::value>> - any_container(const Container &c) : any_container(std::begin(c), std::end(c)) { } - - // initializer_list's aren't deducible, so don't get matched by the above template; we need this - // to explicitly allow implicit conversion from one: - template ::value>> - any_container(const std::initializer_list &c) : any_container(c.begin(), c.end()) { } - - // Avoid copying if given an rvalue vector of the correct type. - any_container(std::vector &&v) : v(std::move(v)) { } - - // Moves the vector out of an rvalue any_container - operator std::vector &&() && { return std::move(v); } - - // Dereferencing obtains a reference to the underlying vector - std::vector &operator*() { return v; } - const std::vector &operator*() const { return v; } - - // -> lets you call methods on the underlying vector - std::vector *operator->() { return &v; } - const std::vector *operator->() const { return &v; } -}; - -NAMESPACE_END(detail) - - - -NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/ptocr/postprocess/dbprocess/include/pybind11/detail/descr.h b/ptocr/postprocess/dbprocess/include/pybind11/detail/descr.h deleted file mode 100644 index 8d404e5..0000000 --- a/ptocr/postprocess/dbprocess/include/pybind11/detail/descr.h +++ /dev/null @@ -1,100 +0,0 @@ -/* - pybind11/detail/descr.h: Helper type for concatenating type signatures at compile time - - Copyright (c) 2016 Wenzel Jakob - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#include "common.h" - -NAMESPACE_BEGIN(PYBIND11_NAMESPACE) -NAMESPACE_BEGIN(detail) - -#if !defined(_MSC_VER) -# define PYBIND11_DESCR_CONSTEXPR static constexpr -#else -# define PYBIND11_DESCR_CONSTEXPR const -#endif - -/* Concatenate type signatures at compile time */ -template -struct descr { - char text[N + 1]; - - constexpr descr() : text{'\0'} { } - constexpr descr(char const (&s)[N+1]) : descr(s, make_index_sequence()) { } - - template - constexpr descr(char const (&s)[N+1], index_sequence) : text{s[Is]..., '\0'} { } - - template - constexpr descr(char c, Chars... cs) : text{c, static_cast(cs)..., '\0'} { } - - static constexpr std::array types() { - return {{&typeid(Ts)..., nullptr}}; - } -}; - -template -constexpr descr plus_impl(const descr &a, const descr &b, - index_sequence, index_sequence) { - return {a.text[Is1]..., b.text[Is2]...}; -} - -template -constexpr descr operator+(const descr &a, const descr &b) { - return plus_impl(a, b, make_index_sequence(), make_index_sequence()); -} - -template -constexpr descr _(char const(&text)[N]) { return descr(text); } -constexpr descr<0> _(char const(&)[1]) { return {}; } - -template struct int_to_str : int_to_str { }; -template struct int_to_str<0, Digits...> { - static constexpr auto digits = descr(('0' + Digits)...); -}; - -// Ternary description (like std::conditional) -template -constexpr enable_if_t> _(char const(&text1)[N1], char const(&)[N2]) { - return _(text1); -} -template -constexpr enable_if_t> _(char const(&)[N1], char const(&text2)[N2]) { - return _(text2); -} - -template -constexpr enable_if_t _(const T1 &d, const T2 &) { return d; } -template -constexpr enable_if_t _(const T1 &, const T2 &d) { return d; } - -template auto constexpr _() -> decltype(int_to_str::digits) { - return int_to_str::digits; -} - -template constexpr descr<1, Type> _() { return {'%'}; } - -constexpr descr<0> concat() { return {}; } - -template -constexpr descr concat(const descr &descr) { return descr; } - -template -constexpr auto concat(const descr &d, const Args &...args) - -> decltype(std::declval>() + concat(args...)) { - return d + _(", ") + concat(args...); -} - -template -constexpr descr type_descr(const descr &descr) { - return _("{") + descr + _("}"); -} - -NAMESPACE_END(detail) -NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/ptocr/postprocess/dbprocess/include/pybind11/detail/init.h b/ptocr/postprocess/dbprocess/include/pybind11/detail/init.h deleted file mode 100644 index acfe00b..0000000 --- a/ptocr/postprocess/dbprocess/include/pybind11/detail/init.h +++ /dev/null @@ -1,335 +0,0 @@ -/* - pybind11/detail/init.h: init factory function implementation and support code. - - Copyright (c) 2017 Jason Rhinelander - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#include "class.h" - -NAMESPACE_BEGIN(PYBIND11_NAMESPACE) -NAMESPACE_BEGIN(detail) - -template <> -class type_caster { -public: - bool load(handle h, bool) { - value = reinterpret_cast(h.ptr()); - return true; - } - - template using cast_op_type = value_and_holder &; - operator value_and_holder &() { return *value; } - static constexpr auto name = _(); - -private: - value_and_holder *value = nullptr; -}; - -NAMESPACE_BEGIN(initimpl) - -inline void no_nullptr(void *ptr) { - if (!ptr) throw type_error("pybind11::init(): factory function returned nullptr"); -} - -// Implementing functions for all forms of py::init<...> and py::init(...) -template using Cpp = typename Class::type; -template using Alias = typename Class::type_alias; -template using Holder = typename Class::holder_type; - -template using is_alias_constructible = std::is_constructible, Cpp &&>; - -// Takes a Cpp pointer and returns true if it actually is a polymorphic Alias instance. -template = 0> -bool is_alias(Cpp *ptr) { - return dynamic_cast *>(ptr) != nullptr; -} -// Failing fallback version of the above for a no-alias class (always returns false) -template -constexpr bool is_alias(void *) { return false; } - -// Constructs and returns a new object; if the given arguments don't map to a constructor, we fall -// back to brace aggregate initiailization so that for aggregate initialization can be used with -// py::init, e.g. `py::init` to initialize a `struct T { int a; int b; }`. For -// non-aggregate types, we need to use an ordinary T(...) constructor (invoking as `T{...}` usually -// works, but will not do the expected thing when `T` has an `initializer_list` constructor). -template ::value, int> = 0> -inline Class *construct_or_initialize(Args &&...args) { return new Class(std::forward(args)...); } -template ::value, int> = 0> -inline Class *construct_or_initialize(Args &&...args) { return new Class{std::forward(args)...}; } - -// Attempts to constructs an alias using a `Alias(Cpp &&)` constructor. This allows types with -// an alias to provide only a single Cpp factory function as long as the Alias can be -// constructed from an rvalue reference of the base Cpp type. This means that Alias classes -// can, when appropriate, simply define a `Alias(Cpp &&)` constructor rather than needing to -// inherit all the base class constructors. -template -void construct_alias_from_cpp(std::true_type /*is_alias_constructible*/, - value_and_holder &v_h, Cpp &&base) { - v_h.value_ptr() = new Alias(std::move(base)); -} -template -[[noreturn]] void construct_alias_from_cpp(std::false_type /*!is_alias_constructible*/, - value_and_holder &, Cpp &&) { - throw type_error("pybind11::init(): unable to convert returned instance to required " - "alias class: no `Alias(Class &&)` constructor available"); -} - -// Error-generating fallback for factories that don't match one of the below construction -// mechanisms. -template -void construct(...) { - static_assert(!std::is_same::value /* always false */, - "pybind11::init(): init function must return a compatible pointer, " - "holder, or value"); -} - -// Pointer return v1: the factory function returns a class pointer for a registered class. -// If we don't need an alias (because this class doesn't have one, or because the final type is -// inherited on the Python side) we can simply take over ownership. Otherwise we need to try to -// construct an Alias from the returned base instance. -template -void construct(value_and_holder &v_h, Cpp *ptr, bool need_alias) { - no_nullptr(ptr); - if (Class::has_alias && need_alias && !is_alias(ptr)) { - // We're going to try to construct an alias by moving the cpp type. Whether or not - // that succeeds, we still need to destroy the original cpp pointer (either the - // moved away leftover, if the alias construction works, or the value itself if we - // throw an error), but we can't just call `delete ptr`: it might have a special - // deleter, or might be shared_from_this. So we construct a holder around it as if - // it was a normal instance, then steal the holder away into a local variable; thus - // the holder and destruction happens when we leave the C++ scope, and the holder - // class gets to handle the destruction however it likes. - v_h.value_ptr() = ptr; - v_h.set_instance_registered(true); // To prevent init_instance from registering it - v_h.type->init_instance(v_h.inst, nullptr); // Set up the holder - Holder temp_holder(std::move(v_h.holder>())); // Steal the holder - v_h.type->dealloc(v_h); // Destroys the moved-out holder remains, resets value ptr to null - v_h.set_instance_registered(false); - - construct_alias_from_cpp(is_alias_constructible{}, v_h, std::move(*ptr)); - } else { - // Otherwise the type isn't inherited, so we don't need an Alias - v_h.value_ptr() = ptr; - } -} - -// Pointer return v2: a factory that always returns an alias instance ptr. We simply take over -// ownership of the pointer. -template = 0> -void construct(value_and_holder &v_h, Alias *alias_ptr, bool) { - no_nullptr(alias_ptr); - v_h.value_ptr() = static_cast *>(alias_ptr); -} - -// Holder return: copy its pointer, and move or copy the returned holder into the new instance's -// holder. This also handles types like std::shared_ptr and std::unique_ptr where T is a -// derived type (through those holder's implicit conversion from derived class holder constructors). -template -void construct(value_and_holder &v_h, Holder holder, bool need_alias) { - auto *ptr = holder_helper>::get(holder); - // If we need an alias, check that the held pointer is actually an alias instance - if (Class::has_alias && need_alias && !is_alias(ptr)) - throw type_error("pybind11::init(): construction failed: returned holder-wrapped instance " - "is not an alias instance"); - - v_h.value_ptr() = ptr; - v_h.type->init_instance(v_h.inst, &holder); -} - -// return-by-value version 1: returning a cpp class by value. If the class has an alias and an -// alias is required the alias must have an `Alias(Cpp &&)` constructor so that we can construct -// the alias from the base when needed (i.e. because of Python-side inheritance). When we don't -// need it, we simply move-construct the cpp value into a new instance. -template -void construct(value_and_holder &v_h, Cpp &&result, bool need_alias) { - static_assert(std::is_move_constructible>::value, - "pybind11::init() return-by-value factory function requires a movable class"); - if (Class::has_alias && need_alias) - construct_alias_from_cpp(is_alias_constructible{}, v_h, std::move(result)); - else - v_h.value_ptr() = new Cpp(std::move(result)); -} - -// return-by-value version 2: returning a value of the alias type itself. We move-construct an -// Alias instance (even if no the python-side inheritance is involved). The is intended for -// cases where Alias initialization is always desired. -template -void construct(value_and_holder &v_h, Alias &&result, bool) { - static_assert(std::is_move_constructible>::value, - "pybind11::init() return-by-alias-value factory function requires a movable alias class"); - v_h.value_ptr() = new Alias(std::move(result)); -} - -// Implementing class for py::init<...>() -template -struct constructor { - template = 0> - static void execute(Class &cl, const Extra&... extra) { - cl.def("__init__", [](value_and_holder &v_h, Args... args) { - v_h.value_ptr() = construct_or_initialize>(std::forward(args)...); - }, is_new_style_constructor(), extra...); - } - - template , Args...>::value, int> = 0> - static void execute(Class &cl, const Extra&... extra) { - cl.def("__init__", [](value_and_holder &v_h, Args... args) { - if (Py_TYPE(v_h.inst) == v_h.type->type) - v_h.value_ptr() = construct_or_initialize>(std::forward(args)...); - else - v_h.value_ptr() = construct_or_initialize>(std::forward(args)...); - }, is_new_style_constructor(), extra...); - } - - template , Args...>::value, int> = 0> - static void execute(Class &cl, const Extra&... extra) { - cl.def("__init__", [](value_and_holder &v_h, Args... args) { - v_h.value_ptr() = construct_or_initialize>(std::forward(args)...); - }, is_new_style_constructor(), extra...); - } -}; - -// Implementing class for py::init_alias<...>() -template struct alias_constructor { - template , Args...>::value, int> = 0> - static void execute(Class &cl, const Extra&... extra) { - cl.def("__init__", [](value_and_holder &v_h, Args... args) { - v_h.value_ptr() = construct_or_initialize>(std::forward(args)...); - }, is_new_style_constructor(), extra...); - } -}; - -// Implementation class for py::init(Func) and py::init(Func, AliasFunc) -template , typename = function_signature_t> -struct factory; - -// Specialization for py::init(Func) -template -struct factory { - remove_reference_t class_factory; - - factory(Func &&f) : class_factory(std::forward(f)) { } - - // The given class either has no alias or has no separate alias factory; - // this always constructs the class itself. If the class is registered with an alias - // type and an alias instance is needed (i.e. because the final type is a Python class - // inheriting from the C++ type) the returned value needs to either already be an alias - // instance, or the alias needs to be constructible from a `Class &&` argument. - template - void execute(Class &cl, const Extra &...extra) && { - #if defined(PYBIND11_CPP14) - cl.def("__init__", [func = std::move(class_factory)] - #else - auto &func = class_factory; - cl.def("__init__", [func] - #endif - (value_and_holder &v_h, Args... args) { - construct(v_h, func(std::forward(args)...), - Py_TYPE(v_h.inst) != v_h.type->type); - }, is_new_style_constructor(), extra...); - } -}; - -// Specialization for py::init(Func, AliasFunc) -template -struct factory { - static_assert(sizeof...(CArgs) == sizeof...(AArgs), - "pybind11::init(class_factory, alias_factory): class and alias factories " - "must have identical argument signatures"); - static_assert(all_of...>::value, - "pybind11::init(class_factory, alias_factory): class and alias factories " - "must have identical argument signatures"); - - remove_reference_t class_factory; - remove_reference_t alias_factory; - - factory(CFunc &&c, AFunc &&a) - : class_factory(std::forward(c)), alias_factory(std::forward(a)) { } - - // The class factory is called when the `self` type passed to `__init__` is the direct - // class (i.e. not inherited), the alias factory when `self` is a Python-side subtype. - template - void execute(Class &cl, const Extra&... extra) && { - static_assert(Class::has_alias, "The two-argument version of `py::init()` can " - "only be used if the class has an alias"); - #if defined(PYBIND11_CPP14) - cl.def("__init__", [class_func = std::move(class_factory), alias_func = std::move(alias_factory)] - #else - auto &class_func = class_factory; - auto &alias_func = alias_factory; - cl.def("__init__", [class_func, alias_func] - #endif - (value_and_holder &v_h, CArgs... args) { - if (Py_TYPE(v_h.inst) == v_h.type->type) - // If the instance type equals the registered type we don't have inheritance, so - // don't need the alias and can construct using the class function: - construct(v_h, class_func(std::forward(args)...), false); - else - construct(v_h, alias_func(std::forward(args)...), true); - }, is_new_style_constructor(), extra...); - } -}; - -/// Set just the C++ state. Same as `__init__`. -template -void setstate(value_and_holder &v_h, T &&result, bool need_alias) { - construct(v_h, std::forward(result), need_alias); -} - -/// Set both the C++ and Python states -template ::value, int> = 0> -void setstate(value_and_holder &v_h, std::pair &&result, bool need_alias) { - construct(v_h, std::move(result.first), need_alias); - setattr((PyObject *) v_h.inst, "__dict__", result.second); -} - -/// Implementation for py::pickle(GetState, SetState) -template , typename = function_signature_t> -struct pickle_factory; - -template -struct pickle_factory { - static_assert(std::is_same, intrinsic_t>::value, - "The type returned by `__getstate__` must be the same " - "as the argument accepted by `__setstate__`"); - - remove_reference_t get; - remove_reference_t set; - - pickle_factory(Get get, Set set) - : get(std::forward(get)), set(std::forward(set)) { } - - template - void execute(Class &cl, const Extra &...extra) && { - cl.def("__getstate__", std::move(get)); - -#if defined(PYBIND11_CPP14) - cl.def("__setstate__", [func = std::move(set)] -#else - auto &func = set; - cl.def("__setstate__", [func] -#endif - (value_and_holder &v_h, ArgState state) { - setstate(v_h, func(std::forward(state)), - Py_TYPE(v_h.inst) != v_h.type->type); - }, is_new_style_constructor(), extra...); - } -}; - -NAMESPACE_END(initimpl) -NAMESPACE_END(detail) -NAMESPACE_END(pybind11) diff --git a/ptocr/postprocess/dbprocess/include/pybind11/detail/internals.h b/ptocr/postprocess/dbprocess/include/pybind11/detail/internals.h deleted file mode 100644 index 6d7dc5c..0000000 --- a/ptocr/postprocess/dbprocess/include/pybind11/detail/internals.h +++ /dev/null @@ -1,291 +0,0 @@ -/* - pybind11/detail/internals.h: Internal data structure and related functions - - Copyright (c) 2017 Wenzel Jakob - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#include "../pytypes.h" - -NAMESPACE_BEGIN(PYBIND11_NAMESPACE) -NAMESPACE_BEGIN(detail) -// Forward declarations -inline PyTypeObject *make_static_property_type(); -inline PyTypeObject *make_default_metaclass(); -inline PyObject *make_object_base_type(PyTypeObject *metaclass); - -// The old Python Thread Local Storage (TLS) API is deprecated in Python 3.7 in favor of the new -// Thread Specific Storage (TSS) API. -#if PY_VERSION_HEX >= 0x03070000 -# define PYBIND11_TLS_KEY_INIT(var) Py_tss_t *var = nullptr -# define PYBIND11_TLS_GET_VALUE(key) PyThread_tss_get((key)) -# define PYBIND11_TLS_REPLACE_VALUE(key, value) PyThread_tss_set((key), (tstate)) -# define PYBIND11_TLS_DELETE_VALUE(key) PyThread_tss_set((key), nullptr) -#else - // Usually an int but a long on Cygwin64 with Python 3.x -# define PYBIND11_TLS_KEY_INIT(var) decltype(PyThread_create_key()) var = 0 -# define PYBIND11_TLS_GET_VALUE(key) PyThread_get_key_value((key)) -# if PY_MAJOR_VERSION < 3 -# define PYBIND11_TLS_DELETE_VALUE(key) \ - PyThread_delete_key_value(key) -# define PYBIND11_TLS_REPLACE_VALUE(key, value) \ - do { \ - PyThread_delete_key_value((key)); \ - PyThread_set_key_value((key), (value)); \ - } while (false) -# else -# define PYBIND11_TLS_DELETE_VALUE(key) \ - PyThread_set_key_value((key), nullptr) -# define PYBIND11_TLS_REPLACE_VALUE(key, value) \ - PyThread_set_key_value((key), (value)) -# endif -#endif - -// Python loads modules by default with dlopen with the RTLD_LOCAL flag; under libc++ and possibly -// other STLs, this means `typeid(A)` from one module won't equal `typeid(A)` from another module -// even when `A` is the same, non-hidden-visibility type (e.g. from a common include). Under -// libstdc++, this doesn't happen: equality and the type_index hash are based on the type name, -// which works. If not under a known-good stl, provide our own name-based hash and equality -// functions that use the type name. -#if defined(__GLIBCXX__) -inline bool same_type(const std::type_info &lhs, const std::type_info &rhs) { return lhs == rhs; } -using type_hash = std::hash; -using type_equal_to = std::equal_to; -#else -inline bool same_type(const std::type_info &lhs, const std::type_info &rhs) { - return lhs.name() == rhs.name() || std::strcmp(lhs.name(), rhs.name()) == 0; -} - -struct type_hash { - size_t operator()(const std::type_index &t) const { - size_t hash = 5381; - const char *ptr = t.name(); - while (auto c = static_cast(*ptr++)) - hash = (hash * 33) ^ c; - return hash; - } -}; - -struct type_equal_to { - bool operator()(const std::type_index &lhs, const std::type_index &rhs) const { - return lhs.name() == rhs.name() || std::strcmp(lhs.name(), rhs.name()) == 0; - } -}; -#endif - -template -using type_map = std::unordered_map; - -struct overload_hash { - inline size_t operator()(const std::pair& v) const { - size_t value = std::hash()(v.first); - value ^= std::hash()(v.second) + 0x9e3779b9 + (value<<6) + (value>>2); - return value; - } -}; - -/// Internal data structure used to track registered instances and types. -/// Whenever binary incompatible changes are made to this structure, -/// `PYBIND11_INTERNALS_VERSION` must be incremented. -struct internals { - type_map registered_types_cpp; // std::type_index -> pybind11's type information - std::unordered_map> registered_types_py; // PyTypeObject* -> base type_info(s) - std::unordered_multimap registered_instances; // void * -> instance* - std::unordered_set, overload_hash> inactive_overload_cache; - type_map> direct_conversions; - std::unordered_map> patients; - std::forward_list registered_exception_translators; - std::unordered_map shared_data; // Custom data to be shared across extensions - std::vector loader_patient_stack; // Used by `loader_life_support` - std::forward_list static_strings; // Stores the std::strings backing detail::c_str() - PyTypeObject *static_property_type; - PyTypeObject *default_metaclass; - PyObject *instance_base; -#if defined(WITH_THREAD) - PYBIND11_TLS_KEY_INIT(tstate); - PyInterpreterState *istate = nullptr; -#endif -}; - -/// Additional type information which does not fit into the PyTypeObject. -/// Changes to this struct also require bumping `PYBIND11_INTERNALS_VERSION`. -struct type_info { - PyTypeObject *type; - const std::type_info *cpptype; - size_t type_size, type_align, holder_size_in_ptrs; - void *(*operator_new)(size_t); - void (*init_instance)(instance *, const void *); - void (*dealloc)(value_and_holder &v_h); - std::vector implicit_conversions; - std::vector> implicit_casts; - std::vector *direct_conversions; - buffer_info *(*get_buffer)(PyObject *, void *) = nullptr; - void *get_buffer_data = nullptr; - void *(*module_local_load)(PyObject *, const type_info *) = nullptr; - /* A simple type never occurs as a (direct or indirect) parent - * of a class that makes use of multiple inheritance */ - bool simple_type : 1; - /* True if there is no multiple inheritance in this type's inheritance tree */ - bool simple_ancestors : 1; - /* for base vs derived holder_type checks */ - bool default_holder : 1; - /* true if this is a type registered with py::module_local */ - bool module_local : 1; -}; - -/// Tracks the `internals` and `type_info` ABI version independent of the main library version -#define PYBIND11_INTERNALS_VERSION 3 - -#if defined(_DEBUG) -# define PYBIND11_BUILD_TYPE "_debug" -#else -# define PYBIND11_BUILD_TYPE "" -#endif - -#if defined(WITH_THREAD) -# define PYBIND11_INTERNALS_KIND "" -#else -# define PYBIND11_INTERNALS_KIND "_without_thread" -#endif - -#define PYBIND11_INTERNALS_ID "__pybind11_internals_v" \ - PYBIND11_TOSTRING(PYBIND11_INTERNALS_VERSION) PYBIND11_INTERNALS_KIND PYBIND11_BUILD_TYPE "__" - -#define PYBIND11_MODULE_LOCAL_ID "__pybind11_module_local_v" \ - PYBIND11_TOSTRING(PYBIND11_INTERNALS_VERSION) PYBIND11_INTERNALS_KIND PYBIND11_BUILD_TYPE "__" - -/// Each module locally stores a pointer to the `internals` data. The data -/// itself is shared among modules with the same `PYBIND11_INTERNALS_ID`. -inline internals **&get_internals_pp() { - static internals **internals_pp = nullptr; - return internals_pp; -} - -/// Return a reference to the current `internals` data -PYBIND11_NOINLINE inline internals &get_internals() { - auto **&internals_pp = get_internals_pp(); - if (internals_pp && *internals_pp) - return **internals_pp; - - constexpr auto *id = PYBIND11_INTERNALS_ID; - auto builtins = handle(PyEval_GetBuiltins()); - if (builtins.contains(id) && isinstance(builtins[id])) { - internals_pp = static_cast(capsule(builtins[id])); - - // We loaded builtins through python's builtins, which means that our `error_already_set` - // and `builtin_exception` may be different local classes than the ones set up in the - // initial exception translator, below, so add another for our local exception classes. - // - // libstdc++ doesn't require this (types there are identified only by name) -#if !defined(__GLIBCXX__) - (*internals_pp)->registered_exception_translators.push_front( - [](std::exception_ptr p) -> void { - try { - if (p) std::rethrow_exception(p); - } catch (error_already_set &e) { e.restore(); return; - } catch (const builtin_exception &e) { e.set_error(); return; - } - } - ); -#endif - } else { - if (!internals_pp) internals_pp = new internals*(); - auto *&internals_ptr = *internals_pp; - internals_ptr = new internals(); -#if defined(WITH_THREAD) - PyEval_InitThreads(); - PyThreadState *tstate = PyThreadState_Get(); - #if PY_VERSION_HEX >= 0x03070000 - internals_ptr->tstate = PyThread_tss_alloc(); - if (!internals_ptr->tstate || PyThread_tss_create(internals_ptr->tstate)) - pybind11_fail("get_internals: could not successfully initialize the TSS key!"); - PyThread_tss_set(internals_ptr->tstate, tstate); - #else - internals_ptr->tstate = PyThread_create_key(); - if (internals_ptr->tstate == -1) - pybind11_fail("get_internals: could not successfully initialize the TLS key!"); - PyThread_set_key_value(internals_ptr->tstate, tstate); - #endif - internals_ptr->istate = tstate->interp; -#endif - builtins[id] = capsule(internals_pp); - internals_ptr->registered_exception_translators.push_front( - [](std::exception_ptr p) -> void { - try { - if (p) std::rethrow_exception(p); - } catch (error_already_set &e) { e.restore(); return; - } catch (const builtin_exception &e) { e.set_error(); return; - } catch (const std::bad_alloc &e) { PyErr_SetString(PyExc_MemoryError, e.what()); return; - } catch (const std::domain_error &e) { PyErr_SetString(PyExc_ValueError, e.what()); return; - } catch (const std::invalid_argument &e) { PyErr_SetString(PyExc_ValueError, e.what()); return; - } catch (const std::length_error &e) { PyErr_SetString(PyExc_ValueError, e.what()); return; - } catch (const std::out_of_range &e) { PyErr_SetString(PyExc_IndexError, e.what()); return; - } catch (const std::range_error &e) { PyErr_SetString(PyExc_ValueError, e.what()); return; - } catch (const std::exception &e) { PyErr_SetString(PyExc_RuntimeError, e.what()); return; - } catch (...) { - PyErr_SetString(PyExc_RuntimeError, "Caught an unknown exception!"); - return; - } - } - ); - internals_ptr->static_property_type = make_static_property_type(); - internals_ptr->default_metaclass = make_default_metaclass(); - internals_ptr->instance_base = make_object_base_type(internals_ptr->default_metaclass); - } - return **internals_pp; -} - -/// Works like `internals.registered_types_cpp`, but for module-local registered types: -inline type_map ®istered_local_types_cpp() { - static type_map locals{}; - return locals; -} - -/// Constructs a std::string with the given arguments, stores it in `internals`, and returns its -/// `c_str()`. Such strings objects have a long storage duration -- the internal strings are only -/// cleared when the program exits or after interpreter shutdown (when embedding), and so are -/// suitable for c-style strings needed by Python internals (such as PyTypeObject's tp_name). -template -const char *c_str(Args &&...args) { - auto &strings = get_internals().static_strings; - strings.emplace_front(std::forward(args)...); - return strings.front().c_str(); -} - -NAMESPACE_END(detail) - -/// Returns a named pointer that is shared among all extension modules (using the same -/// pybind11 version) running in the current interpreter. Names starting with underscores -/// are reserved for internal usage. Returns `nullptr` if no matching entry was found. -inline PYBIND11_NOINLINE void *get_shared_data(const std::string &name) { - auto &internals = detail::get_internals(); - auto it = internals.shared_data.find(name); - return it != internals.shared_data.end() ? it->second : nullptr; -} - -/// Set the shared data that can be later recovered by `get_shared_data()`. -inline PYBIND11_NOINLINE void *set_shared_data(const std::string &name, void *data) { - detail::get_internals().shared_data[name] = data; - return data; -} - -/// Returns a typed reference to a shared data entry (by using `get_shared_data()`) if -/// such entry exists. Otherwise, a new object of default-constructible type `T` is -/// added to the shared data under the given name and a reference to it is returned. -template -T &get_or_create_shared_data(const std::string &name) { - auto &internals = detail::get_internals(); - auto it = internals.shared_data.find(name); - T *ptr = (T *) (it != internals.shared_data.end() ? it->second : nullptr); - if (!ptr) { - ptr = new T(); - internals.shared_data[name] = ptr; - } - return *ptr; -} - -NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/ptocr/postprocess/dbprocess/include/pybind11/detail/typeid.h b/ptocr/postprocess/dbprocess/include/pybind11/detail/typeid.h deleted file mode 100644 index 6f36aab..0000000 --- a/ptocr/postprocess/dbprocess/include/pybind11/detail/typeid.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - pybind11/detail/typeid.h: Compiler-independent access to type identifiers - - Copyright (c) 2016 Wenzel Jakob - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#include -#include - -#if defined(__GNUG__) -#include -#endif - -NAMESPACE_BEGIN(PYBIND11_NAMESPACE) -NAMESPACE_BEGIN(detail) -/// Erase all occurrences of a substring -inline void erase_all(std::string &string, const std::string &search) { - for (size_t pos = 0;;) { - pos = string.find(search, pos); - if (pos == std::string::npos) break; - string.erase(pos, search.length()); - } -} - -PYBIND11_NOINLINE inline void clean_type_id(std::string &name) { -#if defined(__GNUG__) - int status = 0; - std::unique_ptr res { - abi::__cxa_demangle(name.c_str(), nullptr, nullptr, &status), std::free }; - if (status == 0) - name = res.get(); -#else - detail::erase_all(name, "class "); - detail::erase_all(name, "struct "); - detail::erase_all(name, "enum "); -#endif - detail::erase_all(name, "pybind11::"); -} -NAMESPACE_END(detail) - -/// Return a string representation of a C++ type -template static std::string type_id() { - std::string name(typeid(T).name()); - detail::clean_type_id(name); - return name; -} - -NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/ptocr/postprocess/dbprocess/include/pybind11/eigen.h b/ptocr/postprocess/dbprocess/include/pybind11/eigen.h deleted file mode 100644 index d963d96..0000000 --- a/ptocr/postprocess/dbprocess/include/pybind11/eigen.h +++ /dev/null @@ -1,607 +0,0 @@ -/* - pybind11/eigen.h: Transparent conversion for dense and sparse Eigen matrices - - Copyright (c) 2016 Wenzel Jakob - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#include "numpy.h" - -#if defined(__INTEL_COMPILER) -# pragma warning(disable: 1682) // implicit conversion of a 64-bit integral type to a smaller integral type (potential portability problem) -#elif defined(__GNUG__) || defined(__clang__) -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wconversion" -# pragma GCC diagnostic ignored "-Wdeprecated-declarations" -# ifdef __clang__ -// Eigen generates a bunch of implicit-copy-constructor-is-deprecated warnings with -Wdeprecated -// under Clang, so disable that warning here: -# pragma GCC diagnostic ignored "-Wdeprecated" -# endif -# if __GNUC__ >= 7 -# pragma GCC diagnostic ignored "-Wint-in-bool-context" -# endif -#endif - -#if defined(_MSC_VER) -# pragma warning(push) -# pragma warning(disable: 4127) // warning C4127: Conditional expression is constant -# pragma warning(disable: 4996) // warning C4996: std::unary_negate is deprecated in C++17 -#endif - -#include -#include - -// Eigen prior to 3.2.7 doesn't have proper move constructors--but worse, some classes get implicit -// move constructors that break things. We could detect this an explicitly copy, but an extra copy -// of matrices seems highly undesirable. -static_assert(EIGEN_VERSION_AT_LEAST(3,2,7), "Eigen support in pybind11 requires Eigen >= 3.2.7"); - -NAMESPACE_BEGIN(PYBIND11_NAMESPACE) - -// Provide a convenience alias for easier pass-by-ref usage with fully dynamic strides: -using EigenDStride = Eigen::Stride; -template using EigenDRef = Eigen::Ref; -template using EigenDMap = Eigen::Map; - -NAMESPACE_BEGIN(detail) - -#if EIGEN_VERSION_AT_LEAST(3,3,0) -using EigenIndex = Eigen::Index; -#else -using EigenIndex = EIGEN_DEFAULT_DENSE_INDEX_TYPE; -#endif - -// Matches Eigen::Map, Eigen::Ref, blocks, etc: -template using is_eigen_dense_map = all_of, std::is_base_of, T>>; -template using is_eigen_mutable_map = std::is_base_of, T>; -template using is_eigen_dense_plain = all_of>, is_template_base_of>; -template using is_eigen_sparse = is_template_base_of; -// Test for objects inheriting from EigenBase that aren't captured by the above. This -// basically covers anything that can be assigned to a dense matrix but that don't have a typical -// matrix data layout that can be copied from their .data(). For example, DiagonalMatrix and -// SelfAdjointView fall into this category. -template using is_eigen_other = all_of< - is_template_base_of, - negation, is_eigen_dense_plain, is_eigen_sparse>> ->; - -// Captures numpy/eigen conformability status (returned by EigenProps::conformable()): -template struct EigenConformable { - bool conformable = false; - EigenIndex rows = 0, cols = 0; - EigenDStride stride{0, 0}; // Only valid if negativestrides is false! - bool negativestrides = false; // If true, do not use stride! - - EigenConformable(bool fits = false) : conformable{fits} {} - // Matrix type: - EigenConformable(EigenIndex r, EigenIndex c, - EigenIndex rstride, EigenIndex cstride) : - conformable{true}, rows{r}, cols{c} { - // TODO: when Eigen bug #747 is fixed, remove the tests for non-negativity. http://eigen.tuxfamily.org/bz/show_bug.cgi?id=747 - if (rstride < 0 || cstride < 0) { - negativestrides = true; - } else { - stride = {EigenRowMajor ? rstride : cstride /* outer stride */, - EigenRowMajor ? cstride : rstride /* inner stride */ }; - } - } - // Vector type: - EigenConformable(EigenIndex r, EigenIndex c, EigenIndex stride) - : EigenConformable(r, c, r == 1 ? c*stride : stride, c == 1 ? r : r*stride) {} - - template bool stride_compatible() const { - // To have compatible strides, we need (on both dimensions) one of fully dynamic strides, - // matching strides, or a dimension size of 1 (in which case the stride value is irrelevant) - return - !negativestrides && - (props::inner_stride == Eigen::Dynamic || props::inner_stride == stride.inner() || - (EigenRowMajor ? cols : rows) == 1) && - (props::outer_stride == Eigen::Dynamic || props::outer_stride == stride.outer() || - (EigenRowMajor ? rows : cols) == 1); - } - operator bool() const { return conformable; } -}; - -template struct eigen_extract_stride { using type = Type; }; -template -struct eigen_extract_stride> { using type = StrideType; }; -template -struct eigen_extract_stride> { using type = StrideType; }; - -// Helper struct for extracting information from an Eigen type -template struct EigenProps { - using Type = Type_; - using Scalar = typename Type::Scalar; - using StrideType = typename eigen_extract_stride::type; - static constexpr EigenIndex - rows = Type::RowsAtCompileTime, - cols = Type::ColsAtCompileTime, - size = Type::SizeAtCompileTime; - static constexpr bool - row_major = Type::IsRowMajor, - vector = Type::IsVectorAtCompileTime, // At least one dimension has fixed size 1 - fixed_rows = rows != Eigen::Dynamic, - fixed_cols = cols != Eigen::Dynamic, - fixed = size != Eigen::Dynamic, // Fully-fixed size - dynamic = !fixed_rows && !fixed_cols; // Fully-dynamic size - - template using if_zero = std::integral_constant; - static constexpr EigenIndex inner_stride = if_zero::value, - outer_stride = if_zero::value; - static constexpr bool dynamic_stride = inner_stride == Eigen::Dynamic && outer_stride == Eigen::Dynamic; - static constexpr bool requires_row_major = !dynamic_stride && !vector && (row_major ? inner_stride : outer_stride) == 1; - static constexpr bool requires_col_major = !dynamic_stride && !vector && (row_major ? outer_stride : inner_stride) == 1; - - // Takes an input array and determines whether we can make it fit into the Eigen type. If - // the array is a vector, we attempt to fit it into either an Eigen 1xN or Nx1 vector - // (preferring the latter if it will fit in either, i.e. for a fully dynamic matrix type). - static EigenConformable conformable(const array &a) { - const auto dims = a.ndim(); - if (dims < 1 || dims > 2) - return false; - - if (dims == 2) { // Matrix type: require exact match (or dynamic) - - EigenIndex - np_rows = a.shape(0), - np_cols = a.shape(1), - np_rstride = a.strides(0) / static_cast(sizeof(Scalar)), - np_cstride = a.strides(1) / static_cast(sizeof(Scalar)); - if ((fixed_rows && np_rows != rows) || (fixed_cols && np_cols != cols)) - return false; - - return {np_rows, np_cols, np_rstride, np_cstride}; - } - - // Otherwise we're storing an n-vector. Only one of the strides will be used, but whichever - // is used, we want the (single) numpy stride value. - const EigenIndex n = a.shape(0), - stride = a.strides(0) / static_cast(sizeof(Scalar)); - - if (vector) { // Eigen type is a compile-time vector - if (fixed && size != n) - return false; // Vector size mismatch - return {rows == 1 ? 1 : n, cols == 1 ? 1 : n, stride}; - } - else if (fixed) { - // The type has a fixed size, but is not a vector: abort - return false; - } - else if (fixed_cols) { - // Since this isn't a vector, cols must be != 1. We allow this only if it exactly - // equals the number of elements (rows is Dynamic, and so 1 row is allowed). - if (cols != n) return false; - return {1, n, stride}; - } - else { - // Otherwise it's either fully dynamic, or column dynamic; both become a column vector - if (fixed_rows && rows != n) return false; - return {n, 1, stride}; - } - } - - static constexpr bool show_writeable = is_eigen_dense_map::value && is_eigen_mutable_map::value; - static constexpr bool show_order = is_eigen_dense_map::value; - static constexpr bool show_c_contiguous = show_order && requires_row_major; - static constexpr bool show_f_contiguous = !show_c_contiguous && show_order && requires_col_major; - - static constexpr auto descriptor = - _("numpy.ndarray[") + npy_format_descriptor::name + - _("[") + _(_<(size_t) rows>(), _("m")) + - _(", ") + _(_<(size_t) cols>(), _("n")) + - _("]") + - // For a reference type (e.g. Ref) we have other constraints that might need to be - // satisfied: writeable=True (for a mutable reference), and, depending on the map's stride - // options, possibly f_contiguous or c_contiguous. We include them in the descriptor output - // to provide some hint as to why a TypeError is occurring (otherwise it can be confusing to - // see that a function accepts a 'numpy.ndarray[float64[3,2]]' and an error message that you - // *gave* a numpy.ndarray of the right type and dimensions. - _(", flags.writeable", "") + - _(", flags.c_contiguous", "") + - _(", flags.f_contiguous", "") + - _("]"); -}; - -// Casts an Eigen type to numpy array. If given a base, the numpy array references the src data, -// otherwise it'll make a copy. writeable lets you turn off the writeable flag for the array. -template handle eigen_array_cast(typename props::Type const &src, handle base = handle(), bool writeable = true) { - constexpr ssize_t elem_size = sizeof(typename props::Scalar); - array a; - if (props::vector) - a = array({ src.size() }, { elem_size * src.innerStride() }, src.data(), base); - else - a = array({ src.rows(), src.cols() }, { elem_size * src.rowStride(), elem_size * src.colStride() }, - src.data(), base); - - if (!writeable) - array_proxy(a.ptr())->flags &= ~detail::npy_api::NPY_ARRAY_WRITEABLE_; - - return a.release(); -} - -// Takes an lvalue ref to some Eigen type and a (python) base object, creating a numpy array that -// reference the Eigen object's data with `base` as the python-registered base class (if omitted, -// the base will be set to None, and lifetime management is up to the caller). The numpy array is -// non-writeable if the given type is const. -template -handle eigen_ref_array(Type &src, handle parent = none()) { - // none here is to get past array's should-we-copy detection, which currently always - // copies when there is no base. Setting the base to None should be harmless. - return eigen_array_cast(src, parent, !std::is_const::value); -} - -// Takes a pointer to some dense, plain Eigen type, builds a capsule around it, then returns a numpy -// array that references the encapsulated data with a python-side reference to the capsule to tie -// its destruction to that of any dependent python objects. Const-ness is determined by whether or -// not the Type of the pointer given is const. -template ::value>> -handle eigen_encapsulate(Type *src) { - capsule base(src, [](void *o) { delete static_cast(o); }); - return eigen_ref_array(*src, base); -} - -// Type caster for regular, dense matrix types (e.g. MatrixXd), but not maps/refs/etc. of dense -// types. -template -struct type_caster::value>> { - using Scalar = typename Type::Scalar; - using props = EigenProps; - - bool load(handle src, bool convert) { - // If we're in no-convert mode, only load if given an array of the correct type - if (!convert && !isinstance>(src)) - return false; - - // Coerce into an array, but don't do type conversion yet; the copy below handles it. - auto buf = array::ensure(src); - - if (!buf) - return false; - - auto dims = buf.ndim(); - if (dims < 1 || dims > 2) - return false; - - auto fits = props::conformable(buf); - if (!fits) - return false; - - // Allocate the new type, then build a numpy reference into it - value = Type(fits.rows, fits.cols); - auto ref = reinterpret_steal(eigen_ref_array(value)); - if (dims == 1) ref = ref.squeeze(); - else if (ref.ndim() == 1) buf = buf.squeeze(); - - int result = detail::npy_api::get().PyArray_CopyInto_(ref.ptr(), buf.ptr()); - - if (result < 0) { // Copy failed! - PyErr_Clear(); - return false; - } - - return true; - } - -private: - - // Cast implementation - template - static handle cast_impl(CType *src, return_value_policy policy, handle parent) { - switch (policy) { - case return_value_policy::take_ownership: - case return_value_policy::automatic: - return eigen_encapsulate(src); - case return_value_policy::move: - return eigen_encapsulate(new CType(std::move(*src))); - case return_value_policy::copy: - return eigen_array_cast(*src); - case return_value_policy::reference: - case return_value_policy::automatic_reference: - return eigen_ref_array(*src); - case return_value_policy::reference_internal: - return eigen_ref_array(*src, parent); - default: - throw cast_error("unhandled return_value_policy: should not happen!"); - }; - } - -public: - - // Normal returned non-reference, non-const value: - static handle cast(Type &&src, return_value_policy /* policy */, handle parent) { - return cast_impl(&src, return_value_policy::move, parent); - } - // If you return a non-reference const, we mark the numpy array readonly: - static handle cast(const Type &&src, return_value_policy /* policy */, handle parent) { - return cast_impl(&src, return_value_policy::move, parent); - } - // lvalue reference return; default (automatic) becomes copy - static handle cast(Type &src, return_value_policy policy, handle parent) { - if (policy == return_value_policy::automatic || policy == return_value_policy::automatic_reference) - policy = return_value_policy::copy; - return cast_impl(&src, policy, parent); - } - // const lvalue reference return; default (automatic) becomes copy - static handle cast(const Type &src, return_value_policy policy, handle parent) { - if (policy == return_value_policy::automatic || policy == return_value_policy::automatic_reference) - policy = return_value_policy::copy; - return cast(&src, policy, parent); - } - // non-const pointer return - static handle cast(Type *src, return_value_policy policy, handle parent) { - return cast_impl(src, policy, parent); - } - // const pointer return - static handle cast(const Type *src, return_value_policy policy, handle parent) { - return cast_impl(src, policy, parent); - } - - static constexpr auto name = props::descriptor; - - operator Type*() { return &value; } - operator Type&() { return value; } - operator Type&&() && { return std::move(value); } - template using cast_op_type = movable_cast_op_type; - -private: - Type value; -}; - -// Base class for casting reference/map/block/etc. objects back to python. -template struct eigen_map_caster { -private: - using props = EigenProps; - -public: - - // Directly referencing a ref/map's data is a bit dangerous (whatever the map/ref points to has - // to stay around), but we'll allow it under the assumption that you know what you're doing (and - // have an appropriate keep_alive in place). We return a numpy array pointing directly at the - // ref's data (The numpy array ends up read-only if the ref was to a const matrix type.) Note - // that this means you need to ensure you don't destroy the object in some other way (e.g. with - // an appropriate keep_alive, or with a reference to a statically allocated matrix). - static handle cast(const MapType &src, return_value_policy policy, handle parent) { - switch (policy) { - case return_value_policy::copy: - return eigen_array_cast(src); - case return_value_policy::reference_internal: - return eigen_array_cast(src, parent, is_eigen_mutable_map::value); - case return_value_policy::reference: - case return_value_policy::automatic: - case return_value_policy::automatic_reference: - return eigen_array_cast(src, none(), is_eigen_mutable_map::value); - default: - // move, take_ownership don't make any sense for a ref/map: - pybind11_fail("Invalid return_value_policy for Eigen Map/Ref/Block type"); - } - } - - static constexpr auto name = props::descriptor; - - // Explicitly delete these: support python -> C++ conversion on these (i.e. these can be return - // types but not bound arguments). We still provide them (with an explicitly delete) so that - // you end up here if you try anyway. - bool load(handle, bool) = delete; - operator MapType() = delete; - template using cast_op_type = MapType; -}; - -// We can return any map-like object (but can only load Refs, specialized next): -template struct type_caster::value>> - : eigen_map_caster {}; - -// Loader for Ref<...> arguments. See the documentation for info on how to make this work without -// copying (it requires some extra effort in many cases). -template -struct type_caster< - Eigen::Ref, - enable_if_t>::value> -> : public eigen_map_caster> { -private: - using Type = Eigen::Ref; - using props = EigenProps; - using Scalar = typename props::Scalar; - using MapType = Eigen::Map; - using Array = array_t; - static constexpr bool need_writeable = is_eigen_mutable_map::value; - // Delay construction (these have no default constructor) - std::unique_ptr map; - std::unique_ptr ref; - // Our array. When possible, this is just a numpy array pointing to the source data, but - // sometimes we can't avoid copying (e.g. input is not a numpy array at all, has an incompatible - // layout, or is an array of a type that needs to be converted). Using a numpy temporary - // (rather than an Eigen temporary) saves an extra copy when we need both type conversion and - // storage order conversion. (Note that we refuse to use this temporary copy when loading an - // argument for a Ref with M non-const, i.e. a read-write reference). - Array copy_or_ref; -public: - bool load(handle src, bool convert) { - // First check whether what we have is already an array of the right type. If not, we can't - // avoid a copy (because the copy is also going to do type conversion). - bool need_copy = !isinstance(src); - - EigenConformable fits; - if (!need_copy) { - // We don't need a converting copy, but we also need to check whether the strides are - // compatible with the Ref's stride requirements - Array aref = reinterpret_borrow(src); - - if (aref && (!need_writeable || aref.writeable())) { - fits = props::conformable(aref); - if (!fits) return false; // Incompatible dimensions - if (!fits.template stride_compatible()) - need_copy = true; - else - copy_or_ref = std::move(aref); - } - else { - need_copy = true; - } - } - - if (need_copy) { - // We need to copy: If we need a mutable reference, or we're not supposed to convert - // (either because we're in the no-convert overload pass, or because we're explicitly - // instructed not to copy (via `py::arg().noconvert()`) we have to fail loading. - if (!convert || need_writeable) return false; - - Array copy = Array::ensure(src); - if (!copy) return false; - fits = props::conformable(copy); - if (!fits || !fits.template stride_compatible()) - return false; - copy_or_ref = std::move(copy); - loader_life_support::add_patient(copy_or_ref); - } - - ref.reset(); - map.reset(new MapType(data(copy_or_ref), fits.rows, fits.cols, make_stride(fits.stride.outer(), fits.stride.inner()))); - ref.reset(new Type(*map)); - - return true; - } - - operator Type*() { return ref.get(); } - operator Type&() { return *ref; } - template using cast_op_type = pybind11::detail::cast_op_type<_T>; - -private: - template ::value, int> = 0> - Scalar *data(Array &a) { return a.mutable_data(); } - - template ::value, int> = 0> - const Scalar *data(Array &a) { return a.data(); } - - // Attempt to figure out a constructor of `Stride` that will work. - // If both strides are fixed, use a default constructor: - template using stride_ctor_default = bool_constant< - S::InnerStrideAtCompileTime != Eigen::Dynamic && S::OuterStrideAtCompileTime != Eigen::Dynamic && - std::is_default_constructible::value>; - // Otherwise, if there is a two-index constructor, assume it is (outer,inner) like - // Eigen::Stride, and use it: - template using stride_ctor_dual = bool_constant< - !stride_ctor_default::value && std::is_constructible::value>; - // Otherwise, if there is a one-index constructor, and just one of the strides is dynamic, use - // it (passing whichever stride is dynamic). - template using stride_ctor_outer = bool_constant< - !any_of, stride_ctor_dual>::value && - S::OuterStrideAtCompileTime == Eigen::Dynamic && S::InnerStrideAtCompileTime != Eigen::Dynamic && - std::is_constructible::value>; - template using stride_ctor_inner = bool_constant< - !any_of, stride_ctor_dual>::value && - S::InnerStrideAtCompileTime == Eigen::Dynamic && S::OuterStrideAtCompileTime != Eigen::Dynamic && - std::is_constructible::value>; - - template ::value, int> = 0> - static S make_stride(EigenIndex, EigenIndex) { return S(); } - template ::value, int> = 0> - static S make_stride(EigenIndex outer, EigenIndex inner) { return S(outer, inner); } - template ::value, int> = 0> - static S make_stride(EigenIndex outer, EigenIndex) { return S(outer); } - template ::value, int> = 0> - static S make_stride(EigenIndex, EigenIndex inner) { return S(inner); } - -}; - -// type_caster for special matrix types (e.g. DiagonalMatrix), which are EigenBase, but not -// EigenDense (i.e. they don't have a data(), at least not with the usual matrix layout). -// load() is not supported, but we can cast them into the python domain by first copying to a -// regular Eigen::Matrix, then casting that. -template -struct type_caster::value>> { -protected: - using Matrix = Eigen::Matrix; - using props = EigenProps; -public: - static handle cast(const Type &src, return_value_policy /* policy */, handle /* parent */) { - handle h = eigen_encapsulate(new Matrix(src)); - return h; - } - static handle cast(const Type *src, return_value_policy policy, handle parent) { return cast(*src, policy, parent); } - - static constexpr auto name = props::descriptor; - - // Explicitly delete these: support python -> C++ conversion on these (i.e. these can be return - // types but not bound arguments). We still provide them (with an explicitly delete) so that - // you end up here if you try anyway. - bool load(handle, bool) = delete; - operator Type() = delete; - template using cast_op_type = Type; -}; - -template -struct type_caster::value>> { - typedef typename Type::Scalar Scalar; - typedef remove_reference_t().outerIndexPtr())> StorageIndex; - typedef typename Type::Index Index; - static constexpr bool rowMajor = Type::IsRowMajor; - - bool load(handle src, bool) { - if (!src) - return false; - - auto obj = reinterpret_borrow(src); - object sparse_module = module::import("scipy.sparse"); - object matrix_type = sparse_module.attr( - rowMajor ? "csr_matrix" : "csc_matrix"); - - if (!obj.get_type().is(matrix_type)) { - try { - obj = matrix_type(obj); - } catch (const error_already_set &) { - return false; - } - } - - auto values = array_t((object) obj.attr("data")); - auto innerIndices = array_t((object) obj.attr("indices")); - auto outerIndices = array_t((object) obj.attr("indptr")); - auto shape = pybind11::tuple((pybind11::object) obj.attr("shape")); - auto nnz = obj.attr("nnz").cast(); - - if (!values || !innerIndices || !outerIndices) - return false; - - value = Eigen::MappedSparseMatrix( - shape[0].cast(), shape[1].cast(), nnz, - outerIndices.mutable_data(), innerIndices.mutable_data(), values.mutable_data()); - - return true; - } - - static handle cast(const Type &src, return_value_policy /* policy */, handle /* parent */) { - const_cast(src).makeCompressed(); - - object matrix_type = module::import("scipy.sparse").attr( - rowMajor ? "csr_matrix" : "csc_matrix"); - - array data(src.nonZeros(), src.valuePtr()); - array outerIndices((rowMajor ? src.rows() : src.cols()) + 1, src.outerIndexPtr()); - array innerIndices(src.nonZeros(), src.innerIndexPtr()); - - return matrix_type( - std::make_tuple(data, innerIndices, outerIndices), - std::make_pair(src.rows(), src.cols()) - ).release(); - } - - PYBIND11_TYPE_CASTER(Type, _<(Type::IsRowMajor) != 0>("scipy.sparse.csr_matrix[", "scipy.sparse.csc_matrix[") - + npy_format_descriptor::name + _("]")); -}; - -NAMESPACE_END(detail) -NAMESPACE_END(PYBIND11_NAMESPACE) - -#if defined(__GNUG__) || defined(__clang__) -# pragma GCC diagnostic pop -#elif defined(_MSC_VER) -# pragma warning(pop) -#endif diff --git a/ptocr/postprocess/dbprocess/include/pybind11/embed.h b/ptocr/postprocess/dbprocess/include/pybind11/embed.h deleted file mode 100644 index 7265588..0000000 --- a/ptocr/postprocess/dbprocess/include/pybind11/embed.h +++ /dev/null @@ -1,200 +0,0 @@ -/* - pybind11/embed.h: Support for embedding the interpreter - - Copyright (c) 2017 Wenzel Jakob - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#include "pybind11.h" -#include "eval.h" - -#if defined(PYPY_VERSION) -# error Embedding the interpreter is not supported with PyPy -#endif - -#if PY_MAJOR_VERSION >= 3 -# define PYBIND11_EMBEDDED_MODULE_IMPL(name) \ - extern "C" PyObject *pybind11_init_impl_##name() { \ - return pybind11_init_wrapper_##name(); \ - } -#else -# define PYBIND11_EMBEDDED_MODULE_IMPL(name) \ - extern "C" void pybind11_init_impl_##name() { \ - pybind11_init_wrapper_##name(); \ - } -#endif - -/** \rst - Add a new module to the table of builtins for the interpreter. Must be - defined in global scope. The first macro parameter is the name of the - module (without quotes). The second parameter is the variable which will - be used as the interface to add functions and classes to the module. - - .. code-block:: cpp - - PYBIND11_EMBEDDED_MODULE(example, m) { - // ... initialize functions and classes here - m.def("foo", []() { - return "Hello, World!"; - }); - } - \endrst */ -#define PYBIND11_EMBEDDED_MODULE(name, variable) \ - static void PYBIND11_CONCAT(pybind11_init_, name)(pybind11::module &); \ - static PyObject PYBIND11_CONCAT(*pybind11_init_wrapper_, name)() { \ - auto m = pybind11::module(PYBIND11_TOSTRING(name)); \ - try { \ - PYBIND11_CONCAT(pybind11_init_, name)(m); \ - return m.ptr(); \ - } catch (pybind11::error_already_set &e) { \ - PyErr_SetString(PyExc_ImportError, e.what()); \ - return nullptr; \ - } catch (const std::exception &e) { \ - PyErr_SetString(PyExc_ImportError, e.what()); \ - return nullptr; \ - } \ - } \ - PYBIND11_EMBEDDED_MODULE_IMPL(name) \ - pybind11::detail::embedded_module name(PYBIND11_TOSTRING(name), \ - PYBIND11_CONCAT(pybind11_init_impl_, name)); \ - void PYBIND11_CONCAT(pybind11_init_, name)(pybind11::module &variable) - - -NAMESPACE_BEGIN(PYBIND11_NAMESPACE) -NAMESPACE_BEGIN(detail) - -/// Python 2.7/3.x compatible version of `PyImport_AppendInittab` and error checks. -struct embedded_module { -#if PY_MAJOR_VERSION >= 3 - using init_t = PyObject *(*)(); -#else - using init_t = void (*)(); -#endif - embedded_module(const char *name, init_t init) { - if (Py_IsInitialized()) - pybind11_fail("Can't add new modules after the interpreter has been initialized"); - - auto result = PyImport_AppendInittab(name, init); - if (result == -1) - pybind11_fail("Insufficient memory to add a new module"); - } -}; - -NAMESPACE_END(detail) - -/** \rst - Initialize the Python interpreter. No other pybind11 or CPython API functions can be - called before this is done; with the exception of `PYBIND11_EMBEDDED_MODULE`. The - optional parameter can be used to skip the registration of signal handlers (see the - `Python documentation`_ for details). Calling this function again after the interpreter - has already been initialized is a fatal error. - - If initializing the Python interpreter fails, then the program is terminated. (This - is controlled by the CPython runtime and is an exception to pybind11's normal behavior - of throwing exceptions on errors.) - - .. _Python documentation: https://docs.python.org/3/c-api/init.html#c.Py_InitializeEx - \endrst */ -inline void initialize_interpreter(bool init_signal_handlers = true) { - if (Py_IsInitialized()) - pybind11_fail("The interpreter is already running"); - - Py_InitializeEx(init_signal_handlers ? 1 : 0); - - // Make .py files in the working directory available by default - module::import("sys").attr("path").cast().append("."); -} - -/** \rst - Shut down the Python interpreter. No pybind11 or CPython API functions can be called - after this. In addition, pybind11 objects must not outlive the interpreter: - - .. code-block:: cpp - - { // BAD - py::initialize_interpreter(); - auto hello = py::str("Hello, World!"); - py::finalize_interpreter(); - } // <-- BOOM, hello's destructor is called after interpreter shutdown - - { // GOOD - py::initialize_interpreter(); - { // scoped - auto hello = py::str("Hello, World!"); - } // <-- OK, hello is cleaned up properly - py::finalize_interpreter(); - } - - { // BETTER - py::scoped_interpreter guard{}; - auto hello = py::str("Hello, World!"); - } - - .. warning:: - - The interpreter can be restarted by calling `initialize_interpreter` again. - Modules created using pybind11 can be safely re-initialized. However, Python - itself cannot completely unload binary extension modules and there are several - caveats with regard to interpreter restarting. All the details can be found - in the CPython documentation. In short, not all interpreter memory may be - freed, either due to reference cycles or user-created global data. - - \endrst */ -inline void finalize_interpreter() { - handle builtins(PyEval_GetBuiltins()); - const char *id = PYBIND11_INTERNALS_ID; - - // Get the internals pointer (without creating it if it doesn't exist). It's possible for the - // internals to be created during Py_Finalize() (e.g. if a py::capsule calls `get_internals()` - // during destruction), so we get the pointer-pointer here and check it after Py_Finalize(). - detail::internals **internals_ptr_ptr = detail::get_internals_pp(); - // It could also be stashed in builtins, so look there too: - if (builtins.contains(id) && isinstance(builtins[id])) - internals_ptr_ptr = capsule(builtins[id]); - - Py_Finalize(); - - if (internals_ptr_ptr) { - delete *internals_ptr_ptr; - *internals_ptr_ptr = nullptr; - } -} - -/** \rst - Scope guard version of `initialize_interpreter` and `finalize_interpreter`. - This a move-only guard and only a single instance can exist. - - .. code-block:: cpp - - #include - - int main() { - py::scoped_interpreter guard{}; - py::print(Hello, World!); - } // <-- interpreter shutdown - \endrst */ -class scoped_interpreter { -public: - scoped_interpreter(bool init_signal_handlers = true) { - initialize_interpreter(init_signal_handlers); - } - - scoped_interpreter(const scoped_interpreter &) = delete; - scoped_interpreter(scoped_interpreter &&other) noexcept { other.is_valid = false; } - scoped_interpreter &operator=(const scoped_interpreter &) = delete; - scoped_interpreter &operator=(scoped_interpreter &&) = delete; - - ~scoped_interpreter() { - if (is_valid) - finalize_interpreter(); - } - -private: - bool is_valid = true; -}; - -NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/ptocr/postprocess/dbprocess/include/pybind11/eval.h b/ptocr/postprocess/dbprocess/include/pybind11/eval.h deleted file mode 100644 index ea85ba1..0000000 --- a/ptocr/postprocess/dbprocess/include/pybind11/eval.h +++ /dev/null @@ -1,117 +0,0 @@ -/* - pybind11/exec.h: Support for evaluating Python expressions and statements - from strings and files - - Copyright (c) 2016 Klemens Morgenstern and - Wenzel Jakob - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#include "pybind11.h" - -NAMESPACE_BEGIN(PYBIND11_NAMESPACE) - -enum eval_mode { - /// Evaluate a string containing an isolated expression - eval_expr, - - /// Evaluate a string containing a single statement. Returns \c none - eval_single_statement, - - /// Evaluate a string containing a sequence of statement. Returns \c none - eval_statements -}; - -template -object eval(str expr, object global = globals(), object local = object()) { - if (!local) - local = global; - - /* PyRun_String does not accept a PyObject / encoding specifier, - this seems to be the only alternative */ - std::string buffer = "# -*- coding: utf-8 -*-\n" + (std::string) expr; - - int start; - switch (mode) { - case eval_expr: start = Py_eval_input; break; - case eval_single_statement: start = Py_single_input; break; - case eval_statements: start = Py_file_input; break; - default: pybind11_fail("invalid evaluation mode"); - } - - PyObject *result = PyRun_String(buffer.c_str(), start, global.ptr(), local.ptr()); - if (!result) - throw error_already_set(); - return reinterpret_steal(result); -} - -template -object eval(const char (&s)[N], object global = globals(), object local = object()) { - /* Support raw string literals by removing common leading whitespace */ - auto expr = (s[0] == '\n') ? str(module::import("textwrap").attr("dedent")(s)) - : str(s); - return eval(expr, global, local); -} - -inline void exec(str expr, object global = globals(), object local = object()) { - eval(expr, global, local); -} - -template -void exec(const char (&s)[N], object global = globals(), object local = object()) { - eval(s, global, local); -} - -template -object eval_file(str fname, object global = globals(), object local = object()) { - if (!local) - local = global; - - int start; - switch (mode) { - case eval_expr: start = Py_eval_input; break; - case eval_single_statement: start = Py_single_input; break; - case eval_statements: start = Py_file_input; break; - default: pybind11_fail("invalid evaluation mode"); - } - - int closeFile = 1; - std::string fname_str = (std::string) fname; -#if PY_VERSION_HEX >= 0x03040000 - FILE *f = _Py_fopen_obj(fname.ptr(), "r"); -#elif PY_VERSION_HEX >= 0x03000000 - FILE *f = _Py_fopen(fname.ptr(), "r"); -#else - /* No unicode support in open() :( */ - auto fobj = reinterpret_steal(PyFile_FromString( - const_cast(fname_str.c_str()), - const_cast("r"))); - FILE *f = nullptr; - if (fobj) - f = PyFile_AsFile(fobj.ptr()); - closeFile = 0; -#endif - if (!f) { - PyErr_Clear(); - pybind11_fail("File \"" + fname_str + "\" could not be opened!"); - } - -#if PY_VERSION_HEX < 0x03000000 && defined(PYPY_VERSION) - PyObject *result = PyRun_File(f, fname_str.c_str(), start, global.ptr(), - local.ptr()); - (void) closeFile; -#else - PyObject *result = PyRun_FileEx(f, fname_str.c_str(), start, global.ptr(), - local.ptr(), closeFile); -#endif - - if (!result) - throw error_already_set(); - return reinterpret_steal(result); -} - -NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/ptocr/postprocess/dbprocess/include/pybind11/functional.h b/ptocr/postprocess/dbprocess/include/pybind11/functional.h deleted file mode 100644 index 9cdf21f..0000000 --- a/ptocr/postprocess/dbprocess/include/pybind11/functional.h +++ /dev/null @@ -1,83 +0,0 @@ -/* - pybind11/functional.h: std::function<> support - - Copyright (c) 2016 Wenzel Jakob - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#include "pybind11.h" -#include - -NAMESPACE_BEGIN(PYBIND11_NAMESPACE) -NAMESPACE_BEGIN(detail) - -template -struct type_caster> { - using type = std::function; - using retval_type = conditional_t::value, void_type, Return>; - using function_type = Return (*) (Args...); - -public: - bool load(handle src, bool convert) { - if (src.is_none()) { - // Defer accepting None to other overloads (if we aren't in convert mode): - if (!convert) return false; - return true; - } - - if (!isinstance(src)) - return false; - - auto func = reinterpret_borrow(src); - - /* - When passing a C++ function as an argument to another C++ - function via Python, every function call would normally involve - a full C++ -> Python -> C++ roundtrip, which can be prohibitive. - Here, we try to at least detect the case where the function is - stateless (i.e. function pointer or lambda function without - captured variables), in which case the roundtrip can be avoided. - */ - if (auto cfunc = func.cpp_function()) { - auto c = reinterpret_borrow(PyCFunction_GET_SELF(cfunc.ptr())); - auto rec = (function_record *) c; - - if (rec && rec->is_stateless && - same_type(typeid(function_type), *reinterpret_cast(rec->data[1]))) { - struct capture { function_type f; }; - value = ((capture *) &rec->data)->f; - return true; - } - } - - value = [func](Args... args) -> Return { - gil_scoped_acquire acq; - object retval(func(std::forward(args)...)); - /* Visual studio 2015 parser issue: need parentheses around this expression */ - return (retval.template cast()); - }; - return true; - } - - template - static handle cast(Func &&f_, return_value_policy policy, handle /* parent */) { - if (!f_) - return none().inc_ref(); - - auto result = f_.template target(); - if (result) - return cpp_function(*result, policy).release(); - else - return cpp_function(std::forward(f_), policy).release(); - } - - PYBIND11_TYPE_CASTER(type, _("Callable[[") + concat(make_caster::name...) + _("], ") - + make_caster::name + _("]")); -}; - -NAMESPACE_END(detail) -NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/ptocr/postprocess/dbprocess/include/pybind11/iostream.h b/ptocr/postprocess/dbprocess/include/pybind11/iostream.h deleted file mode 100644 index 182e8ee..0000000 --- a/ptocr/postprocess/dbprocess/include/pybind11/iostream.h +++ /dev/null @@ -1,200 +0,0 @@ -/* - pybind11/iostream.h -- Tools to assist with redirecting cout and cerr to Python - - Copyright (c) 2017 Henry F. Schreiner - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#include "pybind11.h" - -#include -#include -#include -#include -#include - -NAMESPACE_BEGIN(PYBIND11_NAMESPACE) -NAMESPACE_BEGIN(detail) - -// Buffer that writes to Python instead of C++ -class pythonbuf : public std::streambuf { -private: - using traits_type = std::streambuf::traits_type; - - char d_buffer[1024]; - object pywrite; - object pyflush; - - int overflow(int c) { - if (!traits_type::eq_int_type(c, traits_type::eof())) { - *pptr() = traits_type::to_char_type(c); - pbump(1); - } - return sync() == 0 ? traits_type::not_eof(c) : traits_type::eof(); - } - - int sync() { - if (pbase() != pptr()) { - // This subtraction cannot be negative, so dropping the sign - str line(pbase(), static_cast(pptr() - pbase())); - - pywrite(line); - pyflush(); - - setp(pbase(), epptr()); - } - return 0; - } - -public: - pythonbuf(object pyostream) - : pywrite(pyostream.attr("write")), - pyflush(pyostream.attr("flush")) { - setp(d_buffer, d_buffer + sizeof(d_buffer) - 1); - } - - /// Sync before destroy - ~pythonbuf() { - sync(); - } -}; - -NAMESPACE_END(detail) - - -/** \rst - This a move-only guard that redirects output. - - .. code-block:: cpp - - #include - - ... - - { - py::scoped_ostream_redirect output; - std::cout << "Hello, World!"; // Python stdout - } // <-- return std::cout to normal - - You can explicitly pass the c++ stream and the python object, - for example to guard stderr instead. - - .. code-block:: cpp - - { - py::scoped_ostream_redirect output{std::cerr, py::module::import("sys").attr("stderr")}; - std::cerr << "Hello, World!"; - } - \endrst */ -class scoped_ostream_redirect { -protected: - std::streambuf *old; - std::ostream &costream; - detail::pythonbuf buffer; - -public: - scoped_ostream_redirect( - std::ostream &costream = std::cout, - object pyostream = module::import("sys").attr("stdout")) - : costream(costream), buffer(pyostream) { - old = costream.rdbuf(&buffer); - } - - ~scoped_ostream_redirect() { - costream.rdbuf(old); - } - - scoped_ostream_redirect(const scoped_ostream_redirect &) = delete; - scoped_ostream_redirect(scoped_ostream_redirect &&other) = default; - scoped_ostream_redirect &operator=(const scoped_ostream_redirect &) = delete; - scoped_ostream_redirect &operator=(scoped_ostream_redirect &&) = delete; -}; - - -/** \rst - Like `scoped_ostream_redirect`, but redirects cerr by default. This class - is provided primary to make ``py::call_guard`` easier to make. - - .. code-block:: cpp - - m.def("noisy_func", &noisy_func, - py::call_guard()); - -\endrst */ -class scoped_estream_redirect : public scoped_ostream_redirect { -public: - scoped_estream_redirect( - std::ostream &costream = std::cerr, - object pyostream = module::import("sys").attr("stderr")) - : scoped_ostream_redirect(costream,pyostream) {} -}; - - -NAMESPACE_BEGIN(detail) - -// Class to redirect output as a context manager. C++ backend. -class OstreamRedirect { - bool do_stdout_; - bool do_stderr_; - std::unique_ptr redirect_stdout; - std::unique_ptr redirect_stderr; - -public: - OstreamRedirect(bool do_stdout = true, bool do_stderr = true) - : do_stdout_(do_stdout), do_stderr_(do_stderr) {} - - void enter() { - if (do_stdout_) - redirect_stdout.reset(new scoped_ostream_redirect()); - if (do_stderr_) - redirect_stderr.reset(new scoped_estream_redirect()); - } - - void exit() { - redirect_stdout.reset(); - redirect_stderr.reset(); - } -}; - -NAMESPACE_END(detail) - -/** \rst - This is a helper function to add a C++ redirect context manager to Python - instead of using a C++ guard. To use it, add the following to your binding code: - - .. code-block:: cpp - - #include - - ... - - py::add_ostream_redirect(m, "ostream_redirect"); - - You now have a Python context manager that redirects your output: - - .. code-block:: python - - with m.ostream_redirect(): - m.print_to_cout_function() - - This manager can optionally be told which streams to operate on: - - .. code-block:: python - - with m.ostream_redirect(stdout=true, stderr=true): - m.noisy_function_with_error_printing() - - \endrst */ -inline class_ add_ostream_redirect(module m, std::string name = "ostream_redirect") { - return class_(m, name.c_str(), module_local()) - .def(init(), arg("stdout")=true, arg("stderr")=true) - .def("__enter__", &detail::OstreamRedirect::enter) - .def("__exit__", [](detail::OstreamRedirect &self_, args) { self_.exit(); }); -} - -NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/ptocr/postprocess/dbprocess/include/pybind11/numpy.h b/ptocr/postprocess/dbprocess/include/pybind11/numpy.h deleted file mode 100644 index 37471d8..0000000 --- a/ptocr/postprocess/dbprocess/include/pybind11/numpy.h +++ /dev/null @@ -1,1610 +0,0 @@ -/* - pybind11/numpy.h: Basic NumPy support, vectorize() wrapper - - Copyright (c) 2016 Wenzel Jakob - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#include "pybind11.h" -#include "complex.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if defined(_MSC_VER) -# pragma warning(push) -# pragma warning(disable: 4127) // warning C4127: Conditional expression is constant -#endif - -/* This will be true on all flat address space platforms and allows us to reduce the - whole npy_intp / ssize_t / Py_intptr_t business down to just ssize_t for all size - and dimension types (e.g. shape, strides, indexing), instead of inflicting this - upon the library user. */ -static_assert(sizeof(ssize_t) == sizeof(Py_intptr_t), "ssize_t != Py_intptr_t"); - -NAMESPACE_BEGIN(PYBIND11_NAMESPACE) - -class array; // Forward declaration - -NAMESPACE_BEGIN(detail) -template struct npy_format_descriptor; - -struct PyArrayDescr_Proxy { - PyObject_HEAD - PyObject *typeobj; - char kind; - char type; - char byteorder; - char flags; - int type_num; - int elsize; - int alignment; - char *subarray; - PyObject *fields; - PyObject *names; -}; - -struct PyArray_Proxy { - PyObject_HEAD - char *data; - int nd; - ssize_t *dimensions; - ssize_t *strides; - PyObject *base; - PyObject *descr; - int flags; -}; - -struct PyVoidScalarObject_Proxy { - PyObject_VAR_HEAD - char *obval; - PyArrayDescr_Proxy *descr; - int flags; - PyObject *base; -}; - -struct numpy_type_info { - PyObject* dtype_ptr; - std::string format_str; -}; - -struct numpy_internals { - std::unordered_map registered_dtypes; - - numpy_type_info *get_type_info(const std::type_info& tinfo, bool throw_if_missing = true) { - auto it = registered_dtypes.find(std::type_index(tinfo)); - if (it != registered_dtypes.end()) - return &(it->second); - if (throw_if_missing) - pybind11_fail(std::string("NumPy type info missing for ") + tinfo.name()); - return nullptr; - } - - template numpy_type_info *get_type_info(bool throw_if_missing = true) { - return get_type_info(typeid(typename std::remove_cv::type), throw_if_missing); - } -}; - -inline PYBIND11_NOINLINE void load_numpy_internals(numpy_internals* &ptr) { - ptr = &get_or_create_shared_data("_numpy_internals"); -} - -inline numpy_internals& get_numpy_internals() { - static numpy_internals* ptr = nullptr; - if (!ptr) - load_numpy_internals(ptr); - return *ptr; -} - -struct npy_api { - enum constants { - NPY_ARRAY_C_CONTIGUOUS_ = 0x0001, - NPY_ARRAY_F_CONTIGUOUS_ = 0x0002, - NPY_ARRAY_OWNDATA_ = 0x0004, - NPY_ARRAY_FORCECAST_ = 0x0010, - NPY_ARRAY_ENSUREARRAY_ = 0x0040, - NPY_ARRAY_ALIGNED_ = 0x0100, - NPY_ARRAY_WRITEABLE_ = 0x0400, - NPY_BOOL_ = 0, - NPY_BYTE_, NPY_UBYTE_, - NPY_SHORT_, NPY_USHORT_, - NPY_INT_, NPY_UINT_, - NPY_LONG_, NPY_ULONG_, - NPY_LONGLONG_, NPY_ULONGLONG_, - NPY_FLOAT_, NPY_DOUBLE_, NPY_LONGDOUBLE_, - NPY_CFLOAT_, NPY_CDOUBLE_, NPY_CLONGDOUBLE_, - NPY_OBJECT_ = 17, - NPY_STRING_, NPY_UNICODE_, NPY_VOID_ - }; - - typedef struct { - Py_intptr_t *ptr; - int len; - } PyArray_Dims; - - static npy_api& get() { - static npy_api api = lookup(); - return api; - } - - bool PyArray_Check_(PyObject *obj) const { - return (bool) PyObject_TypeCheck(obj, PyArray_Type_); - } - bool PyArrayDescr_Check_(PyObject *obj) const { - return (bool) PyObject_TypeCheck(obj, PyArrayDescr_Type_); - } - - unsigned int (*PyArray_GetNDArrayCFeatureVersion_)(); - PyObject *(*PyArray_DescrFromType_)(int); - PyObject *(*PyArray_NewFromDescr_) - (PyTypeObject *, PyObject *, int, Py_intptr_t *, - Py_intptr_t *, void *, int, PyObject *); - PyObject *(*PyArray_DescrNewFromType_)(int); - int (*PyArray_CopyInto_)(PyObject *, PyObject *); - PyObject *(*PyArray_NewCopy_)(PyObject *, int); - PyTypeObject *PyArray_Type_; - PyTypeObject *PyVoidArrType_Type_; - PyTypeObject *PyArrayDescr_Type_; - PyObject *(*PyArray_DescrFromScalar_)(PyObject *); - PyObject *(*PyArray_FromAny_) (PyObject *, PyObject *, int, int, int, PyObject *); - int (*PyArray_DescrConverter_) (PyObject *, PyObject **); - bool (*PyArray_EquivTypes_) (PyObject *, PyObject *); - int (*PyArray_GetArrayParamsFromObject_)(PyObject *, PyObject *, char, PyObject **, int *, - Py_ssize_t *, PyObject **, PyObject *); - PyObject *(*PyArray_Squeeze_)(PyObject *); - int (*PyArray_SetBaseObject_)(PyObject *, PyObject *); - PyObject* (*PyArray_Resize_)(PyObject*, PyArray_Dims*, int, int); -private: - enum functions { - API_PyArray_GetNDArrayCFeatureVersion = 211, - API_PyArray_Type = 2, - API_PyArrayDescr_Type = 3, - API_PyVoidArrType_Type = 39, - API_PyArray_DescrFromType = 45, - API_PyArray_DescrFromScalar = 57, - API_PyArray_FromAny = 69, - API_PyArray_Resize = 80, - API_PyArray_CopyInto = 82, - API_PyArray_NewCopy = 85, - API_PyArray_NewFromDescr = 94, - API_PyArray_DescrNewFromType = 9, - API_PyArray_DescrConverter = 174, - API_PyArray_EquivTypes = 182, - API_PyArray_GetArrayParamsFromObject = 278, - API_PyArray_Squeeze = 136, - API_PyArray_SetBaseObject = 282 - }; - - static npy_api lookup() { - module m = module::import("numpy.core.multiarray"); - auto c = m.attr("_ARRAY_API"); -#if PY_MAJOR_VERSION >= 3 - void **api_ptr = (void **) PyCapsule_GetPointer(c.ptr(), NULL); -#else - void **api_ptr = (void **) PyCObject_AsVoidPtr(c.ptr()); -#endif - npy_api api; -#define DECL_NPY_API(Func) api.Func##_ = (decltype(api.Func##_)) api_ptr[API_##Func]; - DECL_NPY_API(PyArray_GetNDArrayCFeatureVersion); - if (api.PyArray_GetNDArrayCFeatureVersion_() < 0x7) - pybind11_fail("pybind11 numpy support requires numpy >= 1.7.0"); - DECL_NPY_API(PyArray_Type); - DECL_NPY_API(PyVoidArrType_Type); - DECL_NPY_API(PyArrayDescr_Type); - DECL_NPY_API(PyArray_DescrFromType); - DECL_NPY_API(PyArray_DescrFromScalar); - DECL_NPY_API(PyArray_FromAny); - DECL_NPY_API(PyArray_Resize); - DECL_NPY_API(PyArray_CopyInto); - DECL_NPY_API(PyArray_NewCopy); - DECL_NPY_API(PyArray_NewFromDescr); - DECL_NPY_API(PyArray_DescrNewFromType); - DECL_NPY_API(PyArray_DescrConverter); - DECL_NPY_API(PyArray_EquivTypes); - DECL_NPY_API(PyArray_GetArrayParamsFromObject); - DECL_NPY_API(PyArray_Squeeze); - DECL_NPY_API(PyArray_SetBaseObject); -#undef DECL_NPY_API - return api; - } -}; - -inline PyArray_Proxy* array_proxy(void* ptr) { - return reinterpret_cast(ptr); -} - -inline const PyArray_Proxy* array_proxy(const void* ptr) { - return reinterpret_cast(ptr); -} - -inline PyArrayDescr_Proxy* array_descriptor_proxy(PyObject* ptr) { - return reinterpret_cast(ptr); -} - -inline const PyArrayDescr_Proxy* array_descriptor_proxy(const PyObject* ptr) { - return reinterpret_cast(ptr); -} - -inline bool check_flags(const void* ptr, int flag) { - return (flag == (array_proxy(ptr)->flags & flag)); -} - -template struct is_std_array : std::false_type { }; -template struct is_std_array> : std::true_type { }; -template struct is_complex : std::false_type { }; -template struct is_complex> : std::true_type { }; - -template struct array_info_scalar { - typedef T type; - static constexpr bool is_array = false; - static constexpr bool is_empty = false; - static constexpr auto extents = _(""); - static void append_extents(list& /* shape */) { } -}; -// Computes underlying type and a comma-separated list of extents for array -// types (any mix of std::array and built-in arrays). An array of char is -// treated as scalar because it gets special handling. -template struct array_info : array_info_scalar { }; -template struct array_info> { - using type = typename array_info::type; - static constexpr bool is_array = true; - static constexpr bool is_empty = (N == 0) || array_info::is_empty; - static constexpr size_t extent = N; - - // appends the extents to shape - static void append_extents(list& shape) { - shape.append(N); - array_info::append_extents(shape); - } - - static constexpr auto extents = _::is_array>( - concat(_(), array_info::extents), _() - ); -}; -// For numpy we have special handling for arrays of characters, so we don't include -// the size in the array extents. -template struct array_info : array_info_scalar { }; -template struct array_info> : array_info_scalar> { }; -template struct array_info : array_info> { }; -template using remove_all_extents_t = typename array_info::type; - -template using is_pod_struct = all_of< - std::is_standard_layout, // since we're accessing directly in memory we need a standard layout type -#if !defined(__GNUG__) || defined(_LIBCPP_VERSION) || defined(_GLIBCXX_USE_CXX11_ABI) - // _GLIBCXX_USE_CXX11_ABI indicates that we're using libstdc++ from GCC 5 or newer, independent - // of the actual compiler (Clang can also use libstdc++, but it always defines __GNUC__ == 4). - std::is_trivially_copyable, -#else - // GCC 4 doesn't implement is_trivially_copyable, so approximate it - std::is_trivially_destructible, - satisfies_any_of, -#endif - satisfies_none_of ->; - -template ssize_t byte_offset_unsafe(const Strides &) { return 0; } -template -ssize_t byte_offset_unsafe(const Strides &strides, ssize_t i, Ix... index) { - return i * strides[Dim] + byte_offset_unsafe(strides, index...); -} - -/** - * Proxy class providing unsafe, unchecked const access to array data. This is constructed through - * the `unchecked()` method of `array` or the `unchecked()` method of `array_t`. `Dims` - * will be -1 for dimensions determined at runtime. - */ -template -class unchecked_reference { -protected: - static constexpr bool Dynamic = Dims < 0; - const unsigned char *data_; - // Storing the shape & strides in local variables (i.e. these arrays) allows the compiler to - // make large performance gains on big, nested loops, but requires compile-time dimensions - conditional_t> - shape_, strides_; - const ssize_t dims_; - - friend class pybind11::array; - // Constructor for compile-time dimensions: - template - unchecked_reference(const void *data, const ssize_t *shape, const ssize_t *strides, enable_if_t) - : data_{reinterpret_cast(data)}, dims_{Dims} { - for (size_t i = 0; i < (size_t) dims_; i++) { - shape_[i] = shape[i]; - strides_[i] = strides[i]; - } - } - // Constructor for runtime dimensions: - template - unchecked_reference(const void *data, const ssize_t *shape, const ssize_t *strides, enable_if_t dims) - : data_{reinterpret_cast(data)}, shape_{shape}, strides_{strides}, dims_{dims} {} - -public: - /** - * Unchecked const reference access to data at the given indices. For a compile-time known - * number of dimensions, this requires the correct number of arguments; for run-time - * dimensionality, this is not checked (and so is up to the caller to use safely). - */ - template const T &operator()(Ix... index) const { - static_assert(ssize_t{sizeof...(Ix)} == Dims || Dynamic, - "Invalid number of indices for unchecked array reference"); - return *reinterpret_cast(data_ + byte_offset_unsafe(strides_, ssize_t(index)...)); - } - /** - * Unchecked const reference access to data; this operator only participates if the reference - * is to a 1-dimensional array. When present, this is exactly equivalent to `obj(index)`. - */ - template > - const T &operator[](ssize_t index) const { return operator()(index); } - - /// Pointer access to the data at the given indices. - template const T *data(Ix... ix) const { return &operator()(ssize_t(ix)...); } - - /// Returns the item size, i.e. sizeof(T) - constexpr static ssize_t itemsize() { return sizeof(T); } - - /// Returns the shape (i.e. size) of dimension `dim` - ssize_t shape(ssize_t dim) const { return shape_[(size_t) dim]; } - - /// Returns the number of dimensions of the array - ssize_t ndim() const { return dims_; } - - /// Returns the total number of elements in the referenced array, i.e. the product of the shapes - template - enable_if_t size() const { - return std::accumulate(shape_.begin(), shape_.end(), (ssize_t) 1, std::multiplies()); - } - template - enable_if_t size() const { - return std::accumulate(shape_, shape_ + ndim(), (ssize_t) 1, std::multiplies()); - } - - /// Returns the total number of bytes used by the referenced data. Note that the actual span in - /// memory may be larger if the referenced array has non-contiguous strides (e.g. for a slice). - ssize_t nbytes() const { - return size() * itemsize(); - } -}; - -template -class unchecked_mutable_reference : public unchecked_reference { - friend class pybind11::array; - using ConstBase = unchecked_reference; - using ConstBase::ConstBase; - using ConstBase::Dynamic; -public: - /// Mutable, unchecked access to data at the given indices. - template T& operator()(Ix... index) { - static_assert(ssize_t{sizeof...(Ix)} == Dims || Dynamic, - "Invalid number of indices for unchecked array reference"); - return const_cast(ConstBase::operator()(index...)); - } - /** - * Mutable, unchecked access data at the given index; this operator only participates if the - * reference is to a 1-dimensional array (or has runtime dimensions). When present, this is - * exactly equivalent to `obj(index)`. - */ - template > - T &operator[](ssize_t index) { return operator()(index); } - - /// Mutable pointer access to the data at the given indices. - template T *mutable_data(Ix... ix) { return &operator()(ssize_t(ix)...); } -}; - -template -struct type_caster> { - static_assert(Dim == 0 && Dim > 0 /* always fail */, "unchecked array proxy object is not castable"); -}; -template -struct type_caster> : type_caster> {}; - -NAMESPACE_END(detail) - -class dtype : public object { -public: - PYBIND11_OBJECT_DEFAULT(dtype, object, detail::npy_api::get().PyArrayDescr_Check_); - - explicit dtype(const buffer_info &info) { - dtype descr(_dtype_from_pep3118()(PYBIND11_STR_TYPE(info.format))); - // If info.itemsize == 0, use the value calculated from the format string - m_ptr = descr.strip_padding(info.itemsize ? info.itemsize : descr.itemsize()).release().ptr(); - } - - explicit dtype(const std::string &format) { - m_ptr = from_args(pybind11::str(format)).release().ptr(); - } - - dtype(const char *format) : dtype(std::string(format)) { } - - dtype(list names, list formats, list offsets, ssize_t itemsize) { - dict args; - args["names"] = names; - args["formats"] = formats; - args["offsets"] = offsets; - args["itemsize"] = pybind11::int_(itemsize); - m_ptr = from_args(args).release().ptr(); - } - - /// This is essentially the same as calling numpy.dtype(args) in Python. - static dtype from_args(object args) { - PyObject *ptr = nullptr; - if (!detail::npy_api::get().PyArray_DescrConverter_(args.ptr(), &ptr) || !ptr) - throw error_already_set(); - return reinterpret_steal(ptr); - } - - /// Return dtype associated with a C++ type. - template static dtype of() { - return detail::npy_format_descriptor::type>::dtype(); - } - - /// Size of the data type in bytes. - ssize_t itemsize() const { - return detail::array_descriptor_proxy(m_ptr)->elsize; - } - - /// Returns true for structured data types. - bool has_fields() const { - return detail::array_descriptor_proxy(m_ptr)->names != nullptr; - } - - /// Single-character type code. - char kind() const { - return detail::array_descriptor_proxy(m_ptr)->kind; - } - -private: - static object _dtype_from_pep3118() { - static PyObject *obj = module::import("numpy.core._internal") - .attr("_dtype_from_pep3118").cast().release().ptr(); - return reinterpret_borrow(obj); - } - - dtype strip_padding(ssize_t itemsize) { - // Recursively strip all void fields with empty names that are generated for - // padding fields (as of NumPy v1.11). - if (!has_fields()) - return *this; - - struct field_descr { PYBIND11_STR_TYPE name; object format; pybind11::int_ offset; }; - std::vector field_descriptors; - - for (auto field : attr("fields").attr("items")()) { - auto spec = field.cast(); - auto name = spec[0].cast(); - auto format = spec[1].cast()[0].cast(); - auto offset = spec[1].cast()[1].cast(); - if (!len(name) && format.kind() == 'V') - continue; - field_descriptors.push_back({(PYBIND11_STR_TYPE) name, format.strip_padding(format.itemsize()), offset}); - } - - std::sort(field_descriptors.begin(), field_descriptors.end(), - [](const field_descr& a, const field_descr& b) { - return a.offset.cast() < b.offset.cast(); - }); - - list names, formats, offsets; - for (auto& descr : field_descriptors) { - names.append(descr.name); - formats.append(descr.format); - offsets.append(descr.offset); - } - return dtype(names, formats, offsets, itemsize); - } -}; - -class array : public buffer { -public: - PYBIND11_OBJECT_CVT(array, buffer, detail::npy_api::get().PyArray_Check_, raw_array) - - enum { - c_style = detail::npy_api::NPY_ARRAY_C_CONTIGUOUS_, - f_style = detail::npy_api::NPY_ARRAY_F_CONTIGUOUS_, - forcecast = detail::npy_api::NPY_ARRAY_FORCECAST_ - }; - - array() : array({{0}}, static_cast(nullptr)) {} - - using ShapeContainer = detail::any_container; - using StridesContainer = detail::any_container; - - // Constructs an array taking shape/strides from arbitrary container types - array(const pybind11::dtype &dt, ShapeContainer shape, StridesContainer strides, - const void *ptr = nullptr, handle base = handle()) { - - if (strides->empty()) - *strides = c_strides(*shape, dt.itemsize()); - - auto ndim = shape->size(); - if (ndim != strides->size()) - pybind11_fail("NumPy: shape ndim doesn't match strides ndim"); - auto descr = dt; - - int flags = 0; - if (base && ptr) { - if (isinstance(base)) - /* Copy flags from base (except ownership bit) */ - flags = reinterpret_borrow(base).flags() & ~detail::npy_api::NPY_ARRAY_OWNDATA_; - else - /* Writable by default, easy to downgrade later on if needed */ - flags = detail::npy_api::NPY_ARRAY_WRITEABLE_; - } - - auto &api = detail::npy_api::get(); - auto tmp = reinterpret_steal(api.PyArray_NewFromDescr_( - api.PyArray_Type_, descr.release().ptr(), (int) ndim, shape->data(), strides->data(), - const_cast(ptr), flags, nullptr)); - if (!tmp) - throw error_already_set(); - if (ptr) { - if (base) { - api.PyArray_SetBaseObject_(tmp.ptr(), base.inc_ref().ptr()); - } else { - tmp = reinterpret_steal(api.PyArray_NewCopy_(tmp.ptr(), -1 /* any order */)); - } - } - m_ptr = tmp.release().ptr(); - } - - array(const pybind11::dtype &dt, ShapeContainer shape, const void *ptr = nullptr, handle base = handle()) - : array(dt, std::move(shape), {}, ptr, base) { } - - template ::value && !std::is_same::value>> - array(const pybind11::dtype &dt, T count, const void *ptr = nullptr, handle base = handle()) - : array(dt, {{count}}, ptr, base) { } - - template - array(ShapeContainer shape, StridesContainer strides, const T *ptr, handle base = handle()) - : array(pybind11::dtype::of(), std::move(shape), std::move(strides), ptr, base) { } - - template - array(ShapeContainer shape, const T *ptr, handle base = handle()) - : array(std::move(shape), {}, ptr, base) { } - - template - explicit array(ssize_t count, const T *ptr, handle base = handle()) : array({count}, {}, ptr, base) { } - - explicit array(const buffer_info &info) - : array(pybind11::dtype(info), info.shape, info.strides, info.ptr) { } - - /// Array descriptor (dtype) - pybind11::dtype dtype() const { - return reinterpret_borrow(detail::array_proxy(m_ptr)->descr); - } - - /// Total number of elements - ssize_t size() const { - return std::accumulate(shape(), shape() + ndim(), (ssize_t) 1, std::multiplies()); - } - - /// Byte size of a single element - ssize_t itemsize() const { - return detail::array_descriptor_proxy(detail::array_proxy(m_ptr)->descr)->elsize; - } - - /// Total number of bytes - ssize_t nbytes() const { - return size() * itemsize(); - } - - /// Number of dimensions - ssize_t ndim() const { - return detail::array_proxy(m_ptr)->nd; - } - - /// Base object - object base() const { - return reinterpret_borrow(detail::array_proxy(m_ptr)->base); - } - - /// Dimensions of the array - const ssize_t* shape() const { - return detail::array_proxy(m_ptr)->dimensions; - } - - /// Dimension along a given axis - ssize_t shape(ssize_t dim) const { - if (dim >= ndim()) - fail_dim_check(dim, "invalid axis"); - return shape()[dim]; - } - - /// Strides of the array - const ssize_t* strides() const { - return detail::array_proxy(m_ptr)->strides; - } - - /// Stride along a given axis - ssize_t strides(ssize_t dim) const { - if (dim >= ndim()) - fail_dim_check(dim, "invalid axis"); - return strides()[dim]; - } - - /// Return the NumPy array flags - int flags() const { - return detail::array_proxy(m_ptr)->flags; - } - - /// If set, the array is writeable (otherwise the buffer is read-only) - bool writeable() const { - return detail::check_flags(m_ptr, detail::npy_api::NPY_ARRAY_WRITEABLE_); - } - - /// If set, the array owns the data (will be freed when the array is deleted) - bool owndata() const { - return detail::check_flags(m_ptr, detail::npy_api::NPY_ARRAY_OWNDATA_); - } - - /// Pointer to the contained data. If index is not provided, points to the - /// beginning of the buffer. May throw if the index would lead to out of bounds access. - template const void* data(Ix... index) const { - return static_cast(detail::array_proxy(m_ptr)->data + offset_at(index...)); - } - - /// Mutable pointer to the contained data. If index is not provided, points to the - /// beginning of the buffer. May throw if the index would lead to out of bounds access. - /// May throw if the array is not writeable. - template void* mutable_data(Ix... index) { - check_writeable(); - return static_cast(detail::array_proxy(m_ptr)->data + offset_at(index...)); - } - - /// Byte offset from beginning of the array to a given index (full or partial). - /// May throw if the index would lead to out of bounds access. - template ssize_t offset_at(Ix... index) const { - if ((ssize_t) sizeof...(index) > ndim()) - fail_dim_check(sizeof...(index), "too many indices for an array"); - return byte_offset(ssize_t(index)...); - } - - ssize_t offset_at() const { return 0; } - - /// Item count from beginning of the array to a given index (full or partial). - /// May throw if the index would lead to out of bounds access. - template ssize_t index_at(Ix... index) const { - return offset_at(index...) / itemsize(); - } - - /** - * Returns a proxy object that provides access to the array's data without bounds or - * dimensionality checking. Will throw if the array is missing the `writeable` flag. Use with - * care: the array must not be destroyed or reshaped for the duration of the returned object, - * and the caller must take care not to access invalid dimensions or dimension indices. - */ - template detail::unchecked_mutable_reference mutable_unchecked() & { - if (Dims >= 0 && ndim() != Dims) - throw std::domain_error("array has incorrect number of dimensions: " + std::to_string(ndim()) + - "; expected " + std::to_string(Dims)); - return detail::unchecked_mutable_reference(mutable_data(), shape(), strides(), ndim()); - } - - /** - * Returns a proxy object that provides const access to the array's data without bounds or - * dimensionality checking. Unlike `mutable_unchecked()`, this does not require that the - * underlying array have the `writable` flag. Use with care: the array must not be destroyed or - * reshaped for the duration of the returned object, and the caller must take care not to access - * invalid dimensions or dimension indices. - */ - template detail::unchecked_reference unchecked() const & { - if (Dims >= 0 && ndim() != Dims) - throw std::domain_error("array has incorrect number of dimensions: " + std::to_string(ndim()) + - "; expected " + std::to_string(Dims)); - return detail::unchecked_reference(data(), shape(), strides(), ndim()); - } - - /// Return a new view with all of the dimensions of length 1 removed - array squeeze() { - auto& api = detail::npy_api::get(); - return reinterpret_steal(api.PyArray_Squeeze_(m_ptr)); - } - - /// Resize array to given shape - /// If refcheck is true and more that one reference exist to this array - /// then resize will succeed only if it makes a reshape, i.e. original size doesn't change - void resize(ShapeContainer new_shape, bool refcheck = true) { - detail::npy_api::PyArray_Dims d = { - new_shape->data(), int(new_shape->size()) - }; - // try to resize, set ordering param to -1 cause it's not used anyway - object new_array = reinterpret_steal( - detail::npy_api::get().PyArray_Resize_(m_ptr, &d, int(refcheck), -1) - ); - if (!new_array) throw error_already_set(); - if (isinstance(new_array)) { *this = std::move(new_array); } - } - - /// Ensure that the argument is a NumPy array - /// In case of an error, nullptr is returned and the Python error is cleared. - static array ensure(handle h, int ExtraFlags = 0) { - auto result = reinterpret_steal(raw_array(h.ptr(), ExtraFlags)); - if (!result) - PyErr_Clear(); - return result; - } - -protected: - template friend struct detail::npy_format_descriptor; - - void fail_dim_check(ssize_t dim, const std::string& msg) const { - throw index_error(msg + ": " + std::to_string(dim) + - " (ndim = " + std::to_string(ndim()) + ")"); - } - - template ssize_t byte_offset(Ix... index) const { - check_dimensions(index...); - return detail::byte_offset_unsafe(strides(), ssize_t(index)...); - } - - void check_writeable() const { - if (!writeable()) - throw std::domain_error("array is not writeable"); - } - - // Default, C-style strides - static std::vector c_strides(const std::vector &shape, ssize_t itemsize) { - auto ndim = shape.size(); - std::vector strides(ndim, itemsize); - if (ndim > 0) - for (size_t i = ndim - 1; i > 0; --i) - strides[i - 1] = strides[i] * shape[i]; - return strides; - } - - // F-style strides; default when constructing an array_t with `ExtraFlags & f_style` - static std::vector f_strides(const std::vector &shape, ssize_t itemsize) { - auto ndim = shape.size(); - std::vector strides(ndim, itemsize); - for (size_t i = 1; i < ndim; ++i) - strides[i] = strides[i - 1] * shape[i - 1]; - return strides; - } - - template void check_dimensions(Ix... index) const { - check_dimensions_impl(ssize_t(0), shape(), ssize_t(index)...); - } - - void check_dimensions_impl(ssize_t, const ssize_t*) const { } - - template void check_dimensions_impl(ssize_t axis, const ssize_t* shape, ssize_t i, Ix... index) const { - if (i >= *shape) { - throw index_error(std::string("index ") + std::to_string(i) + - " is out of bounds for axis " + std::to_string(axis) + - " with size " + std::to_string(*shape)); - } - check_dimensions_impl(axis + 1, shape + 1, index...); - } - - /// Create array from any object -- always returns a new reference - static PyObject *raw_array(PyObject *ptr, int ExtraFlags = 0) { - if (ptr == nullptr) { - PyErr_SetString(PyExc_ValueError, "cannot create a pybind11::array from a nullptr"); - return nullptr; - } - return detail::npy_api::get().PyArray_FromAny_( - ptr, nullptr, 0, 0, detail::npy_api::NPY_ARRAY_ENSUREARRAY_ | ExtraFlags, nullptr); - } -}; - -template class array_t : public array { -private: - struct private_ctor {}; - // Delegating constructor needed when both moving and accessing in the same constructor - array_t(private_ctor, ShapeContainer &&shape, StridesContainer &&strides, const T *ptr, handle base) - : array(std::move(shape), std::move(strides), ptr, base) {} -public: - static_assert(!detail::array_info::is_array, "Array types cannot be used with array_t"); - - using value_type = T; - - array_t() : array(0, static_cast(nullptr)) {} - array_t(handle h, borrowed_t) : array(h, borrowed_t{}) { } - array_t(handle h, stolen_t) : array(h, stolen_t{}) { } - - PYBIND11_DEPRECATED("Use array_t::ensure() instead") - array_t(handle h, bool is_borrowed) : array(raw_array_t(h.ptr()), stolen_t{}) { - if (!m_ptr) PyErr_Clear(); - if (!is_borrowed) Py_XDECREF(h.ptr()); - } - - array_t(const object &o) : array(raw_array_t(o.ptr()), stolen_t{}) { - if (!m_ptr) throw error_already_set(); - } - - explicit array_t(const buffer_info& info) : array(info) { } - - array_t(ShapeContainer shape, StridesContainer strides, const T *ptr = nullptr, handle base = handle()) - : array(std::move(shape), std::move(strides), ptr, base) { } - - explicit array_t(ShapeContainer shape, const T *ptr = nullptr, handle base = handle()) - : array_t(private_ctor{}, std::move(shape), - ExtraFlags & f_style ? f_strides(*shape, itemsize()) : c_strides(*shape, itemsize()), - ptr, base) { } - - explicit array_t(size_t count, const T *ptr = nullptr, handle base = handle()) - : array({count}, {}, ptr, base) { } - - constexpr ssize_t itemsize() const { - return sizeof(T); - } - - template ssize_t index_at(Ix... index) const { - return offset_at(index...) / itemsize(); - } - - template const T* data(Ix... index) const { - return static_cast(array::data(index...)); - } - - template T* mutable_data(Ix... index) { - return static_cast(array::mutable_data(index...)); - } - - // Reference to element at a given index - template const T& at(Ix... index) const { - if (sizeof...(index) != ndim()) - fail_dim_check(sizeof...(index), "index dimension mismatch"); - return *(static_cast(array::data()) + byte_offset(ssize_t(index)...) / itemsize()); - } - - // Mutable reference to element at a given index - template T& mutable_at(Ix... index) { - if (sizeof...(index) != ndim()) - fail_dim_check(sizeof...(index), "index dimension mismatch"); - return *(static_cast(array::mutable_data()) + byte_offset(ssize_t(index)...) / itemsize()); - } - - /** - * Returns a proxy object that provides access to the array's data without bounds or - * dimensionality checking. Will throw if the array is missing the `writeable` flag. Use with - * care: the array must not be destroyed or reshaped for the duration of the returned object, - * and the caller must take care not to access invalid dimensions or dimension indices. - */ - template detail::unchecked_mutable_reference mutable_unchecked() & { - return array::mutable_unchecked(); - } - - /** - * Returns a proxy object that provides const access to the array's data without bounds or - * dimensionality checking. Unlike `unchecked()`, this does not require that the underlying - * array have the `writable` flag. Use with care: the array must not be destroyed or reshaped - * for the duration of the returned object, and the caller must take care not to access invalid - * dimensions or dimension indices. - */ - template detail::unchecked_reference unchecked() const & { - return array::unchecked(); - } - - /// Ensure that the argument is a NumPy array of the correct dtype (and if not, try to convert - /// it). In case of an error, nullptr is returned and the Python error is cleared. - static array_t ensure(handle h) { - auto result = reinterpret_steal(raw_array_t(h.ptr())); - if (!result) - PyErr_Clear(); - return result; - } - - static bool check_(handle h) { - const auto &api = detail::npy_api::get(); - return api.PyArray_Check_(h.ptr()) - && api.PyArray_EquivTypes_(detail::array_proxy(h.ptr())->descr, dtype::of().ptr()); - } - -protected: - /// Create array from any object -- always returns a new reference - static PyObject *raw_array_t(PyObject *ptr) { - if (ptr == nullptr) { - PyErr_SetString(PyExc_ValueError, "cannot create a pybind11::array_t from a nullptr"); - return nullptr; - } - return detail::npy_api::get().PyArray_FromAny_( - ptr, dtype::of().release().ptr(), 0, 0, - detail::npy_api::NPY_ARRAY_ENSUREARRAY_ | ExtraFlags, nullptr); - } -}; - -template -struct format_descriptor::value>> { - static std::string format() { - return detail::npy_format_descriptor::type>::format(); - } -}; - -template struct format_descriptor { - static std::string format() { return std::to_string(N) + "s"; } -}; -template struct format_descriptor> { - static std::string format() { return std::to_string(N) + "s"; } -}; - -template -struct format_descriptor::value>> { - static std::string format() { - return format_descriptor< - typename std::remove_cv::type>::type>::format(); - } -}; - -template -struct format_descriptor::is_array>> { - static std::string format() { - using namespace detail; - static constexpr auto extents = _("(") + array_info::extents + _(")"); - return extents.text + format_descriptor>::format(); - } -}; - -NAMESPACE_BEGIN(detail) -template -struct pyobject_caster> { - using type = array_t; - - bool load(handle src, bool convert) { - if (!convert && !type::check_(src)) - return false; - value = type::ensure(src); - return static_cast(value); - } - - static handle cast(const handle &src, return_value_policy /* policy */, handle /* parent */) { - return src.inc_ref(); - } - PYBIND11_TYPE_CASTER(type, handle_type_name::name); -}; - -template -struct compare_buffer_info::value>> { - static bool compare(const buffer_info& b) { - return npy_api::get().PyArray_EquivTypes_(dtype::of().ptr(), dtype(b).ptr()); - } -}; - -template -struct npy_format_descriptor_name; - -template -struct npy_format_descriptor_name::value>> { - static constexpr auto name = _::value>( - _("bool"), _::value>("int", "uint") + _() - ); -}; - -template -struct npy_format_descriptor_name::value>> { - static constexpr auto name = _::value || std::is_same::value>( - _("float") + _(), _("longdouble") - ); -}; - -template -struct npy_format_descriptor_name::value>> { - static constexpr auto name = _::value - || std::is_same::value>( - _("complex") + _(), _("longcomplex") - ); -}; - -template -struct npy_format_descriptor::value>> - : npy_format_descriptor_name { -private: - // NB: the order here must match the one in common.h - constexpr static const int values[15] = { - npy_api::NPY_BOOL_, - npy_api::NPY_BYTE_, npy_api::NPY_UBYTE_, npy_api::NPY_SHORT_, npy_api::NPY_USHORT_, - npy_api::NPY_INT_, npy_api::NPY_UINT_, npy_api::NPY_LONGLONG_, npy_api::NPY_ULONGLONG_, - npy_api::NPY_FLOAT_, npy_api::NPY_DOUBLE_, npy_api::NPY_LONGDOUBLE_, - npy_api::NPY_CFLOAT_, npy_api::NPY_CDOUBLE_, npy_api::NPY_CLONGDOUBLE_ - }; - -public: - static constexpr int value = values[detail::is_fmt_numeric::index]; - - static pybind11::dtype dtype() { - if (auto ptr = npy_api::get().PyArray_DescrFromType_(value)) - return reinterpret_borrow(ptr); - pybind11_fail("Unsupported buffer format!"); - } -}; - -#define PYBIND11_DECL_CHAR_FMT \ - static constexpr auto name = _("S") + _(); \ - static pybind11::dtype dtype() { return pybind11::dtype(std::string("S") + std::to_string(N)); } -template struct npy_format_descriptor { PYBIND11_DECL_CHAR_FMT }; -template struct npy_format_descriptor> { PYBIND11_DECL_CHAR_FMT }; -#undef PYBIND11_DECL_CHAR_FMT - -template struct npy_format_descriptor::is_array>> { -private: - using base_descr = npy_format_descriptor::type>; -public: - static_assert(!array_info::is_empty, "Zero-sized arrays are not supported"); - - static constexpr auto name = _("(") + array_info::extents + _(")") + base_descr::name; - static pybind11::dtype dtype() { - list shape; - array_info::append_extents(shape); - return pybind11::dtype::from_args(pybind11::make_tuple(base_descr::dtype(), shape)); - } -}; - -template struct npy_format_descriptor::value>> { -private: - using base_descr = npy_format_descriptor::type>; -public: - static constexpr auto name = base_descr::name; - static pybind11::dtype dtype() { return base_descr::dtype(); } -}; - -struct field_descriptor { - const char *name; - ssize_t offset; - ssize_t size; - std::string format; - dtype descr; -}; - -inline PYBIND11_NOINLINE void register_structured_dtype( - any_container fields, - const std::type_info& tinfo, ssize_t itemsize, - bool (*direct_converter)(PyObject *, void *&)) { - - auto& numpy_internals = get_numpy_internals(); - if (numpy_internals.get_type_info(tinfo, false)) - pybind11_fail("NumPy: dtype is already registered"); - - list names, formats, offsets; - for (auto field : *fields) { - if (!field.descr) - pybind11_fail(std::string("NumPy: unsupported field dtype: `") + - field.name + "` @ " + tinfo.name()); - names.append(PYBIND11_STR_TYPE(field.name)); - formats.append(field.descr); - offsets.append(pybind11::int_(field.offset)); - } - auto dtype_ptr = pybind11::dtype(names, formats, offsets, itemsize).release().ptr(); - - // There is an existing bug in NumPy (as of v1.11): trailing bytes are - // not encoded explicitly into the format string. This will supposedly - // get fixed in v1.12; for further details, see these: - // - https://github.com/numpy/numpy/issues/7797 - // - https://github.com/numpy/numpy/pull/7798 - // Because of this, we won't use numpy's logic to generate buffer format - // strings and will just do it ourselves. - std::vector ordered_fields(std::move(fields)); - std::sort(ordered_fields.begin(), ordered_fields.end(), - [](const field_descriptor &a, const field_descriptor &b) { return a.offset < b.offset; }); - ssize_t offset = 0; - std::ostringstream oss; - // mark the structure as unaligned with '^', because numpy and C++ don't - // always agree about alignment (particularly for complex), and we're - // explicitly listing all our padding. This depends on none of the fields - // overriding the endianness. Putting the ^ in front of individual fields - // isn't guaranteed to work due to https://github.com/numpy/numpy/issues/9049 - oss << "^T{"; - for (auto& field : ordered_fields) { - if (field.offset > offset) - oss << (field.offset - offset) << 'x'; - oss << field.format << ':' << field.name << ':'; - offset = field.offset + field.size; - } - if (itemsize > offset) - oss << (itemsize - offset) << 'x'; - oss << '}'; - auto format_str = oss.str(); - - // Sanity check: verify that NumPy properly parses our buffer format string - auto& api = npy_api::get(); - auto arr = array(buffer_info(nullptr, itemsize, format_str, 1)); - if (!api.PyArray_EquivTypes_(dtype_ptr, arr.dtype().ptr())) - pybind11_fail("NumPy: invalid buffer descriptor!"); - - auto tindex = std::type_index(tinfo); - numpy_internals.registered_dtypes[tindex] = { dtype_ptr, format_str }; - get_internals().direct_conversions[tindex].push_back(direct_converter); -} - -template struct npy_format_descriptor { - static_assert(is_pod_struct::value, "Attempt to use a non-POD or unimplemented POD type as a numpy dtype"); - - static constexpr auto name = make_caster::name; - - static pybind11::dtype dtype() { - return reinterpret_borrow(dtype_ptr()); - } - - static std::string format() { - static auto format_str = get_numpy_internals().get_type_info(true)->format_str; - return format_str; - } - - static void register_dtype(any_container fields) { - register_structured_dtype(std::move(fields), typeid(typename std::remove_cv::type), - sizeof(T), &direct_converter); - } - -private: - static PyObject* dtype_ptr() { - static PyObject* ptr = get_numpy_internals().get_type_info(true)->dtype_ptr; - return ptr; - } - - static bool direct_converter(PyObject *obj, void*& value) { - auto& api = npy_api::get(); - if (!PyObject_TypeCheck(obj, api.PyVoidArrType_Type_)) - return false; - if (auto descr = reinterpret_steal(api.PyArray_DescrFromScalar_(obj))) { - if (api.PyArray_EquivTypes_(dtype_ptr(), descr.ptr())) { - value = ((PyVoidScalarObject_Proxy *) obj)->obval; - return true; - } - } - return false; - } -}; - -#ifdef __CLION_IDE__ // replace heavy macro with dummy code for the IDE (doesn't affect code) -# define PYBIND11_NUMPY_DTYPE(Type, ...) ((void)0) -# define PYBIND11_NUMPY_DTYPE_EX(Type, ...) ((void)0) -#else - -#define PYBIND11_FIELD_DESCRIPTOR_EX(T, Field, Name) \ - ::pybind11::detail::field_descriptor { \ - Name, offsetof(T, Field), sizeof(decltype(std::declval().Field)), \ - ::pybind11::format_descriptor().Field)>::format(), \ - ::pybind11::detail::npy_format_descriptor().Field)>::dtype() \ - } - -// Extract name, offset and format descriptor for a struct field -#define PYBIND11_FIELD_DESCRIPTOR(T, Field) PYBIND11_FIELD_DESCRIPTOR_EX(T, Field, #Field) - -// The main idea of this macro is borrowed from https://github.com/swansontec/map-macro -// (C) William Swanson, Paul Fultz -#define PYBIND11_EVAL0(...) __VA_ARGS__ -#define PYBIND11_EVAL1(...) PYBIND11_EVAL0 (PYBIND11_EVAL0 (PYBIND11_EVAL0 (__VA_ARGS__))) -#define PYBIND11_EVAL2(...) PYBIND11_EVAL1 (PYBIND11_EVAL1 (PYBIND11_EVAL1 (__VA_ARGS__))) -#define PYBIND11_EVAL3(...) PYBIND11_EVAL2 (PYBIND11_EVAL2 (PYBIND11_EVAL2 (__VA_ARGS__))) -#define PYBIND11_EVAL4(...) PYBIND11_EVAL3 (PYBIND11_EVAL3 (PYBIND11_EVAL3 (__VA_ARGS__))) -#define PYBIND11_EVAL(...) PYBIND11_EVAL4 (PYBIND11_EVAL4 (PYBIND11_EVAL4 (__VA_ARGS__))) -#define PYBIND11_MAP_END(...) -#define PYBIND11_MAP_OUT -#define PYBIND11_MAP_COMMA , -#define PYBIND11_MAP_GET_END() 0, PYBIND11_MAP_END -#define PYBIND11_MAP_NEXT0(test, next, ...) next PYBIND11_MAP_OUT -#define PYBIND11_MAP_NEXT1(test, next) PYBIND11_MAP_NEXT0 (test, next, 0) -#define PYBIND11_MAP_NEXT(test, next) PYBIND11_MAP_NEXT1 (PYBIND11_MAP_GET_END test, next) -#ifdef _MSC_VER // MSVC is not as eager to expand macros, hence this workaround -#define PYBIND11_MAP_LIST_NEXT1(test, next) \ - PYBIND11_EVAL0 (PYBIND11_MAP_NEXT0 (test, PYBIND11_MAP_COMMA next, 0)) -#else -#define PYBIND11_MAP_LIST_NEXT1(test, next) \ - PYBIND11_MAP_NEXT0 (test, PYBIND11_MAP_COMMA next, 0) -#endif -#define PYBIND11_MAP_LIST_NEXT(test, next) \ - PYBIND11_MAP_LIST_NEXT1 (PYBIND11_MAP_GET_END test, next) -#define PYBIND11_MAP_LIST0(f, t, x, peek, ...) \ - f(t, x) PYBIND11_MAP_LIST_NEXT (peek, PYBIND11_MAP_LIST1) (f, t, peek, __VA_ARGS__) -#define PYBIND11_MAP_LIST1(f, t, x, peek, ...) \ - f(t, x) PYBIND11_MAP_LIST_NEXT (peek, PYBIND11_MAP_LIST0) (f, t, peek, __VA_ARGS__) -// PYBIND11_MAP_LIST(f, t, a1, a2, ...) expands to f(t, a1), f(t, a2), ... -#define PYBIND11_MAP_LIST(f, t, ...) \ - PYBIND11_EVAL (PYBIND11_MAP_LIST1 (f, t, __VA_ARGS__, (), 0)) - -#define PYBIND11_NUMPY_DTYPE(Type, ...) \ - ::pybind11::detail::npy_format_descriptor::register_dtype \ - (::std::vector<::pybind11::detail::field_descriptor> \ - {PYBIND11_MAP_LIST (PYBIND11_FIELD_DESCRIPTOR, Type, __VA_ARGS__)}) - -#ifdef _MSC_VER -#define PYBIND11_MAP2_LIST_NEXT1(test, next) \ - PYBIND11_EVAL0 (PYBIND11_MAP_NEXT0 (test, PYBIND11_MAP_COMMA next, 0)) -#else -#define PYBIND11_MAP2_LIST_NEXT1(test, next) \ - PYBIND11_MAP_NEXT0 (test, PYBIND11_MAP_COMMA next, 0) -#endif -#define PYBIND11_MAP2_LIST_NEXT(test, next) \ - PYBIND11_MAP2_LIST_NEXT1 (PYBIND11_MAP_GET_END test, next) -#define PYBIND11_MAP2_LIST0(f, t, x1, x2, peek, ...) \ - f(t, x1, x2) PYBIND11_MAP2_LIST_NEXT (peek, PYBIND11_MAP2_LIST1) (f, t, peek, __VA_ARGS__) -#define PYBIND11_MAP2_LIST1(f, t, x1, x2, peek, ...) \ - f(t, x1, x2) PYBIND11_MAP2_LIST_NEXT (peek, PYBIND11_MAP2_LIST0) (f, t, peek, __VA_ARGS__) -// PYBIND11_MAP2_LIST(f, t, a1, a2, ...) expands to f(t, a1, a2), f(t, a3, a4), ... -#define PYBIND11_MAP2_LIST(f, t, ...) \ - PYBIND11_EVAL (PYBIND11_MAP2_LIST1 (f, t, __VA_ARGS__, (), 0)) - -#define PYBIND11_NUMPY_DTYPE_EX(Type, ...) \ - ::pybind11::detail::npy_format_descriptor::register_dtype \ - (::std::vector<::pybind11::detail::field_descriptor> \ - {PYBIND11_MAP2_LIST (PYBIND11_FIELD_DESCRIPTOR_EX, Type, __VA_ARGS__)}) - -#endif // __CLION_IDE__ - -template -using array_iterator = typename std::add_pointer::type; - -template -array_iterator array_begin(const buffer_info& buffer) { - return array_iterator(reinterpret_cast(buffer.ptr)); -} - -template -array_iterator array_end(const buffer_info& buffer) { - return array_iterator(reinterpret_cast(buffer.ptr) + buffer.size); -} - -class common_iterator { -public: - using container_type = std::vector; - using value_type = container_type::value_type; - using size_type = container_type::size_type; - - common_iterator() : p_ptr(0), m_strides() {} - - common_iterator(void* ptr, const container_type& strides, const container_type& shape) - : p_ptr(reinterpret_cast(ptr)), m_strides(strides.size()) { - m_strides.back() = static_cast(strides.back()); - for (size_type i = m_strides.size() - 1; i != 0; --i) { - size_type j = i - 1; - value_type s = static_cast(shape[i]); - m_strides[j] = strides[j] + m_strides[i] - strides[i] * s; - } - } - - void increment(size_type dim) { - p_ptr += m_strides[dim]; - } - - void* data() const { - return p_ptr; - } - -private: - char* p_ptr; - container_type m_strides; -}; - -template class multi_array_iterator { -public: - using container_type = std::vector; - - multi_array_iterator(const std::array &buffers, - const container_type &shape) - : m_shape(shape.size()), m_index(shape.size(), 0), - m_common_iterator() { - - // Manual copy to avoid conversion warning if using std::copy - for (size_t i = 0; i < shape.size(); ++i) - m_shape[i] = shape[i]; - - container_type strides(shape.size()); - for (size_t i = 0; i < N; ++i) - init_common_iterator(buffers[i], shape, m_common_iterator[i], strides); - } - - multi_array_iterator& operator++() { - for (size_t j = m_index.size(); j != 0; --j) { - size_t i = j - 1; - if (++m_index[i] != m_shape[i]) { - increment_common_iterator(i); - break; - } else { - m_index[i] = 0; - } - } - return *this; - } - - template T* data() const { - return reinterpret_cast(m_common_iterator[K].data()); - } - -private: - - using common_iter = common_iterator; - - void init_common_iterator(const buffer_info &buffer, - const container_type &shape, - common_iter &iterator, - container_type &strides) { - auto buffer_shape_iter = buffer.shape.rbegin(); - auto buffer_strides_iter = buffer.strides.rbegin(); - auto shape_iter = shape.rbegin(); - auto strides_iter = strides.rbegin(); - - while (buffer_shape_iter != buffer.shape.rend()) { - if (*shape_iter == *buffer_shape_iter) - *strides_iter = *buffer_strides_iter; - else - *strides_iter = 0; - - ++buffer_shape_iter; - ++buffer_strides_iter; - ++shape_iter; - ++strides_iter; - } - - std::fill(strides_iter, strides.rend(), 0); - iterator = common_iter(buffer.ptr, strides, shape); - } - - void increment_common_iterator(size_t dim) { - for (auto &iter : m_common_iterator) - iter.increment(dim); - } - - container_type m_shape; - container_type m_index; - std::array m_common_iterator; -}; - -enum class broadcast_trivial { non_trivial, c_trivial, f_trivial }; - -// Populates the shape and number of dimensions for the set of buffers. Returns a broadcast_trivial -// enum value indicating whether the broadcast is "trivial"--that is, has each buffer being either a -// singleton or a full-size, C-contiguous (`c_trivial`) or Fortran-contiguous (`f_trivial`) storage -// buffer; returns `non_trivial` otherwise. -template -broadcast_trivial broadcast(const std::array &buffers, ssize_t &ndim, std::vector &shape) { - ndim = std::accumulate(buffers.begin(), buffers.end(), ssize_t(0), [](ssize_t res, const buffer_info &buf) { - return std::max(res, buf.ndim); - }); - - shape.clear(); - shape.resize((size_t) ndim, 1); - - // Figure out the output size, and make sure all input arrays conform (i.e. are either size 1 or - // the full size). - for (size_t i = 0; i < N; ++i) { - auto res_iter = shape.rbegin(); - auto end = buffers[i].shape.rend(); - for (auto shape_iter = buffers[i].shape.rbegin(); shape_iter != end; ++shape_iter, ++res_iter) { - const auto &dim_size_in = *shape_iter; - auto &dim_size_out = *res_iter; - - // Each input dimension can either be 1 or `n`, but `n` values must match across buffers - if (dim_size_out == 1) - dim_size_out = dim_size_in; - else if (dim_size_in != 1 && dim_size_in != dim_size_out) - pybind11_fail("pybind11::vectorize: incompatible size/dimension of inputs!"); - } - } - - bool trivial_broadcast_c = true; - bool trivial_broadcast_f = true; - for (size_t i = 0; i < N && (trivial_broadcast_c || trivial_broadcast_f); ++i) { - if (buffers[i].size == 1) - continue; - - // Require the same number of dimensions: - if (buffers[i].ndim != ndim) - return broadcast_trivial::non_trivial; - - // Require all dimensions be full-size: - if (!std::equal(buffers[i].shape.cbegin(), buffers[i].shape.cend(), shape.cbegin())) - return broadcast_trivial::non_trivial; - - // Check for C contiguity (but only if previous inputs were also C contiguous) - if (trivial_broadcast_c) { - ssize_t expect_stride = buffers[i].itemsize; - auto end = buffers[i].shape.crend(); - for (auto shape_iter = buffers[i].shape.crbegin(), stride_iter = buffers[i].strides.crbegin(); - trivial_broadcast_c && shape_iter != end; ++shape_iter, ++stride_iter) { - if (expect_stride == *stride_iter) - expect_stride *= *shape_iter; - else - trivial_broadcast_c = false; - } - } - - // Check for Fortran contiguity (if previous inputs were also F contiguous) - if (trivial_broadcast_f) { - ssize_t expect_stride = buffers[i].itemsize; - auto end = buffers[i].shape.cend(); - for (auto shape_iter = buffers[i].shape.cbegin(), stride_iter = buffers[i].strides.cbegin(); - trivial_broadcast_f && shape_iter != end; ++shape_iter, ++stride_iter) { - if (expect_stride == *stride_iter) - expect_stride *= *shape_iter; - else - trivial_broadcast_f = false; - } - } - } - - return - trivial_broadcast_c ? broadcast_trivial::c_trivial : - trivial_broadcast_f ? broadcast_trivial::f_trivial : - broadcast_trivial::non_trivial; -} - -template -struct vectorize_arg { - static_assert(!std::is_rvalue_reference::value, "Functions with rvalue reference arguments cannot be vectorized"); - // The wrapped function gets called with this type: - using call_type = remove_reference_t; - // Is this a vectorized argument? - static constexpr bool vectorize = - satisfies_any_of::value && - satisfies_none_of::value && - (!std::is_reference::value || - (std::is_lvalue_reference::value && std::is_const::value)); - // Accept this type: an array for vectorized types, otherwise the type as-is: - using type = conditional_t, array::forcecast>, T>; -}; - -template -struct vectorize_helper { -private: - static constexpr size_t N = sizeof...(Args); - static constexpr size_t NVectorized = constexpr_sum(vectorize_arg::vectorize...); - static_assert(NVectorized >= 1, - "pybind11::vectorize(...) requires a function with at least one vectorizable argument"); - -public: - template - explicit vectorize_helper(T &&f) : f(std::forward(f)) { } - - object operator()(typename vectorize_arg::type... args) { - return run(args..., - make_index_sequence(), - select_indices::vectorize...>(), - make_index_sequence()); - } - -private: - remove_reference_t f; - - // Internal compiler error in MSVC 19.16.27025.1 (Visual Studio 2017 15.9.4), when compiling with "/permissive-" flag - // when arg_call_types is manually inlined. - using arg_call_types = std::tuple::call_type...>; - template using param_n_t = typename std::tuple_element::type; - - // Runs a vectorized function given arguments tuple and three index sequences: - // - Index is the full set of 0 ... (N-1) argument indices; - // - VIndex is the subset of argument indices with vectorized parameters, letting us access - // vectorized arguments (anything not in this sequence is passed through) - // - BIndex is a incremental sequence (beginning at 0) of the same size as VIndex, so that - // we can store vectorized buffer_infos in an array (argument VIndex has its buffer at - // index BIndex in the array). - template object run( - typename vectorize_arg::type &...args, - index_sequence i_seq, index_sequence vi_seq, index_sequence bi_seq) { - - // Pointers to values the function was called with; the vectorized ones set here will start - // out as array_t pointers, but they will be changed them to T pointers before we make - // call the wrapped function. Non-vectorized pointers are left as-is. - std::array params{{ &args... }}; - - // The array of `buffer_info`s of vectorized arguments: - std::array buffers{{ reinterpret_cast(params[VIndex])->request()... }}; - - /* Determine dimensions parameters of output array */ - ssize_t nd = 0; - std::vector shape(0); - auto trivial = broadcast(buffers, nd, shape); - size_t ndim = (size_t) nd; - - size_t size = std::accumulate(shape.begin(), shape.end(), (size_t) 1, std::multiplies()); - - // If all arguments are 0-dimension arrays (i.e. single values) return a plain value (i.e. - // not wrapped in an array). - if (size == 1 && ndim == 0) { - PYBIND11_EXPAND_SIDE_EFFECTS(params[VIndex] = buffers[BIndex].ptr); - return cast(f(*reinterpret_cast *>(params[Index])...)); - } - - array_t result; - if (trivial == broadcast_trivial::f_trivial) result = array_t(shape); - else result = array_t(shape); - - if (size == 0) return result; - - /* Call the function */ - if (trivial == broadcast_trivial::non_trivial) - apply_broadcast(buffers, params, result, i_seq, vi_seq, bi_seq); - else - apply_trivial(buffers, params, result.mutable_data(), size, i_seq, vi_seq, bi_seq); - - return result; - } - - template - void apply_trivial(std::array &buffers, - std::array ¶ms, - Return *out, - size_t size, - index_sequence, index_sequence, index_sequence) { - - // Initialize an array of mutable byte references and sizes with references set to the - // appropriate pointer in `params`; as we iterate, we'll increment each pointer by its size - // (except for singletons, which get an increment of 0). - std::array, NVectorized> vecparams{{ - std::pair( - reinterpret_cast(params[VIndex] = buffers[BIndex].ptr), - buffers[BIndex].size == 1 ? 0 : sizeof(param_n_t) - )... - }}; - - for (size_t i = 0; i < size; ++i) { - out[i] = f(*reinterpret_cast *>(params[Index])...); - for (auto &x : vecparams) x.first += x.second; - } - } - - template - void apply_broadcast(std::array &buffers, - std::array ¶ms, - array_t &output_array, - index_sequence, index_sequence, index_sequence) { - - buffer_info output = output_array.request(); - multi_array_iterator input_iter(buffers, output.shape); - - for (array_iterator iter = array_begin(output), end = array_end(output); - iter != end; - ++iter, ++input_iter) { - PYBIND11_EXPAND_SIDE_EFFECTS(( - params[VIndex] = input_iter.template data() - )); - *iter = f(*reinterpret_cast *>(std::get(params))...); - } - } -}; - -template -vectorize_helper -vectorize_extractor(const Func &f, Return (*) (Args ...)) { - return detail::vectorize_helper(f); -} - -template struct handle_type_name> { - static constexpr auto name = _("numpy.ndarray[") + npy_format_descriptor::name + _("]"); -}; - -NAMESPACE_END(detail) - -// Vanilla pointer vectorizer: -template -detail::vectorize_helper -vectorize(Return (*f) (Args ...)) { - return detail::vectorize_helper(f); -} - -// lambda vectorizer: -template ::value, int> = 0> -auto vectorize(Func &&f) -> decltype( - detail::vectorize_extractor(std::forward(f), (detail::function_signature_t *) nullptr)) { - return detail::vectorize_extractor(std::forward(f), (detail::function_signature_t *) nullptr); -} - -// Vectorize a class method (non-const): -template ())), Return, Class *, Args...>> -Helper vectorize(Return (Class::*f)(Args...)) { - return Helper(std::mem_fn(f)); -} - -// Vectorize a class method (const): -template ())), Return, const Class *, Args...>> -Helper vectorize(Return (Class::*f)(Args...) const) { - return Helper(std::mem_fn(f)); -} - -NAMESPACE_END(PYBIND11_NAMESPACE) - -#if defined(_MSC_VER) -#pragma warning(pop) -#endif diff --git a/ptocr/postprocess/dbprocess/include/pybind11/operators.h b/ptocr/postprocess/dbprocess/include/pybind11/operators.h deleted file mode 100644 index b3dd62c..0000000 --- a/ptocr/postprocess/dbprocess/include/pybind11/operators.h +++ /dev/null @@ -1,168 +0,0 @@ -/* - pybind11/operator.h: Metatemplates for operator overloading - - Copyright (c) 2016 Wenzel Jakob - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#include "pybind11.h" - -#if defined(__clang__) && !defined(__INTEL_COMPILER) -# pragma clang diagnostic ignored "-Wunsequenced" // multiple unsequenced modifications to 'self' (when using def(py::self OP Type())) -#elif defined(_MSC_VER) -# pragma warning(push) -# pragma warning(disable: 4127) // warning C4127: Conditional expression is constant -#endif - -NAMESPACE_BEGIN(PYBIND11_NAMESPACE) -NAMESPACE_BEGIN(detail) - -/// Enumeration with all supported operator types -enum op_id : int { - op_add, op_sub, op_mul, op_div, op_mod, op_divmod, op_pow, op_lshift, - op_rshift, op_and, op_xor, op_or, op_neg, op_pos, op_abs, op_invert, - op_int, op_long, op_float, op_str, op_cmp, op_gt, op_ge, op_lt, op_le, - op_eq, op_ne, op_iadd, op_isub, op_imul, op_idiv, op_imod, op_ilshift, - op_irshift, op_iand, op_ixor, op_ior, op_complex, op_bool, op_nonzero, - op_repr, op_truediv, op_itruediv, op_hash -}; - -enum op_type : int { - op_l, /* base type on left */ - op_r, /* base type on right */ - op_u /* unary operator */ -}; - -struct self_t { }; -static const self_t self = self_t(); - -/// Type for an unused type slot -struct undefined_t { }; - -/// Don't warn about an unused variable -inline self_t __self() { return self; } - -/// base template of operator implementations -template struct op_impl { }; - -/// Operator implementation generator -template struct op_ { - template void execute(Class &cl, const Extra&... extra) const { - using Base = typename Class::type; - using L_type = conditional_t::value, Base, L>; - using R_type = conditional_t::value, Base, R>; - using op = op_impl; - cl.def(op::name(), &op::execute, is_operator(), extra...); - #if PY_MAJOR_VERSION < 3 - if (id == op_truediv || id == op_itruediv) - cl.def(id == op_itruediv ? "__idiv__" : ot == op_l ? "__div__" : "__rdiv__", - &op::execute, is_operator(), extra...); - #endif - } - template void execute_cast(Class &cl, const Extra&... extra) const { - using Base = typename Class::type; - using L_type = conditional_t::value, Base, L>; - using R_type = conditional_t::value, Base, R>; - using op = op_impl; - cl.def(op::name(), &op::execute_cast, is_operator(), extra...); - #if PY_MAJOR_VERSION < 3 - if (id == op_truediv || id == op_itruediv) - cl.def(id == op_itruediv ? "__idiv__" : ot == op_l ? "__div__" : "__rdiv__", - &op::execute, is_operator(), extra...); - #endif - } -}; - -#define PYBIND11_BINARY_OPERATOR(id, rid, op, expr) \ -template struct op_impl { \ - static char const* name() { return "__" #id "__"; } \ - static auto execute(const L &l, const R &r) -> decltype(expr) { return (expr); } \ - static B execute_cast(const L &l, const R &r) { return B(expr); } \ -}; \ -template struct op_impl { \ - static char const* name() { return "__" #rid "__"; } \ - static auto execute(const R &r, const L &l) -> decltype(expr) { return (expr); } \ - static B execute_cast(const R &r, const L &l) { return B(expr); } \ -}; \ -inline op_ op(const self_t &, const self_t &) { \ - return op_(); \ -} \ -template op_ op(const self_t &, const T &) { \ - return op_(); \ -} \ -template op_ op(const T &, const self_t &) { \ - return op_(); \ -} - -#define PYBIND11_INPLACE_OPERATOR(id, op, expr) \ -template struct op_impl { \ - static char const* name() { return "__" #id "__"; } \ - static auto execute(L &l, const R &r) -> decltype(expr) { return expr; } \ - static B execute_cast(L &l, const R &r) { return B(expr); } \ -}; \ -template op_ op(const self_t &, const T &) { \ - return op_(); \ -} - -#define PYBIND11_UNARY_OPERATOR(id, op, expr) \ -template struct op_impl { \ - static char const* name() { return "__" #id "__"; } \ - static auto execute(const L &l) -> decltype(expr) { return expr; } \ - static B execute_cast(const L &l) { return B(expr); } \ -}; \ -inline op_ op(const self_t &) { \ - return op_(); \ -} - -PYBIND11_BINARY_OPERATOR(sub, rsub, operator-, l - r) -PYBIND11_BINARY_OPERATOR(add, radd, operator+, l + r) -PYBIND11_BINARY_OPERATOR(mul, rmul, operator*, l * r) -PYBIND11_BINARY_OPERATOR(truediv, rtruediv, operator/, l / r) -PYBIND11_BINARY_OPERATOR(mod, rmod, operator%, l % r) -PYBIND11_BINARY_OPERATOR(lshift, rlshift, operator<<, l << r) -PYBIND11_BINARY_OPERATOR(rshift, rrshift, operator>>, l >> r) -PYBIND11_BINARY_OPERATOR(and, rand, operator&, l & r) -PYBIND11_BINARY_OPERATOR(xor, rxor, operator^, l ^ r) -PYBIND11_BINARY_OPERATOR(eq, eq, operator==, l == r) -PYBIND11_BINARY_OPERATOR(ne, ne, operator!=, l != r) -PYBIND11_BINARY_OPERATOR(or, ror, operator|, l | r) -PYBIND11_BINARY_OPERATOR(gt, lt, operator>, l > r) -PYBIND11_BINARY_OPERATOR(ge, le, operator>=, l >= r) -PYBIND11_BINARY_OPERATOR(lt, gt, operator<, l < r) -PYBIND11_BINARY_OPERATOR(le, ge, operator<=, l <= r) -//PYBIND11_BINARY_OPERATOR(pow, rpow, pow, std::pow(l, r)) -PYBIND11_INPLACE_OPERATOR(iadd, operator+=, l += r) -PYBIND11_INPLACE_OPERATOR(isub, operator-=, l -= r) -PYBIND11_INPLACE_OPERATOR(imul, operator*=, l *= r) -PYBIND11_INPLACE_OPERATOR(itruediv, operator/=, l /= r) -PYBIND11_INPLACE_OPERATOR(imod, operator%=, l %= r) -PYBIND11_INPLACE_OPERATOR(ilshift, operator<<=, l <<= r) -PYBIND11_INPLACE_OPERATOR(irshift, operator>>=, l >>= r) -PYBIND11_INPLACE_OPERATOR(iand, operator&=, l &= r) -PYBIND11_INPLACE_OPERATOR(ixor, operator^=, l ^= r) -PYBIND11_INPLACE_OPERATOR(ior, operator|=, l |= r) -PYBIND11_UNARY_OPERATOR(neg, operator-, -l) -PYBIND11_UNARY_OPERATOR(pos, operator+, +l) -PYBIND11_UNARY_OPERATOR(abs, abs, std::abs(l)) -PYBIND11_UNARY_OPERATOR(hash, hash, std::hash()(l)) -PYBIND11_UNARY_OPERATOR(invert, operator~, (~l)) -PYBIND11_UNARY_OPERATOR(bool, operator!, !!l) -PYBIND11_UNARY_OPERATOR(int, int_, (int) l) -PYBIND11_UNARY_OPERATOR(float, float_, (double) l) - -#undef PYBIND11_BINARY_OPERATOR -#undef PYBIND11_INPLACE_OPERATOR -#undef PYBIND11_UNARY_OPERATOR -NAMESPACE_END(detail) - -using detail::self; - -NAMESPACE_END(PYBIND11_NAMESPACE) - -#if defined(_MSC_VER) -# pragma warning(pop) -#endif diff --git a/ptocr/postprocess/dbprocess/include/pybind11/options.h b/ptocr/postprocess/dbprocess/include/pybind11/options.h deleted file mode 100644 index cc1e1f6..0000000 --- a/ptocr/postprocess/dbprocess/include/pybind11/options.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - pybind11/options.h: global settings that are configurable at runtime. - - Copyright (c) 2016 Wenzel Jakob - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#include "detail/common.h" - -NAMESPACE_BEGIN(PYBIND11_NAMESPACE) - -class options { -public: - - // Default RAII constructor, which leaves settings as they currently are. - options() : previous_state(global_state()) {} - - // Class is non-copyable. - options(const options&) = delete; - options& operator=(const options&) = delete; - - // Destructor, which restores settings that were in effect before. - ~options() { - global_state() = previous_state; - } - - // Setter methods (affect the global state): - - options& disable_user_defined_docstrings() & { global_state().show_user_defined_docstrings = false; return *this; } - - options& enable_user_defined_docstrings() & { global_state().show_user_defined_docstrings = true; return *this; } - - options& disable_function_signatures() & { global_state().show_function_signatures = false; return *this; } - - options& enable_function_signatures() & { global_state().show_function_signatures = true; return *this; } - - // Getter methods (return the global state): - - static bool show_user_defined_docstrings() { return global_state().show_user_defined_docstrings; } - - static bool show_function_signatures() { return global_state().show_function_signatures; } - - // This type is not meant to be allocated on the heap. - void* operator new(size_t) = delete; - -private: - - struct state { - bool show_user_defined_docstrings = true; //< Include user-supplied texts in docstrings. - bool show_function_signatures = true; //< Include auto-generated function signatures in docstrings. - }; - - static state &global_state() { - static state instance; - return instance; - } - - state previous_state; -}; - -NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/ptocr/postprocess/dbprocess/include/pybind11/pybind11.h b/ptocr/postprocess/dbprocess/include/pybind11/pybind11.h deleted file mode 100644 index 7fa0f0e..0000000 --- a/ptocr/postprocess/dbprocess/include/pybind11/pybind11.h +++ /dev/null @@ -1,2094 +0,0 @@ -/* - pybind11/pybind11.h: Main header file of the C++11 python - binding generator library - - Copyright (c) 2016 Wenzel Jakob - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#if defined(__INTEL_COMPILER) -# pragma warning push -# pragma warning disable 68 // integer conversion resulted in a change of sign -# pragma warning disable 186 // pointless comparison of unsigned integer with zero -# pragma warning disable 878 // incompatible exception specifications -# pragma warning disable 1334 // the "template" keyword used for syntactic disambiguation may only be used within a template -# pragma warning disable 1682 // implicit conversion of a 64-bit integral type to a smaller integral type (potential portability problem) -# pragma warning disable 1786 // function "strdup" was declared deprecated -# pragma warning disable 1875 // offsetof applied to non-POD (Plain Old Data) types is nonstandard -# pragma warning disable 2196 // warning #2196: routine is both "inline" and "noinline" -#elif defined(_MSC_VER) -# pragma warning(push) -# pragma warning(disable: 4100) // warning C4100: Unreferenced formal parameter -# pragma warning(disable: 4127) // warning C4127: Conditional expression is constant -# pragma warning(disable: 4512) // warning C4512: Assignment operator was implicitly defined as deleted -# pragma warning(disable: 4800) // warning C4800: 'int': forcing value to bool 'true' or 'false' (performance warning) -# pragma warning(disable: 4996) // warning C4996: The POSIX name for this item is deprecated. Instead, use the ISO C and C++ conformant name -# pragma warning(disable: 4702) // warning C4702: unreachable code -# pragma warning(disable: 4522) // warning C4522: multiple assignment operators specified -#elif defined(__GNUG__) && !defined(__clang__) -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wunused-but-set-parameter" -# pragma GCC diagnostic ignored "-Wunused-but-set-variable" -# pragma GCC diagnostic ignored "-Wmissing-field-initializers" -# pragma GCC diagnostic ignored "-Wstrict-aliasing" -# pragma GCC diagnostic ignored "-Wattributes" -# if __GNUC__ >= 7 -# pragma GCC diagnostic ignored "-Wnoexcept-type" -# endif -#endif - -#include "attr.h" -#include "options.h" -#include "detail/class.h" -#include "detail/init.h" - -NAMESPACE_BEGIN(PYBIND11_NAMESPACE) - -/// Wraps an arbitrary C++ function/method/lambda function/.. into a callable Python object -class cpp_function : public function { -public: - cpp_function() { } - cpp_function(std::nullptr_t) { } - - /// Construct a cpp_function from a vanilla function pointer - template - cpp_function(Return (*f)(Args...), const Extra&... extra) { - initialize(f, f, extra...); - } - - /// Construct a cpp_function from a lambda function (possibly with internal state) - template ::value>> - cpp_function(Func &&f, const Extra&... extra) { - initialize(std::forward(f), - (detail::function_signature_t *) nullptr, extra...); - } - - /// Construct a cpp_function from a class method (non-const) - template - cpp_function(Return (Class::*f)(Arg...), const Extra&... extra) { - initialize([f](Class *c, Arg... args) -> Return { return (c->*f)(args...); }, - (Return (*) (Class *, Arg...)) nullptr, extra...); - } - - /// Construct a cpp_function from a class method (const) - template - cpp_function(Return (Class::*f)(Arg...) const, const Extra&... extra) { - initialize([f](const Class *c, Arg... args) -> Return { return (c->*f)(args...); }, - (Return (*)(const Class *, Arg ...)) nullptr, extra...); - } - - /// Return the function name - object name() const { return attr("__name__"); } - -protected: - /// Space optimization: don't inline this frequently instantiated fragment - PYBIND11_NOINLINE detail::function_record *make_function_record() { - return new detail::function_record(); - } - - /// Special internal constructor for functors, lambda functions, etc. - template - void initialize(Func &&f, Return (*)(Args...), const Extra&... extra) { - using namespace detail; - struct capture { remove_reference_t f; }; - - /* Store the function including any extra state it might have (e.g. a lambda capture object) */ - auto rec = make_function_record(); - - /* Store the capture object directly in the function record if there is enough space */ - if (sizeof(capture) <= sizeof(rec->data)) { - /* Without these pragmas, GCC warns that there might not be - enough space to use the placement new operator. However, the - 'if' statement above ensures that this is the case. */ -#if defined(__GNUG__) && !defined(__clang__) && __GNUC__ >= 6 -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wplacement-new" -#endif - new ((capture *) &rec->data) capture { std::forward(f) }; -#if defined(__GNUG__) && !defined(__clang__) && __GNUC__ >= 6 -# pragma GCC diagnostic pop -#endif - if (!std::is_trivially_destructible::value) - rec->free_data = [](function_record *r) { ((capture *) &r->data)->~capture(); }; - } else { - rec->data[0] = new capture { std::forward(f) }; - rec->free_data = [](function_record *r) { delete ((capture *) r->data[0]); }; - } - - /* Type casters for the function arguments and return value */ - using cast_in = argument_loader; - using cast_out = make_caster< - conditional_t::value, void_type, Return> - >; - - static_assert(expected_num_args(sizeof...(Args), cast_in::has_args, cast_in::has_kwargs), - "The number of argument annotations does not match the number of function arguments"); - - /* Dispatch code which converts function arguments and performs the actual function call */ - rec->impl = [](function_call &call) -> handle { - cast_in args_converter; - - /* Try to cast the function arguments into the C++ domain */ - if (!args_converter.load_args(call)) - return PYBIND11_TRY_NEXT_OVERLOAD; - - /* Invoke call policy pre-call hook */ - process_attributes::precall(call); - - /* Get a pointer to the capture object */ - auto data = (sizeof(capture) <= sizeof(call.func.data) - ? &call.func.data : call.func.data[0]); - capture *cap = const_cast(reinterpret_cast(data)); - - /* Override policy for rvalues -- usually to enforce rvp::move on an rvalue */ - return_value_policy policy = return_value_policy_override::policy(call.func.policy); - - /* Function scope guard -- defaults to the compile-to-nothing `void_type` */ - using Guard = extract_guard_t; - - /* Perform the function call */ - handle result = cast_out::cast( - std::move(args_converter).template call(cap->f), policy, call.parent); - - /* Invoke call policy post-call hook */ - process_attributes::postcall(call, result); - - return result; - }; - - /* Process any user-provided function attributes */ - process_attributes::init(extra..., rec); - - /* Generate a readable signature describing the function's arguments and return value types */ - static constexpr auto signature = _("(") + cast_in::arg_names + _(") -> ") + cast_out::name; - PYBIND11_DESCR_CONSTEXPR auto types = decltype(signature)::types(); - - /* Register the function with Python from generic (non-templated) code */ - initialize_generic(rec, signature.text, types.data(), sizeof...(Args)); - - if (cast_in::has_args) rec->has_args = true; - if (cast_in::has_kwargs) rec->has_kwargs = true; - - /* Stash some additional information used by an important optimization in 'functional.h' */ - using FunctionType = Return (*)(Args...); - constexpr bool is_function_ptr = - std::is_convertible::value && - sizeof(capture) == sizeof(void *); - if (is_function_ptr) { - rec->is_stateless = true; - rec->data[1] = const_cast(reinterpret_cast(&typeid(FunctionType))); - } - } - - /// Register a function call with Python (generic non-templated code goes here) - void initialize_generic(detail::function_record *rec, const char *text, - const std::type_info *const *types, size_t args) { - - /* Create copies of all referenced C-style strings */ - rec->name = strdup(rec->name ? rec->name : ""); - if (rec->doc) rec->doc = strdup(rec->doc); - for (auto &a: rec->args) { - if (a.name) - a.name = strdup(a.name); - if (a.descr) - a.descr = strdup(a.descr); - else if (a.value) - a.descr = strdup(a.value.attr("__repr__")().cast().c_str()); - } - - rec->is_constructor = !strcmp(rec->name, "__init__") || !strcmp(rec->name, "__setstate__"); - -#if !defined(NDEBUG) && !defined(PYBIND11_DISABLE_NEW_STYLE_INIT_WARNING) - if (rec->is_constructor && !rec->is_new_style_constructor) { - const auto class_name = std::string(((PyTypeObject *) rec->scope.ptr())->tp_name); - const auto func_name = std::string(rec->name); - PyErr_WarnEx( - PyExc_FutureWarning, - ("pybind11-bound class '" + class_name + "' is using an old-style " - "placement-new '" + func_name + "' which has been deprecated. See " - "the upgrade guide in pybind11's docs. This message is only visible " - "when compiled in debug mode.").c_str(), 0 - ); - } -#endif - - /* Generate a proper function signature */ - std::string signature; - size_t type_index = 0, arg_index = 0; - for (auto *pc = text; *pc != '\0'; ++pc) { - const auto c = *pc; - - if (c == '{') { - // Write arg name for everything except *args and **kwargs. - if (*(pc + 1) == '*') - continue; - - if (arg_index < rec->args.size() && rec->args[arg_index].name) { - signature += rec->args[arg_index].name; - } else if (arg_index == 0 && rec->is_method) { - signature += "self"; - } else { - signature += "arg" + std::to_string(arg_index - (rec->is_method ? 1 : 0)); - } - signature += ": "; - } else if (c == '}') { - // Write default value if available. - if (arg_index < rec->args.size() && rec->args[arg_index].descr) { - signature += " = "; - signature += rec->args[arg_index].descr; - } - arg_index++; - } else if (c == '%') { - const std::type_info *t = types[type_index++]; - if (!t) - pybind11_fail("Internal error while parsing type signature (1)"); - if (auto tinfo = detail::get_type_info(*t)) { - handle th((PyObject *) tinfo->type); - signature += - th.attr("__module__").cast() + "." + - th.attr("__qualname__").cast(); // Python 3.3+, but we backport it to earlier versions - } else if (rec->is_new_style_constructor && arg_index == 0) { - // A new-style `__init__` takes `self` as `value_and_holder`. - // Rewrite it to the proper class type. - signature += - rec->scope.attr("__module__").cast() + "." + - rec->scope.attr("__qualname__").cast(); - } else { - std::string tname(t->name()); - detail::clean_type_id(tname); - signature += tname; - } - } else { - signature += c; - } - } - if (arg_index != args || types[type_index] != nullptr) - pybind11_fail("Internal error while parsing type signature (2)"); - -#if PY_MAJOR_VERSION < 3 - if (strcmp(rec->name, "__next__") == 0) { - std::free(rec->name); - rec->name = strdup("next"); - } else if (strcmp(rec->name, "__bool__") == 0) { - std::free(rec->name); - rec->name = strdup("__nonzero__"); - } -#endif - rec->signature = strdup(signature.c_str()); - rec->args.shrink_to_fit(); - rec->nargs = (std::uint16_t) args; - - if (rec->sibling && PYBIND11_INSTANCE_METHOD_CHECK(rec->sibling.ptr())) - rec->sibling = PYBIND11_INSTANCE_METHOD_GET_FUNCTION(rec->sibling.ptr()); - - detail::function_record *chain = nullptr, *chain_start = rec; - if (rec->sibling) { - if (PyCFunction_Check(rec->sibling.ptr())) { - auto rec_capsule = reinterpret_borrow(PyCFunction_GET_SELF(rec->sibling.ptr())); - chain = (detail::function_record *) rec_capsule; - /* Never append a method to an overload chain of a parent class; - instead, hide the parent's overloads in this case */ - if (!chain->scope.is(rec->scope)) - chain = nullptr; - } - // Don't trigger for things like the default __init__, which are wrapper_descriptors that we are intentionally replacing - else if (!rec->sibling.is_none() && rec->name[0] != '_') - pybind11_fail("Cannot overload existing non-function object \"" + std::string(rec->name) + - "\" with a function of the same name"); - } - - if (!chain) { - /* No existing overload was found, create a new function object */ - rec->def = new PyMethodDef(); - std::memset(rec->def, 0, sizeof(PyMethodDef)); - rec->def->ml_name = rec->name; - rec->def->ml_meth = reinterpret_cast(reinterpret_cast(*dispatcher)); - rec->def->ml_flags = METH_VARARGS | METH_KEYWORDS; - - capsule rec_capsule(rec, [](void *ptr) { - destruct((detail::function_record *) ptr); - }); - - object scope_module; - if (rec->scope) { - if (hasattr(rec->scope, "__module__")) { - scope_module = rec->scope.attr("__module__"); - } else if (hasattr(rec->scope, "__name__")) { - scope_module = rec->scope.attr("__name__"); - } - } - - m_ptr = PyCFunction_NewEx(rec->def, rec_capsule.ptr(), scope_module.ptr()); - if (!m_ptr) - pybind11_fail("cpp_function::cpp_function(): Could not allocate function object"); - } else { - /* Append at the end of the overload chain */ - m_ptr = rec->sibling.ptr(); - inc_ref(); - chain_start = chain; - if (chain->is_method != rec->is_method) - pybind11_fail("overloading a method with both static and instance methods is not supported; " - #if defined(NDEBUG) - "compile in debug mode for more details" - #else - "error while attempting to bind " + std::string(rec->is_method ? "instance" : "static") + " method " + - std::string(pybind11::str(rec->scope.attr("__name__"))) + "." + std::string(rec->name) + signature - #endif - ); - while (chain->next) - chain = chain->next; - chain->next = rec; - } - - std::string signatures; - int index = 0; - /* Create a nice pydoc rec including all signatures and - docstrings of the functions in the overload chain */ - if (chain && options::show_function_signatures()) { - // First a generic signature - signatures += rec->name; - signatures += "(*args, **kwargs)\n"; - signatures += "Overloaded function.\n\n"; - } - // Then specific overload signatures - bool first_user_def = true; - for (auto it = chain_start; it != nullptr; it = it->next) { - if (options::show_function_signatures()) { - if (index > 0) signatures += "\n"; - if (chain) - signatures += std::to_string(++index) + ". "; - signatures += rec->name; - signatures += it->signature; - signatures += "\n"; - } - if (it->doc && strlen(it->doc) > 0 && options::show_user_defined_docstrings()) { - // If we're appending another docstring, and aren't printing function signatures, we - // need to append a newline first: - if (!options::show_function_signatures()) { - if (first_user_def) first_user_def = false; - else signatures += "\n"; - } - if (options::show_function_signatures()) signatures += "\n"; - signatures += it->doc; - if (options::show_function_signatures()) signatures += "\n"; - } - } - - /* Install docstring */ - PyCFunctionObject *func = (PyCFunctionObject *) m_ptr; - if (func->m_ml->ml_doc) - std::free(const_cast(func->m_ml->ml_doc)); - func->m_ml->ml_doc = strdup(signatures.c_str()); - - if (rec->is_method) { - m_ptr = PYBIND11_INSTANCE_METHOD_NEW(m_ptr, rec->scope.ptr()); - if (!m_ptr) - pybind11_fail("cpp_function::cpp_function(): Could not allocate instance method object"); - Py_DECREF(func); - } - } - - /// When a cpp_function is GCed, release any memory allocated by pybind11 - static void destruct(detail::function_record *rec) { - while (rec) { - detail::function_record *next = rec->next; - if (rec->free_data) - rec->free_data(rec); - std::free((char *) rec->name); - std::free((char *) rec->doc); - std::free((char *) rec->signature); - for (auto &arg: rec->args) { - std::free(const_cast(arg.name)); - std::free(const_cast(arg.descr)); - arg.value.dec_ref(); - } - if (rec->def) { - std::free(const_cast(rec->def->ml_doc)); - delete rec->def; - } - delete rec; - rec = next; - } - } - - /// Main dispatch logic for calls to functions bound using pybind11 - static PyObject *dispatcher(PyObject *self, PyObject *args_in, PyObject *kwargs_in) { - using namespace detail; - - /* Iterator over the list of potentially admissible overloads */ - const function_record *overloads = (function_record *) PyCapsule_GetPointer(self, nullptr), - *it = overloads; - - /* Need to know how many arguments + keyword arguments there are to pick the right overload */ - const size_t n_args_in = (size_t) PyTuple_GET_SIZE(args_in); - - handle parent = n_args_in > 0 ? PyTuple_GET_ITEM(args_in, 0) : nullptr, - result = PYBIND11_TRY_NEXT_OVERLOAD; - - auto self_value_and_holder = value_and_holder(); - if (overloads->is_constructor) { - const auto tinfo = get_type_info((PyTypeObject *) overloads->scope.ptr()); - const auto pi = reinterpret_cast(parent.ptr()); - self_value_and_holder = pi->get_value_and_holder(tinfo, false); - - if (!self_value_and_holder.type || !self_value_and_holder.inst) { - PyErr_SetString(PyExc_TypeError, "__init__(self, ...) called with invalid `self` argument"); - return nullptr; - } - - // If this value is already registered it must mean __init__ is invoked multiple times; - // we really can't support that in C++, so just ignore the second __init__. - if (self_value_and_holder.instance_registered()) - return none().release().ptr(); - } - - try { - // We do this in two passes: in the first pass, we load arguments with `convert=false`; - // in the second, we allow conversion (except for arguments with an explicit - // py::arg().noconvert()). This lets us prefer calls without conversion, with - // conversion as a fallback. - std::vector second_pass; - - // However, if there are no overloads, we can just skip the no-convert pass entirely - const bool overloaded = it != nullptr && it->next != nullptr; - - for (; it != nullptr; it = it->next) { - - /* For each overload: - 1. Copy all positional arguments we were given, also checking to make sure that - named positional arguments weren't *also* specified via kwarg. - 2. If we weren't given enough, try to make up the omitted ones by checking - whether they were provided by a kwarg matching the `py::arg("name")` name. If - so, use it (and remove it from kwargs; if not, see if the function binding - provided a default that we can use. - 3. Ensure that either all keyword arguments were "consumed", or that the function - takes a kwargs argument to accept unconsumed kwargs. - 4. Any positional arguments still left get put into a tuple (for args), and any - leftover kwargs get put into a dict. - 5. Pack everything into a vector; if we have py::args or py::kwargs, they are an - extra tuple or dict at the end of the positional arguments. - 6. Call the function call dispatcher (function_record::impl) - - If one of these fail, move on to the next overload and keep trying until we get a - result other than PYBIND11_TRY_NEXT_OVERLOAD. - */ - - const function_record &func = *it; - size_t pos_args = func.nargs; // Number of positional arguments that we need - if (func.has_args) --pos_args; // (but don't count py::args - if (func.has_kwargs) --pos_args; // or py::kwargs) - - if (!func.has_args && n_args_in > pos_args) - continue; // Too many arguments for this overload - - if (n_args_in < pos_args && func.args.size() < pos_args) - continue; // Not enough arguments given, and not enough defaults to fill in the blanks - - function_call call(func, parent); - - size_t args_to_copy = std::min(pos_args, n_args_in); - size_t args_copied = 0; - - // 0. Inject new-style `self` argument - if (func.is_new_style_constructor) { - // The `value` may have been preallocated by an old-style `__init__` - // if it was a preceding candidate for overload resolution. - if (self_value_and_holder) - self_value_and_holder.type->dealloc(self_value_and_holder); - - call.init_self = PyTuple_GET_ITEM(args_in, 0); - call.args.push_back(reinterpret_cast(&self_value_and_holder)); - call.args_convert.push_back(false); - ++args_copied; - } - - // 1. Copy any position arguments given. - bool bad_arg = false; - for (; args_copied < args_to_copy; ++args_copied) { - const argument_record *arg_rec = args_copied < func.args.size() ? &func.args[args_copied] : nullptr; - if (kwargs_in && arg_rec && arg_rec->name && PyDict_GetItemString(kwargs_in, arg_rec->name)) { - bad_arg = true; - break; - } - - handle arg(PyTuple_GET_ITEM(args_in, args_copied)); - if (arg_rec && !arg_rec->none && arg.is_none()) { - bad_arg = true; - break; - } - call.args.push_back(arg); - call.args_convert.push_back(arg_rec ? arg_rec->convert : true); - } - if (bad_arg) - continue; // Maybe it was meant for another overload (issue #688) - - // We'll need to copy this if we steal some kwargs for defaults - dict kwargs = reinterpret_borrow(kwargs_in); - - // 2. Check kwargs and, failing that, defaults that may help complete the list - if (args_copied < pos_args) { - bool copied_kwargs = false; - - for (; args_copied < pos_args; ++args_copied) { - const auto &arg = func.args[args_copied]; - - handle value; - if (kwargs_in && arg.name) - value = PyDict_GetItemString(kwargs.ptr(), arg.name); - - if (value) { - // Consume a kwargs value - if (!copied_kwargs) { - kwargs = reinterpret_steal(PyDict_Copy(kwargs.ptr())); - copied_kwargs = true; - } - PyDict_DelItemString(kwargs.ptr(), arg.name); - } else if (arg.value) { - value = arg.value; - } - - if (value) { - call.args.push_back(value); - call.args_convert.push_back(arg.convert); - } - else - break; - } - - if (args_copied < pos_args) - continue; // Not enough arguments, defaults, or kwargs to fill the positional arguments - } - - // 3. Check everything was consumed (unless we have a kwargs arg) - if (kwargs && kwargs.size() > 0 && !func.has_kwargs) - continue; // Unconsumed kwargs, but no py::kwargs argument to accept them - - // 4a. If we have a py::args argument, create a new tuple with leftovers - if (func.has_args) { - tuple extra_args; - if (args_to_copy == 0) { - // We didn't copy out any position arguments from the args_in tuple, so we - // can reuse it directly without copying: - extra_args = reinterpret_borrow(args_in); - } else if (args_copied >= n_args_in) { - extra_args = tuple(0); - } else { - size_t args_size = n_args_in - args_copied; - extra_args = tuple(args_size); - for (size_t i = 0; i < args_size; ++i) { - extra_args[i] = PyTuple_GET_ITEM(args_in, args_copied + i); - } - } - call.args.push_back(extra_args); - call.args_convert.push_back(false); - call.args_ref = std::move(extra_args); - } - - // 4b. If we have a py::kwargs, pass on any remaining kwargs - if (func.has_kwargs) { - if (!kwargs.ptr()) - kwargs = dict(); // If we didn't get one, send an empty one - call.args.push_back(kwargs); - call.args_convert.push_back(false); - call.kwargs_ref = std::move(kwargs); - } - - // 5. Put everything in a vector. Not technically step 5, we've been building it - // in `call.args` all along. - #if !defined(NDEBUG) - if (call.args.size() != func.nargs || call.args_convert.size() != func.nargs) - pybind11_fail("Internal error: function call dispatcher inserted wrong number of arguments!"); - #endif - - std::vector second_pass_convert; - if (overloaded) { - // We're in the first no-convert pass, so swap out the conversion flags for a - // set of all-false flags. If the call fails, we'll swap the flags back in for - // the conversion-allowed call below. - second_pass_convert.resize(func.nargs, false); - call.args_convert.swap(second_pass_convert); - } - - // 6. Call the function. - try { - loader_life_support guard{}; - result = func.impl(call); - } catch (reference_cast_error &) { - result = PYBIND11_TRY_NEXT_OVERLOAD; - } - - if (result.ptr() != PYBIND11_TRY_NEXT_OVERLOAD) - break; - - if (overloaded) { - // The (overloaded) call failed; if the call has at least one argument that - // permits conversion (i.e. it hasn't been explicitly specified `.noconvert()`) - // then add this call to the list of second pass overloads to try. - for (size_t i = func.is_method ? 1 : 0; i < pos_args; i++) { - if (second_pass_convert[i]) { - // Found one: swap the converting flags back in and store the call for - // the second pass. - call.args_convert.swap(second_pass_convert); - second_pass.push_back(std::move(call)); - break; - } - } - } - } - - if (overloaded && !second_pass.empty() && result.ptr() == PYBIND11_TRY_NEXT_OVERLOAD) { - // The no-conversion pass finished without success, try again with conversion allowed - for (auto &call : second_pass) { - try { - loader_life_support guard{}; - result = call.func.impl(call); - } catch (reference_cast_error &) { - result = PYBIND11_TRY_NEXT_OVERLOAD; - } - - if (result.ptr() != PYBIND11_TRY_NEXT_OVERLOAD) { - // The error reporting logic below expects 'it' to be valid, as it would be - // if we'd encountered this failure in the first-pass loop. - if (!result) - it = &call.func; - break; - } - } - } - } catch (error_already_set &e) { - e.restore(); - return nullptr; - } catch (...) { - /* When an exception is caught, give each registered exception - translator a chance to translate it to a Python exception - in reverse order of registration. - - A translator may choose to do one of the following: - - - catch the exception and call PyErr_SetString or PyErr_SetObject - to set a standard (or custom) Python exception, or - - do nothing and let the exception fall through to the next translator, or - - delegate translation to the next translator by throwing a new type of exception. */ - - auto last_exception = std::current_exception(); - auto ®istered_exception_translators = get_internals().registered_exception_translators; - for (auto& translator : registered_exception_translators) { - try { - translator(last_exception); - } catch (...) { - last_exception = std::current_exception(); - continue; - } - return nullptr; - } - PyErr_SetString(PyExc_SystemError, "Exception escaped from default exception translator!"); - return nullptr; - } - - auto append_note_if_missing_header_is_suspected = [](std::string &msg) { - if (msg.find("std::") != std::string::npos) { - msg += "\n\n" - "Did you forget to `#include `? Or ,\n" - ", , etc. Some automatic\n" - "conversions are optional and require extra headers to be included\n" - "when compiling your pybind11 module."; - } - }; - - if (result.ptr() == PYBIND11_TRY_NEXT_OVERLOAD) { - if (overloads->is_operator) - return handle(Py_NotImplemented).inc_ref().ptr(); - - std::string msg = std::string(overloads->name) + "(): incompatible " + - std::string(overloads->is_constructor ? "constructor" : "function") + - " arguments. The following argument types are supported:\n"; - - int ctr = 0; - for (const function_record *it2 = overloads; it2 != nullptr; it2 = it2->next) { - msg += " "+ std::to_string(++ctr) + ". "; - - bool wrote_sig = false; - if (overloads->is_constructor) { - // For a constructor, rewrite `(self: Object, arg0, ...) -> NoneType` as `Object(arg0, ...)` - std::string sig = it2->signature; - size_t start = sig.find('(') + 7; // skip "(self: " - if (start < sig.size()) { - // End at the , for the next argument - size_t end = sig.find(", "), next = end + 2; - size_t ret = sig.rfind(" -> "); - // Or the ), if there is no comma: - if (end >= sig.size()) next = end = sig.find(')'); - if (start < end && next < sig.size()) { - msg.append(sig, start, end - start); - msg += '('; - msg.append(sig, next, ret - next); - wrote_sig = true; - } - } - } - if (!wrote_sig) msg += it2->signature; - - msg += "\n"; - } - msg += "\nInvoked with: "; - auto args_ = reinterpret_borrow(args_in); - bool some_args = false; - for (size_t ti = overloads->is_constructor ? 1 : 0; ti < args_.size(); ++ti) { - if (!some_args) some_args = true; - else msg += ", "; - msg += pybind11::repr(args_[ti]); - } - if (kwargs_in) { - auto kwargs = reinterpret_borrow(kwargs_in); - if (kwargs.size() > 0) { - if (some_args) msg += "; "; - msg += "kwargs: "; - bool first = true; - for (auto kwarg : kwargs) { - if (first) first = false; - else msg += ", "; - msg += pybind11::str("{}={!r}").format(kwarg.first, kwarg.second); - } - } - } - - append_note_if_missing_header_is_suspected(msg); - PyErr_SetString(PyExc_TypeError, msg.c_str()); - return nullptr; - } else if (!result) { - std::string msg = "Unable to convert function return value to a " - "Python type! The signature was\n\t"; - msg += it->signature; - append_note_if_missing_header_is_suspected(msg); - PyErr_SetString(PyExc_TypeError, msg.c_str()); - return nullptr; - } else { - if (overloads->is_constructor && !self_value_and_holder.holder_constructed()) { - auto *pi = reinterpret_cast(parent.ptr()); - self_value_and_holder.type->init_instance(pi, nullptr); - } - return result.ptr(); - } - } -}; - -/// Wrapper for Python extension modules -class module : public object { -public: - PYBIND11_OBJECT_DEFAULT(module, object, PyModule_Check) - - /// Create a new top-level Python module with the given name and docstring - explicit module(const char *name, const char *doc = nullptr) { - if (!options::show_user_defined_docstrings()) doc = nullptr; -#if PY_MAJOR_VERSION >= 3 - PyModuleDef *def = new PyModuleDef(); - std::memset(def, 0, sizeof(PyModuleDef)); - def->m_name = name; - def->m_doc = doc; - def->m_size = -1; - Py_INCREF(def); - m_ptr = PyModule_Create(def); -#else - m_ptr = Py_InitModule3(name, nullptr, doc); -#endif - if (m_ptr == nullptr) - pybind11_fail("Internal error in module::module()"); - inc_ref(); - } - - /** \rst - Create Python binding for a new function within the module scope. ``Func`` - can be a plain C++ function, a function pointer, or a lambda function. For - details on the ``Extra&& ... extra`` argument, see section :ref:`extras`. - \endrst */ - template - module &def(const char *name_, Func &&f, const Extra& ... extra) { - cpp_function func(std::forward(f), name(name_), scope(*this), - sibling(getattr(*this, name_, none())), extra...); - // NB: allow overwriting here because cpp_function sets up a chain with the intention of - // overwriting (and has already checked internally that it isn't overwriting non-functions). - add_object(name_, func, true /* overwrite */); - return *this; - } - - /** \rst - Create and return a new Python submodule with the given name and docstring. - This also works recursively, i.e. - - .. code-block:: cpp - - py::module m("example", "pybind11 example plugin"); - py::module m2 = m.def_submodule("sub", "A submodule of 'example'"); - py::module m3 = m2.def_submodule("subsub", "A submodule of 'example.sub'"); - \endrst */ - module def_submodule(const char *name, const char *doc = nullptr) { - std::string full_name = std::string(PyModule_GetName(m_ptr)) - + std::string(".") + std::string(name); - auto result = reinterpret_borrow(PyImport_AddModule(full_name.c_str())); - if (doc && options::show_user_defined_docstrings()) - result.attr("__doc__") = pybind11::str(doc); - attr(name) = result; - return result; - } - - /// Import and return a module or throws `error_already_set`. - static module import(const char *name) { - PyObject *obj = PyImport_ImportModule(name); - if (!obj) - throw error_already_set(); - return reinterpret_steal(obj); - } - - /// Reload the module or throws `error_already_set`. - void reload() { - PyObject *obj = PyImport_ReloadModule(ptr()); - if (!obj) - throw error_already_set(); - *this = reinterpret_steal(obj); - } - - // Adds an object to the module using the given name. Throws if an object with the given name - // already exists. - // - // overwrite should almost always be false: attempting to overwrite objects that pybind11 has - // established will, in most cases, break things. - PYBIND11_NOINLINE void add_object(const char *name, handle obj, bool overwrite = false) { - if (!overwrite && hasattr(*this, name)) - pybind11_fail("Error during initialization: multiple incompatible definitions with name \"" + - std::string(name) + "\""); - - PyModule_AddObject(ptr(), name, obj.inc_ref().ptr() /* steals a reference */); - } -}; - -/// \ingroup python_builtins -/// Return a dictionary representing the global variables in the current execution frame, -/// or ``__main__.__dict__`` if there is no frame (usually when the interpreter is embedded). -inline dict globals() { - PyObject *p = PyEval_GetGlobals(); - return reinterpret_borrow(p ? p : module::import("__main__").attr("__dict__").ptr()); -} - -NAMESPACE_BEGIN(detail) -/// Generic support for creating new Python heap types -class generic_type : public object { - template friend class class_; -public: - PYBIND11_OBJECT_DEFAULT(generic_type, object, PyType_Check) -protected: - void initialize(const type_record &rec) { - if (rec.scope && hasattr(rec.scope, rec.name)) - pybind11_fail("generic_type: cannot initialize type \"" + std::string(rec.name) + - "\": an object with that name is already defined"); - - if (rec.module_local ? get_local_type_info(*rec.type) : get_global_type_info(*rec.type)) - pybind11_fail("generic_type: type \"" + std::string(rec.name) + - "\" is already registered!"); - - m_ptr = make_new_python_type(rec); - - /* Register supplemental type information in C++ dict */ - auto *tinfo = new detail::type_info(); - tinfo->type = (PyTypeObject *) m_ptr; - tinfo->cpptype = rec.type; - tinfo->type_size = rec.type_size; - tinfo->type_align = rec.type_align; - tinfo->operator_new = rec.operator_new; - tinfo->holder_size_in_ptrs = size_in_ptrs(rec.holder_size); - tinfo->init_instance = rec.init_instance; - tinfo->dealloc = rec.dealloc; - tinfo->simple_type = true; - tinfo->simple_ancestors = true; - tinfo->default_holder = rec.default_holder; - tinfo->module_local = rec.module_local; - - auto &internals = get_internals(); - auto tindex = std::type_index(*rec.type); - tinfo->direct_conversions = &internals.direct_conversions[tindex]; - if (rec.module_local) - registered_local_types_cpp()[tindex] = tinfo; - else - internals.registered_types_cpp[tindex] = tinfo; - internals.registered_types_py[(PyTypeObject *) m_ptr] = { tinfo }; - - if (rec.bases.size() > 1 || rec.multiple_inheritance) { - mark_parents_nonsimple(tinfo->type); - tinfo->simple_ancestors = false; - } - else if (rec.bases.size() == 1) { - auto parent_tinfo = get_type_info((PyTypeObject *) rec.bases[0].ptr()); - tinfo->simple_ancestors = parent_tinfo->simple_ancestors; - } - - if (rec.module_local) { - // Stash the local typeinfo and loader so that external modules can access it. - tinfo->module_local_load = &type_caster_generic::local_load; - setattr(m_ptr, PYBIND11_MODULE_LOCAL_ID, capsule(tinfo)); - } - } - - /// Helper function which tags all parents of a type using mult. inheritance - void mark_parents_nonsimple(PyTypeObject *value) { - auto t = reinterpret_borrow(value->tp_bases); - for (handle h : t) { - auto tinfo2 = get_type_info((PyTypeObject *) h.ptr()); - if (tinfo2) - tinfo2->simple_type = false; - mark_parents_nonsimple((PyTypeObject *) h.ptr()); - } - } - - void install_buffer_funcs( - buffer_info *(*get_buffer)(PyObject *, void *), - void *get_buffer_data) { - PyHeapTypeObject *type = (PyHeapTypeObject*) m_ptr; - auto tinfo = detail::get_type_info(&type->ht_type); - - if (!type->ht_type.tp_as_buffer) - pybind11_fail( - "To be able to register buffer protocol support for the type '" + - std::string(tinfo->type->tp_name) + - "' the associated class<>(..) invocation must " - "include the pybind11::buffer_protocol() annotation!"); - - tinfo->get_buffer = get_buffer; - tinfo->get_buffer_data = get_buffer_data; - } - - // rec_func must be set for either fget or fset. - void def_property_static_impl(const char *name, - handle fget, handle fset, - detail::function_record *rec_func) { - const auto is_static = rec_func && !(rec_func->is_method && rec_func->scope); - const auto has_doc = rec_func && rec_func->doc && pybind11::options::show_user_defined_docstrings(); - auto property = handle((PyObject *) (is_static ? get_internals().static_property_type - : &PyProperty_Type)); - attr(name) = property(fget.ptr() ? fget : none(), - fset.ptr() ? fset : none(), - /*deleter*/none(), - pybind11::str(has_doc ? rec_func->doc : "")); - } -}; - -/// Set the pointer to operator new if it exists. The cast is needed because it can be overloaded. -template (T::operator new))>> -void set_operator_new(type_record *r) { r->operator_new = &T::operator new; } - -template void set_operator_new(...) { } - -template struct has_operator_delete : std::false_type { }; -template struct has_operator_delete(T::operator delete))>> - : std::true_type { }; -template struct has_operator_delete_size : std::false_type { }; -template struct has_operator_delete_size(T::operator delete))>> - : std::true_type { }; -/// Call class-specific delete if it exists or global otherwise. Can also be an overload set. -template ::value, int> = 0> -void call_operator_delete(T *p, size_t, size_t) { T::operator delete(p); } -template ::value && has_operator_delete_size::value, int> = 0> -void call_operator_delete(T *p, size_t s, size_t) { T::operator delete(p, s); } - -inline void call_operator_delete(void *p, size_t s, size_t a) { - (void)s; (void)a; -#if defined(PYBIND11_CPP17) - if (a > __STDCPP_DEFAULT_NEW_ALIGNMENT__) - ::operator delete(p, s, std::align_val_t(a)); - else - ::operator delete(p, s); -#else - ::operator delete(p); -#endif -} - -NAMESPACE_END(detail) - -/// Given a pointer to a member function, cast it to its `Derived` version. -/// Forward everything else unchanged. -template -auto method_adaptor(F &&f) -> decltype(std::forward(f)) { return std::forward(f); } - -template -auto method_adaptor(Return (Class::*pmf)(Args...)) -> Return (Derived::*)(Args...) { - static_assert(detail::is_accessible_base_of::value, - "Cannot bind an inaccessible base class method; use a lambda definition instead"); - return pmf; -} - -template -auto method_adaptor(Return (Class::*pmf)(Args...) const) -> Return (Derived::*)(Args...) const { - static_assert(detail::is_accessible_base_of::value, - "Cannot bind an inaccessible base class method; use a lambda definition instead"); - return pmf; -} - -template -class class_ : public detail::generic_type { - template using is_holder = detail::is_holder_type; - template using is_subtype = detail::is_strict_base_of; - template using is_base = detail::is_strict_base_of; - // struct instead of using here to help MSVC: - template struct is_valid_class_option : - detail::any_of, is_subtype, is_base> {}; - -public: - using type = type_; - using type_alias = detail::exactly_one_t; - constexpr static bool has_alias = !std::is_void::value; - using holder_type = detail::exactly_one_t, options...>; - - static_assert(detail::all_of...>::value, - "Unknown/invalid class_ template parameters provided"); - - static_assert(!has_alias || std::is_polymorphic::value, - "Cannot use an alias class with a non-polymorphic type"); - - PYBIND11_OBJECT(class_, generic_type, PyType_Check) - - template - class_(handle scope, const char *name, const Extra &... extra) { - using namespace detail; - - // MI can only be specified via class_ template options, not constructor parameters - static_assert( - none_of...>::value || // no base class arguments, or: - ( constexpr_sum(is_pyobject::value...) == 1 && // Exactly one base - constexpr_sum(is_base::value...) == 0 && // no template option bases - none_of...>::value), // no multiple_inheritance attr - "Error: multiple inheritance bases must be specified via class_ template options"); - - type_record record; - record.scope = scope; - record.name = name; - record.type = &typeid(type); - record.type_size = sizeof(conditional_t); - record.type_align = alignof(conditional_t&); - record.holder_size = sizeof(holder_type); - record.init_instance = init_instance; - record.dealloc = dealloc; - record.default_holder = detail::is_instantiation::value; - - set_operator_new(&record); - - /* Register base classes specified via template arguments to class_, if any */ - PYBIND11_EXPAND_SIDE_EFFECTS(add_base(record)); - - /* Process optional arguments, if any */ - process_attributes::init(extra..., &record); - - generic_type::initialize(record); - - if (has_alias) { - auto &instances = record.module_local ? registered_local_types_cpp() : get_internals().registered_types_cpp; - instances[std::type_index(typeid(type_alias))] = instances[std::type_index(typeid(type))]; - } - } - - template ::value, int> = 0> - static void add_base(detail::type_record &rec) { - rec.add_base(typeid(Base), [](void *src) -> void * { - return static_cast(reinterpret_cast(src)); - }); - } - - template ::value, int> = 0> - static void add_base(detail::type_record &) { } - - template - class_ &def(const char *name_, Func&& f, const Extra&... extra) { - cpp_function cf(method_adaptor(std::forward(f)), name(name_), is_method(*this), - sibling(getattr(*this, name_, none())), extra...); - attr(cf.name()) = cf; - return *this; - } - - template class_ & - def_static(const char *name_, Func &&f, const Extra&... extra) { - static_assert(!std::is_member_function_pointer::value, - "def_static(...) called with a non-static member function pointer"); - cpp_function cf(std::forward(f), name(name_), scope(*this), - sibling(getattr(*this, name_, none())), extra...); - attr(cf.name()) = cf; - return *this; - } - - template - class_ &def(const detail::op_ &op, const Extra&... extra) { - op.execute(*this, extra...); - return *this; - } - - template - class_ & def_cast(const detail::op_ &op, const Extra&... extra) { - op.execute_cast(*this, extra...); - return *this; - } - - template - class_ &def(const detail::initimpl::constructor &init, const Extra&... extra) { - init.execute(*this, extra...); - return *this; - } - - template - class_ &def(const detail::initimpl::alias_constructor &init, const Extra&... extra) { - init.execute(*this, extra...); - return *this; - } - - template - class_ &def(detail::initimpl::factory &&init, const Extra&... extra) { - std::move(init).execute(*this, extra...); - return *this; - } - - template - class_ &def(detail::initimpl::pickle_factory &&pf, const Extra &...extra) { - std::move(pf).execute(*this, extra...); - return *this; - } - - template class_& def_buffer(Func &&func) { - struct capture { Func func; }; - capture *ptr = new capture { std::forward(func) }; - install_buffer_funcs([](PyObject *obj, void *ptr) -> buffer_info* { - detail::make_caster caster; - if (!caster.load(obj, false)) - return nullptr; - return new buffer_info(((capture *) ptr)->func(caster)); - }, ptr); - return *this; - } - - template - class_ &def_buffer(Return (Class::*func)(Args...)) { - return def_buffer([func] (type &obj) { return (obj.*func)(); }); - } - - template - class_ &def_buffer(Return (Class::*func)(Args...) const) { - return def_buffer([func] (const type &obj) { return (obj.*func)(); }); - } - - template - class_ &def_readwrite(const char *name, D C::*pm, const Extra&... extra) { - static_assert(std::is_base_of::value, "def_readwrite() requires a class member (or base class member)"); - cpp_function fget([pm](const type &c) -> const D &{ return c.*pm; }, is_method(*this)), - fset([pm](type &c, const D &value) { c.*pm = value; }, is_method(*this)); - def_property(name, fget, fset, return_value_policy::reference_internal, extra...); - return *this; - } - - template - class_ &def_readonly(const char *name, const D C::*pm, const Extra& ...extra) { - static_assert(std::is_base_of::value, "def_readonly() requires a class member (or base class member)"); - cpp_function fget([pm](const type &c) -> const D &{ return c.*pm; }, is_method(*this)); - def_property_readonly(name, fget, return_value_policy::reference_internal, extra...); - return *this; - } - - template - class_ &def_readwrite_static(const char *name, D *pm, const Extra& ...extra) { - cpp_function fget([pm](object) -> const D &{ return *pm; }, scope(*this)), - fset([pm](object, const D &value) { *pm = value; }, scope(*this)); - def_property_static(name, fget, fset, return_value_policy::reference, extra...); - return *this; - } - - template - class_ &def_readonly_static(const char *name, const D *pm, const Extra& ...extra) { - cpp_function fget([pm](object) -> const D &{ return *pm; }, scope(*this)); - def_property_readonly_static(name, fget, return_value_policy::reference, extra...); - return *this; - } - - /// Uses return_value_policy::reference_internal by default - template - class_ &def_property_readonly(const char *name, const Getter &fget, const Extra& ...extra) { - return def_property_readonly(name, cpp_function(method_adaptor(fget)), - return_value_policy::reference_internal, extra...); - } - - /// Uses cpp_function's return_value_policy by default - template - class_ &def_property_readonly(const char *name, const cpp_function &fget, const Extra& ...extra) { - return def_property(name, fget, nullptr, extra...); - } - - /// Uses return_value_policy::reference by default - template - class_ &def_property_readonly_static(const char *name, const Getter &fget, const Extra& ...extra) { - return def_property_readonly_static(name, cpp_function(fget), return_value_policy::reference, extra...); - } - - /// Uses cpp_function's return_value_policy by default - template - class_ &def_property_readonly_static(const char *name, const cpp_function &fget, const Extra& ...extra) { - return def_property_static(name, fget, nullptr, extra...); - } - - /// Uses return_value_policy::reference_internal by default - template - class_ &def_property(const char *name, const Getter &fget, const Setter &fset, const Extra& ...extra) { - return def_property(name, fget, cpp_function(method_adaptor(fset)), extra...); - } - template - class_ &def_property(const char *name, const Getter &fget, const cpp_function &fset, const Extra& ...extra) { - return def_property(name, cpp_function(method_adaptor(fget)), fset, - return_value_policy::reference_internal, extra...); - } - - /// Uses cpp_function's return_value_policy by default - template - class_ &def_property(const char *name, const cpp_function &fget, const cpp_function &fset, const Extra& ...extra) { - return def_property_static(name, fget, fset, is_method(*this), extra...); - } - - /// Uses return_value_policy::reference by default - template - class_ &def_property_static(const char *name, const Getter &fget, const cpp_function &fset, const Extra& ...extra) { - return def_property_static(name, cpp_function(fget), fset, return_value_policy::reference, extra...); - } - - /// Uses cpp_function's return_value_policy by default - template - class_ &def_property_static(const char *name, const cpp_function &fget, const cpp_function &fset, const Extra& ...extra) { - auto rec_fget = get_function_record(fget), rec_fset = get_function_record(fset); - auto *rec_active = rec_fget; - if (rec_fget) { - char *doc_prev = rec_fget->doc; /* 'extra' field may include a property-specific documentation string */ - detail::process_attributes::init(extra..., rec_fget); - if (rec_fget->doc && rec_fget->doc != doc_prev) { - free(doc_prev); - rec_fget->doc = strdup(rec_fget->doc); - } - } - if (rec_fset) { - char *doc_prev = rec_fset->doc; - detail::process_attributes::init(extra..., rec_fset); - if (rec_fset->doc && rec_fset->doc != doc_prev) { - free(doc_prev); - rec_fset->doc = strdup(rec_fset->doc); - } - if (! rec_active) rec_active = rec_fset; - } - def_property_static_impl(name, fget, fset, rec_active); - return *this; - } - -private: - /// Initialize holder object, variant 1: object derives from enable_shared_from_this - template - static void init_holder(detail::instance *inst, detail::value_and_holder &v_h, - const holder_type * /* unused */, const std::enable_shared_from_this * /* dummy */) { - try { - auto sh = std::dynamic_pointer_cast( - v_h.value_ptr()->shared_from_this()); - if (sh) { - new (std::addressof(v_h.holder())) holder_type(std::move(sh)); - v_h.set_holder_constructed(); - } - } catch (const std::bad_weak_ptr &) {} - - if (!v_h.holder_constructed() && inst->owned) { - new (std::addressof(v_h.holder())) holder_type(v_h.value_ptr()); - v_h.set_holder_constructed(); - } - } - - static void init_holder_from_existing(const detail::value_and_holder &v_h, - const holder_type *holder_ptr, std::true_type /*is_copy_constructible*/) { - new (std::addressof(v_h.holder())) holder_type(*reinterpret_cast(holder_ptr)); - } - - static void init_holder_from_existing(const detail::value_and_holder &v_h, - const holder_type *holder_ptr, std::false_type /*is_copy_constructible*/) { - new (std::addressof(v_h.holder())) holder_type(std::move(*const_cast(holder_ptr))); - } - - /// Initialize holder object, variant 2: try to construct from existing holder object, if possible - static void init_holder(detail::instance *inst, detail::value_and_holder &v_h, - const holder_type *holder_ptr, const void * /* dummy -- not enable_shared_from_this) */) { - if (holder_ptr) { - init_holder_from_existing(v_h, holder_ptr, std::is_copy_constructible()); - v_h.set_holder_constructed(); - } else if (inst->owned || detail::always_construct_holder::value) { - new (std::addressof(v_h.holder())) holder_type(v_h.value_ptr()); - v_h.set_holder_constructed(); - } - } - - /// Performs instance initialization including constructing a holder and registering the known - /// instance. Should be called as soon as the `type` value_ptr is set for an instance. Takes an - /// optional pointer to an existing holder to use; if not specified and the instance is - /// `.owned`, a new holder will be constructed to manage the value pointer. - static void init_instance(detail::instance *inst, const void *holder_ptr) { - auto v_h = inst->get_value_and_holder(detail::get_type_info(typeid(type))); - if (!v_h.instance_registered()) { - register_instance(inst, v_h.value_ptr(), v_h.type); - v_h.set_instance_registered(); - } - init_holder(inst, v_h, (const holder_type *) holder_ptr, v_h.value_ptr()); - } - - /// Deallocates an instance; via holder, if constructed; otherwise via operator delete. - static void dealloc(detail::value_and_holder &v_h) { - if (v_h.holder_constructed()) { - v_h.holder().~holder_type(); - v_h.set_holder_constructed(false); - } - else { - detail::call_operator_delete(v_h.value_ptr(), - v_h.type->type_size, - v_h.type->type_align - ); - } - v_h.value_ptr() = nullptr; - } - - static detail::function_record *get_function_record(handle h) { - h = detail::get_function(h); - return h ? (detail::function_record *) reinterpret_borrow(PyCFunction_GET_SELF(h.ptr())) - : nullptr; - } -}; - -/// Binds an existing constructor taking arguments Args... -template detail::initimpl::constructor init() { return {}; } -/// Like `init()`, but the instance is always constructed through the alias class (even -/// when not inheriting on the Python side). -template detail::initimpl::alias_constructor init_alias() { return {}; } - -/// Binds a factory function as a constructor -template > -Ret init(Func &&f) { return {std::forward(f)}; } - -/// Dual-argument factory function: the first function is called when no alias is needed, the second -/// when an alias is needed (i.e. due to python-side inheritance). Arguments must be identical. -template > -Ret init(CFunc &&c, AFunc &&a) { - return {std::forward(c), std::forward(a)}; -} - -/// Binds pickling functions `__getstate__` and `__setstate__` and ensures that the type -/// returned by `__getstate__` is the same as the argument accepted by `__setstate__`. -template -detail::initimpl::pickle_factory pickle(GetState &&g, SetState &&s) { - return {std::forward(g), std::forward(s)}; -} - -NAMESPACE_BEGIN(detail) -struct enum_base { - enum_base(handle base, handle parent) : m_base(base), m_parent(parent) { } - - PYBIND11_NOINLINE void init(bool is_arithmetic, bool is_convertible) { - m_base.attr("__entries") = dict(); - auto property = handle((PyObject *) &PyProperty_Type); - auto static_property = handle((PyObject *) get_internals().static_property_type); - - m_base.attr("__repr__") = cpp_function( - [](handle arg) -> str { - handle type = arg.get_type(); - object type_name = type.attr("__name__"); - dict entries = type.attr("__entries"); - for (const auto &kv : entries) { - object other = kv.second[int_(0)]; - if (other.equal(arg)) - return pybind11::str("{}.{}").format(type_name, kv.first); - } - return pybind11::str("{}.???").format(type_name); - }, is_method(m_base) - ); - - m_base.attr("name") = property(cpp_function( - [](handle arg) -> str { - dict entries = arg.get_type().attr("__entries"); - for (const auto &kv : entries) { - if (handle(kv.second[int_(0)]).equal(arg)) - return pybind11::str(kv.first); - } - return "???"; - }, is_method(m_base) - )); - - m_base.attr("__doc__") = static_property(cpp_function( - [](handle arg) -> std::string { - std::string docstring; - dict entries = arg.attr("__entries"); - if (((PyTypeObject *) arg.ptr())->tp_doc) - docstring += std::string(((PyTypeObject *) arg.ptr())->tp_doc) + "\n\n"; - docstring += "Members:"; - for (const auto &kv : entries) { - auto key = std::string(pybind11::str(kv.first)); - auto comment = kv.second[int_(1)]; - docstring += "\n\n " + key; - if (!comment.is_none()) - docstring += " : " + (std::string) pybind11::str(comment); - } - return docstring; - } - ), none(), none(), ""); - - m_base.attr("__members__") = static_property(cpp_function( - [](handle arg) -> dict { - dict entries = arg.attr("__entries"), m; - for (const auto &kv : entries) - m[kv.first] = kv.second[int_(0)]; - return m; - }), none(), none(), "" - ); - - #define PYBIND11_ENUM_OP_STRICT(op, expr, strict_behavior) \ - m_base.attr(op) = cpp_function( \ - [](object a, object b) { \ - if (!a.get_type().is(b.get_type())) \ - strict_behavior; \ - return expr; \ - }, \ - is_method(m_base)) - - #define PYBIND11_ENUM_OP_CONV(op, expr) \ - m_base.attr(op) = cpp_function( \ - [](object a_, object b_) { \ - int_ a(a_), b(b_); \ - return expr; \ - }, \ - is_method(m_base)) - - if (is_convertible) { - PYBIND11_ENUM_OP_CONV("__eq__", !b.is_none() && a.equal(b)); - PYBIND11_ENUM_OP_CONV("__ne__", b.is_none() || !a.equal(b)); - - if (is_arithmetic) { - PYBIND11_ENUM_OP_CONV("__lt__", a < b); - PYBIND11_ENUM_OP_CONV("__gt__", a > b); - PYBIND11_ENUM_OP_CONV("__le__", a <= b); - PYBIND11_ENUM_OP_CONV("__ge__", a >= b); - PYBIND11_ENUM_OP_CONV("__and__", a & b); - PYBIND11_ENUM_OP_CONV("__rand__", a & b); - PYBIND11_ENUM_OP_CONV("__or__", a | b); - PYBIND11_ENUM_OP_CONV("__ror__", a | b); - PYBIND11_ENUM_OP_CONV("__xor__", a ^ b); - PYBIND11_ENUM_OP_CONV("__rxor__", a ^ b); - } - } else { - PYBIND11_ENUM_OP_STRICT("__eq__", int_(a).equal(int_(b)), return false); - PYBIND11_ENUM_OP_STRICT("__ne__", !int_(a).equal(int_(b)), return true); - - if (is_arithmetic) { - #define PYBIND11_THROW throw type_error("Expected an enumeration of matching type!"); - PYBIND11_ENUM_OP_STRICT("__lt__", int_(a) < int_(b), PYBIND11_THROW); - PYBIND11_ENUM_OP_STRICT("__gt__", int_(a) > int_(b), PYBIND11_THROW); - PYBIND11_ENUM_OP_STRICT("__le__", int_(a) <= int_(b), PYBIND11_THROW); - PYBIND11_ENUM_OP_STRICT("__ge__", int_(a) >= int_(b), PYBIND11_THROW); - #undef PYBIND11_THROW - } - } - - #undef PYBIND11_ENUM_OP_CONV - #undef PYBIND11_ENUM_OP_STRICT - - object getstate = cpp_function( - [](object arg) { return int_(arg); }, is_method(m_base)); - - m_base.attr("__getstate__") = getstate; - m_base.attr("__hash__") = getstate; - } - - PYBIND11_NOINLINE void value(char const* name_, object value, const char *doc = nullptr) { - dict entries = m_base.attr("__entries"); - str name(name_); - if (entries.contains(name)) { - std::string type_name = (std::string) str(m_base.attr("__name__")); - throw value_error(type_name + ": element \"" + std::string(name_) + "\" already exists!"); - } - - entries[name] = std::make_pair(value, doc); - m_base.attr(name) = value; - } - - PYBIND11_NOINLINE void export_values() { - dict entries = m_base.attr("__entries"); - for (const auto &kv : entries) - m_parent.attr(kv.first) = kv.second[int_(0)]; - } - - handle m_base; - handle m_parent; -}; - -NAMESPACE_END(detail) - -/// Binds C++ enumerations and enumeration classes to Python -template class enum_ : public class_ { -public: - using Base = class_; - using Base::def; - using Base::attr; - using Base::def_property_readonly; - using Base::def_property_readonly_static; - using Scalar = typename std::underlying_type::type; - - template - enum_(const handle &scope, const char *name, const Extra&... extra) - : class_(scope, name, extra...), m_base(*this, scope) { - constexpr bool is_arithmetic = detail::any_of...>::value; - constexpr bool is_convertible = std::is_convertible::value; - m_base.init(is_arithmetic, is_convertible); - - def(init([](Scalar i) { return static_cast(i); })); - def("__int__", [](Type value) { return (Scalar) value; }); - #if PY_MAJOR_VERSION < 3 - def("__long__", [](Type value) { return (Scalar) value; }); - #endif - cpp_function setstate( - [](Type &value, Scalar arg) { value = static_cast(arg); }, - is_method(*this)); - attr("__setstate__") = setstate; - } - - /// Export enumeration entries into the parent scope - enum_& export_values() { - m_base.export_values(); - return *this; - } - - /// Add an enumeration entry - enum_& value(char const* name, Type value, const char *doc = nullptr) { - m_base.value(name, pybind11::cast(value, return_value_policy::copy), doc); - return *this; - } - -private: - detail::enum_base m_base; -}; - -NAMESPACE_BEGIN(detail) - - -inline void keep_alive_impl(handle nurse, handle patient) { - if (!nurse || !patient) - pybind11_fail("Could not activate keep_alive!"); - - if (patient.is_none() || nurse.is_none()) - return; /* Nothing to keep alive or nothing to be kept alive by */ - - auto tinfo = all_type_info(Py_TYPE(nurse.ptr())); - if (!tinfo.empty()) { - /* It's a pybind-registered type, so we can store the patient in the - * internal list. */ - add_patient(nurse.ptr(), patient.ptr()); - } - else { - /* Fall back to clever approach based on weak references taken from - * Boost.Python. This is not used for pybind-registered types because - * the objects can be destroyed out-of-order in a GC pass. */ - cpp_function disable_lifesupport( - [patient](handle weakref) { patient.dec_ref(); weakref.dec_ref(); }); - - weakref wr(nurse, disable_lifesupport); - - patient.inc_ref(); /* reference patient and leak the weak reference */ - (void) wr.release(); - } -} - -PYBIND11_NOINLINE inline void keep_alive_impl(size_t Nurse, size_t Patient, function_call &call, handle ret) { - auto get_arg = [&](size_t n) { - if (n == 0) - return ret; - else if (n == 1 && call.init_self) - return call.init_self; - else if (n <= call.args.size()) - return call.args[n - 1]; - return handle(); - }; - - keep_alive_impl(get_arg(Nurse), get_arg(Patient)); -} - -inline std::pair all_type_info_get_cache(PyTypeObject *type) { - auto res = get_internals().registered_types_py -#ifdef __cpp_lib_unordered_map_try_emplace - .try_emplace(type); -#else - .emplace(type, std::vector()); -#endif - if (res.second) { - // New cache entry created; set up a weak reference to automatically remove it if the type - // gets destroyed: - weakref((PyObject *) type, cpp_function([type](handle wr) { - get_internals().registered_types_py.erase(type); - wr.dec_ref(); - })).release(); - } - - return res; -} - -template -struct iterator_state { - Iterator it; - Sentinel end; - bool first_or_done; -}; - -NAMESPACE_END(detail) - -/// Makes a python iterator from a first and past-the-end C++ InputIterator. -template ()), - typename... Extra> -iterator make_iterator(Iterator first, Sentinel last, Extra &&... extra) { - typedef detail::iterator_state state; - - if (!detail::get_type_info(typeid(state), false)) { - class_(handle(), "iterator", pybind11::module_local()) - .def("__iter__", [](state &s) -> state& { return s; }) - .def("__next__", [](state &s) -> ValueType { - if (!s.first_or_done) - ++s.it; - else - s.first_or_done = false; - if (s.it == s.end) { - s.first_or_done = true; - throw stop_iteration(); - } - return *s.it; - }, std::forward(extra)..., Policy); - } - - return cast(state{first, last, true}); -} - -/// Makes an python iterator over the keys (`.first`) of a iterator over pairs from a -/// first and past-the-end InputIterator. -template ()).first), - typename... Extra> -iterator make_key_iterator(Iterator first, Sentinel last, Extra &&... extra) { - typedef detail::iterator_state state; - - if (!detail::get_type_info(typeid(state), false)) { - class_(handle(), "iterator", pybind11::module_local()) - .def("__iter__", [](state &s) -> state& { return s; }) - .def("__next__", [](state &s) -> KeyType { - if (!s.first_or_done) - ++s.it; - else - s.first_or_done = false; - if (s.it == s.end) { - s.first_or_done = true; - throw stop_iteration(); - } - return (*s.it).first; - }, std::forward(extra)..., Policy); - } - - return cast(state{first, last, true}); -} - -/// Makes an iterator over values of an stl container or other container supporting -/// `std::begin()`/`std::end()` -template iterator make_iterator(Type &value, Extra&&... extra) { - return make_iterator(std::begin(value), std::end(value), extra...); -} - -/// Makes an iterator over the keys (`.first`) of a stl map-like container supporting -/// `std::begin()`/`std::end()` -template iterator make_key_iterator(Type &value, Extra&&... extra) { - return make_key_iterator(std::begin(value), std::end(value), extra...); -} - -template void implicitly_convertible() { - struct set_flag { - bool &flag; - set_flag(bool &flag) : flag(flag) { flag = true; } - ~set_flag() { flag = false; } - }; - auto implicit_caster = [](PyObject *obj, PyTypeObject *type) -> PyObject * { - static bool currently_used = false; - if (currently_used) // implicit conversions are non-reentrant - return nullptr; - set_flag flag_helper(currently_used); - if (!detail::make_caster().load(obj, false)) - return nullptr; - tuple args(1); - args[0] = obj; - PyObject *result = PyObject_Call((PyObject *) type, args.ptr(), nullptr); - if (result == nullptr) - PyErr_Clear(); - return result; - }; - - if (auto tinfo = detail::get_type_info(typeid(OutputType))) - tinfo->implicit_conversions.push_back(implicit_caster); - else - pybind11_fail("implicitly_convertible: Unable to find type " + type_id()); -} - -template -void register_exception_translator(ExceptionTranslator&& translator) { - detail::get_internals().registered_exception_translators.push_front( - std::forward(translator)); -} - -/** - * Wrapper to generate a new Python exception type. - * - * This should only be used with PyErr_SetString for now. - * It is not (yet) possible to use as a py::base. - * Template type argument is reserved for future use. - */ -template -class exception : public object { -public: - exception() = default; - exception(handle scope, const char *name, PyObject *base = PyExc_Exception) { - std::string full_name = scope.attr("__name__").cast() + - std::string(".") + name; - m_ptr = PyErr_NewException(const_cast(full_name.c_str()), base, NULL); - if (hasattr(scope, name)) - pybind11_fail("Error during initialization: multiple incompatible " - "definitions with name \"" + std::string(name) + "\""); - scope.attr(name) = *this; - } - - // Sets the current python exception to this exception object with the given message - void operator()(const char *message) { - PyErr_SetString(m_ptr, message); - } -}; - -NAMESPACE_BEGIN(detail) -// Returns a reference to a function-local static exception object used in the simple -// register_exception approach below. (It would be simpler to have the static local variable -// directly in register_exception, but that makes clang <3.5 segfault - issue #1349). -template -exception &get_exception_object() { static exception ex; return ex; } -NAMESPACE_END(detail) - -/** - * Registers a Python exception in `m` of the given `name` and installs an exception translator to - * translate the C++ exception to the created Python exception using the exceptions what() method. - * This is intended for simple exception translations; for more complex translation, register the - * exception object and translator directly. - */ -template -exception ®ister_exception(handle scope, - const char *name, - PyObject *base = PyExc_Exception) { - auto &ex = detail::get_exception_object(); - if (!ex) ex = exception(scope, name, base); - - register_exception_translator([](std::exception_ptr p) { - if (!p) return; - try { - std::rethrow_exception(p); - } catch (const CppException &e) { - detail::get_exception_object()(e.what()); - } - }); - return ex; -} - -NAMESPACE_BEGIN(detail) -PYBIND11_NOINLINE inline void print(tuple args, dict kwargs) { - auto strings = tuple(args.size()); - for (size_t i = 0; i < args.size(); ++i) { - strings[i] = str(args[i]); - } - auto sep = kwargs.contains("sep") ? kwargs["sep"] : cast(" "); - auto line = sep.attr("join")(strings); - - object file; - if (kwargs.contains("file")) { - file = kwargs["file"].cast(); - } else { - try { - file = module::import("sys").attr("stdout"); - } catch (const error_already_set &) { - /* If print() is called from code that is executed as - part of garbage collection during interpreter shutdown, - importing 'sys' can fail. Give up rather than crashing the - interpreter in this case. */ - return; - } - } - - auto write = file.attr("write"); - write(line); - write(kwargs.contains("end") ? kwargs["end"] : cast("\n")); - - if (kwargs.contains("flush") && kwargs["flush"].cast()) - file.attr("flush")(); -} -NAMESPACE_END(detail) - -template -void print(Args &&...args) { - auto c = detail::collect_arguments(std::forward(args)...); - detail::print(c.args(), c.kwargs()); -} - -#if defined(WITH_THREAD) && !defined(PYPY_VERSION) - -/* The functions below essentially reproduce the PyGILState_* API using a RAII - * pattern, but there are a few important differences: - * - * 1. When acquiring the GIL from an non-main thread during the finalization - * phase, the GILState API blindly terminates the calling thread, which - * is often not what is wanted. This API does not do this. - * - * 2. The gil_scoped_release function can optionally cut the relationship - * of a PyThreadState and its associated thread, which allows moving it to - * another thread (this is a fairly rare/advanced use case). - * - * 3. The reference count of an acquired thread state can be controlled. This - * can be handy to prevent cases where callbacks issued from an external - * thread would otherwise constantly construct and destroy thread state data - * structures. - * - * See the Python bindings of NanoGUI (http://github.com/wjakob/nanogui) for an - * example which uses features 2 and 3 to migrate the Python thread of - * execution to another thread (to run the event loop on the original thread, - * in this case). - */ - -class gil_scoped_acquire { -public: - PYBIND11_NOINLINE gil_scoped_acquire() { - auto const &internals = detail::get_internals(); - tstate = (PyThreadState *) PYBIND11_TLS_GET_VALUE(internals.tstate); - - if (!tstate) { - /* Check if the GIL was acquired using the PyGILState_* API instead (e.g. if - calling from a Python thread). Since we use a different key, this ensures - we don't create a new thread state and deadlock in PyEval_AcquireThread - below. Note we don't save this state with internals.tstate, since we don't - create it we would fail to clear it (its reference count should be > 0). */ - tstate = PyGILState_GetThisThreadState(); - } - - if (!tstate) { - tstate = PyThreadState_New(internals.istate); - #if !defined(NDEBUG) - if (!tstate) - pybind11_fail("scoped_acquire: could not create thread state!"); - #endif - tstate->gilstate_counter = 0; - PYBIND11_TLS_REPLACE_VALUE(internals.tstate, tstate); - } else { - release = detail::get_thread_state_unchecked() != tstate; - } - - if (release) { - /* Work around an annoying assertion in PyThreadState_Swap */ - #if defined(Py_DEBUG) - PyInterpreterState *interp = tstate->interp; - tstate->interp = nullptr; - #endif - PyEval_AcquireThread(tstate); - #if defined(Py_DEBUG) - tstate->interp = interp; - #endif - } - - inc_ref(); - } - - void inc_ref() { - ++tstate->gilstate_counter; - } - - PYBIND11_NOINLINE void dec_ref() { - --tstate->gilstate_counter; - #if !defined(NDEBUG) - if (detail::get_thread_state_unchecked() != tstate) - pybind11_fail("scoped_acquire::dec_ref(): thread state must be current!"); - if (tstate->gilstate_counter < 0) - pybind11_fail("scoped_acquire::dec_ref(): reference count underflow!"); - #endif - if (tstate->gilstate_counter == 0) { - #if !defined(NDEBUG) - if (!release) - pybind11_fail("scoped_acquire::dec_ref(): internal error!"); - #endif - PyThreadState_Clear(tstate); - PyThreadState_DeleteCurrent(); - PYBIND11_TLS_DELETE_VALUE(detail::get_internals().tstate); - release = false; - } - } - - PYBIND11_NOINLINE ~gil_scoped_acquire() { - dec_ref(); - if (release) - PyEval_SaveThread(); - } -private: - PyThreadState *tstate = nullptr; - bool release = true; -}; - -class gil_scoped_release { -public: - explicit gil_scoped_release(bool disassoc = false) : disassoc(disassoc) { - // `get_internals()` must be called here unconditionally in order to initialize - // `internals.tstate` for subsequent `gil_scoped_acquire` calls. Otherwise, an - // initialization race could occur as multiple threads try `gil_scoped_acquire`. - const auto &internals = detail::get_internals(); - tstate = PyEval_SaveThread(); - if (disassoc) { - auto key = internals.tstate; - PYBIND11_TLS_DELETE_VALUE(key); - } - } - ~gil_scoped_release() { - if (!tstate) - return; - PyEval_RestoreThread(tstate); - if (disassoc) { - auto key = detail::get_internals().tstate; - PYBIND11_TLS_REPLACE_VALUE(key, tstate); - } - } -private: - PyThreadState *tstate; - bool disassoc; -}; -#elif defined(PYPY_VERSION) -class gil_scoped_acquire { - PyGILState_STATE state; -public: - gil_scoped_acquire() { state = PyGILState_Ensure(); } - ~gil_scoped_acquire() { PyGILState_Release(state); } -}; - -class gil_scoped_release { - PyThreadState *state; -public: - gil_scoped_release() { state = PyEval_SaveThread(); } - ~gil_scoped_release() { PyEval_RestoreThread(state); } -}; -#else -class gil_scoped_acquire { }; -class gil_scoped_release { }; -#endif - -error_already_set::~error_already_set() { - if (type) { - error_scope scope; - gil_scoped_acquire gil; - type.release().dec_ref(); - value.release().dec_ref(); - trace.release().dec_ref(); - } -} - -inline function get_type_overload(const void *this_ptr, const detail::type_info *this_type, const char *name) { - handle self = detail::get_object_handle(this_ptr, this_type); - if (!self) - return function(); - handle type = self.get_type(); - auto key = std::make_pair(type.ptr(), name); - - /* Cache functions that aren't overloaded in Python to avoid - many costly Python dictionary lookups below */ - auto &cache = detail::get_internals().inactive_overload_cache; - if (cache.find(key) != cache.end()) - return function(); - - function overload = getattr(self, name, function()); - if (overload.is_cpp_function()) { - cache.insert(key); - return function(); - } - - /* Don't call dispatch code if invoked from overridden function. - Unfortunately this doesn't work on PyPy. */ -#if !defined(PYPY_VERSION) - PyFrameObject *frame = PyThreadState_Get()->frame; - if (frame && (std::string) str(frame->f_code->co_name) == name && - frame->f_code->co_argcount > 0) { - PyFrame_FastToLocals(frame); - PyObject *self_caller = PyDict_GetItem( - frame->f_locals, PyTuple_GET_ITEM(frame->f_code->co_varnames, 0)); - if (self_caller == self.ptr()) - return function(); - } -#else - /* PyPy currently doesn't provide a detailed cpyext emulation of - frame objects, so we have to emulate this using Python. This - is going to be slow..*/ - dict d; d["self"] = self; d["name"] = pybind11::str(name); - PyObject *result = PyRun_String( - "import inspect\n" - "frame = inspect.currentframe()\n" - "if frame is not None:\n" - " frame = frame.f_back\n" - " if frame is not None and str(frame.f_code.co_name) == name and " - "frame.f_code.co_argcount > 0:\n" - " self_caller = frame.f_locals[frame.f_code.co_varnames[0]]\n" - " if self_caller == self:\n" - " self = None\n", - Py_file_input, d.ptr(), d.ptr()); - if (result == nullptr) - throw error_already_set(); - if (d["self"].is_none()) - return function(); - Py_DECREF(result); -#endif - - return overload; -} - -template function get_overload(const T *this_ptr, const char *name) { - auto tinfo = detail::get_type_info(typeid(T)); - return tinfo ? get_type_overload(this_ptr, tinfo, name) : function(); -} - -#define PYBIND11_OVERLOAD_INT(ret_type, cname, name, ...) { \ - pybind11::gil_scoped_acquire gil; \ - pybind11::function overload = pybind11::get_overload(static_cast(this), name); \ - if (overload) { \ - auto o = overload(__VA_ARGS__); \ - if (pybind11::detail::cast_is_temporary_value_reference::value) { \ - static pybind11::detail::overload_caster_t caster; \ - return pybind11::detail::cast_ref(std::move(o), caster); \ - } \ - else return pybind11::detail::cast_safe(std::move(o)); \ - } \ - } - -#define PYBIND11_OVERLOAD_NAME(ret_type, cname, name, fn, ...) \ - PYBIND11_OVERLOAD_INT(PYBIND11_TYPE(ret_type), PYBIND11_TYPE(cname), name, __VA_ARGS__) \ - return cname::fn(__VA_ARGS__) - -#define PYBIND11_OVERLOAD_PURE_NAME(ret_type, cname, name, fn, ...) \ - PYBIND11_OVERLOAD_INT(PYBIND11_TYPE(ret_type), PYBIND11_TYPE(cname), name, __VA_ARGS__) \ - pybind11::pybind11_fail("Tried to call pure virtual function \"" PYBIND11_STRINGIFY(cname) "::" name "\""); - -#define PYBIND11_OVERLOAD(ret_type, cname, fn, ...) \ - PYBIND11_OVERLOAD_NAME(PYBIND11_TYPE(ret_type), PYBIND11_TYPE(cname), #fn, fn, __VA_ARGS__) - -#define PYBIND11_OVERLOAD_PURE(ret_type, cname, fn, ...) \ - PYBIND11_OVERLOAD_PURE_NAME(PYBIND11_TYPE(ret_type), PYBIND11_TYPE(cname), #fn, fn, __VA_ARGS__) - -NAMESPACE_END(PYBIND11_NAMESPACE) - -#if defined(_MSC_VER) && !defined(__INTEL_COMPILER) -# pragma warning(pop) -#elif defined(__GNUG__) && !defined(__clang__) -# pragma GCC diagnostic pop -#endif diff --git a/ptocr/postprocess/dbprocess/include/pybind11/pytypes.h b/ptocr/postprocess/dbprocess/include/pybind11/pytypes.h deleted file mode 100644 index 3329fda..0000000 --- a/ptocr/postprocess/dbprocess/include/pybind11/pytypes.h +++ /dev/null @@ -1,1438 +0,0 @@ -/* - pybind11/pytypes.h: Convenience wrapper classes for basic Python types - - Copyright (c) 2016 Wenzel Jakob - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#include "detail/common.h" -#include "buffer_info.h" -#include -#include - -NAMESPACE_BEGIN(PYBIND11_NAMESPACE) - -/* A few forward declarations */ -class handle; class object; -class str; class iterator; -struct arg; struct arg_v; - -NAMESPACE_BEGIN(detail) -class args_proxy; -inline bool isinstance_generic(handle obj, const std::type_info &tp); - -// Accessor forward declarations -template class accessor; -namespace accessor_policies { - struct obj_attr; - struct str_attr; - struct generic_item; - struct sequence_item; - struct list_item; - struct tuple_item; -} -using obj_attr_accessor = accessor; -using str_attr_accessor = accessor; -using item_accessor = accessor; -using sequence_accessor = accessor; -using list_accessor = accessor; -using tuple_accessor = accessor; - -/// Tag and check to identify a class which implements the Python object API -class pyobject_tag { }; -template using is_pyobject = std::is_base_of>; - -/** \rst - A mixin class which adds common functions to `handle`, `object` and various accessors. - The only requirement for `Derived` is to implement ``PyObject *Derived::ptr() const``. -\endrst */ -template -class object_api : public pyobject_tag { - const Derived &derived() const { return static_cast(*this); } - -public: - /** \rst - Return an iterator equivalent to calling ``iter()`` in Python. The object - must be a collection which supports the iteration protocol. - \endrst */ - iterator begin() const; - /// Return a sentinel which ends iteration. - iterator end() const; - - /** \rst - Return an internal functor to invoke the object's sequence protocol. Casting - the returned ``detail::item_accessor`` instance to a `handle` or `object` - subclass causes a corresponding call to ``__getitem__``. Assigning a `handle` - or `object` subclass causes a call to ``__setitem__``. - \endrst */ - item_accessor operator[](handle key) const; - /// See above (the only difference is that they key is provided as a string literal) - item_accessor operator[](const char *key) const; - - /** \rst - Return an internal functor to access the object's attributes. Casting the - returned ``detail::obj_attr_accessor`` instance to a `handle` or `object` - subclass causes a corresponding call to ``getattr``. Assigning a `handle` - or `object` subclass causes a call to ``setattr``. - \endrst */ - obj_attr_accessor attr(handle key) const; - /// See above (the only difference is that they key is provided as a string literal) - str_attr_accessor attr(const char *key) const; - - /** \rst - Matches * unpacking in Python, e.g. to unpack arguments out of a ``tuple`` - or ``list`` for a function call. Applying another * to the result yields - ** unpacking, e.g. to unpack a dict as function keyword arguments. - See :ref:`calling_python_functions`. - \endrst */ - args_proxy operator*() const; - - /// Check if the given item is contained within this object, i.e. ``item in obj``. - template bool contains(T &&item) const; - - /** \rst - Assuming the Python object is a function or implements the ``__call__`` - protocol, ``operator()`` invokes the underlying function, passing an - arbitrary set of parameters. The result is returned as a `object` and - may need to be converted back into a Python object using `handle::cast()`. - - When some of the arguments cannot be converted to Python objects, the - function will throw a `cast_error` exception. When the Python function - call fails, a `error_already_set` exception is thrown. - \endrst */ - template - object operator()(Args &&...args) const; - template - PYBIND11_DEPRECATED("call(...) was deprecated in favor of operator()(...)") - object call(Args&&... args) const; - - /// Equivalent to ``obj is other`` in Python. - bool is(object_api const& other) const { return derived().ptr() == other.derived().ptr(); } - /// Equivalent to ``obj is None`` in Python. - bool is_none() const { return derived().ptr() == Py_None; } - /// Equivalent to obj == other in Python - bool equal(object_api const &other) const { return rich_compare(other, Py_EQ); } - bool not_equal(object_api const &other) const { return rich_compare(other, Py_NE); } - bool operator<(object_api const &other) const { return rich_compare(other, Py_LT); } - bool operator<=(object_api const &other) const { return rich_compare(other, Py_LE); } - bool operator>(object_api const &other) const { return rich_compare(other, Py_GT); } - bool operator>=(object_api const &other) const { return rich_compare(other, Py_GE); } - - object operator-() const; - object operator~() const; - object operator+(object_api const &other) const; - object operator+=(object_api const &other) const; - object operator-(object_api const &other) const; - object operator-=(object_api const &other) const; - object operator*(object_api const &other) const; - object operator*=(object_api const &other) const; - object operator/(object_api const &other) const; - object operator/=(object_api const &other) const; - object operator|(object_api const &other) const; - object operator|=(object_api const &other) const; - object operator&(object_api const &other) const; - object operator&=(object_api const &other) const; - object operator^(object_api const &other) const; - object operator^=(object_api const &other) const; - object operator<<(object_api const &other) const; - object operator<<=(object_api const &other) const; - object operator>>(object_api const &other) const; - object operator>>=(object_api const &other) const; - - PYBIND11_DEPRECATED("Use py::str(obj) instead") - pybind11::str str() const; - - /// Get or set the object's docstring, i.e. ``obj.__doc__``. - str_attr_accessor doc() const; - - /// Return the object's current reference count - int ref_count() const { return static_cast(Py_REFCNT(derived().ptr())); } - /// Return a handle to the Python type object underlying the instance - handle get_type() const; - -private: - bool rich_compare(object_api const &other, int value) const; -}; - -NAMESPACE_END(detail) - -/** \rst - Holds a reference to a Python object (no reference counting) - - The `handle` class is a thin wrapper around an arbitrary Python object (i.e. a - ``PyObject *`` in Python's C API). It does not perform any automatic reference - counting and merely provides a basic C++ interface to various Python API functions. - - .. seealso:: - The `object` class inherits from `handle` and adds automatic reference - counting features. -\endrst */ -class handle : public detail::object_api { -public: - /// The default constructor creates a handle with a ``nullptr``-valued pointer - handle() = default; - /// Creates a ``handle`` from the given raw Python object pointer - handle(PyObject *ptr) : m_ptr(ptr) { } // Allow implicit conversion from PyObject* - - /// Return the underlying ``PyObject *`` pointer - PyObject *ptr() const { return m_ptr; } - PyObject *&ptr() { return m_ptr; } - - /** \rst - Manually increase the reference count of the Python object. Usually, it is - preferable to use the `object` class which derives from `handle` and calls - this function automatically. Returns a reference to itself. - \endrst */ - const handle& inc_ref() const & { Py_XINCREF(m_ptr); return *this; } - - /** \rst - Manually decrease the reference count of the Python object. Usually, it is - preferable to use the `object` class which derives from `handle` and calls - this function automatically. Returns a reference to itself. - \endrst */ - const handle& dec_ref() const & { Py_XDECREF(m_ptr); return *this; } - - /** \rst - Attempt to cast the Python object into the given C++ type. A `cast_error` - will be throw upon failure. - \endrst */ - template T cast() const; - /// Return ``true`` when the `handle` wraps a valid Python object - explicit operator bool() const { return m_ptr != nullptr; } - /** \rst - Deprecated: Check that the underlying pointers are the same. - Equivalent to ``obj1 is obj2`` in Python. - \endrst */ - PYBIND11_DEPRECATED("Use obj1.is(obj2) instead") - bool operator==(const handle &h) const { return m_ptr == h.m_ptr; } - PYBIND11_DEPRECATED("Use !obj1.is(obj2) instead") - bool operator!=(const handle &h) const { return m_ptr != h.m_ptr; } - PYBIND11_DEPRECATED("Use handle::operator bool() instead") - bool check() const { return m_ptr != nullptr; } -protected: - PyObject *m_ptr = nullptr; -}; - -/** \rst - Holds a reference to a Python object (with reference counting) - - Like `handle`, the `object` class is a thin wrapper around an arbitrary Python - object (i.e. a ``PyObject *`` in Python's C API). In contrast to `handle`, it - optionally increases the object's reference count upon construction, and it - *always* decreases the reference count when the `object` instance goes out of - scope and is destructed. When using `object` instances consistently, it is much - easier to get reference counting right at the first attempt. -\endrst */ -class object : public handle { -public: - object() = default; - PYBIND11_DEPRECATED("Use reinterpret_borrow() or reinterpret_steal()") - object(handle h, bool is_borrowed) : handle(h) { if (is_borrowed) inc_ref(); } - /// Copy constructor; always increases the reference count - object(const object &o) : handle(o) { inc_ref(); } - /// Move constructor; steals the object from ``other`` and preserves its reference count - object(object &&other) noexcept { m_ptr = other.m_ptr; other.m_ptr = nullptr; } - /// Destructor; automatically calls `handle::dec_ref()` - ~object() { dec_ref(); } - - /** \rst - Resets the internal pointer to ``nullptr`` without without decreasing the - object's reference count. The function returns a raw handle to the original - Python object. - \endrst */ - handle release() { - PyObject *tmp = m_ptr; - m_ptr = nullptr; - return handle(tmp); - } - - object& operator=(const object &other) { - other.inc_ref(); - dec_ref(); - m_ptr = other.m_ptr; - return *this; - } - - object& operator=(object &&other) noexcept { - if (this != &other) { - handle temp(m_ptr); - m_ptr = other.m_ptr; - other.m_ptr = nullptr; - temp.dec_ref(); - } - return *this; - } - - // Calling cast() on an object lvalue just copies (via handle::cast) - template T cast() const &; - // Calling on an object rvalue does a move, if needed and/or possible - template T cast() &&; - -protected: - // Tags for choosing constructors from raw PyObject * - struct borrowed_t { }; - struct stolen_t { }; - - template friend T reinterpret_borrow(handle); - template friend T reinterpret_steal(handle); - -public: - // Only accessible from derived classes and the reinterpret_* functions - object(handle h, borrowed_t) : handle(h) { inc_ref(); } - object(handle h, stolen_t) : handle(h) { } -}; - -/** \rst - Declare that a `handle` or ``PyObject *`` is a certain type and borrow the reference. - The target type ``T`` must be `object` or one of its derived classes. The function - doesn't do any conversions or checks. It's up to the user to make sure that the - target type is correct. - - .. code-block:: cpp - - PyObject *p = PyList_GetItem(obj, index); - py::object o = reinterpret_borrow(p); - // or - py::tuple t = reinterpret_borrow(p); // <-- `p` must be already be a `tuple` -\endrst */ -template T reinterpret_borrow(handle h) { return {h, object::borrowed_t{}}; } - -/** \rst - Like `reinterpret_borrow`, but steals the reference. - - .. code-block:: cpp - - PyObject *p = PyObject_Str(obj); - py::str s = reinterpret_steal(p); // <-- `p` must be already be a `str` -\endrst */ -template T reinterpret_steal(handle h) { return {h, object::stolen_t{}}; } - -NAMESPACE_BEGIN(detail) -inline std::string error_string(); -NAMESPACE_END(detail) - -/// Fetch and hold an error which was already set in Python. An instance of this is typically -/// thrown to propagate python-side errors back through C++ which can either be caught manually or -/// else falls back to the function dispatcher (which then raises the captured error back to -/// python). -class error_already_set : public std::runtime_error { -public: - /// Constructs a new exception from the current Python error indicator, if any. The current - /// Python error indicator will be cleared. - error_already_set() : std::runtime_error(detail::error_string()) { - PyErr_Fetch(&type.ptr(), &value.ptr(), &trace.ptr()); - } - - error_already_set(const error_already_set &) = default; - error_already_set(error_already_set &&) = default; - - inline ~error_already_set(); - - /// Give the currently-held error back to Python, if any. If there is currently a Python error - /// already set it is cleared first. After this call, the current object no longer stores the - /// error variables (but the `.what()` string is still available). - void restore() { PyErr_Restore(type.release().ptr(), value.release().ptr(), trace.release().ptr()); } - - // Does nothing; provided for backwards compatibility. - PYBIND11_DEPRECATED("Use of error_already_set.clear() is deprecated") - void clear() {} - - /// Check if the currently trapped error type matches the given Python exception class (or a - /// subclass thereof). May also be passed a tuple to search for any exception class matches in - /// the given tuple. - bool matches(handle ex) const { return PyErr_GivenExceptionMatches(ex.ptr(), type.ptr()); } - -private: - object type, value, trace; -}; - -/** \defgroup python_builtins _ - Unless stated otherwise, the following C++ functions behave the same - as their Python counterparts. - */ - -/** \ingroup python_builtins - \rst - Return true if ``obj`` is an instance of ``T``. Type ``T`` must be a subclass of - `object` or a class which was exposed to Python as ``py::class_``. -\endrst */ -template ::value, int> = 0> -bool isinstance(handle obj) { return T::check_(obj); } - -template ::value, int> = 0> -bool isinstance(handle obj) { return detail::isinstance_generic(obj, typeid(T)); } - -template <> inline bool isinstance(handle obj) = delete; -template <> inline bool isinstance(handle obj) { return obj.ptr() != nullptr; } - -/// \ingroup python_builtins -/// Return true if ``obj`` is an instance of the ``type``. -inline bool isinstance(handle obj, handle type) { - const auto result = PyObject_IsInstance(obj.ptr(), type.ptr()); - if (result == -1) - throw error_already_set(); - return result != 0; -} - -/// \addtogroup python_builtins -/// @{ -inline bool hasattr(handle obj, handle name) { - return PyObject_HasAttr(obj.ptr(), name.ptr()) == 1; -} - -inline bool hasattr(handle obj, const char *name) { - return PyObject_HasAttrString(obj.ptr(), name) == 1; -} - -inline void delattr(handle obj, handle name) { - if (PyObject_DelAttr(obj.ptr(), name.ptr()) != 0) { throw error_already_set(); } -} - -inline void delattr(handle obj, const char *name) { - if (PyObject_DelAttrString(obj.ptr(), name) != 0) { throw error_already_set(); } -} - -inline object getattr(handle obj, handle name) { - PyObject *result = PyObject_GetAttr(obj.ptr(), name.ptr()); - if (!result) { throw error_already_set(); } - return reinterpret_steal(result); -} - -inline object getattr(handle obj, const char *name) { - PyObject *result = PyObject_GetAttrString(obj.ptr(), name); - if (!result) { throw error_already_set(); } - return reinterpret_steal(result); -} - -inline object getattr(handle obj, handle name, handle default_) { - if (PyObject *result = PyObject_GetAttr(obj.ptr(), name.ptr())) { - return reinterpret_steal(result); - } else { - PyErr_Clear(); - return reinterpret_borrow(default_); - } -} - -inline object getattr(handle obj, const char *name, handle default_) { - if (PyObject *result = PyObject_GetAttrString(obj.ptr(), name)) { - return reinterpret_steal(result); - } else { - PyErr_Clear(); - return reinterpret_borrow(default_); - } -} - -inline void setattr(handle obj, handle name, handle value) { - if (PyObject_SetAttr(obj.ptr(), name.ptr(), value.ptr()) != 0) { throw error_already_set(); } -} - -inline void setattr(handle obj, const char *name, handle value) { - if (PyObject_SetAttrString(obj.ptr(), name, value.ptr()) != 0) { throw error_already_set(); } -} - -inline ssize_t hash(handle obj) { - auto h = PyObject_Hash(obj.ptr()); - if (h == -1) { throw error_already_set(); } - return h; -} - -/// @} python_builtins - -NAMESPACE_BEGIN(detail) -inline handle get_function(handle value) { - if (value) { -#if PY_MAJOR_VERSION >= 3 - if (PyInstanceMethod_Check(value.ptr())) - value = PyInstanceMethod_GET_FUNCTION(value.ptr()); - else -#endif - if (PyMethod_Check(value.ptr())) - value = PyMethod_GET_FUNCTION(value.ptr()); - } - return value; -} - -// Helper aliases/functions to support implicit casting of values given to python accessors/methods. -// When given a pyobject, this simply returns the pyobject as-is; for other C++ type, the value goes -// through pybind11::cast(obj) to convert it to an `object`. -template ::value, int> = 0> -auto object_or_cast(T &&o) -> decltype(std::forward(o)) { return std::forward(o); } -// The following casting version is implemented in cast.h: -template ::value, int> = 0> -object object_or_cast(T &&o); -// Match a PyObject*, which we want to convert directly to handle via its converting constructor -inline handle object_or_cast(PyObject *ptr) { return ptr; } - -template -class accessor : public object_api> { - using key_type = typename Policy::key_type; - -public: - accessor(handle obj, key_type key) : obj(obj), key(std::move(key)) { } - accessor(const accessor &) = default; - accessor(accessor &&) = default; - - // accessor overload required to override default assignment operator (templates are not allowed - // to replace default compiler-generated assignments). - void operator=(const accessor &a) && { std::move(*this).operator=(handle(a)); } - void operator=(const accessor &a) & { operator=(handle(a)); } - - template void operator=(T &&value) && { - Policy::set(obj, key, object_or_cast(std::forward(value))); - } - template void operator=(T &&value) & { - get_cache() = reinterpret_borrow(object_or_cast(std::forward(value))); - } - - template - PYBIND11_DEPRECATED("Use of obj.attr(...) as bool is deprecated in favor of pybind11::hasattr(obj, ...)") - explicit operator enable_if_t::value || - std::is_same::value, bool>() const { - return hasattr(obj, key); - } - template - PYBIND11_DEPRECATED("Use of obj[key] as bool is deprecated in favor of obj.contains(key)") - explicit operator enable_if_t::value, bool>() const { - return obj.contains(key); - } - - operator object() const { return get_cache(); } - PyObject *ptr() const { return get_cache().ptr(); } - template T cast() const { return get_cache().template cast(); } - -private: - object &get_cache() const { - if (!cache) { cache = Policy::get(obj, key); } - return cache; - } - -private: - handle obj; - key_type key; - mutable object cache; -}; - -NAMESPACE_BEGIN(accessor_policies) -struct obj_attr { - using key_type = object; - static object get(handle obj, handle key) { return getattr(obj, key); } - static void set(handle obj, handle key, handle val) { setattr(obj, key, val); } -}; - -struct str_attr { - using key_type = const char *; - static object get(handle obj, const char *key) { return getattr(obj, key); } - static void set(handle obj, const char *key, handle val) { setattr(obj, key, val); } -}; - -struct generic_item { - using key_type = object; - - static object get(handle obj, handle key) { - PyObject *result = PyObject_GetItem(obj.ptr(), key.ptr()); - if (!result) { throw error_already_set(); } - return reinterpret_steal(result); - } - - static void set(handle obj, handle key, handle val) { - if (PyObject_SetItem(obj.ptr(), key.ptr(), val.ptr()) != 0) { throw error_already_set(); } - } -}; - -struct sequence_item { - using key_type = size_t; - - static object get(handle obj, size_t index) { - PyObject *result = PySequence_GetItem(obj.ptr(), static_cast(index)); - if (!result) { throw error_already_set(); } - return reinterpret_steal(result); - } - - static void set(handle obj, size_t index, handle val) { - // PySequence_SetItem does not steal a reference to 'val' - if (PySequence_SetItem(obj.ptr(), static_cast(index), val.ptr()) != 0) { - throw error_already_set(); - } - } -}; - -struct list_item { - using key_type = size_t; - - static object get(handle obj, size_t index) { - PyObject *result = PyList_GetItem(obj.ptr(), static_cast(index)); - if (!result) { throw error_already_set(); } - return reinterpret_borrow(result); - } - - static void set(handle obj, size_t index, handle val) { - // PyList_SetItem steals a reference to 'val' - if (PyList_SetItem(obj.ptr(), static_cast(index), val.inc_ref().ptr()) != 0) { - throw error_already_set(); - } - } -}; - -struct tuple_item { - using key_type = size_t; - - static object get(handle obj, size_t index) { - PyObject *result = PyTuple_GetItem(obj.ptr(), static_cast(index)); - if (!result) { throw error_already_set(); } - return reinterpret_borrow(result); - } - - static void set(handle obj, size_t index, handle val) { - // PyTuple_SetItem steals a reference to 'val' - if (PyTuple_SetItem(obj.ptr(), static_cast(index), val.inc_ref().ptr()) != 0) { - throw error_already_set(); - } - } -}; -NAMESPACE_END(accessor_policies) - -/// STL iterator template used for tuple, list, sequence and dict -template -class generic_iterator : public Policy { - using It = generic_iterator; - -public: - using difference_type = ssize_t; - using iterator_category = typename Policy::iterator_category; - using value_type = typename Policy::value_type; - using reference = typename Policy::reference; - using pointer = typename Policy::pointer; - - generic_iterator() = default; - generic_iterator(handle seq, ssize_t index) : Policy(seq, index) { } - - reference operator*() const { return Policy::dereference(); } - reference operator[](difference_type n) const { return *(*this + n); } - pointer operator->() const { return **this; } - - It &operator++() { Policy::increment(); return *this; } - It operator++(int) { auto copy = *this; Policy::increment(); return copy; } - It &operator--() { Policy::decrement(); return *this; } - It operator--(int) { auto copy = *this; Policy::decrement(); return copy; } - It &operator+=(difference_type n) { Policy::advance(n); return *this; } - It &operator-=(difference_type n) { Policy::advance(-n); return *this; } - - friend It operator+(const It &a, difference_type n) { auto copy = a; return copy += n; } - friend It operator+(difference_type n, const It &b) { return b + n; } - friend It operator-(const It &a, difference_type n) { auto copy = a; return copy -= n; } - friend difference_type operator-(const It &a, const It &b) { return a.distance_to(b); } - - friend bool operator==(const It &a, const It &b) { return a.equal(b); } - friend bool operator!=(const It &a, const It &b) { return !(a == b); } - friend bool operator< (const It &a, const It &b) { return b - a > 0; } - friend bool operator> (const It &a, const It &b) { return b < a; } - friend bool operator>=(const It &a, const It &b) { return !(a < b); } - friend bool operator<=(const It &a, const It &b) { return !(a > b); } -}; - -NAMESPACE_BEGIN(iterator_policies) -/// Quick proxy class needed to implement ``operator->`` for iterators which can't return pointers -template -struct arrow_proxy { - T value; - - arrow_proxy(T &&value) : value(std::move(value)) { } - T *operator->() const { return &value; } -}; - -/// Lightweight iterator policy using just a simple pointer: see ``PySequence_Fast_ITEMS`` -class sequence_fast_readonly { -protected: - using iterator_category = std::random_access_iterator_tag; - using value_type = handle; - using reference = const handle; - using pointer = arrow_proxy; - - sequence_fast_readonly(handle obj, ssize_t n) : ptr(PySequence_Fast_ITEMS(obj.ptr()) + n) { } - - reference dereference() const { return *ptr; } - void increment() { ++ptr; } - void decrement() { --ptr; } - void advance(ssize_t n) { ptr += n; } - bool equal(const sequence_fast_readonly &b) const { return ptr == b.ptr; } - ssize_t distance_to(const sequence_fast_readonly &b) const { return ptr - b.ptr; } - -private: - PyObject **ptr; -}; - -/// Full read and write access using the sequence protocol: see ``detail::sequence_accessor`` -class sequence_slow_readwrite { -protected: - using iterator_category = std::random_access_iterator_tag; - using value_type = object; - using reference = sequence_accessor; - using pointer = arrow_proxy; - - sequence_slow_readwrite(handle obj, ssize_t index) : obj(obj), index(index) { } - - reference dereference() const { return {obj, static_cast(index)}; } - void increment() { ++index; } - void decrement() { --index; } - void advance(ssize_t n) { index += n; } - bool equal(const sequence_slow_readwrite &b) const { return index == b.index; } - ssize_t distance_to(const sequence_slow_readwrite &b) const { return index - b.index; } - -private: - handle obj; - ssize_t index; -}; - -/// Python's dictionary protocol permits this to be a forward iterator -class dict_readonly { -protected: - using iterator_category = std::forward_iterator_tag; - using value_type = std::pair; - using reference = const value_type; - using pointer = arrow_proxy; - - dict_readonly() = default; - dict_readonly(handle obj, ssize_t pos) : obj(obj), pos(pos) { increment(); } - - reference dereference() const { return {key, value}; } - void increment() { if (!PyDict_Next(obj.ptr(), &pos, &key, &value)) { pos = -1; } } - bool equal(const dict_readonly &b) const { return pos == b.pos; } - -private: - handle obj; - PyObject *key, *value; - ssize_t pos = -1; -}; -NAMESPACE_END(iterator_policies) - -#if !defined(PYPY_VERSION) -using tuple_iterator = generic_iterator; -using list_iterator = generic_iterator; -#else -using tuple_iterator = generic_iterator; -using list_iterator = generic_iterator; -#endif - -using sequence_iterator = generic_iterator; -using dict_iterator = generic_iterator; - -inline bool PyIterable_Check(PyObject *obj) { - PyObject *iter = PyObject_GetIter(obj); - if (iter) { - Py_DECREF(iter); - return true; - } else { - PyErr_Clear(); - return false; - } -} - -inline bool PyNone_Check(PyObject *o) { return o == Py_None; } -#if PY_MAJOR_VERSION >= 3 -inline bool PyEllipsis_Check(PyObject *o) { return o == Py_Ellipsis; } -#endif - -inline bool PyUnicode_Check_Permissive(PyObject *o) { return PyUnicode_Check(o) || PYBIND11_BYTES_CHECK(o); } - -class kwargs_proxy : public handle { -public: - explicit kwargs_proxy(handle h) : handle(h) { } -}; - -class args_proxy : public handle { -public: - explicit args_proxy(handle h) : handle(h) { } - kwargs_proxy operator*() const { return kwargs_proxy(*this); } -}; - -/// Python argument categories (using PEP 448 terms) -template using is_keyword = std::is_base_of; -template using is_s_unpacking = std::is_same; // * unpacking -template using is_ds_unpacking = std::is_same; // ** unpacking -template using is_positional = satisfies_none_of; -template using is_keyword_or_ds = satisfies_any_of; - -// Call argument collector forward declarations -template -class simple_collector; -template -class unpacking_collector; - -NAMESPACE_END(detail) - -// TODO: After the deprecated constructors are removed, this macro can be simplified by -// inheriting ctors: `using Parent::Parent`. It's not an option right now because -// the `using` statement triggers the parent deprecation warning even if the ctor -// isn't even used. -#define PYBIND11_OBJECT_COMMON(Name, Parent, CheckFun) \ - public: \ - PYBIND11_DEPRECATED("Use reinterpret_borrow<"#Name">() or reinterpret_steal<"#Name">()") \ - Name(handle h, bool is_borrowed) : Parent(is_borrowed ? Parent(h, borrowed_t{}) : Parent(h, stolen_t{})) { } \ - Name(handle h, borrowed_t) : Parent(h, borrowed_t{}) { } \ - Name(handle h, stolen_t) : Parent(h, stolen_t{}) { } \ - PYBIND11_DEPRECATED("Use py::isinstance(obj) instead") \ - bool check() const { return m_ptr != nullptr && (bool) CheckFun(m_ptr); } \ - static bool check_(handle h) { return h.ptr() != nullptr && CheckFun(h.ptr()); } - -#define PYBIND11_OBJECT_CVT(Name, Parent, CheckFun, ConvertFun) \ - PYBIND11_OBJECT_COMMON(Name, Parent, CheckFun) \ - /* This is deliberately not 'explicit' to allow implicit conversion from object: */ \ - Name(const object &o) \ - : Parent(check_(o) ? o.inc_ref().ptr() : ConvertFun(o.ptr()), stolen_t{}) \ - { if (!m_ptr) throw error_already_set(); } \ - Name(object &&o) \ - : Parent(check_(o) ? o.release().ptr() : ConvertFun(o.ptr()), stolen_t{}) \ - { if (!m_ptr) throw error_already_set(); } \ - template \ - Name(const ::pybind11::detail::accessor &a) : Name(object(a)) { } - -#define PYBIND11_OBJECT(Name, Parent, CheckFun) \ - PYBIND11_OBJECT_COMMON(Name, Parent, CheckFun) \ - /* This is deliberately not 'explicit' to allow implicit conversion from object: */ \ - Name(const object &o) : Parent(o) { } \ - Name(object &&o) : Parent(std::move(o)) { } - -#define PYBIND11_OBJECT_DEFAULT(Name, Parent, CheckFun) \ - PYBIND11_OBJECT(Name, Parent, CheckFun) \ - Name() : Parent() { } - -/// \addtogroup pytypes -/// @{ - -/** \rst - Wraps a Python iterator so that it can also be used as a C++ input iterator - - Caveat: copying an iterator does not (and cannot) clone the internal - state of the Python iterable. This also applies to the post-increment - operator. This iterator should only be used to retrieve the current - value using ``operator*()``. -\endrst */ -class iterator : public object { -public: - using iterator_category = std::input_iterator_tag; - using difference_type = ssize_t; - using value_type = handle; - using reference = const handle; - using pointer = const handle *; - - PYBIND11_OBJECT_DEFAULT(iterator, object, PyIter_Check) - - iterator& operator++() { - advance(); - return *this; - } - - iterator operator++(int) { - auto rv = *this; - advance(); - return rv; - } - - reference operator*() const { - if (m_ptr && !value.ptr()) { - auto& self = const_cast(*this); - self.advance(); - } - return value; - } - - pointer operator->() const { operator*(); return &value; } - - /** \rst - The value which marks the end of the iteration. ``it == iterator::sentinel()`` - is equivalent to catching ``StopIteration`` in Python. - - .. code-block:: cpp - - void foo(py::iterator it) { - while (it != py::iterator::sentinel()) { - // use `*it` - ++it; - } - } - \endrst */ - static iterator sentinel() { return {}; } - - friend bool operator==(const iterator &a, const iterator &b) { return a->ptr() == b->ptr(); } - friend bool operator!=(const iterator &a, const iterator &b) { return a->ptr() != b->ptr(); } - -private: - void advance() { - value = reinterpret_steal(PyIter_Next(m_ptr)); - if (PyErr_Occurred()) { throw error_already_set(); } - } - -private: - object value = {}; -}; - -class iterable : public object { -public: - PYBIND11_OBJECT_DEFAULT(iterable, object, detail::PyIterable_Check) -}; - -class bytes; - -class str : public object { -public: - PYBIND11_OBJECT_CVT(str, object, detail::PyUnicode_Check_Permissive, raw_str) - - str(const char *c, size_t n) - : object(PyUnicode_FromStringAndSize(c, (ssize_t) n), stolen_t{}) { - if (!m_ptr) pybind11_fail("Could not allocate string object!"); - } - - // 'explicit' is explicitly omitted from the following constructors to allow implicit conversion to py::str from C++ string-like objects - str(const char *c = "") - : object(PyUnicode_FromString(c), stolen_t{}) { - if (!m_ptr) pybind11_fail("Could not allocate string object!"); - } - - str(const std::string &s) : str(s.data(), s.size()) { } - - explicit str(const bytes &b); - - /** \rst - Return a string representation of the object. This is analogous to - the ``str()`` function in Python. - \endrst */ - explicit str(handle h) : object(raw_str(h.ptr()), stolen_t{}) { } - - operator std::string() const { - object temp = *this; - if (PyUnicode_Check(m_ptr)) { - temp = reinterpret_steal(PyUnicode_AsUTF8String(m_ptr)); - if (!temp) - pybind11_fail("Unable to extract string contents! (encoding issue)"); - } - char *buffer; - ssize_t length; - if (PYBIND11_BYTES_AS_STRING_AND_SIZE(temp.ptr(), &buffer, &length)) - pybind11_fail("Unable to extract string contents! (invalid type)"); - return std::string(buffer, (size_t) length); - } - - template - str format(Args &&...args) const { - return attr("format")(std::forward(args)...); - } - -private: - /// Return string representation -- always returns a new reference, even if already a str - static PyObject *raw_str(PyObject *op) { - PyObject *str_value = PyObject_Str(op); -#if PY_MAJOR_VERSION < 3 - if (!str_value) throw error_already_set(); - PyObject *unicode = PyUnicode_FromEncodedObject(str_value, "utf-8", nullptr); - Py_XDECREF(str_value); str_value = unicode; -#endif - return str_value; - } -}; -/// @} pytypes - -inline namespace literals { -/** \rst - String literal version of `str` - \endrst */ -inline str operator"" _s(const char *s, size_t size) { return {s, size}; } -} - -/// \addtogroup pytypes -/// @{ -class bytes : public object { -public: - PYBIND11_OBJECT(bytes, object, PYBIND11_BYTES_CHECK) - - // Allow implicit conversion: - bytes(const char *c = "") - : object(PYBIND11_BYTES_FROM_STRING(c), stolen_t{}) { - if (!m_ptr) pybind11_fail("Could not allocate bytes object!"); - } - - bytes(const char *c, size_t n) - : object(PYBIND11_BYTES_FROM_STRING_AND_SIZE(c, (ssize_t) n), stolen_t{}) { - if (!m_ptr) pybind11_fail("Could not allocate bytes object!"); - } - - // Allow implicit conversion: - bytes(const std::string &s) : bytes(s.data(), s.size()) { } - - explicit bytes(const pybind11::str &s); - - operator std::string() const { - char *buffer; - ssize_t length; - if (PYBIND11_BYTES_AS_STRING_AND_SIZE(m_ptr, &buffer, &length)) - pybind11_fail("Unable to extract bytes contents!"); - return std::string(buffer, (size_t) length); - } -}; - -inline bytes::bytes(const pybind11::str &s) { - object temp = s; - if (PyUnicode_Check(s.ptr())) { - temp = reinterpret_steal(PyUnicode_AsUTF8String(s.ptr())); - if (!temp) - pybind11_fail("Unable to extract string contents! (encoding issue)"); - } - char *buffer; - ssize_t length; - if (PYBIND11_BYTES_AS_STRING_AND_SIZE(temp.ptr(), &buffer, &length)) - pybind11_fail("Unable to extract string contents! (invalid type)"); - auto obj = reinterpret_steal(PYBIND11_BYTES_FROM_STRING_AND_SIZE(buffer, length)); - if (!obj) - pybind11_fail("Could not allocate bytes object!"); - m_ptr = obj.release().ptr(); -} - -inline str::str(const bytes& b) { - char *buffer; - ssize_t length; - if (PYBIND11_BYTES_AS_STRING_AND_SIZE(b.ptr(), &buffer, &length)) - pybind11_fail("Unable to extract bytes contents!"); - auto obj = reinterpret_steal(PyUnicode_FromStringAndSize(buffer, (ssize_t) length)); - if (!obj) - pybind11_fail("Could not allocate string object!"); - m_ptr = obj.release().ptr(); -} - -class none : public object { -public: - PYBIND11_OBJECT(none, object, detail::PyNone_Check) - none() : object(Py_None, borrowed_t{}) { } -}; - -#if PY_MAJOR_VERSION >= 3 -class ellipsis : public object { -public: - PYBIND11_OBJECT(ellipsis, object, detail::PyEllipsis_Check) - ellipsis() : object(Py_Ellipsis, borrowed_t{}) { } -}; -#endif - -class bool_ : public object { -public: - PYBIND11_OBJECT_CVT(bool_, object, PyBool_Check, raw_bool) - bool_() : object(Py_False, borrowed_t{}) { } - // Allow implicit conversion from and to `bool`: - bool_(bool value) : object(value ? Py_True : Py_False, borrowed_t{}) { } - operator bool() const { return m_ptr && PyLong_AsLong(m_ptr) != 0; } - -private: - /// Return the truth value of an object -- always returns a new reference - static PyObject *raw_bool(PyObject *op) { - const auto value = PyObject_IsTrue(op); - if (value == -1) return nullptr; - return handle(value ? Py_True : Py_False).inc_ref().ptr(); - } -}; - -NAMESPACE_BEGIN(detail) -// Converts a value to the given unsigned type. If an error occurs, you get back (Unsigned) -1; -// otherwise you get back the unsigned long or unsigned long long value cast to (Unsigned). -// (The distinction is critically important when casting a returned -1 error value to some other -// unsigned type: (A)-1 != (B)-1 when A and B are unsigned types of different sizes). -template -Unsigned as_unsigned(PyObject *o) { - if (sizeof(Unsigned) <= sizeof(unsigned long) -#if PY_VERSION_HEX < 0x03000000 - || PyInt_Check(o) -#endif - ) { - unsigned long v = PyLong_AsUnsignedLong(o); - return v == (unsigned long) -1 && PyErr_Occurred() ? (Unsigned) -1 : (Unsigned) v; - } - else { - unsigned long long v = PyLong_AsUnsignedLongLong(o); - return v == (unsigned long long) -1 && PyErr_Occurred() ? (Unsigned) -1 : (Unsigned) v; - } -} -NAMESPACE_END(detail) - -class int_ : public object { -public: - PYBIND11_OBJECT_CVT(int_, object, PYBIND11_LONG_CHECK, PyNumber_Long) - int_() : object(PyLong_FromLong(0), stolen_t{}) { } - // Allow implicit conversion from C++ integral types: - template ::value, int> = 0> - int_(T value) { - if (sizeof(T) <= sizeof(long)) { - if (std::is_signed::value) - m_ptr = PyLong_FromLong((long) value); - else - m_ptr = PyLong_FromUnsignedLong((unsigned long) value); - } else { - if (std::is_signed::value) - m_ptr = PyLong_FromLongLong((long long) value); - else - m_ptr = PyLong_FromUnsignedLongLong((unsigned long long) value); - } - if (!m_ptr) pybind11_fail("Could not allocate int object!"); - } - - template ::value, int> = 0> - operator T() const { - return std::is_unsigned::value - ? detail::as_unsigned(m_ptr) - : sizeof(T) <= sizeof(long) - ? (T) PyLong_AsLong(m_ptr) - : (T) PYBIND11_LONG_AS_LONGLONG(m_ptr); - } -}; - -class float_ : public object { -public: - PYBIND11_OBJECT_CVT(float_, object, PyFloat_Check, PyNumber_Float) - // Allow implicit conversion from float/double: - float_(float value) : object(PyFloat_FromDouble((double) value), stolen_t{}) { - if (!m_ptr) pybind11_fail("Could not allocate float object!"); - } - float_(double value = .0) : object(PyFloat_FromDouble((double) value), stolen_t{}) { - if (!m_ptr) pybind11_fail("Could not allocate float object!"); - } - operator float() const { return (float) PyFloat_AsDouble(m_ptr); } - operator double() const { return (double) PyFloat_AsDouble(m_ptr); } -}; - -class weakref : public object { -public: - PYBIND11_OBJECT_DEFAULT(weakref, object, PyWeakref_Check) - explicit weakref(handle obj, handle callback = {}) - : object(PyWeakref_NewRef(obj.ptr(), callback.ptr()), stolen_t{}) { - if (!m_ptr) pybind11_fail("Could not allocate weak reference!"); - } -}; - -class slice : public object { -public: - PYBIND11_OBJECT_DEFAULT(slice, object, PySlice_Check) - slice(ssize_t start_, ssize_t stop_, ssize_t step_) { - int_ start(start_), stop(stop_), step(step_); - m_ptr = PySlice_New(start.ptr(), stop.ptr(), step.ptr()); - if (!m_ptr) pybind11_fail("Could not allocate slice object!"); - } - bool compute(size_t length, size_t *start, size_t *stop, size_t *step, - size_t *slicelength) const { - return PySlice_GetIndicesEx((PYBIND11_SLICE_OBJECT *) m_ptr, - (ssize_t) length, (ssize_t *) start, - (ssize_t *) stop, (ssize_t *) step, - (ssize_t *) slicelength) == 0; - } -}; - -class capsule : public object { -public: - PYBIND11_OBJECT_DEFAULT(capsule, object, PyCapsule_CheckExact) - PYBIND11_DEPRECATED("Use reinterpret_borrow() or reinterpret_steal()") - capsule(PyObject *ptr, bool is_borrowed) : object(is_borrowed ? object(ptr, borrowed_t{}) : object(ptr, stolen_t{})) { } - - explicit capsule(const void *value, const char *name = nullptr, void (*destructor)(PyObject *) = nullptr) - : object(PyCapsule_New(const_cast(value), name, destructor), stolen_t{}) { - if (!m_ptr) - pybind11_fail("Could not allocate capsule object!"); - } - - PYBIND11_DEPRECATED("Please pass a destructor that takes a void pointer as input") - capsule(const void *value, void (*destruct)(PyObject *)) - : object(PyCapsule_New(const_cast(value), nullptr, destruct), stolen_t{}) { - if (!m_ptr) - pybind11_fail("Could not allocate capsule object!"); - } - - capsule(const void *value, void (*destructor)(void *)) { - m_ptr = PyCapsule_New(const_cast(value), nullptr, [](PyObject *o) { - auto destructor = reinterpret_cast(PyCapsule_GetContext(o)); - void *ptr = PyCapsule_GetPointer(o, nullptr); - destructor(ptr); - }); - - if (!m_ptr) - pybind11_fail("Could not allocate capsule object!"); - - if (PyCapsule_SetContext(m_ptr, (void *) destructor) != 0) - pybind11_fail("Could not set capsule context!"); - } - - capsule(void (*destructor)()) { - m_ptr = PyCapsule_New(reinterpret_cast(destructor), nullptr, [](PyObject *o) { - auto destructor = reinterpret_cast(PyCapsule_GetPointer(o, nullptr)); - destructor(); - }); - - if (!m_ptr) - pybind11_fail("Could not allocate capsule object!"); - } - - template operator T *() const { - auto name = this->name(); - T * result = static_cast(PyCapsule_GetPointer(m_ptr, name)); - if (!result) pybind11_fail("Unable to extract capsule contents!"); - return result; - } - - const char *name() const { return PyCapsule_GetName(m_ptr); } -}; - -class tuple : public object { -public: - PYBIND11_OBJECT_CVT(tuple, object, PyTuple_Check, PySequence_Tuple) - explicit tuple(size_t size = 0) : object(PyTuple_New((ssize_t) size), stolen_t{}) { - if (!m_ptr) pybind11_fail("Could not allocate tuple object!"); - } - size_t size() const { return (size_t) PyTuple_Size(m_ptr); } - detail::tuple_accessor operator[](size_t index) const { return {*this, index}; } - detail::item_accessor operator[](handle h) const { return object::operator[](h); } - detail::tuple_iterator begin() const { return {*this, 0}; } - detail::tuple_iterator end() const { return {*this, PyTuple_GET_SIZE(m_ptr)}; } -}; - -class dict : public object { -public: - PYBIND11_OBJECT_CVT(dict, object, PyDict_Check, raw_dict) - dict() : object(PyDict_New(), stolen_t{}) { - if (!m_ptr) pybind11_fail("Could not allocate dict object!"); - } - template ...>::value>, - // MSVC workaround: it can't compile an out-of-line definition, so defer the collector - typename collector = detail::deferred_t, Args...>> - explicit dict(Args &&...args) : dict(collector(std::forward(args)...).kwargs()) { } - - size_t size() const { return (size_t) PyDict_Size(m_ptr); } - detail::dict_iterator begin() const { return {*this, 0}; } - detail::dict_iterator end() const { return {}; } - void clear() const { PyDict_Clear(ptr()); } - bool contains(handle key) const { return PyDict_Contains(ptr(), key.ptr()) == 1; } - bool contains(const char *key) const { return PyDict_Contains(ptr(), pybind11::str(key).ptr()) == 1; } - -private: - /// Call the `dict` Python type -- always returns a new reference - static PyObject *raw_dict(PyObject *op) { - if (PyDict_Check(op)) - return handle(op).inc_ref().ptr(); - return PyObject_CallFunctionObjArgs((PyObject *) &PyDict_Type, op, nullptr); - } -}; - -class sequence : public object { -public: - PYBIND11_OBJECT_DEFAULT(sequence, object, PySequence_Check) - size_t size() const { return (size_t) PySequence_Size(m_ptr); } - detail::sequence_accessor operator[](size_t index) const { return {*this, index}; } - detail::item_accessor operator[](handle h) const { return object::operator[](h); } - detail::sequence_iterator begin() const { return {*this, 0}; } - detail::sequence_iterator end() const { return {*this, PySequence_Size(m_ptr)}; } -}; - -class list : public object { -public: - PYBIND11_OBJECT_CVT(list, object, PyList_Check, PySequence_List) - explicit list(size_t size = 0) : object(PyList_New((ssize_t) size), stolen_t{}) { - if (!m_ptr) pybind11_fail("Could not allocate list object!"); - } - size_t size() const { return (size_t) PyList_Size(m_ptr); } - detail::list_accessor operator[](size_t index) const { return {*this, index}; } - detail::item_accessor operator[](handle h) const { return object::operator[](h); } - detail::list_iterator begin() const { return {*this, 0}; } - detail::list_iterator end() const { return {*this, PyList_GET_SIZE(m_ptr)}; } - template void append(T &&val) const { - PyList_Append(m_ptr, detail::object_or_cast(std::forward(val)).ptr()); - } -}; - -class args : public tuple { PYBIND11_OBJECT_DEFAULT(args, tuple, PyTuple_Check) }; -class kwargs : public dict { PYBIND11_OBJECT_DEFAULT(kwargs, dict, PyDict_Check) }; - -class set : public object { -public: - PYBIND11_OBJECT_CVT(set, object, PySet_Check, PySet_New) - set() : object(PySet_New(nullptr), stolen_t{}) { - if (!m_ptr) pybind11_fail("Could not allocate set object!"); - } - size_t size() const { return (size_t) PySet_Size(m_ptr); } - template bool add(T &&val) const { - return PySet_Add(m_ptr, detail::object_or_cast(std::forward(val)).ptr()) == 0; - } - void clear() const { PySet_Clear(m_ptr); } -}; - -class function : public object { -public: - PYBIND11_OBJECT_DEFAULT(function, object, PyCallable_Check) - handle cpp_function() const { - handle fun = detail::get_function(m_ptr); - if (fun && PyCFunction_Check(fun.ptr())) - return fun; - return handle(); - } - bool is_cpp_function() const { return (bool) cpp_function(); } -}; - -class buffer : public object { -public: - PYBIND11_OBJECT_DEFAULT(buffer, object, PyObject_CheckBuffer) - - buffer_info request(bool writable = false) { - int flags = PyBUF_STRIDES | PyBUF_FORMAT; - if (writable) flags |= PyBUF_WRITABLE; - Py_buffer *view = new Py_buffer(); - if (PyObject_GetBuffer(m_ptr, view, flags) != 0) { - delete view; - throw error_already_set(); - } - return buffer_info(view); - } -}; - -class memoryview : public object { -public: - explicit memoryview(const buffer_info& info) { - static Py_buffer buf { }; - // Py_buffer uses signed sizes, strides and shape!.. - static std::vector py_strides { }; - static std::vector py_shape { }; - buf.buf = info.ptr; - buf.itemsize = info.itemsize; - buf.format = const_cast(info.format.c_str()); - buf.ndim = (int) info.ndim; - buf.len = info.size; - py_strides.clear(); - py_shape.clear(); - for (size_t i = 0; i < (size_t) info.ndim; ++i) { - py_strides.push_back(info.strides[i]); - py_shape.push_back(info.shape[i]); - } - buf.strides = py_strides.data(); - buf.shape = py_shape.data(); - buf.suboffsets = nullptr; - buf.readonly = false; - buf.internal = nullptr; - - m_ptr = PyMemoryView_FromBuffer(&buf); - if (!m_ptr) - pybind11_fail("Unable to create memoryview from buffer descriptor"); - } - - PYBIND11_OBJECT_CVT(memoryview, object, PyMemoryView_Check, PyMemoryView_FromObject) -}; -/// @} pytypes - -/// \addtogroup python_builtins -/// @{ -inline size_t len(handle h) { - ssize_t result = PyObject_Length(h.ptr()); - if (result < 0) - pybind11_fail("Unable to compute length of object"); - return (size_t) result; -} - -inline str repr(handle h) { - PyObject *str_value = PyObject_Repr(h.ptr()); - if (!str_value) throw error_already_set(); -#if PY_MAJOR_VERSION < 3 - PyObject *unicode = PyUnicode_FromEncodedObject(str_value, "utf-8", nullptr); - Py_XDECREF(str_value); str_value = unicode; - if (!str_value) throw error_already_set(); -#endif - return reinterpret_steal(str_value); -} - -inline iterator iter(handle obj) { - PyObject *result = PyObject_GetIter(obj.ptr()); - if (!result) { throw error_already_set(); } - return reinterpret_steal(result); -} -/// @} python_builtins - -NAMESPACE_BEGIN(detail) -template iterator object_api::begin() const { return iter(derived()); } -template iterator object_api::end() const { return iterator::sentinel(); } -template item_accessor object_api::operator[](handle key) const { - return {derived(), reinterpret_borrow(key)}; -} -template item_accessor object_api::operator[](const char *key) const { - return {derived(), pybind11::str(key)}; -} -template obj_attr_accessor object_api::attr(handle key) const { - return {derived(), reinterpret_borrow(key)}; -} -template str_attr_accessor object_api::attr(const char *key) const { - return {derived(), key}; -} -template args_proxy object_api::operator*() const { - return args_proxy(derived().ptr()); -} -template template bool object_api::contains(T &&item) const { - return attr("__contains__")(std::forward(item)).template cast(); -} - -template -pybind11::str object_api::str() const { return pybind11::str(derived()); } - -template -str_attr_accessor object_api::doc() const { return attr("__doc__"); } - -template -handle object_api::get_type() const { return (PyObject *) Py_TYPE(derived().ptr()); } - -template -bool object_api::rich_compare(object_api const &other, int value) const { - int rv = PyObject_RichCompareBool(derived().ptr(), other.derived().ptr(), value); - if (rv == -1) - throw error_already_set(); - return rv == 1; -} - -#define PYBIND11_MATH_OPERATOR_UNARY(op, fn) \ - template object object_api::op() const { \ - object result = reinterpret_steal(fn(derived().ptr())); \ - if (!result.ptr()) \ - throw error_already_set(); \ - return result; \ - } - -#define PYBIND11_MATH_OPERATOR_BINARY(op, fn) \ - template \ - object object_api::op(object_api const &other) const { \ - object result = reinterpret_steal( \ - fn(derived().ptr(), other.derived().ptr())); \ - if (!result.ptr()) \ - throw error_already_set(); \ - return result; \ - } - -PYBIND11_MATH_OPERATOR_UNARY (operator~, PyNumber_Invert) -PYBIND11_MATH_OPERATOR_UNARY (operator-, PyNumber_Negative) -PYBIND11_MATH_OPERATOR_BINARY(operator+, PyNumber_Add) -PYBIND11_MATH_OPERATOR_BINARY(operator+=, PyNumber_InPlaceAdd) -PYBIND11_MATH_OPERATOR_BINARY(operator-, PyNumber_Subtract) -PYBIND11_MATH_OPERATOR_BINARY(operator-=, PyNumber_InPlaceSubtract) -PYBIND11_MATH_OPERATOR_BINARY(operator*, PyNumber_Multiply) -PYBIND11_MATH_OPERATOR_BINARY(operator*=, PyNumber_InPlaceMultiply) -PYBIND11_MATH_OPERATOR_BINARY(operator/, PyNumber_TrueDivide) -PYBIND11_MATH_OPERATOR_BINARY(operator/=, PyNumber_InPlaceTrueDivide) -PYBIND11_MATH_OPERATOR_BINARY(operator|, PyNumber_Or) -PYBIND11_MATH_OPERATOR_BINARY(operator|=, PyNumber_InPlaceOr) -PYBIND11_MATH_OPERATOR_BINARY(operator&, PyNumber_And) -PYBIND11_MATH_OPERATOR_BINARY(operator&=, PyNumber_InPlaceAnd) -PYBIND11_MATH_OPERATOR_BINARY(operator^, PyNumber_Xor) -PYBIND11_MATH_OPERATOR_BINARY(operator^=, PyNumber_InPlaceXor) -PYBIND11_MATH_OPERATOR_BINARY(operator<<, PyNumber_Lshift) -PYBIND11_MATH_OPERATOR_BINARY(operator<<=, PyNumber_InPlaceLshift) -PYBIND11_MATH_OPERATOR_BINARY(operator>>, PyNumber_Rshift) -PYBIND11_MATH_OPERATOR_BINARY(operator>>=, PyNumber_InPlaceRshift) - -#undef PYBIND11_MATH_OPERATOR_UNARY -#undef PYBIND11_MATH_OPERATOR_BINARY - -NAMESPACE_END(detail) -NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/ptocr/postprocess/dbprocess/include/pybind11/stl.h b/ptocr/postprocess/dbprocess/include/pybind11/stl.h deleted file mode 100644 index 32f8d29..0000000 --- a/ptocr/postprocess/dbprocess/include/pybind11/stl.h +++ /dev/null @@ -1,386 +0,0 @@ -/* - pybind11/stl.h: Transparent conversion for STL data types - - Copyright (c) 2016 Wenzel Jakob - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#include "pybind11.h" -#include -#include -#include -#include -#include -#include -#include -#include - -#if defined(_MSC_VER) -#pragma warning(push) -#pragma warning(disable: 4127) // warning C4127: Conditional expression is constant -#endif - -#ifdef __has_include -// std::optional (but including it in c++14 mode isn't allowed) -# if defined(PYBIND11_CPP17) && __has_include() -# include -# define PYBIND11_HAS_OPTIONAL 1 -# endif -// std::experimental::optional (but not allowed in c++11 mode) -# if defined(PYBIND11_CPP14) && (__has_include() && \ - !__has_include()) -# include -# define PYBIND11_HAS_EXP_OPTIONAL 1 -# endif -// std::variant -# if defined(PYBIND11_CPP17) && __has_include() -# include -# define PYBIND11_HAS_VARIANT 1 -# endif -#elif defined(_MSC_VER) && defined(PYBIND11_CPP17) -# include -# include -# define PYBIND11_HAS_OPTIONAL 1 -# define PYBIND11_HAS_VARIANT 1 -#endif - -NAMESPACE_BEGIN(PYBIND11_NAMESPACE) -NAMESPACE_BEGIN(detail) - -/// Extracts an const lvalue reference or rvalue reference for U based on the type of T (e.g. for -/// forwarding a container element). Typically used indirect via forwarded_type(), below. -template -using forwarded_type = conditional_t< - std::is_lvalue_reference::value, remove_reference_t &, remove_reference_t &&>; - -/// Forwards a value U as rvalue or lvalue according to whether T is rvalue or lvalue; typically -/// used for forwarding a container's elements. -template -forwarded_type forward_like(U &&u) { - return std::forward>(std::forward(u)); -} - -template struct set_caster { - using type = Type; - using key_conv = make_caster; - - bool load(handle src, bool convert) { - if (!isinstance(src)) - return false; - auto s = reinterpret_borrow(src); - value.clear(); - for (auto entry : s) { - key_conv conv; - if (!conv.load(entry, convert)) - return false; - value.insert(cast_op(std::move(conv))); - } - return true; - } - - template - static handle cast(T &&src, return_value_policy policy, handle parent) { - if (!std::is_lvalue_reference::value) - policy = return_value_policy_override::policy(policy); - pybind11::set s; - for (auto &&value : src) { - auto value_ = reinterpret_steal(key_conv::cast(forward_like(value), policy, parent)); - if (!value_ || !s.add(value_)) - return handle(); - } - return s.release(); - } - - PYBIND11_TYPE_CASTER(type, _("Set[") + key_conv::name + _("]")); -}; - -template struct map_caster { - using key_conv = make_caster; - using value_conv = make_caster; - - bool load(handle src, bool convert) { - if (!isinstance(src)) - return false; - auto d = reinterpret_borrow(src); - value.clear(); - for (auto it : d) { - key_conv kconv; - value_conv vconv; - if (!kconv.load(it.first.ptr(), convert) || - !vconv.load(it.second.ptr(), convert)) - return false; - value.emplace(cast_op(std::move(kconv)), cast_op(std::move(vconv))); - } - return true; - } - - template - static handle cast(T &&src, return_value_policy policy, handle parent) { - dict d; - return_value_policy policy_key = policy; - return_value_policy policy_value = policy; - if (!std::is_lvalue_reference::value) { - policy_key = return_value_policy_override::policy(policy_key); - policy_value = return_value_policy_override::policy(policy_value); - } - for (auto &&kv : src) { - auto key = reinterpret_steal(key_conv::cast(forward_like(kv.first), policy_key, parent)); - auto value = reinterpret_steal(value_conv::cast(forward_like(kv.second), policy_value, parent)); - if (!key || !value) - return handle(); - d[key] = value; - } - return d.release(); - } - - PYBIND11_TYPE_CASTER(Type, _("Dict[") + key_conv::name + _(", ") + value_conv::name + _("]")); -}; - -template struct list_caster { - using value_conv = make_caster; - - bool load(handle src, bool convert) { - if (!isinstance(src) || isinstance(src)) - return false; - auto s = reinterpret_borrow(src); - value.clear(); - reserve_maybe(s, &value); - for (auto it : s) { - value_conv conv; - if (!conv.load(it, convert)) - return false; - value.push_back(cast_op(std::move(conv))); - } - return true; - } - -private: - template ().reserve(0)), void>::value, int> = 0> - void reserve_maybe(sequence s, Type *) { value.reserve(s.size()); } - void reserve_maybe(sequence, void *) { } - -public: - template - static handle cast(T &&src, return_value_policy policy, handle parent) { - if (!std::is_lvalue_reference::value) - policy = return_value_policy_override::policy(policy); - list l(src.size()); - size_t index = 0; - for (auto &&value : src) { - auto value_ = reinterpret_steal(value_conv::cast(forward_like(value), policy, parent)); - if (!value_) - return handle(); - PyList_SET_ITEM(l.ptr(), (ssize_t) index++, value_.release().ptr()); // steals a reference - } - return l.release(); - } - - PYBIND11_TYPE_CASTER(Type, _("List[") + value_conv::name + _("]")); -}; - -template struct type_caster> - : list_caster, Type> { }; - -template struct type_caster> - : list_caster, Type> { }; - -template struct type_caster> - : list_caster, Type> { }; - -template struct array_caster { - using value_conv = make_caster; - -private: - template - bool require_size(enable_if_t size) { - if (value.size() != size) - value.resize(size); - return true; - } - template - bool require_size(enable_if_t size) { - return size == Size; - } - -public: - bool load(handle src, bool convert) { - if (!isinstance(src)) - return false; - auto l = reinterpret_borrow(src); - if (!require_size(l.size())) - return false; - size_t ctr = 0; - for (auto it : l) { - value_conv conv; - if (!conv.load(it, convert)) - return false; - value[ctr++] = cast_op(std::move(conv)); - } - return true; - } - - template - static handle cast(T &&src, return_value_policy policy, handle parent) { - list l(src.size()); - size_t index = 0; - for (auto &&value : src) { - auto value_ = reinterpret_steal(value_conv::cast(forward_like(value), policy, parent)); - if (!value_) - return handle(); - PyList_SET_ITEM(l.ptr(), (ssize_t) index++, value_.release().ptr()); // steals a reference - } - return l.release(); - } - - PYBIND11_TYPE_CASTER(ArrayType, _("List[") + value_conv::name + _(_(""), _("[") + _() + _("]")) + _("]")); -}; - -template struct type_caster> - : array_caster, Type, false, Size> { }; - -template struct type_caster> - : array_caster, Type, true> { }; - -template struct type_caster> - : set_caster, Key> { }; - -template struct type_caster> - : set_caster, Key> { }; - -template struct type_caster> - : map_caster, Key, Value> { }; - -template struct type_caster> - : map_caster, Key, Value> { }; - -// This type caster is intended to be used for std::optional and std::experimental::optional -template struct optional_caster { - using value_conv = make_caster; - - template - static handle cast(T_ &&src, return_value_policy policy, handle parent) { - if (!src) - return none().inc_ref(); - policy = return_value_policy_override::policy(policy); - return value_conv::cast(*std::forward(src), policy, parent); - } - - bool load(handle src, bool convert) { - if (!src) { - return false; - } else if (src.is_none()) { - return true; // default-constructed value is already empty - } - value_conv inner_caster; - if (!inner_caster.load(src, convert)) - return false; - - value.emplace(cast_op(std::move(inner_caster))); - return true; - } - - PYBIND11_TYPE_CASTER(T, _("Optional[") + value_conv::name + _("]")); -}; - -#if PYBIND11_HAS_OPTIONAL -template struct type_caster> - : public optional_caster> {}; - -template<> struct type_caster - : public void_caster {}; -#endif - -#if PYBIND11_HAS_EXP_OPTIONAL -template struct type_caster> - : public optional_caster> {}; - -template<> struct type_caster - : public void_caster {}; -#endif - -/// Visit a variant and cast any found type to Python -struct variant_caster_visitor { - return_value_policy policy; - handle parent; - - using result_type = handle; // required by boost::variant in C++11 - - template - result_type operator()(T &&src) const { - return make_caster::cast(std::forward(src), policy, parent); - } -}; - -/// Helper class which abstracts away variant's `visit` function. `std::variant` and similar -/// `namespace::variant` types which provide a `namespace::visit()` function are handled here -/// automatically using argument-dependent lookup. Users can provide specializations for other -/// variant-like classes, e.g. `boost::variant` and `boost::apply_visitor`. -template class Variant> -struct visit_helper { - template - static auto call(Args &&...args) -> decltype(visit(std::forward(args)...)) { - return visit(std::forward(args)...); - } -}; - -/// Generic variant caster -template struct variant_caster; - -template class V, typename... Ts> -struct variant_caster> { - static_assert(sizeof...(Ts) > 0, "Variant must consist of at least one alternative."); - - template - bool load_alternative(handle src, bool convert, type_list) { - auto caster = make_caster(); - if (caster.load(src, convert)) { - value = cast_op(caster); - return true; - } - return load_alternative(src, convert, type_list{}); - } - - bool load_alternative(handle, bool, type_list<>) { return false; } - - bool load(handle src, bool convert) { - // Do a first pass without conversions to improve constructor resolution. - // E.g. `py::int_(1).cast>()` needs to fill the `int` - // slot of the variant. Without two-pass loading `double` would be filled - // because it appears first and a conversion is possible. - if (convert && load_alternative(src, false, type_list{})) - return true; - return load_alternative(src, convert, type_list{}); - } - - template - static handle cast(Variant &&src, return_value_policy policy, handle parent) { - return visit_helper::call(variant_caster_visitor{policy, parent}, - std::forward(src)); - } - - using Type = V; - PYBIND11_TYPE_CASTER(Type, _("Union[") + detail::concat(make_caster::name...) + _("]")); -}; - -#if PYBIND11_HAS_VARIANT -template -struct type_caster> : variant_caster> { }; -#endif - -NAMESPACE_END(detail) - -inline std::ostream &operator<<(std::ostream &os, const handle &obj) { - os << (std::string) str(obj); - return os; -} - -NAMESPACE_END(PYBIND11_NAMESPACE) - -#if defined(_MSC_VER) -#pragma warning(pop) -#endif diff --git a/ptocr/postprocess/dbprocess/include/pybind11/stl_bind.h b/ptocr/postprocess/dbprocess/include/pybind11/stl_bind.h deleted file mode 100644 index 38dd68f..0000000 --- a/ptocr/postprocess/dbprocess/include/pybind11/stl_bind.h +++ /dev/null @@ -1,599 +0,0 @@ -/* - pybind11/std_bind.h: Binding generators for STL data types - - Copyright (c) 2016 Sergey Lyskov and Wenzel Jakob - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#include "detail/common.h" -#include "operators.h" - -#include -#include - -NAMESPACE_BEGIN(PYBIND11_NAMESPACE) -NAMESPACE_BEGIN(detail) - -/* SFINAE helper class used by 'is_comparable */ -template struct container_traits { - template static std::true_type test_comparable(decltype(std::declval() == std::declval())*); - template static std::false_type test_comparable(...); - template static std::true_type test_value(typename T2::value_type *); - template static std::false_type test_value(...); - template static std::true_type test_pair(typename T2::first_type *, typename T2::second_type *); - template static std::false_type test_pair(...); - - static constexpr const bool is_comparable = std::is_same(nullptr))>::value; - static constexpr const bool is_pair = std::is_same(nullptr, nullptr))>::value; - static constexpr const bool is_vector = std::is_same(nullptr))>::value; - static constexpr const bool is_element = !is_pair && !is_vector; -}; - -/* Default: is_comparable -> std::false_type */ -template -struct is_comparable : std::false_type { }; - -/* For non-map data structures, check whether operator== can be instantiated */ -template -struct is_comparable< - T, enable_if_t::is_element && - container_traits::is_comparable>> - : std::true_type { }; - -/* For a vector/map data structure, recursively check the value type (which is std::pair for maps) */ -template -struct is_comparable::is_vector>> { - static constexpr const bool value = - is_comparable::value; -}; - -/* For pairs, recursively check the two data types */ -template -struct is_comparable::is_pair>> { - static constexpr const bool value = - is_comparable::value && - is_comparable::value; -}; - -/* Fallback functions */ -template void vector_if_copy_constructible(const Args &...) { } -template void vector_if_equal_operator(const Args &...) { } -template void vector_if_insertion_operator(const Args &...) { } -template void vector_modifiers(const Args &...) { } - -template -void vector_if_copy_constructible(enable_if_t::value, Class_> &cl) { - cl.def(init(), "Copy constructor"); -} - -template -void vector_if_equal_operator(enable_if_t::value, Class_> &cl) { - using T = typename Vector::value_type; - - cl.def(self == self); - cl.def(self != self); - - cl.def("count", - [](const Vector &v, const T &x) { - return std::count(v.begin(), v.end(), x); - }, - arg("x"), - "Return the number of times ``x`` appears in the list" - ); - - cl.def("remove", [](Vector &v, const T &x) { - auto p = std::find(v.begin(), v.end(), x); - if (p != v.end()) - v.erase(p); - else - throw value_error(); - }, - arg("x"), - "Remove the first item from the list whose value is x. " - "It is an error if there is no such item." - ); - - cl.def("__contains__", - [](const Vector &v, const T &x) { - return std::find(v.begin(), v.end(), x) != v.end(); - }, - arg("x"), - "Return true the container contains ``x``" - ); -} - -// Vector modifiers -- requires a copyable vector_type: -// (Technically, some of these (pop and __delitem__) don't actually require copyability, but it seems -// silly to allow deletion but not insertion, so include them here too.) -template -void vector_modifiers(enable_if_t::value, Class_> &cl) { - using T = typename Vector::value_type; - using SizeType = typename Vector::size_type; - using DiffType = typename Vector::difference_type; - - cl.def("append", - [](Vector &v, const T &value) { v.push_back(value); }, - arg("x"), - "Add an item to the end of the list"); - - cl.def(init([](iterable it) { - auto v = std::unique_ptr(new Vector()); - v->reserve(len(it)); - for (handle h : it) - v->push_back(h.cast()); - return v.release(); - })); - - cl.def("extend", - [](Vector &v, const Vector &src) { - v.insert(v.end(), src.begin(), src.end()); - }, - arg("L"), - "Extend the list by appending all the items in the given list" - ); - - cl.def("insert", - [](Vector &v, SizeType i, const T &x) { - if (i > v.size()) - throw index_error(); - v.insert(v.begin() + (DiffType) i, x); - }, - arg("i") , arg("x"), - "Insert an item at a given position." - ); - - cl.def("pop", - [](Vector &v) { - if (v.empty()) - throw index_error(); - T t = v.back(); - v.pop_back(); - return t; - }, - "Remove and return the last item" - ); - - cl.def("pop", - [](Vector &v, SizeType i) { - if (i >= v.size()) - throw index_error(); - T t = v[i]; - v.erase(v.begin() + (DiffType) i); - return t; - }, - arg("i"), - "Remove and return the item at index ``i``" - ); - - cl.def("__setitem__", - [](Vector &v, SizeType i, const T &t) { - if (i >= v.size()) - throw index_error(); - v[i] = t; - } - ); - - /// Slicing protocol - cl.def("__getitem__", - [](const Vector &v, slice slice) -> Vector * { - size_t start, stop, step, slicelength; - - if (!slice.compute(v.size(), &start, &stop, &step, &slicelength)) - throw error_already_set(); - - Vector *seq = new Vector(); - seq->reserve((size_t) slicelength); - - for (size_t i=0; ipush_back(v[start]); - start += step; - } - return seq; - }, - arg("s"), - "Retrieve list elements using a slice object" - ); - - cl.def("__setitem__", - [](Vector &v, slice slice, const Vector &value) { - size_t start, stop, step, slicelength; - if (!slice.compute(v.size(), &start, &stop, &step, &slicelength)) - throw error_already_set(); - - if (slicelength != value.size()) - throw std::runtime_error("Left and right hand size of slice assignment have different sizes!"); - - for (size_t i=0; i= v.size()) - throw index_error(); - v.erase(v.begin() + DiffType(i)); - }, - "Delete the list elements at index ``i``" - ); - - cl.def("__delitem__", - [](Vector &v, slice slice) { - size_t start, stop, step, slicelength; - - if (!slice.compute(v.size(), &start, &stop, &step, &slicelength)) - throw error_already_set(); - - if (step == 1 && false) { - v.erase(v.begin() + (DiffType) start, v.begin() + DiffType(start + slicelength)); - } else { - for (size_t i = 0; i < slicelength; ++i) { - v.erase(v.begin() + DiffType(start)); - start += step - 1; - } - } - }, - "Delete list elements using a slice object" - ); - -} - -// If the type has an operator[] that doesn't return a reference (most notably std::vector), -// we have to access by copying; otherwise we return by reference. -template using vector_needs_copy = negation< - std::is_same()[typename Vector::size_type()]), typename Vector::value_type &>>; - -// The usual case: access and iterate by reference -template -void vector_accessor(enable_if_t::value, Class_> &cl) { - using T = typename Vector::value_type; - using SizeType = typename Vector::size_type; - using ItType = typename Vector::iterator; - - cl.def("__getitem__", - [](Vector &v, SizeType i) -> T & { - if (i >= v.size()) - throw index_error(); - return v[i]; - }, - return_value_policy::reference_internal // ref + keepalive - ); - - cl.def("__iter__", - [](Vector &v) { - return make_iterator< - return_value_policy::reference_internal, ItType, ItType, T&>( - v.begin(), v.end()); - }, - keep_alive<0, 1>() /* Essential: keep list alive while iterator exists */ - ); -} - -// The case for special objects, like std::vector, that have to be returned-by-copy: -template -void vector_accessor(enable_if_t::value, Class_> &cl) { - using T = typename Vector::value_type; - using SizeType = typename Vector::size_type; - using ItType = typename Vector::iterator; - cl.def("__getitem__", - [](const Vector &v, SizeType i) -> T { - if (i >= v.size()) - throw index_error(); - return v[i]; - } - ); - - cl.def("__iter__", - [](Vector &v) { - return make_iterator< - return_value_policy::copy, ItType, ItType, T>( - v.begin(), v.end()); - }, - keep_alive<0, 1>() /* Essential: keep list alive while iterator exists */ - ); -} - -template auto vector_if_insertion_operator(Class_ &cl, std::string const &name) - -> decltype(std::declval() << std::declval(), void()) { - using size_type = typename Vector::size_type; - - cl.def("__repr__", - [name](Vector &v) { - std::ostringstream s; - s << name << '['; - for (size_type i=0; i < v.size(); ++i) { - s << v[i]; - if (i != v.size() - 1) - s << ", "; - } - s << ']'; - return s.str(); - }, - "Return the canonical string representation of this list." - ); -} - -// Provide the buffer interface for vectors if we have data() and we have a format for it -// GCC seems to have "void std::vector::data()" - doing SFINAE on the existence of data() is insufficient, we need to check it returns an appropriate pointer -template -struct vector_has_data_and_format : std::false_type {}; -template -struct vector_has_data_and_format::format(), std::declval().data()), typename Vector::value_type*>::value>> : std::true_type {}; - -// Add the buffer interface to a vector -template -enable_if_t...>::value> -vector_buffer(Class_& cl) { - using T = typename Vector::value_type; - - static_assert(vector_has_data_and_format::value, "There is not an appropriate format descriptor for this vector"); - - // numpy.h declares this for arbitrary types, but it may raise an exception and crash hard at runtime if PYBIND11_NUMPY_DTYPE hasn't been called, so check here - format_descriptor::format(); - - cl.def_buffer([](Vector& v) -> buffer_info { - return buffer_info(v.data(), static_cast(sizeof(T)), format_descriptor::format(), 1, {v.size()}, {sizeof(T)}); - }); - - cl.def(init([](buffer buf) { - auto info = buf.request(); - if (info.ndim != 1 || info.strides[0] % static_cast(sizeof(T))) - throw type_error("Only valid 1D buffers can be copied to a vector"); - if (!detail::compare_buffer_info::compare(info) || (ssize_t) sizeof(T) != info.itemsize) - throw type_error("Format mismatch (Python: " + info.format + " C++: " + format_descriptor::format() + ")"); - - auto vec = std::unique_ptr(new Vector()); - vec->reserve((size_t) info.shape[0]); - T *p = static_cast(info.ptr); - ssize_t step = info.strides[0] / static_cast(sizeof(T)); - T *end = p + info.shape[0] * step; - for (; p != end; p += step) - vec->push_back(*p); - return vec.release(); - })); - - return; -} - -template -enable_if_t...>::value> vector_buffer(Class_&) {} - -NAMESPACE_END(detail) - -// -// std::vector -// -template , typename... Args> -class_ bind_vector(handle scope, std::string const &name, Args&&... args) { - using Class_ = class_; - - // If the value_type is unregistered (e.g. a converting type) or is itself registered - // module-local then make the vector binding module-local as well: - using vtype = typename Vector::value_type; - auto vtype_info = detail::get_type_info(typeid(vtype)); - bool local = !vtype_info || vtype_info->module_local; - - Class_ cl(scope, name.c_str(), pybind11::module_local(local), std::forward(args)...); - - // Declare the buffer interface if a buffer_protocol() is passed in - detail::vector_buffer(cl); - - cl.def(init<>()); - - // Register copy constructor (if possible) - detail::vector_if_copy_constructible(cl); - - // Register comparison-related operators and functions (if possible) - detail::vector_if_equal_operator(cl); - - // Register stream insertion operator (if possible) - detail::vector_if_insertion_operator(cl, name); - - // Modifiers require copyable vector value type - detail::vector_modifiers(cl); - - // Accessor and iterator; return by value if copyable, otherwise we return by ref + keep-alive - detail::vector_accessor(cl); - - cl.def("__bool__", - [](const Vector &v) -> bool { - return !v.empty(); - }, - "Check whether the list is nonempty" - ); - - cl.def("__len__", &Vector::size); - - - - -#if 0 - // C++ style functions deprecated, leaving it here as an example - cl.def(init()); - - cl.def("resize", - (void (Vector::*) (size_type count)) & Vector::resize, - "changes the number of elements stored"); - - cl.def("erase", - [](Vector &v, SizeType i) { - if (i >= v.size()) - throw index_error(); - v.erase(v.begin() + i); - }, "erases element at index ``i``"); - - cl.def("empty", &Vector::empty, "checks whether the container is empty"); - cl.def("size", &Vector::size, "returns the number of elements"); - cl.def("push_back", (void (Vector::*)(const T&)) &Vector::push_back, "adds an element to the end"); - cl.def("pop_back", &Vector::pop_back, "removes the last element"); - - cl.def("max_size", &Vector::max_size, "returns the maximum possible number of elements"); - cl.def("reserve", &Vector::reserve, "reserves storage"); - cl.def("capacity", &Vector::capacity, "returns the number of elements that can be held in currently allocated storage"); - cl.def("shrink_to_fit", &Vector::shrink_to_fit, "reduces memory usage by freeing unused memory"); - - cl.def("clear", &Vector::clear, "clears the contents"); - cl.def("swap", &Vector::swap, "swaps the contents"); - - cl.def("front", [](Vector &v) { - if (v.size()) return v.front(); - else throw index_error(); - }, "access the first element"); - - cl.def("back", [](Vector &v) { - if (v.size()) return v.back(); - else throw index_error(); - }, "access the last element "); - -#endif - - return cl; -} - - - -// -// std::map, std::unordered_map -// - -NAMESPACE_BEGIN(detail) - -/* Fallback functions */ -template void map_if_insertion_operator(const Args &...) { } -template void map_assignment(const Args &...) { } - -// Map assignment when copy-assignable: just copy the value -template -void map_assignment(enable_if_t::value, Class_> &cl) { - using KeyType = typename Map::key_type; - using MappedType = typename Map::mapped_type; - - cl.def("__setitem__", - [](Map &m, const KeyType &k, const MappedType &v) { - auto it = m.find(k); - if (it != m.end()) it->second = v; - else m.emplace(k, v); - } - ); -} - -// Not copy-assignable, but still copy-constructible: we can update the value by erasing and reinserting -template -void map_assignment(enable_if_t< - !std::is_copy_assignable::value && - is_copy_constructible::value, - Class_> &cl) { - using KeyType = typename Map::key_type; - using MappedType = typename Map::mapped_type; - - cl.def("__setitem__", - [](Map &m, const KeyType &k, const MappedType &v) { - // We can't use m[k] = v; because value type might not be default constructable - auto r = m.emplace(k, v); - if (!r.second) { - // value type is not copy assignable so the only way to insert it is to erase it first... - m.erase(r.first); - m.emplace(k, v); - } - } - ); -} - - -template auto map_if_insertion_operator(Class_ &cl, std::string const &name) --> decltype(std::declval() << std::declval() << std::declval(), void()) { - - cl.def("__repr__", - [name](Map &m) { - std::ostringstream s; - s << name << '{'; - bool f = false; - for (auto const &kv : m) { - if (f) - s << ", "; - s << kv.first << ": " << kv.second; - f = true; - } - s << '}'; - return s.str(); - }, - "Return the canonical string representation of this map." - ); -} - - -NAMESPACE_END(detail) - -template , typename... Args> -class_ bind_map(handle scope, const std::string &name, Args&&... args) { - using KeyType = typename Map::key_type; - using MappedType = typename Map::mapped_type; - using Class_ = class_; - - // If either type is a non-module-local bound type then make the map binding non-local as well; - // otherwise (e.g. both types are either module-local or converting) the map will be - // module-local. - auto tinfo = detail::get_type_info(typeid(MappedType)); - bool local = !tinfo || tinfo->module_local; - if (local) { - tinfo = detail::get_type_info(typeid(KeyType)); - local = !tinfo || tinfo->module_local; - } - - Class_ cl(scope, name.c_str(), pybind11::module_local(local), std::forward(args)...); - - cl.def(init<>()); - - // Register stream insertion operator (if possible) - detail::map_if_insertion_operator(cl, name); - - cl.def("__bool__", - [](const Map &m) -> bool { return !m.empty(); }, - "Check whether the map is nonempty" - ); - - cl.def("__iter__", - [](Map &m) { return make_key_iterator(m.begin(), m.end()); }, - keep_alive<0, 1>() /* Essential: keep list alive while iterator exists */ - ); - - cl.def("items", - [](Map &m) { return make_iterator(m.begin(), m.end()); }, - keep_alive<0, 1>() /* Essential: keep list alive while iterator exists */ - ); - - cl.def("__getitem__", - [](Map &m, const KeyType &k) -> MappedType & { - auto it = m.find(k); - if (it == m.end()) - throw key_error(); - return it->second; - }, - return_value_policy::reference_internal // ref + keepalive - ); - - // Assignment provided only if the type is copyable - detail::map_assignment(cl); - - cl.def("__delitem__", - [](Map &m, const KeyType &k) { - auto it = m.find(k); - if (it == m.end()) - throw key_error(); - m.erase(it); - } - ); - - cl.def("__len__", &Map::size); - - return cl; -} - -NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/ptocr/postprocess/dbprocess/include/pybind11/typeid.h b/ptocr/postprocess/dbprocess/include/pybind11/typeid.h deleted file mode 100644 index c903fb1..0000000 --- a/ptocr/postprocess/dbprocess/include/pybind11/typeid.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - pybind11/typeid.h: Compiler-independent access to type identifiers - - Copyright (c) 2016 Wenzel Jakob - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#include -#include - -#if defined(__GNUG__) -#include -#endif - -NAMESPACE_BEGIN(pybind11) -NAMESPACE_BEGIN(detail) -/// Erase all occurrences of a substring -inline void erase_all(std::string &string, const std::string &search) { - for (size_t pos = 0;;) { - pos = string.find(search, pos); - if (pos == std::string::npos) break; - string.erase(pos, search.length()); - } -} - -PYBIND11_NOINLINE inline void clean_type_id(std::string &name) { -#if defined(__GNUG__) - int status = 0; - std::unique_ptr res { - abi::__cxa_demangle(name.c_str(), nullptr, nullptr, &status), std::free }; - if (status == 0) - name = res.get(); -#else - detail::erase_all(name, "class "); - detail::erase_all(name, "struct "); - detail::erase_all(name, "enum "); -#endif - detail::erase_all(name, "pybind11::"); -} -NAMESPACE_END(detail) - -/// Return a string representation of a C++ type -template static std::string type_id() { - std::string name(typeid(T).name()); - detail::clean_type_id(name); - return name; -} - -NAMESPACE_END(pybind11) diff --git a/ptocr/postprocess/lanms/.gitignore b/ptocr/postprocess/lanms/.gitignore deleted file mode 100644 index 6a57227..0000000 --- a/ptocr/postprocess/lanms/.gitignore +++ /dev/null @@ -1 +0,0 @@ -adaptor.so diff --git a/ptocr/postprocess/lanms/.ycm_extra_conf.py b/ptocr/postprocess/lanms/.ycm_extra_conf.py deleted file mode 100644 index cd1a74e..0000000 --- a/ptocr/postprocess/lanms/.ycm_extra_conf.py +++ /dev/null @@ -1,140 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (C) 2014 Google Inc. -# -# This file is part of YouCompleteMe. -# -# YouCompleteMe is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# YouCompleteMe is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with YouCompleteMe. If not, see . - -import os -import sys -import glob -import ycm_core - -# These are the compilation flags that will be used in case there's no -# compilation database set (by default, one is not set). -# CHANGE THIS LIST OF FLAGS. YES, THIS IS THE DROID YOU HAVE BEEN LOOKING FOR. -sys.path.append(os.path.dirname(os.path.abspath(__file__))) - - -BASE_DIR = os.path.dirname(os.path.realpath(__file__)) - -from plumbum.cmd import python_config - - -flags = [ - '-Wall', - '-Wextra', - '-Wnon-virtual-dtor', - '-Winvalid-pch', - '-Wno-unused-local-typedefs', - '-std=c++11', - '-x', 'c++', - '-Iinclude', -] + python_config('--cflags').split() - - -# Set this to the absolute path to the folder (NOT the file!) containing the -# compile_commands.json file to use that instead of 'flags'. See here for -# more details: http://clang.llvm.org/docs/JSONCompilationDatabase.html -# -# Most projects will NOT need to set this to anything; you can just change the -# 'flags' list of compilation flags. -compilation_database_folder = '' - -if os.path.exists( compilation_database_folder ): - database = ycm_core.CompilationDatabase( compilation_database_folder ) -else: - database = None - -SOURCE_EXTENSIONS = [ '.cpp', '.cxx', '.cc', '.c', '.m', '.mm' ] - -def DirectoryOfThisScript(): - return os.path.dirname( os.path.abspath( __file__ ) ) - - -def MakeRelativePathsInFlagsAbsolute( flags, working_directory ): - if not working_directory: - return list( flags ) - new_flags = [] - make_next_absolute = False - path_flags = [ '-isystem', '-I', '-iquote', '--sysroot=' ] - for flag in flags: - new_flag = flag - - if make_next_absolute: - make_next_absolute = False - if not flag.startswith( '/' ): - new_flag = os.path.join( working_directory, flag ) - - for path_flag in path_flags: - if flag == path_flag: - make_next_absolute = True - break - - if flag.startswith( path_flag ): - path = flag[ len( path_flag ): ] - new_flag = path_flag + os.path.join( working_directory, path ) - break - - if new_flag: - new_flags.append( new_flag ) - return new_flags - - -def IsHeaderFile( filename ): - extension = os.path.splitext( filename )[ 1 ] - return extension in [ '.h', '.hxx', '.hpp', '.hh' ] - - -def GetCompilationInfoForFile( filename ): - # The compilation_commands.json file generated by CMake does not have entries - # for header files. So we do our best by asking the db for flags for a - # corresponding source file, if any. If one exists, the flags for that file - # should be good enough. - if IsHeaderFile( filename ): - basename = os.path.splitext( filename )[ 0 ] - for extension in SOURCE_EXTENSIONS: - replacement_file = basename + extension - if os.path.exists( replacement_file ): - compilation_info = database.GetCompilationInfoForFile( - replacement_file ) - if compilation_info.compiler_flags_: - return compilation_info - return None - return database.GetCompilationInfoForFile( filename ) - - -# This is the entry point; this function is called by ycmd to produce flags for -# a file. -def FlagsForFile( filename, **kwargs ): - if database: - # Bear in mind that compilation_info.compiler_flags_ does NOT return a - # python list, but a "list-like" StringVec object - compilation_info = GetCompilationInfoForFile( filename ) - if not compilation_info: - return None - - final_flags = MakeRelativePathsInFlagsAbsolute( - compilation_info.compiler_flags_, - compilation_info.compiler_working_dir_ ) - else: - relative_to = DirectoryOfThisScript() - final_flags = MakeRelativePathsInFlagsAbsolute( flags, relative_to ) - - return { - 'flags': final_flags, - 'do_cache': True - } - diff --git a/ptocr/postprocess/lanms/Makefile b/ptocr/postprocess/lanms/Makefile deleted file mode 100644 index 416871d..0000000 --- a/ptocr/postprocess/lanms/Makefile +++ /dev/null @@ -1,13 +0,0 @@ -CXXFLAGS = -I include -std=c++11 -O3 $(shell python3-config --cflags) -LDFLAGS = $(shell python3-config --ldflags) - -DEPS = lanms.h $(shell find include -xtype f) -CXX_SOURCES = adaptor.cpp include/clipper/clipper.cpp - -LIB_SO = adaptor.so - -$(LIB_SO): $(CXX_SOURCES) $(DEPS) - $(CXX) -o $@ $(CXXFLAGS) $(LDFLAGS) $(CXX_SOURCES) --shared -fPIC - -clean: - rm -rf $(LIB_SO) diff --git a/ptocr/postprocess/lanms/__init__.py b/ptocr/postprocess/lanms/__init__.py deleted file mode 100644 index 649d646..0000000 --- a/ptocr/postprocess/lanms/__init__.py +++ /dev/null @@ -1,20 +0,0 @@ -import subprocess -import os -import numpy as np - -BASE_DIR = os.path.dirname(os.path.realpath(__file__)) - -if subprocess.call(['make', '-C', BASE_DIR]) != 0: # return value - raise RuntimeError('Cannot compile lanms: {}'.format(BASE_DIR)) - - -def merge_quadrangle_n9(polys, thres=0.3, precision=10000): - from .adaptor import merge_quadrangle_n9 as nms_impl - if len(polys) == 0: - return np.array([], dtype='float32') - p = polys.copy() - p[:,:8] *= precision - ret = np.array(nms_impl(p, thres), dtype='float32') - ret[:,:8] /= precision - return ret - diff --git a/ptocr/postprocess/lanms/__main__.py b/ptocr/postprocess/lanms/__main__.py deleted file mode 100644 index 72bba36..0000000 --- a/ptocr/postprocess/lanms/__main__.py +++ /dev/null @@ -1,10 +0,0 @@ -import numpy as np - - -from . import merge_quadrangle_n9 - -if __name__ == '__main__': - # unit square with confidence 1 - q = np.array([0, 0, 0, 1, 1, 1, 1, 0, 1], dtype='float32') - - print(merge_quadrangle_n9(np.array([q, q + 0.1, q + 2]))) diff --git a/ptocr/postprocess/lanms/adaptor.cpp b/ptocr/postprocess/lanms/adaptor.cpp deleted file mode 100644 index 7d38278..0000000 --- a/ptocr/postprocess/lanms/adaptor.cpp +++ /dev/null @@ -1,61 +0,0 @@ -#include "pybind11/pybind11.h" -#include "pybind11/numpy.h" -#include "pybind11/stl.h" -#include "pybind11/stl_bind.h" - -#include "lanms.h" - -namespace py = pybind11; - - -namespace lanms_adaptor { - - std::vector> polys2floats(const std::vector &polys) { - std::vector> ret; - for (size_t i = 0; i < polys.size(); i ++) { - auto &p = polys[i]; - auto &poly = p.poly; - ret.emplace_back(std::vector{ - float(poly[0].X), float(poly[0].Y), - float(poly[1].X), float(poly[1].Y), - float(poly[2].X), float(poly[2].Y), - float(poly[3].X), float(poly[3].Y), - float(p.score), - }); - } - - return ret; - } - - - /** - * - * \param quad_n9 an n-by-9 numpy array, where first 8 numbers denote the - * quadrangle, and the last one is the score - * \param iou_threshold two quadrangles with iou score above this threshold - * will be merged - * - * \return an n-by-9 numpy array, the merged quadrangles - */ - std::vector> merge_quadrangle_n9( - py::array_t quad_n9, - float iou_threshold) { - auto pbuf = quad_n9.request(); - if (pbuf.ndim != 2 || pbuf.shape[1] != 9) - throw std::runtime_error("quadrangles must have a shape of (n, 9)"); - auto n = pbuf.shape[0]; - auto ptr = static_cast(pbuf.ptr); - return polys2floats(lanms::merge_quadrangle_n9(ptr, n, iou_threshold)); - } - -} - -PYBIND11_PLUGIN(adaptor) { - py::module m("adaptor", "NMS"); - - m.def("merge_quadrangle_n9", &lanms_adaptor::merge_quadrangle_n9, - "merge quadrangels"); - - return m.ptr(); -} - diff --git a/ptocr/postprocess/lanms/include/clipper/clipper.cpp b/ptocr/postprocess/lanms/include/clipper/clipper.cpp deleted file mode 100644 index 4993ba9..0000000 --- a/ptocr/postprocess/lanms/include/clipper/clipper.cpp +++ /dev/null @@ -1,4622 +0,0 @@ -/******************************************************************************* -* * -* Author : Angus Johnson * -* Version : 6.4.0 * -* Date : 2 July 2015 * -* Website : http://www.angusj.com * -* Copyright : Angus Johnson 2010-2015 * -* * -* License: * -* Use, modification & distribution is subject to Boost Software License Ver 1. * -* http://www.boost.org/LICENSE_1_0.txt * -* * -* Attributions: * -* The code in this library is an extension of Bala Vatti's clipping algorithm: * -* "A generic solution to polygon clipping" * -* Communications of the ACM, Vol 35, Issue 7 (July 1992) pp 56-63. * -* http://portal.acm.org/citation.cfm?id=129906 * -* * -* Computer graphics and geometric modeling: implementation and algorithms * -* By Max K. Agoston * -* Springer; 1 edition (January 4, 2005) * -* http://books.google.com/books?q=vatti+clipping+agoston * -* * -* See also: * -* "Polygon Offsetting by Computing Winding Numbers" * -* Paper no. DETC2005-85513 pp. 565-575 * -* ASME 2005 International Design Engineering Technical Conferences * -* and Computers and Information in Engineering Conference (IDETC/CIE2005) * -* September 24-28, 2005 , Long Beach, California, USA * -* http://www.me.berkeley.edu/~mcmains/pubs/DAC05OffsetPolygon.pdf * -* * -*******************************************************************************/ - -/******************************************************************************* -* * -* This is a translation of the Delphi Clipper library and the naming style * -* used has retained a Delphi flavour. * -* * -*******************************************************************************/ - -#include "clipper.hpp" -#include -#include -#include -#include -#include -#include -#include -#include - -namespace ClipperLib { - -static double const pi = 3.141592653589793238; -static double const two_pi = pi *2; -static double const def_arc_tolerance = 0.25; - -enum Direction { dRightToLeft, dLeftToRight }; - -static int const Unassigned = -1; //edge not currently 'owning' a solution -static int const Skip = -2; //edge that would otherwise close a path - -#define HORIZONTAL (-1.0E+40) -#define TOLERANCE (1.0e-20) -#define NEAR_ZERO(val) (((val) > -TOLERANCE) && ((val) < TOLERANCE)) - -struct TEdge { - IntPoint Bot; - IntPoint Curr; //current (updated for every new scanbeam) - IntPoint Top; - double Dx; - PolyType PolyTyp; - EdgeSide Side; //side only refers to current side of solution poly - int WindDelta; //1 or -1 depending on winding direction - int WindCnt; - int WindCnt2; //winding count of the opposite polytype - int OutIdx; - TEdge *Next; - TEdge *Prev; - TEdge *NextInLML; - TEdge *NextInAEL; - TEdge *PrevInAEL; - TEdge *NextInSEL; - TEdge *PrevInSEL; -}; - -struct IntersectNode { - TEdge *Edge1; - TEdge *Edge2; - IntPoint Pt; -}; - -struct LocalMinimum { - cInt Y; - TEdge *LeftBound; - TEdge *RightBound; -}; - -struct OutPt; - -//OutRec: contains a path in the clipping solution. Edges in the AEL will -//carry a pointer to an OutRec when they are part of the clipping solution. -struct OutRec { - int Idx; - bool IsHole; - bool IsOpen; - OutRec *FirstLeft; //see comments in clipper.pas - PolyNode *PolyNd; - OutPt *Pts; - OutPt *BottomPt; -}; - -struct OutPt { - int Idx; - IntPoint Pt; - OutPt *Next; - OutPt *Prev; -}; - -struct Join { - OutPt *OutPt1; - OutPt *OutPt2; - IntPoint OffPt; -}; - -struct LocMinSorter -{ - inline bool operator()(const LocalMinimum& locMin1, const LocalMinimum& locMin2) - { - return locMin2.Y < locMin1.Y; - } -}; - -//------------------------------------------------------------------------------ -//------------------------------------------------------------------------------ - -inline cInt Round(double val) -{ - if ((val < 0)) return static_cast(val - 0.5); - else return static_cast(val + 0.5); -} -//------------------------------------------------------------------------------ - -inline cInt Abs(cInt val) -{ - return val < 0 ? -val : val; -} - -//------------------------------------------------------------------------------ -// PolyTree methods ... -//------------------------------------------------------------------------------ - -void PolyTree::Clear() -{ - for (PolyNodes::size_type i = 0; i < AllNodes.size(); ++i) - delete AllNodes[i]; - AllNodes.resize(0); - Childs.resize(0); -} -//------------------------------------------------------------------------------ - -PolyNode* PolyTree::GetFirst() const -{ - if (!Childs.empty()) - return Childs[0]; - else - return 0; -} -//------------------------------------------------------------------------------ - -int PolyTree::Total() const -{ - int result = (int)AllNodes.size(); - //with negative offsets, ignore the hidden outer polygon ... - if (result > 0 && Childs[0] != AllNodes[0]) result--; - return result; -} - -//------------------------------------------------------------------------------ -// PolyNode methods ... -//------------------------------------------------------------------------------ - -PolyNode::PolyNode(): Childs(), Parent(0), Index(0), m_IsOpen(false) -{ -} -//------------------------------------------------------------------------------ - -int PolyNode::ChildCount() const -{ - return (int)Childs.size(); -} -//------------------------------------------------------------------------------ - -void PolyNode::AddChild(PolyNode& child) -{ - unsigned cnt = (unsigned)Childs.size(); - Childs.push_back(&child); - child.Parent = this; - child.Index = cnt; -} -//------------------------------------------------------------------------------ - -PolyNode* PolyNode::GetNext() const -{ - if (!Childs.empty()) - return Childs[0]; - else - return GetNextSiblingUp(); -} -//------------------------------------------------------------------------------ - -PolyNode* PolyNode::GetNextSiblingUp() const -{ - if (!Parent) //protects against PolyTree.GetNextSiblingUp() - return 0; - else if (Index == Parent->Childs.size() - 1) - return Parent->GetNextSiblingUp(); - else - return Parent->Childs[Index + 1]; -} -//------------------------------------------------------------------------------ - -bool PolyNode::IsHole() const -{ - bool result = true; - PolyNode* node = Parent; - while (node) - { - result = !result; - node = node->Parent; - } - return result; -} -//------------------------------------------------------------------------------ - -bool PolyNode::IsOpen() const -{ - return m_IsOpen; -} -//------------------------------------------------------------------------------ - -#ifndef use_int32 - -//------------------------------------------------------------------------------ -// Int128 class (enables safe math on signed 64bit integers) -// eg Int128 val1((long64)9223372036854775807); //ie 2^63 -1 -// Int128 val2((long64)9223372036854775807); -// Int128 val3 = val1 * val2; -// val3.AsString => "85070591730234615847396907784232501249" (8.5e+37) -//------------------------------------------------------------------------------ - -class Int128 -{ - public: - ulong64 lo; - long64 hi; - - Int128(long64 _lo = 0) - { - lo = (ulong64)_lo; - if (_lo < 0) hi = -1; else hi = 0; - } - - - Int128(const Int128 &val): lo(val.lo), hi(val.hi){} - - Int128(const long64& _hi, const ulong64& _lo): lo(_lo), hi(_hi){} - - Int128& operator = (const long64 &val) - { - lo = (ulong64)val; - if (val < 0) hi = -1; else hi = 0; - return *this; - } - - bool operator == (const Int128 &val) const - {return (hi == val.hi && lo == val.lo);} - - bool operator != (const Int128 &val) const - { return !(*this == val);} - - bool operator > (const Int128 &val) const - { - if (hi != val.hi) - return hi > val.hi; - else - return lo > val.lo; - } - - bool operator < (const Int128 &val) const - { - if (hi != val.hi) - return hi < val.hi; - else - return lo < val.lo; - } - - bool operator >= (const Int128 &val) const - { return !(*this < val);} - - bool operator <= (const Int128 &val) const - { return !(*this > val);} - - Int128& operator += (const Int128 &rhs) - { - hi += rhs.hi; - lo += rhs.lo; - if (lo < rhs.lo) hi++; - return *this; - } - - Int128 operator + (const Int128 &rhs) const - { - Int128 result(*this); - result+= rhs; - return result; - } - - Int128& operator -= (const Int128 &rhs) - { - *this += -rhs; - return *this; - } - - Int128 operator - (const Int128 &rhs) const - { - Int128 result(*this); - result -= rhs; - return result; - } - - Int128 operator-() const //unary negation - { - if (lo == 0) - return Int128(-hi, 0); - else - return Int128(~hi, ~lo + 1); - } - - operator double() const - { - const double shift64 = 18446744073709551616.0; //2^64 - if (hi < 0) - { - if (lo == 0) return (double)hi * shift64; - else return -(double)(~lo + ~hi * shift64); - } - else - return (double)(lo + hi * shift64); - } - -}; -//------------------------------------------------------------------------------ - -Int128 Int128Mul (long64 lhs, long64 rhs) -{ - bool negate = (lhs < 0) != (rhs < 0); - - if (lhs < 0) lhs = -lhs; - ulong64 int1Hi = ulong64(lhs) >> 32; - ulong64 int1Lo = ulong64(lhs & 0xFFFFFFFF); - - if (rhs < 0) rhs = -rhs; - ulong64 int2Hi = ulong64(rhs) >> 32; - ulong64 int2Lo = ulong64(rhs & 0xFFFFFFFF); - - //nb: see comments in clipper.pas - ulong64 a = int1Hi * int2Hi; - ulong64 b = int1Lo * int2Lo; - ulong64 c = int1Hi * int2Lo + int1Lo * int2Hi; - - Int128 tmp; - tmp.hi = long64(a + (c >> 32)); - tmp.lo = long64(c << 32); - tmp.lo += long64(b); - if (tmp.lo < b) tmp.hi++; - if (negate) tmp = -tmp; - return tmp; -}; -#endif - -//------------------------------------------------------------------------------ -// Miscellaneous global functions -//------------------------------------------------------------------------------ - -bool Orientation(const Path &poly) -{ - return Area(poly) >= 0; -} -//------------------------------------------------------------------------------ - -double Area(const Path &poly) -{ - int size = (int)poly.size(); - if (size < 3) return 0; - - double a = 0; - for (int i = 0, j = size -1; i < size; ++i) - { - a += ((double)poly[j].X + poly[i].X) * ((double)poly[j].Y - poly[i].Y); - j = i; - } - return -a * 0.5; -} -//------------------------------------------------------------------------------ - -double Area(const OutPt *op) -{ - const OutPt *startOp = op; - if (!op) return 0; - double a = 0; - do { - a += (double)(op->Prev->Pt.X + op->Pt.X) * (double)(op->Prev->Pt.Y - op->Pt.Y); - op = op->Next; - } while (op != startOp); - return a * 0.5; -} -//------------------------------------------------------------------------------ - -double Area(const OutRec &outRec) -{ - return Area(outRec.Pts); -} -//------------------------------------------------------------------------------ - -bool PointIsVertex(const IntPoint &Pt, OutPt *pp) -{ - OutPt *pp2 = pp; - do - { - if (pp2->Pt == Pt) return true; - pp2 = pp2->Next; - } - while (pp2 != pp); - return false; -} -//------------------------------------------------------------------------------ - -//See "The Point in Polygon Problem for Arbitrary Polygons" by Hormann & Agathos -//http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.88.5498&rep=rep1&type=pdf -int PointInPolygon(const IntPoint &pt, const Path &path) -{ - //returns 0 if false, +1 if true, -1 if pt ON polygon boundary - int result = 0; - size_t cnt = path.size(); - if (cnt < 3) return 0; - IntPoint ip = path[0]; - for(size_t i = 1; i <= cnt; ++i) - { - IntPoint ipNext = (i == cnt ? path[0] : path[i]); - if (ipNext.Y == pt.Y) - { - if ((ipNext.X == pt.X) || (ip.Y == pt.Y && - ((ipNext.X > pt.X) == (ip.X < pt.X)))) return -1; - } - if ((ip.Y < pt.Y) != (ipNext.Y < pt.Y)) - { - if (ip.X >= pt.X) - { - if (ipNext.X > pt.X) result = 1 - result; - else - { - double d = (double)(ip.X - pt.X) * (ipNext.Y - pt.Y) - - (double)(ipNext.X - pt.X) * (ip.Y - pt.Y); - if (!d) return -1; - if ((d > 0) == (ipNext.Y > ip.Y)) result = 1 - result; - } - } else - { - if (ipNext.X > pt.X) - { - double d = (double)(ip.X - pt.X) * (ipNext.Y - pt.Y) - - (double)(ipNext.X - pt.X) * (ip.Y - pt.Y); - if (!d) return -1; - if ((d > 0) == (ipNext.Y > ip.Y)) result = 1 - result; - } - } - } - ip = ipNext; - } - return result; -} -//------------------------------------------------------------------------------ - -int PointInPolygon (const IntPoint &pt, OutPt *op) -{ - //returns 0 if false, +1 if true, -1 if pt ON polygon boundary - int result = 0; - OutPt* startOp = op; - for(;;) - { - if (op->Next->Pt.Y == pt.Y) - { - if ((op->Next->Pt.X == pt.X) || (op->Pt.Y == pt.Y && - ((op->Next->Pt.X > pt.X) == (op->Pt.X < pt.X)))) return -1; - } - if ((op->Pt.Y < pt.Y) != (op->Next->Pt.Y < pt.Y)) - { - if (op->Pt.X >= pt.X) - { - if (op->Next->Pt.X > pt.X) result = 1 - result; - else - { - double d = (double)(op->Pt.X - pt.X) * (op->Next->Pt.Y - pt.Y) - - (double)(op->Next->Pt.X - pt.X) * (op->Pt.Y - pt.Y); - if (!d) return -1; - if ((d > 0) == (op->Next->Pt.Y > op->Pt.Y)) result = 1 - result; - } - } else - { - if (op->Next->Pt.X > pt.X) - { - double d = (double)(op->Pt.X - pt.X) * (op->Next->Pt.Y - pt.Y) - - (double)(op->Next->Pt.X - pt.X) * (op->Pt.Y - pt.Y); - if (!d) return -1; - if ((d > 0) == (op->Next->Pt.Y > op->Pt.Y)) result = 1 - result; - } - } - } - op = op->Next; - if (startOp == op) break; - } - return result; -} -//------------------------------------------------------------------------------ - -bool Poly2ContainsPoly1(OutPt *OutPt1, OutPt *OutPt2) -{ - OutPt* op = OutPt1; - do - { - //nb: PointInPolygon returns 0 if false, +1 if true, -1 if pt on polygon - int res = PointInPolygon(op->Pt, OutPt2); - if (res >= 0) return res > 0; - op = op->Next; - } - while (op != OutPt1); - return true; -} -//---------------------------------------------------------------------- - -bool SlopesEqual(const TEdge &e1, const TEdge &e2, bool UseFullInt64Range) -{ -#ifndef use_int32 - if (UseFullInt64Range) - return Int128Mul(e1.Top.Y - e1.Bot.Y, e2.Top.X - e2.Bot.X) == - Int128Mul(e1.Top.X - e1.Bot.X, e2.Top.Y - e2.Bot.Y); - else -#endif - return (e1.Top.Y - e1.Bot.Y) * (e2.Top.X - e2.Bot.X) == - (e1.Top.X - e1.Bot.X) * (e2.Top.Y - e2.Bot.Y); -} -//------------------------------------------------------------------------------ - -bool SlopesEqual(const IntPoint pt1, const IntPoint pt2, - const IntPoint pt3, bool UseFullInt64Range) -{ -#ifndef use_int32 - if (UseFullInt64Range) - return Int128Mul(pt1.Y-pt2.Y, pt2.X-pt3.X) == Int128Mul(pt1.X-pt2.X, pt2.Y-pt3.Y); - else -#endif - return (pt1.Y-pt2.Y)*(pt2.X-pt3.X) == (pt1.X-pt2.X)*(pt2.Y-pt3.Y); -} -//------------------------------------------------------------------------------ - -bool SlopesEqual(const IntPoint pt1, const IntPoint pt2, - const IntPoint pt3, const IntPoint pt4, bool UseFullInt64Range) -{ -#ifndef use_int32 - if (UseFullInt64Range) - return Int128Mul(pt1.Y-pt2.Y, pt3.X-pt4.X) == Int128Mul(pt1.X-pt2.X, pt3.Y-pt4.Y); - else -#endif - return (pt1.Y-pt2.Y)*(pt3.X-pt4.X) == (pt1.X-pt2.X)*(pt3.Y-pt4.Y); -} -//------------------------------------------------------------------------------ - -inline bool IsHorizontal(TEdge &e) -{ - return e.Dx == HORIZONTAL; -} -//------------------------------------------------------------------------------ - -inline double GetDx(const IntPoint pt1, const IntPoint pt2) -{ - return (pt1.Y == pt2.Y) ? - HORIZONTAL : (double)(pt2.X - pt1.X) / (pt2.Y - pt1.Y); -} -//--------------------------------------------------------------------------- - -inline void SetDx(TEdge &e) -{ - cInt dy = (e.Top.Y - e.Bot.Y); - if (dy == 0) e.Dx = HORIZONTAL; - else e.Dx = (double)(e.Top.X - e.Bot.X) / dy; -} -//--------------------------------------------------------------------------- - -inline void SwapSides(TEdge &Edge1, TEdge &Edge2) -{ - EdgeSide Side = Edge1.Side; - Edge1.Side = Edge2.Side; - Edge2.Side = Side; -} -//------------------------------------------------------------------------------ - -inline void SwapPolyIndexes(TEdge &Edge1, TEdge &Edge2) -{ - int OutIdx = Edge1.OutIdx; - Edge1.OutIdx = Edge2.OutIdx; - Edge2.OutIdx = OutIdx; -} -//------------------------------------------------------------------------------ - -inline cInt TopX(TEdge &edge, const cInt currentY) -{ - return ( currentY == edge.Top.Y ) ? - edge.Top.X : edge.Bot.X + Round(edge.Dx *(currentY - edge.Bot.Y)); -} -//------------------------------------------------------------------------------ - -void IntersectPoint(TEdge &Edge1, TEdge &Edge2, IntPoint &ip) -{ -#ifdef use_xyz - ip.Z = 0; -#endif - - double b1, b2; - if (Edge1.Dx == Edge2.Dx) - { - ip.Y = Edge1.Curr.Y; - ip.X = TopX(Edge1, ip.Y); - return; - } - else if (Edge1.Dx == 0) - { - ip.X = Edge1.Bot.X; - if (IsHorizontal(Edge2)) - ip.Y = Edge2.Bot.Y; - else - { - b2 = Edge2.Bot.Y - (Edge2.Bot.X / Edge2.Dx); - ip.Y = Round(ip.X / Edge2.Dx + b2); - } - } - else if (Edge2.Dx == 0) - { - ip.X = Edge2.Bot.X; - if (IsHorizontal(Edge1)) - ip.Y = Edge1.Bot.Y; - else - { - b1 = Edge1.Bot.Y - (Edge1.Bot.X / Edge1.Dx); - ip.Y = Round(ip.X / Edge1.Dx + b1); - } - } - else - { - b1 = Edge1.Bot.X - Edge1.Bot.Y * Edge1.Dx; - b2 = Edge2.Bot.X - Edge2.Bot.Y * Edge2.Dx; - double q = (b2-b1) / (Edge1.Dx - Edge2.Dx); - ip.Y = Round(q); - if (std::fabs(Edge1.Dx) < std::fabs(Edge2.Dx)) - ip.X = Round(Edge1.Dx * q + b1); - else - ip.X = Round(Edge2.Dx * q + b2); - } - - if (ip.Y < Edge1.Top.Y || ip.Y < Edge2.Top.Y) - { - if (Edge1.Top.Y > Edge2.Top.Y) - ip.Y = Edge1.Top.Y; - else - ip.Y = Edge2.Top.Y; - if (std::fabs(Edge1.Dx) < std::fabs(Edge2.Dx)) - ip.X = TopX(Edge1, ip.Y); - else - ip.X = TopX(Edge2, ip.Y); - } - //finally, don't allow 'ip' to be BELOW curr.Y (ie bottom of scanbeam) ... - if (ip.Y > Edge1.Curr.Y) - { - ip.Y = Edge1.Curr.Y; - //use the more vertical edge to derive X ... - if (std::fabs(Edge1.Dx) > std::fabs(Edge2.Dx)) - ip.X = TopX(Edge2, ip.Y); else - ip.X = TopX(Edge1, ip.Y); - } -} -//------------------------------------------------------------------------------ - -void ReversePolyPtLinks(OutPt *pp) -{ - if (!pp) return; - OutPt *pp1, *pp2; - pp1 = pp; - do { - pp2 = pp1->Next; - pp1->Next = pp1->Prev; - pp1->Prev = pp2; - pp1 = pp2; - } while( pp1 != pp ); -} -//------------------------------------------------------------------------------ - -void DisposeOutPts(OutPt*& pp) -{ - if (pp == 0) return; - pp->Prev->Next = 0; - while( pp ) - { - OutPt *tmpPp = pp; - pp = pp->Next; - delete tmpPp; - } -} -//------------------------------------------------------------------------------ - -inline void InitEdge(TEdge* e, TEdge* eNext, TEdge* ePrev, const IntPoint& Pt) -{ - std::memset(e, 0, sizeof(TEdge)); - e->Next = eNext; - e->Prev = ePrev; - e->Curr = Pt; - e->OutIdx = Unassigned; -} -//------------------------------------------------------------------------------ - -void InitEdge2(TEdge& e, PolyType Pt) -{ - if (e.Curr.Y >= e.Next->Curr.Y) - { - e.Bot = e.Curr; - e.Top = e.Next->Curr; - } else - { - e.Top = e.Curr; - e.Bot = e.Next->Curr; - } - SetDx(e); - e.PolyTyp = Pt; -} -//------------------------------------------------------------------------------ - -TEdge* RemoveEdge(TEdge* e) -{ - //removes e from double_linked_list (but without removing from memory) - e->Prev->Next = e->Next; - e->Next->Prev = e->Prev; - TEdge* result = e->Next; - e->Prev = 0; //flag as removed (see ClipperBase.Clear) - return result; -} -//------------------------------------------------------------------------------ - -inline void ReverseHorizontal(TEdge &e) -{ - //swap horizontal edges' Top and Bottom x's so they follow the natural - //progression of the bounds - ie so their xbots will align with the - //adjoining lower edge. [Helpful in the ProcessHorizontal() method.] - std::swap(e.Top.X, e.Bot.X); -#ifdef use_xyz - std::swap(e.Top.Z, e.Bot.Z); -#endif -} -//------------------------------------------------------------------------------ - -void SwapPoints(IntPoint &pt1, IntPoint &pt2) -{ - IntPoint tmp = pt1; - pt1 = pt2; - pt2 = tmp; -} -//------------------------------------------------------------------------------ - -bool GetOverlapSegment(IntPoint pt1a, IntPoint pt1b, IntPoint pt2a, - IntPoint pt2b, IntPoint &pt1, IntPoint &pt2) -{ - //precondition: segments are Collinear. - if (Abs(pt1a.X - pt1b.X) > Abs(pt1a.Y - pt1b.Y)) - { - if (pt1a.X > pt1b.X) SwapPoints(pt1a, pt1b); - if (pt2a.X > pt2b.X) SwapPoints(pt2a, pt2b); - if (pt1a.X > pt2a.X) pt1 = pt1a; else pt1 = pt2a; - if (pt1b.X < pt2b.X) pt2 = pt1b; else pt2 = pt2b; - return pt1.X < pt2.X; - } else - { - if (pt1a.Y < pt1b.Y) SwapPoints(pt1a, pt1b); - if (pt2a.Y < pt2b.Y) SwapPoints(pt2a, pt2b); - if (pt1a.Y < pt2a.Y) pt1 = pt1a; else pt1 = pt2a; - if (pt1b.Y > pt2b.Y) pt2 = pt1b; else pt2 = pt2b; - return pt1.Y > pt2.Y; - } -} -//------------------------------------------------------------------------------ - -bool FirstIsBottomPt(const OutPt* btmPt1, const OutPt* btmPt2) -{ - OutPt *p = btmPt1->Prev; - while ((p->Pt == btmPt1->Pt) && (p != btmPt1)) p = p->Prev; - double dx1p = std::fabs(GetDx(btmPt1->Pt, p->Pt)); - p = btmPt1->Next; - while ((p->Pt == btmPt1->Pt) && (p != btmPt1)) p = p->Next; - double dx1n = std::fabs(GetDx(btmPt1->Pt, p->Pt)); - - p = btmPt2->Prev; - while ((p->Pt == btmPt2->Pt) && (p != btmPt2)) p = p->Prev; - double dx2p = std::fabs(GetDx(btmPt2->Pt, p->Pt)); - p = btmPt2->Next; - while ((p->Pt == btmPt2->Pt) && (p != btmPt2)) p = p->Next; - double dx2n = std::fabs(GetDx(btmPt2->Pt, p->Pt)); - - if (std::max(dx1p, dx1n) == std::max(dx2p, dx2n) && - std::min(dx1p, dx1n) == std::min(dx2p, dx2n)) - return Area(btmPt1) > 0; //if otherwise identical use orientation - else - return (dx1p >= dx2p && dx1p >= dx2n) || (dx1n >= dx2p && dx1n >= dx2n); -} -//------------------------------------------------------------------------------ - -OutPt* GetBottomPt(OutPt *pp) -{ - OutPt* dups = 0; - OutPt* p = pp->Next; - while (p != pp) - { - if (p->Pt.Y > pp->Pt.Y) - { - pp = p; - dups = 0; - } - else if (p->Pt.Y == pp->Pt.Y && p->Pt.X <= pp->Pt.X) - { - if (p->Pt.X < pp->Pt.X) - { - dups = 0; - pp = p; - } else - { - if (p->Next != pp && p->Prev != pp) dups = p; - } - } - p = p->Next; - } - if (dups) - { - //there appears to be at least 2 vertices at BottomPt so ... - while (dups != p) - { - if (!FirstIsBottomPt(p, dups)) pp = dups; - dups = dups->Next; - while (dups->Pt != pp->Pt) dups = dups->Next; - } - } - return pp; -} -//------------------------------------------------------------------------------ - -bool Pt2IsBetweenPt1AndPt3(const IntPoint pt1, - const IntPoint pt2, const IntPoint pt3) -{ - if ((pt1 == pt3) || (pt1 == pt2) || (pt3 == pt2)) - return false; - else if (pt1.X != pt3.X) - return (pt2.X > pt1.X) == (pt2.X < pt3.X); - else - return (pt2.Y > pt1.Y) == (pt2.Y < pt3.Y); -} -//------------------------------------------------------------------------------ - -bool HorzSegmentsOverlap(cInt seg1a, cInt seg1b, cInt seg2a, cInt seg2b) -{ - if (seg1a > seg1b) std::swap(seg1a, seg1b); - if (seg2a > seg2b) std::swap(seg2a, seg2b); - return (seg1a < seg2b) && (seg2a < seg1b); -} - -//------------------------------------------------------------------------------ -// ClipperBase class methods ... -//------------------------------------------------------------------------------ - -ClipperBase::ClipperBase() //constructor -{ - m_CurrentLM = m_MinimaList.begin(); //begin() == end() here - m_UseFullRange = false; -} -//------------------------------------------------------------------------------ - -ClipperBase::~ClipperBase() //destructor -{ - Clear(); -} -//------------------------------------------------------------------------------ - -void RangeTest(const IntPoint& Pt, bool& useFullRange) -{ - if (useFullRange) - { - if (Pt.X > hiRange || Pt.Y > hiRange || -Pt.X > hiRange || -Pt.Y > hiRange) - throw clipperException("Coordinate outside allowed range"); - } - else if (Pt.X > loRange|| Pt.Y > loRange || -Pt.X > loRange || -Pt.Y > loRange) - { - useFullRange = true; - RangeTest(Pt, useFullRange); - } -} -//------------------------------------------------------------------------------ - -TEdge* FindNextLocMin(TEdge* E) -{ - for (;;) - { - while (E->Bot != E->Prev->Bot || E->Curr == E->Top) E = E->Next; - if (!IsHorizontal(*E) && !IsHorizontal(*E->Prev)) break; - while (IsHorizontal(*E->Prev)) E = E->Prev; - TEdge* E2 = E; - while (IsHorizontal(*E)) E = E->Next; - if (E->Top.Y == E->Prev->Bot.Y) continue; //ie just an intermediate horz. - if (E2->Prev->Bot.X < E->Bot.X) E = E2; - break; - } - return E; -} -//------------------------------------------------------------------------------ - -TEdge* ClipperBase::ProcessBound(TEdge* E, bool NextIsForward) -{ - TEdge *Result = E; - TEdge *Horz = 0; - - if (E->OutIdx == Skip) - { - //if edges still remain in the current bound beyond the skip edge then - //create another LocMin and call ProcessBound once more - if (NextIsForward) - { - while (E->Top.Y == E->Next->Bot.Y) E = E->Next; - //don't include top horizontals when parsing a bound a second time, - //they will be contained in the opposite bound ... - while (E != Result && IsHorizontal(*E)) E = E->Prev; - } - else - { - while (E->Top.Y == E->Prev->Bot.Y) E = E->Prev; - while (E != Result && IsHorizontal(*E)) E = E->Next; - } - - if (E == Result) - { - if (NextIsForward) Result = E->Next; - else Result = E->Prev; - } - else - { - //there are more edges in the bound beyond result starting with E - if (NextIsForward) - E = Result->Next; - else - E = Result->Prev; - MinimaList::value_type locMin; - locMin.Y = E->Bot.Y; - locMin.LeftBound = 0; - locMin.RightBound = E; - E->WindDelta = 0; - Result = ProcessBound(E, NextIsForward); - m_MinimaList.push_back(locMin); - } - return Result; - } - - TEdge *EStart; - - if (IsHorizontal(*E)) - { - //We need to be careful with open paths because this may not be a - //true local minima (ie E may be following a skip edge). - //Also, consecutive horz. edges may start heading left before going right. - if (NextIsForward) - EStart = E->Prev; - else - EStart = E->Next; - if (IsHorizontal(*EStart)) //ie an adjoining horizontal skip edge - { - if (EStart->Bot.X != E->Bot.X && EStart->Top.X != E->Bot.X) - ReverseHorizontal(*E); - } - else if (EStart->Bot.X != E->Bot.X) - ReverseHorizontal(*E); - } - - EStart = E; - if (NextIsForward) - { - while (Result->Top.Y == Result->Next->Bot.Y && Result->Next->OutIdx != Skip) - Result = Result->Next; - if (IsHorizontal(*Result) && Result->Next->OutIdx != Skip) - { - //nb: at the top of a bound, horizontals are added to the bound - //only when the preceding edge attaches to the horizontal's left vertex - //unless a Skip edge is encountered when that becomes the top divide - Horz = Result; - while (IsHorizontal(*Horz->Prev)) Horz = Horz->Prev; - if (Horz->Prev->Top.X > Result->Next->Top.X) Result = Horz->Prev; - } - while (E != Result) - { - E->NextInLML = E->Next; - if (IsHorizontal(*E) && E != EStart && - E->Bot.X != E->Prev->Top.X) ReverseHorizontal(*E); - E = E->Next; - } - if (IsHorizontal(*E) && E != EStart && E->Bot.X != E->Prev->Top.X) - ReverseHorizontal(*E); - Result = Result->Next; //move to the edge just beyond current bound - } else - { - while (Result->Top.Y == Result->Prev->Bot.Y && Result->Prev->OutIdx != Skip) - Result = Result->Prev; - if (IsHorizontal(*Result) && Result->Prev->OutIdx != Skip) - { - Horz = Result; - while (IsHorizontal(*Horz->Next)) Horz = Horz->Next; - if (Horz->Next->Top.X == Result->Prev->Top.X || - Horz->Next->Top.X > Result->Prev->Top.X) Result = Horz->Next; - } - - while (E != Result) - { - E->NextInLML = E->Prev; - if (IsHorizontal(*E) && E != EStart && E->Bot.X != E->Next->Top.X) - ReverseHorizontal(*E); - E = E->Prev; - } - if (IsHorizontal(*E) && E != EStart && E->Bot.X != E->Next->Top.X) - ReverseHorizontal(*E); - Result = Result->Prev; //move to the edge just beyond current bound - } - - return Result; -} -//------------------------------------------------------------------------------ - -bool ClipperBase::AddPath(const Path &pg, PolyType PolyTyp, bool Closed) -{ -#ifdef use_lines - if (!Closed && PolyTyp == ptClip) - throw clipperException("AddPath: Open paths must be subject."); -#else - if (!Closed) - throw clipperException("AddPath: Open paths have been disabled."); -#endif - - int highI = (int)pg.size() -1; - if (Closed) while (highI > 0 && (pg[highI] == pg[0])) --highI; - while (highI > 0 && (pg[highI] == pg[highI -1])) --highI; - if ((Closed && highI < 2) || (!Closed && highI < 1)) return false; - - //create a new edge array ... - TEdge *edges = new TEdge [highI +1]; - - bool IsFlat = true; - //1. Basic (first) edge initialization ... - try - { - edges[1].Curr = pg[1]; - RangeTest(pg[0], m_UseFullRange); - RangeTest(pg[highI], m_UseFullRange); - InitEdge(&edges[0], &edges[1], &edges[highI], pg[0]); - InitEdge(&edges[highI], &edges[0], &edges[highI-1], pg[highI]); - for (int i = highI - 1; i >= 1; --i) - { - RangeTest(pg[i], m_UseFullRange); - InitEdge(&edges[i], &edges[i+1], &edges[i-1], pg[i]); - } - } - catch(...) - { - delete [] edges; - throw; //range test fails - } - TEdge *eStart = &edges[0]; - - //2. Remove duplicate vertices, and (when closed) collinear edges ... - TEdge *E = eStart, *eLoopStop = eStart; - for (;;) - { - //nb: allows matching start and end points when not Closed ... - if (E->Curr == E->Next->Curr && (Closed || E->Next != eStart)) - { - if (E == E->Next) break; - if (E == eStart) eStart = E->Next; - E = RemoveEdge(E); - eLoopStop = E; - continue; - } - if (E->Prev == E->Next) - break; //only two vertices - else if (Closed && - SlopesEqual(E->Prev->Curr, E->Curr, E->Next->Curr, m_UseFullRange) && - (!m_PreserveCollinear || - !Pt2IsBetweenPt1AndPt3(E->Prev->Curr, E->Curr, E->Next->Curr))) - { - //Collinear edges are allowed for open paths but in closed paths - //the default is to merge adjacent collinear edges into a single edge. - //However, if the PreserveCollinear property is enabled, only overlapping - //collinear edges (ie spikes) will be removed from closed paths. - if (E == eStart) eStart = E->Next; - E = RemoveEdge(E); - E = E->Prev; - eLoopStop = E; - continue; - } - E = E->Next; - if ((E == eLoopStop) || (!Closed && E->Next == eStart)) break; - } - - if ((!Closed && (E == E->Next)) || (Closed && (E->Prev == E->Next))) - { - delete [] edges; - return false; - } - - if (!Closed) - { - m_HasOpenPaths = true; - eStart->Prev->OutIdx = Skip; - } - - //3. Do second stage of edge initialization ... - E = eStart; - do - { - InitEdge2(*E, PolyTyp); - E = E->Next; - if (IsFlat && E->Curr.Y != eStart->Curr.Y) IsFlat = false; - } - while (E != eStart); - - //4. Finally, add edge bounds to LocalMinima list ... - - //Totally flat paths must be handled differently when adding them - //to LocalMinima list to avoid endless loops etc ... - if (IsFlat) - { - if (Closed) - { - delete [] edges; - return false; - } - E->Prev->OutIdx = Skip; - MinimaList::value_type locMin; - locMin.Y = E->Bot.Y; - locMin.LeftBound = 0; - locMin.RightBound = E; - locMin.RightBound->Side = esRight; - locMin.RightBound->WindDelta = 0; - for (;;) - { - if (E->Bot.X != E->Prev->Top.X) ReverseHorizontal(*E); - if (E->Next->OutIdx == Skip) break; - E->NextInLML = E->Next; - E = E->Next; - } - m_MinimaList.push_back(locMin); - m_edges.push_back(edges); - return true; - } - - m_edges.push_back(edges); - bool leftBoundIsForward; - TEdge* EMin = 0; - - //workaround to avoid an endless loop in the while loop below when - //open paths have matching start and end points ... - if (E->Prev->Bot == E->Prev->Top) E = E->Next; - - for (;;) - { - E = FindNextLocMin(E); - if (E == EMin) break; - else if (!EMin) EMin = E; - - //E and E.Prev now share a local minima (left aligned if horizontal). - //Compare their slopes to find which starts which bound ... - MinimaList::value_type locMin; - locMin.Y = E->Bot.Y; - if (E->Dx < E->Prev->Dx) - { - locMin.LeftBound = E->Prev; - locMin.RightBound = E; - leftBoundIsForward = false; //Q.nextInLML = Q.prev - } else - { - locMin.LeftBound = E; - locMin.RightBound = E->Prev; - leftBoundIsForward = true; //Q.nextInLML = Q.next - } - - if (!Closed) locMin.LeftBound->WindDelta = 0; - else if (locMin.LeftBound->Next == locMin.RightBound) - locMin.LeftBound->WindDelta = -1; - else locMin.LeftBound->WindDelta = 1; - locMin.RightBound->WindDelta = -locMin.LeftBound->WindDelta; - - E = ProcessBound(locMin.LeftBound, leftBoundIsForward); - if (E->OutIdx == Skip) E = ProcessBound(E, leftBoundIsForward); - - TEdge* E2 = ProcessBound(locMin.RightBound, !leftBoundIsForward); - if (E2->OutIdx == Skip) E2 = ProcessBound(E2, !leftBoundIsForward); - - if (locMin.LeftBound->OutIdx == Skip) - locMin.LeftBound = 0; - else if (locMin.RightBound->OutIdx == Skip) - locMin.RightBound = 0; - m_MinimaList.push_back(locMin); - if (!leftBoundIsForward) E = E2; - } - return true; -} -//------------------------------------------------------------------------------ - -bool ClipperBase::AddPaths(const Paths &ppg, PolyType PolyTyp, bool Closed) -{ - bool result = false; - for (Paths::size_type i = 0; i < ppg.size(); ++i) - if (AddPath(ppg[i], PolyTyp, Closed)) result = true; - return result; -} -//------------------------------------------------------------------------------ - -void ClipperBase::Clear() -{ - DisposeLocalMinimaList(); - for (EdgeList::size_type i = 0; i < m_edges.size(); ++i) - { - TEdge* edges = m_edges[i]; - delete [] edges; - } - m_edges.clear(); - m_UseFullRange = false; - m_HasOpenPaths = false; -} -//------------------------------------------------------------------------------ - -void ClipperBase::Reset() -{ - m_CurrentLM = m_MinimaList.begin(); - if (m_CurrentLM == m_MinimaList.end()) return; //ie nothing to process - std::sort(m_MinimaList.begin(), m_MinimaList.end(), LocMinSorter()); - - m_Scanbeam = ScanbeamList(); //clears/resets priority_queue - //reset all edges ... - for (MinimaList::iterator lm = m_MinimaList.begin(); lm != m_MinimaList.end(); ++lm) - { - InsertScanbeam(lm->Y); - TEdge* e = lm->LeftBound; - if (e) - { - e->Curr = e->Bot; - e->Side = esLeft; - e->OutIdx = Unassigned; - } - - e = lm->RightBound; - if (e) - { - e->Curr = e->Bot; - e->Side = esRight; - e->OutIdx = Unassigned; - } - } - m_ActiveEdges = 0; - m_CurrentLM = m_MinimaList.begin(); -} -//------------------------------------------------------------------------------ - -void ClipperBase::DisposeLocalMinimaList() -{ - m_MinimaList.clear(); - m_CurrentLM = m_MinimaList.begin(); -} -//------------------------------------------------------------------------------ - -bool ClipperBase::PopLocalMinima(cInt Y, const LocalMinimum *&locMin) -{ - if (m_CurrentLM == m_MinimaList.end() || (*m_CurrentLM).Y != Y) return false; - locMin = &(*m_CurrentLM); - ++m_CurrentLM; - return true; -} -//------------------------------------------------------------------------------ - -IntRect ClipperBase::GetBounds() -{ - IntRect result; - MinimaList::iterator lm = m_MinimaList.begin(); - if (lm == m_MinimaList.end()) - { - result.left = result.top = result.right = result.bottom = 0; - return result; - } - result.left = lm->LeftBound->Bot.X; - result.top = lm->LeftBound->Bot.Y; - result.right = lm->LeftBound->Bot.X; - result.bottom = lm->LeftBound->Bot.Y; - while (lm != m_MinimaList.end()) - { - //todo - needs fixing for open paths - result.bottom = std::max(result.bottom, lm->LeftBound->Bot.Y); - TEdge* e = lm->LeftBound; - for (;;) { - TEdge* bottomE = e; - while (e->NextInLML) - { - if (e->Bot.X < result.left) result.left = e->Bot.X; - if (e->Bot.X > result.right) result.right = e->Bot.X; - e = e->NextInLML; - } - result.left = std::min(result.left, e->Bot.X); - result.right = std::max(result.right, e->Bot.X); - result.left = std::min(result.left, e->Top.X); - result.right = std::max(result.right, e->Top.X); - result.top = std::min(result.top, e->Top.Y); - if (bottomE == lm->LeftBound) e = lm->RightBound; - else break; - } - ++lm; - } - return result; -} -//------------------------------------------------------------------------------ - -void ClipperBase::InsertScanbeam(const cInt Y) -{ - m_Scanbeam.push(Y); -} -//------------------------------------------------------------------------------ - -bool ClipperBase::PopScanbeam(cInt &Y) -{ - if (m_Scanbeam.empty()) return false; - Y = m_Scanbeam.top(); - m_Scanbeam.pop(); - while (!m_Scanbeam.empty() && Y == m_Scanbeam.top()) { m_Scanbeam.pop(); } // Pop duplicates. - return true; -} -//------------------------------------------------------------------------------ - -void ClipperBase::DisposeAllOutRecs(){ - for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) - DisposeOutRec(i); - m_PolyOuts.clear(); -} -//------------------------------------------------------------------------------ - -void ClipperBase::DisposeOutRec(PolyOutList::size_type index) -{ - OutRec *outRec = m_PolyOuts[index]; - if (outRec->Pts) DisposeOutPts(outRec->Pts); - delete outRec; - m_PolyOuts[index] = 0; -} -//------------------------------------------------------------------------------ - -void ClipperBase::DeleteFromAEL(TEdge *e) -{ - TEdge* AelPrev = e->PrevInAEL; - TEdge* AelNext = e->NextInAEL; - if (!AelPrev && !AelNext && (e != m_ActiveEdges)) return; //already deleted - if (AelPrev) AelPrev->NextInAEL = AelNext; - else m_ActiveEdges = AelNext; - if (AelNext) AelNext->PrevInAEL = AelPrev; - e->NextInAEL = 0; - e->PrevInAEL = 0; -} -//------------------------------------------------------------------------------ - -OutRec* ClipperBase::CreateOutRec() -{ - OutRec* result = new OutRec; - result->IsHole = false; - result->IsOpen = false; - result->FirstLeft = 0; - result->Pts = 0; - result->BottomPt = 0; - result->PolyNd = 0; - m_PolyOuts.push_back(result); - result->Idx = (int)m_PolyOuts.size() - 1; - return result; -} -//------------------------------------------------------------------------------ - -void ClipperBase::SwapPositionsInAEL(TEdge *Edge1, TEdge *Edge2) -{ - //check that one or other edge hasn't already been removed from AEL ... - if (Edge1->NextInAEL == Edge1->PrevInAEL || - Edge2->NextInAEL == Edge2->PrevInAEL) return; - - if (Edge1->NextInAEL == Edge2) - { - TEdge* Next = Edge2->NextInAEL; - if (Next) Next->PrevInAEL = Edge1; - TEdge* Prev = Edge1->PrevInAEL; - if (Prev) Prev->NextInAEL = Edge2; - Edge2->PrevInAEL = Prev; - Edge2->NextInAEL = Edge1; - Edge1->PrevInAEL = Edge2; - Edge1->NextInAEL = Next; - } - else if (Edge2->NextInAEL == Edge1) - { - TEdge* Next = Edge1->NextInAEL; - if (Next) Next->PrevInAEL = Edge2; - TEdge* Prev = Edge2->PrevInAEL; - if (Prev) Prev->NextInAEL = Edge1; - Edge1->PrevInAEL = Prev; - Edge1->NextInAEL = Edge2; - Edge2->PrevInAEL = Edge1; - Edge2->NextInAEL = Next; - } - else - { - TEdge* Next = Edge1->NextInAEL; - TEdge* Prev = Edge1->PrevInAEL; - Edge1->NextInAEL = Edge2->NextInAEL; - if (Edge1->NextInAEL) Edge1->NextInAEL->PrevInAEL = Edge1; - Edge1->PrevInAEL = Edge2->PrevInAEL; - if (Edge1->PrevInAEL) Edge1->PrevInAEL->NextInAEL = Edge1; - Edge2->NextInAEL = Next; - if (Edge2->NextInAEL) Edge2->NextInAEL->PrevInAEL = Edge2; - Edge2->PrevInAEL = Prev; - if (Edge2->PrevInAEL) Edge2->PrevInAEL->NextInAEL = Edge2; - } - - if (!Edge1->PrevInAEL) m_ActiveEdges = Edge1; - else if (!Edge2->PrevInAEL) m_ActiveEdges = Edge2; -} -//------------------------------------------------------------------------------ - -void ClipperBase::UpdateEdgeIntoAEL(TEdge *&e) -{ - if (!e->NextInLML) - throw clipperException("UpdateEdgeIntoAEL: invalid call"); - - e->NextInLML->OutIdx = e->OutIdx; - TEdge* AelPrev = e->PrevInAEL; - TEdge* AelNext = e->NextInAEL; - if (AelPrev) AelPrev->NextInAEL = e->NextInLML; - else m_ActiveEdges = e->NextInLML; - if (AelNext) AelNext->PrevInAEL = e->NextInLML; - e->NextInLML->Side = e->Side; - e->NextInLML->WindDelta = e->WindDelta; - e->NextInLML->WindCnt = e->WindCnt; - e->NextInLML->WindCnt2 = e->WindCnt2; - e = e->NextInLML; - e->Curr = e->Bot; - e->PrevInAEL = AelPrev; - e->NextInAEL = AelNext; - if (!IsHorizontal(*e)) InsertScanbeam(e->Top.Y); -} -//------------------------------------------------------------------------------ - -bool ClipperBase::LocalMinimaPending() -{ - return (m_CurrentLM != m_MinimaList.end()); -} - -//------------------------------------------------------------------------------ -// TClipper methods ... -//------------------------------------------------------------------------------ - -Clipper::Clipper(int initOptions) : ClipperBase() //constructor -{ - m_ExecuteLocked = false; - m_UseFullRange = false; - m_ReverseOutput = ((initOptions & ioReverseSolution) != 0); - m_StrictSimple = ((initOptions & ioStrictlySimple) != 0); - m_PreserveCollinear = ((initOptions & ioPreserveCollinear) != 0); - m_HasOpenPaths = false; -#ifdef use_xyz - m_ZFill = 0; -#endif -} -//------------------------------------------------------------------------------ - -#ifdef use_xyz -void Clipper::ZFillFunction(ZFillCallback zFillFunc) -{ - m_ZFill = zFillFunc; -} -//------------------------------------------------------------------------------ -#endif - -bool Clipper::Execute(ClipType clipType, Paths &solution, PolyFillType fillType) -{ - return Execute(clipType, solution, fillType, fillType); -} -//------------------------------------------------------------------------------ - -bool Clipper::Execute(ClipType clipType, PolyTree &polytree, PolyFillType fillType) -{ - return Execute(clipType, polytree, fillType, fillType); -} -//------------------------------------------------------------------------------ - -bool Clipper::Execute(ClipType clipType, Paths &solution, - PolyFillType subjFillType, PolyFillType clipFillType) -{ - if( m_ExecuteLocked ) return false; - if (m_HasOpenPaths) - throw clipperException("Error: PolyTree struct is needed for open path clipping."); - m_ExecuteLocked = true; - solution.resize(0); - m_SubjFillType = subjFillType; - m_ClipFillType = clipFillType; - m_ClipType = clipType; - m_UsingPolyTree = false; - bool succeeded = ExecuteInternal(); - if (succeeded) BuildResult(solution); - DisposeAllOutRecs(); - m_ExecuteLocked = false; - return succeeded; -} -//------------------------------------------------------------------------------ - -bool Clipper::Execute(ClipType clipType, PolyTree& polytree, - PolyFillType subjFillType, PolyFillType clipFillType) -{ - if( m_ExecuteLocked ) return false; - m_ExecuteLocked = true; - m_SubjFillType = subjFillType; - m_ClipFillType = clipFillType; - m_ClipType = clipType; - m_UsingPolyTree = true; - bool succeeded = ExecuteInternal(); - if (succeeded) BuildResult2(polytree); - DisposeAllOutRecs(); - m_ExecuteLocked = false; - return succeeded; -} -//------------------------------------------------------------------------------ - -void Clipper::FixHoleLinkage(OutRec &outrec) -{ - //skip OutRecs that (a) contain outermost polygons or - //(b) already have the correct owner/child linkage ... - if (!outrec.FirstLeft || - (outrec.IsHole != outrec.FirstLeft->IsHole && - outrec.FirstLeft->Pts)) return; - - OutRec* orfl = outrec.FirstLeft; - while (orfl && ((orfl->IsHole == outrec.IsHole) || !orfl->Pts)) - orfl = orfl->FirstLeft; - outrec.FirstLeft = orfl; -} -//------------------------------------------------------------------------------ - -bool Clipper::ExecuteInternal() -{ - bool succeeded = true; - try { - Reset(); - m_Maxima = MaximaList(); - m_SortedEdges = 0; - - succeeded = true; - cInt botY, topY; - if (!PopScanbeam(botY)) return false; - InsertLocalMinimaIntoAEL(botY); - while (PopScanbeam(topY) || LocalMinimaPending()) - { - ProcessHorizontals(); - ClearGhostJoins(); - if (!ProcessIntersections(topY)) - { - succeeded = false; - break; - } - ProcessEdgesAtTopOfScanbeam(topY); - botY = topY; - InsertLocalMinimaIntoAEL(botY); - } - } - catch(...) - { - succeeded = false; - } - - if (succeeded) - { - //fix orientations ... - for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) - { - OutRec *outRec = m_PolyOuts[i]; - if (!outRec->Pts || outRec->IsOpen) continue; - if ((outRec->IsHole ^ m_ReverseOutput) == (Area(*outRec) > 0)) - ReversePolyPtLinks(outRec->Pts); - } - - if (!m_Joins.empty()) JoinCommonEdges(); - - //unfortunately FixupOutPolygon() must be done after JoinCommonEdges() - for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) - { - OutRec *outRec = m_PolyOuts[i]; - if (!outRec->Pts) continue; - if (outRec->IsOpen) - FixupOutPolyline(*outRec); - else - FixupOutPolygon(*outRec); - } - - if (m_StrictSimple) DoSimplePolygons(); - } - - ClearJoins(); - ClearGhostJoins(); - return succeeded; -} -//------------------------------------------------------------------------------ - -void Clipper::SetWindingCount(TEdge &edge) -{ - TEdge *e = edge.PrevInAEL; - //find the edge of the same polytype that immediately preceeds 'edge' in AEL - while (e && ((e->PolyTyp != edge.PolyTyp) || (e->WindDelta == 0))) e = e->PrevInAEL; - if (!e) - { - if (edge.WindDelta == 0) - { - PolyFillType pft = (edge.PolyTyp == ptSubject ? m_SubjFillType : m_ClipFillType); - edge.WindCnt = (pft == pftNegative ? -1 : 1); - } - else - edge.WindCnt = edge.WindDelta; - edge.WindCnt2 = 0; - e = m_ActiveEdges; //ie get ready to calc WindCnt2 - } - else if (edge.WindDelta == 0 && m_ClipType != ctUnion) - { - edge.WindCnt = 1; - edge.WindCnt2 = e->WindCnt2; - e = e->NextInAEL; //ie get ready to calc WindCnt2 - } - else if (IsEvenOddFillType(edge)) - { - //EvenOdd filling ... - if (edge.WindDelta == 0) - { - //are we inside a subj polygon ... - bool Inside = true; - TEdge *e2 = e->PrevInAEL; - while (e2) - { - if (e2->PolyTyp == e->PolyTyp && e2->WindDelta != 0) - Inside = !Inside; - e2 = e2->PrevInAEL; - } - edge.WindCnt = (Inside ? 0 : 1); - } - else - { - edge.WindCnt = edge.WindDelta; - } - edge.WindCnt2 = e->WindCnt2; - e = e->NextInAEL; //ie get ready to calc WindCnt2 - } - else - { - //nonZero, Positive or Negative filling ... - if (e->WindCnt * e->WindDelta < 0) - { - //prev edge is 'decreasing' WindCount (WC) toward zero - //so we're outside the previous polygon ... - if (Abs(e->WindCnt) > 1) - { - //outside prev poly but still inside another. - //when reversing direction of prev poly use the same WC - if (e->WindDelta * edge.WindDelta < 0) edge.WindCnt = e->WindCnt; - //otherwise continue to 'decrease' WC ... - else edge.WindCnt = e->WindCnt + edge.WindDelta; - } - else - //now outside all polys of same polytype so set own WC ... - edge.WindCnt = (edge.WindDelta == 0 ? 1 : edge.WindDelta); - } else - { - //prev edge is 'increasing' WindCount (WC) away from zero - //so we're inside the previous polygon ... - if (edge.WindDelta == 0) - edge.WindCnt = (e->WindCnt < 0 ? e->WindCnt - 1 : e->WindCnt + 1); - //if wind direction is reversing prev then use same WC - else if (e->WindDelta * edge.WindDelta < 0) edge.WindCnt = e->WindCnt; - //otherwise add to WC ... - else edge.WindCnt = e->WindCnt + edge.WindDelta; - } - edge.WindCnt2 = e->WindCnt2; - e = e->NextInAEL; //ie get ready to calc WindCnt2 - } - - //update WindCnt2 ... - if (IsEvenOddAltFillType(edge)) - { - //EvenOdd filling ... - while (e != &edge) - { - if (e->WindDelta != 0) - edge.WindCnt2 = (edge.WindCnt2 == 0 ? 1 : 0); - e = e->NextInAEL; - } - } else - { - //nonZero, Positive or Negative filling ... - while ( e != &edge ) - { - edge.WindCnt2 += e->WindDelta; - e = e->NextInAEL; - } - } -} -//------------------------------------------------------------------------------ - -bool Clipper::IsEvenOddFillType(const TEdge& edge) const -{ - if (edge.PolyTyp == ptSubject) - return m_SubjFillType == pftEvenOdd; else - return m_ClipFillType == pftEvenOdd; -} -//------------------------------------------------------------------------------ - -bool Clipper::IsEvenOddAltFillType(const TEdge& edge) const -{ - if (edge.PolyTyp == ptSubject) - return m_ClipFillType == pftEvenOdd; else - return m_SubjFillType == pftEvenOdd; -} -//------------------------------------------------------------------------------ - -bool Clipper::IsContributing(const TEdge& edge) const -{ - PolyFillType pft, pft2; - if (edge.PolyTyp == ptSubject) - { - pft = m_SubjFillType; - pft2 = m_ClipFillType; - } else - { - pft = m_ClipFillType; - pft2 = m_SubjFillType; - } - - switch(pft) - { - case pftEvenOdd: - //return false if a subj line has been flagged as inside a subj polygon - if (edge.WindDelta == 0 && edge.WindCnt != 1) return false; - break; - case pftNonZero: - if (Abs(edge.WindCnt) != 1) return false; - break; - case pftPositive: - if (edge.WindCnt != 1) return false; - break; - default: //pftNegative - if (edge.WindCnt != -1) return false; - } - - switch(m_ClipType) - { - case ctIntersection: - switch(pft2) - { - case pftEvenOdd: - case pftNonZero: - return (edge.WindCnt2 != 0); - case pftPositive: - return (edge.WindCnt2 > 0); - default: - return (edge.WindCnt2 < 0); - } - break; - case ctUnion: - switch(pft2) - { - case pftEvenOdd: - case pftNonZero: - return (edge.WindCnt2 == 0); - case pftPositive: - return (edge.WindCnt2 <= 0); - default: - return (edge.WindCnt2 >= 0); - } - break; - case ctDifference: - if (edge.PolyTyp == ptSubject) - switch(pft2) - { - case pftEvenOdd: - case pftNonZero: - return (edge.WindCnt2 == 0); - case pftPositive: - return (edge.WindCnt2 <= 0); - default: - return (edge.WindCnt2 >= 0); - } - else - switch(pft2) - { - case pftEvenOdd: - case pftNonZero: - return (edge.WindCnt2 != 0); - case pftPositive: - return (edge.WindCnt2 > 0); - default: - return (edge.WindCnt2 < 0); - } - break; - case ctXor: - if (edge.WindDelta == 0) //XOr always contributing unless open - switch(pft2) - { - case pftEvenOdd: - case pftNonZero: - return (edge.WindCnt2 == 0); - case pftPositive: - return (edge.WindCnt2 <= 0); - default: - return (edge.WindCnt2 >= 0); - } - else - return true; - break; - default: - return true; - } -} -//------------------------------------------------------------------------------ - -OutPt* Clipper::AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &Pt) -{ - OutPt* result; - TEdge *e, *prevE; - if (IsHorizontal(*e2) || ( e1->Dx > e2->Dx )) - { - result = AddOutPt(e1, Pt); - e2->OutIdx = e1->OutIdx; - e1->Side = esLeft; - e2->Side = esRight; - e = e1; - if (e->PrevInAEL == e2) - prevE = e2->PrevInAEL; - else - prevE = e->PrevInAEL; - } else - { - result = AddOutPt(e2, Pt); - e1->OutIdx = e2->OutIdx; - e1->Side = esRight; - e2->Side = esLeft; - e = e2; - if (e->PrevInAEL == e1) - prevE = e1->PrevInAEL; - else - prevE = e->PrevInAEL; - } - - if (prevE && prevE->OutIdx >= 0) - { - cInt xPrev = TopX(*prevE, Pt.Y); - cInt xE = TopX(*e, Pt.Y); - if (xPrev == xE && (e->WindDelta != 0) && (prevE->WindDelta != 0) && - SlopesEqual(IntPoint(xPrev, Pt.Y), prevE->Top, IntPoint(xE, Pt.Y), e->Top, m_UseFullRange)) - { - OutPt* outPt = AddOutPt(prevE, Pt); - AddJoin(result, outPt, e->Top); - } - } - return result; -} -//------------------------------------------------------------------------------ - -void Clipper::AddLocalMaxPoly(TEdge *e1, TEdge *e2, const IntPoint &Pt) -{ - AddOutPt( e1, Pt ); - if (e2->WindDelta == 0) AddOutPt(e2, Pt); - if( e1->OutIdx == e2->OutIdx ) - { - e1->OutIdx = Unassigned; - e2->OutIdx = Unassigned; - } - else if (e1->OutIdx < e2->OutIdx) - AppendPolygon(e1, e2); - else - AppendPolygon(e2, e1); -} -//------------------------------------------------------------------------------ - -void Clipper::AddEdgeToSEL(TEdge *edge) -{ - //SEL pointers in PEdge are reused to build a list of horizontal edges. - //However, we don't need to worry about order with horizontal edge processing. - if( !m_SortedEdges ) - { - m_SortedEdges = edge; - edge->PrevInSEL = 0; - edge->NextInSEL = 0; - } - else - { - edge->NextInSEL = m_SortedEdges; - edge->PrevInSEL = 0; - m_SortedEdges->PrevInSEL = edge; - m_SortedEdges = edge; - } -} -//------------------------------------------------------------------------------ - -bool Clipper::PopEdgeFromSEL(TEdge *&edge) -{ - if (!m_SortedEdges) return false; - edge = m_SortedEdges; - DeleteFromSEL(m_SortedEdges); - return true; -} -//------------------------------------------------------------------------------ - -void Clipper::CopyAELToSEL() -{ - TEdge* e = m_ActiveEdges; - m_SortedEdges = e; - while ( e ) - { - e->PrevInSEL = e->PrevInAEL; - e->NextInSEL = e->NextInAEL; - e = e->NextInAEL; - } -} -//------------------------------------------------------------------------------ - -void Clipper::AddJoin(OutPt *op1, OutPt *op2, const IntPoint OffPt) -{ - Join* j = new Join; - j->OutPt1 = op1; - j->OutPt2 = op2; - j->OffPt = OffPt; - m_Joins.push_back(j); -} -//------------------------------------------------------------------------------ - -void Clipper::ClearJoins() -{ - for (JoinList::size_type i = 0; i < m_Joins.size(); i++) - delete m_Joins[i]; - m_Joins.resize(0); -} -//------------------------------------------------------------------------------ - -void Clipper::ClearGhostJoins() -{ - for (JoinList::size_type i = 0; i < m_GhostJoins.size(); i++) - delete m_GhostJoins[i]; - m_GhostJoins.resize(0); -} -//------------------------------------------------------------------------------ - -void Clipper::AddGhostJoin(OutPt *op, const IntPoint OffPt) -{ - Join* j = new Join; - j->OutPt1 = op; - j->OutPt2 = 0; - j->OffPt = OffPt; - m_GhostJoins.push_back(j); -} -//------------------------------------------------------------------------------ - -void Clipper::InsertLocalMinimaIntoAEL(const cInt botY) -{ - const LocalMinimum *lm; - while (PopLocalMinima(botY, lm)) - { - TEdge* lb = lm->LeftBound; - TEdge* rb = lm->RightBound; - - OutPt *Op1 = 0; - if (!lb) - { - //nb: don't insert LB into either AEL or SEL - InsertEdgeIntoAEL(rb, 0); - SetWindingCount(*rb); - if (IsContributing(*rb)) - Op1 = AddOutPt(rb, rb->Bot); - } - else if (!rb) - { - InsertEdgeIntoAEL(lb, 0); - SetWindingCount(*lb); - if (IsContributing(*lb)) - Op1 = AddOutPt(lb, lb->Bot); - InsertScanbeam(lb->Top.Y); - } - else - { - InsertEdgeIntoAEL(lb, 0); - InsertEdgeIntoAEL(rb, lb); - SetWindingCount( *lb ); - rb->WindCnt = lb->WindCnt; - rb->WindCnt2 = lb->WindCnt2; - if (IsContributing(*lb)) - Op1 = AddLocalMinPoly(lb, rb, lb->Bot); - InsertScanbeam(lb->Top.Y); - } - - if (rb) - { - if (IsHorizontal(*rb)) - { - AddEdgeToSEL(rb); - if (rb->NextInLML) - InsertScanbeam(rb->NextInLML->Top.Y); - } - else InsertScanbeam( rb->Top.Y ); - } - - if (!lb || !rb) continue; - - //if any output polygons share an edge, they'll need joining later ... - if (Op1 && IsHorizontal(*rb) && - m_GhostJoins.size() > 0 && (rb->WindDelta != 0)) - { - for (JoinList::size_type i = 0; i < m_GhostJoins.size(); ++i) - { - Join* jr = m_GhostJoins[i]; - //if the horizontal Rb and a 'ghost' horizontal overlap, then convert - //the 'ghost' join to a real join ready for later ... - if (HorzSegmentsOverlap(jr->OutPt1->Pt.X, jr->OffPt.X, rb->Bot.X, rb->Top.X)) - AddJoin(jr->OutPt1, Op1, jr->OffPt); - } - } - - if (lb->OutIdx >= 0 && lb->PrevInAEL && - lb->PrevInAEL->Curr.X == lb->Bot.X && - lb->PrevInAEL->OutIdx >= 0 && - SlopesEqual(lb->PrevInAEL->Bot, lb->PrevInAEL->Top, lb->Curr, lb->Top, m_UseFullRange) && - (lb->WindDelta != 0) && (lb->PrevInAEL->WindDelta != 0)) - { - OutPt *Op2 = AddOutPt(lb->PrevInAEL, lb->Bot); - AddJoin(Op1, Op2, lb->Top); - } - - if(lb->NextInAEL != rb) - { - - if (rb->OutIdx >= 0 && rb->PrevInAEL->OutIdx >= 0 && - SlopesEqual(rb->PrevInAEL->Curr, rb->PrevInAEL->Top, rb->Curr, rb->Top, m_UseFullRange) && - (rb->WindDelta != 0) && (rb->PrevInAEL->WindDelta != 0)) - { - OutPt *Op2 = AddOutPt(rb->PrevInAEL, rb->Bot); - AddJoin(Op1, Op2, rb->Top); - } - - TEdge* e = lb->NextInAEL; - if (e) - { - while( e != rb ) - { - //nb: For calculating winding counts etc, IntersectEdges() assumes - //that param1 will be to the Right of param2 ABOVE the intersection ... - IntersectEdges(rb , e , lb->Curr); //order important here - e = e->NextInAEL; - } - } - } - - } -} -//------------------------------------------------------------------------------ - -void Clipper::DeleteFromSEL(TEdge *e) -{ - TEdge* SelPrev = e->PrevInSEL; - TEdge* SelNext = e->NextInSEL; - if( !SelPrev && !SelNext && (e != m_SortedEdges) ) return; //already deleted - if( SelPrev ) SelPrev->NextInSEL = SelNext; - else m_SortedEdges = SelNext; - if( SelNext ) SelNext->PrevInSEL = SelPrev; - e->NextInSEL = 0; - e->PrevInSEL = 0; -} -//------------------------------------------------------------------------------ - -#ifdef use_xyz -void Clipper::SetZ(IntPoint& pt, TEdge& e1, TEdge& e2) -{ - if (pt.Z != 0 || !m_ZFill) return; - else if (pt == e1.Bot) pt.Z = e1.Bot.Z; - else if (pt == e1.Top) pt.Z = e1.Top.Z; - else if (pt == e2.Bot) pt.Z = e2.Bot.Z; - else if (pt == e2.Top) pt.Z = e2.Top.Z; - else (*m_ZFill)(e1.Bot, e1.Top, e2.Bot, e2.Top, pt); -} -//------------------------------------------------------------------------------ -#endif - -void Clipper::IntersectEdges(TEdge *e1, TEdge *e2, IntPoint &Pt) -{ - bool e1Contributing = ( e1->OutIdx >= 0 ); - bool e2Contributing = ( e2->OutIdx >= 0 ); - -#ifdef use_xyz - SetZ(Pt, *e1, *e2); -#endif - -#ifdef use_lines - //if either edge is on an OPEN path ... - if (e1->WindDelta == 0 || e2->WindDelta == 0) - { - //ignore subject-subject open path intersections UNLESS they - //are both open paths, AND they are both 'contributing maximas' ... - if (e1->WindDelta == 0 && e2->WindDelta == 0) return; - - //if intersecting a subj line with a subj poly ... - else if (e1->PolyTyp == e2->PolyTyp && - e1->WindDelta != e2->WindDelta && m_ClipType == ctUnion) - { - if (e1->WindDelta == 0) - { - if (e2Contributing) - { - AddOutPt(e1, Pt); - if (e1Contributing) e1->OutIdx = Unassigned; - } - } - else - { - if (e1Contributing) - { - AddOutPt(e2, Pt); - if (e2Contributing) e2->OutIdx = Unassigned; - } - } - } - else if (e1->PolyTyp != e2->PolyTyp) - { - //toggle subj open path OutIdx on/off when Abs(clip.WndCnt) == 1 ... - if ((e1->WindDelta == 0) && abs(e2->WindCnt) == 1 && - (m_ClipType != ctUnion || e2->WindCnt2 == 0)) - { - AddOutPt(e1, Pt); - if (e1Contributing) e1->OutIdx = Unassigned; - } - else if ((e2->WindDelta == 0) && (abs(e1->WindCnt) == 1) && - (m_ClipType != ctUnion || e1->WindCnt2 == 0)) - { - AddOutPt(e2, Pt); - if (e2Contributing) e2->OutIdx = Unassigned; - } - } - return; - } -#endif - - //update winding counts... - //assumes that e1 will be to the Right of e2 ABOVE the intersection - if ( e1->PolyTyp == e2->PolyTyp ) - { - if ( IsEvenOddFillType( *e1) ) - { - int oldE1WindCnt = e1->WindCnt; - e1->WindCnt = e2->WindCnt; - e2->WindCnt = oldE1WindCnt; - } else - { - if (e1->WindCnt + e2->WindDelta == 0 ) e1->WindCnt = -e1->WindCnt; - else e1->WindCnt += e2->WindDelta; - if ( e2->WindCnt - e1->WindDelta == 0 ) e2->WindCnt = -e2->WindCnt; - else e2->WindCnt -= e1->WindDelta; - } - } else - { - if (!IsEvenOddFillType(*e2)) e1->WindCnt2 += e2->WindDelta; - else e1->WindCnt2 = ( e1->WindCnt2 == 0 ) ? 1 : 0; - if (!IsEvenOddFillType(*e1)) e2->WindCnt2 -= e1->WindDelta; - else e2->WindCnt2 = ( e2->WindCnt2 == 0 ) ? 1 : 0; - } - - PolyFillType e1FillType, e2FillType, e1FillType2, e2FillType2; - if (e1->PolyTyp == ptSubject) - { - e1FillType = m_SubjFillType; - e1FillType2 = m_ClipFillType; - } else - { - e1FillType = m_ClipFillType; - e1FillType2 = m_SubjFillType; - } - if (e2->PolyTyp == ptSubject) - { - e2FillType = m_SubjFillType; - e2FillType2 = m_ClipFillType; - } else - { - e2FillType = m_ClipFillType; - e2FillType2 = m_SubjFillType; - } - - cInt e1Wc, e2Wc; - switch (e1FillType) - { - case pftPositive: e1Wc = e1->WindCnt; break; - case pftNegative: e1Wc = -e1->WindCnt; break; - default: e1Wc = Abs(e1->WindCnt); - } - switch(e2FillType) - { - case pftPositive: e2Wc = e2->WindCnt; break; - case pftNegative: e2Wc = -e2->WindCnt; break; - default: e2Wc = Abs(e2->WindCnt); - } - - if ( e1Contributing && e2Contributing ) - { - if ((e1Wc != 0 && e1Wc != 1) || (e2Wc != 0 && e2Wc != 1) || - (e1->PolyTyp != e2->PolyTyp && m_ClipType != ctXor) ) - { - AddLocalMaxPoly(e1, e2, Pt); - } - else - { - AddOutPt(e1, Pt); - AddOutPt(e2, Pt); - SwapSides( *e1 , *e2 ); - SwapPolyIndexes( *e1 , *e2 ); - } - } - else if ( e1Contributing ) - { - if (e2Wc == 0 || e2Wc == 1) - { - AddOutPt(e1, Pt); - SwapSides(*e1, *e2); - SwapPolyIndexes(*e1, *e2); - } - } - else if ( e2Contributing ) - { - if (e1Wc == 0 || e1Wc == 1) - { - AddOutPt(e2, Pt); - SwapSides(*e1, *e2); - SwapPolyIndexes(*e1, *e2); - } - } - else if ( (e1Wc == 0 || e1Wc == 1) && (e2Wc == 0 || e2Wc == 1)) - { - //neither edge is currently contributing ... - - cInt e1Wc2, e2Wc2; - switch (e1FillType2) - { - case pftPositive: e1Wc2 = e1->WindCnt2; break; - case pftNegative : e1Wc2 = -e1->WindCnt2; break; - default: e1Wc2 = Abs(e1->WindCnt2); - } - switch (e2FillType2) - { - case pftPositive: e2Wc2 = e2->WindCnt2; break; - case pftNegative: e2Wc2 = -e2->WindCnt2; break; - default: e2Wc2 = Abs(e2->WindCnt2); - } - - if (e1->PolyTyp != e2->PolyTyp) - { - AddLocalMinPoly(e1, e2, Pt); - } - else if (e1Wc == 1 && e2Wc == 1) - switch( m_ClipType ) { - case ctIntersection: - if (e1Wc2 > 0 && e2Wc2 > 0) - AddLocalMinPoly(e1, e2, Pt); - break; - case ctUnion: - if ( e1Wc2 <= 0 && e2Wc2 <= 0 ) - AddLocalMinPoly(e1, e2, Pt); - break; - case ctDifference: - if (((e1->PolyTyp == ptClip) && (e1Wc2 > 0) && (e2Wc2 > 0)) || - ((e1->PolyTyp == ptSubject) && (e1Wc2 <= 0) && (e2Wc2 <= 0))) - AddLocalMinPoly(e1, e2, Pt); - break; - case ctXor: - AddLocalMinPoly(e1, e2, Pt); - } - else - SwapSides( *e1, *e2 ); - } -} -//------------------------------------------------------------------------------ - -void Clipper::SetHoleState(TEdge *e, OutRec *outrec) -{ - TEdge *e2 = e->PrevInAEL; - TEdge *eTmp = 0; - while (e2) - { - if (e2->OutIdx >= 0 && e2->WindDelta != 0) - { - if (!eTmp) eTmp = e2; - else if (eTmp->OutIdx == e2->OutIdx) eTmp = 0; - } - e2 = e2->PrevInAEL; - } - if (!eTmp) - { - outrec->FirstLeft = 0; - outrec->IsHole = false; - } - else - { - outrec->FirstLeft = m_PolyOuts[eTmp->OutIdx]; - outrec->IsHole = !outrec->FirstLeft->IsHole; - } -} -//------------------------------------------------------------------------------ - -OutRec* GetLowermostRec(OutRec *outRec1, OutRec *outRec2) -{ - //work out which polygon fragment has the correct hole state ... - if (!outRec1->BottomPt) - outRec1->BottomPt = GetBottomPt(outRec1->Pts); - if (!outRec2->BottomPt) - outRec2->BottomPt = GetBottomPt(outRec2->Pts); - OutPt *OutPt1 = outRec1->BottomPt; - OutPt *OutPt2 = outRec2->BottomPt; - if (OutPt1->Pt.Y > OutPt2->Pt.Y) return outRec1; - else if (OutPt1->Pt.Y < OutPt2->Pt.Y) return outRec2; - else if (OutPt1->Pt.X < OutPt2->Pt.X) return outRec1; - else if (OutPt1->Pt.X > OutPt2->Pt.X) return outRec2; - else if (OutPt1->Next == OutPt1) return outRec2; - else if (OutPt2->Next == OutPt2) return outRec1; - else if (FirstIsBottomPt(OutPt1, OutPt2)) return outRec1; - else return outRec2; -} -//------------------------------------------------------------------------------ - -bool OutRec1RightOfOutRec2(OutRec* outRec1, OutRec* outRec2) -{ - do - { - outRec1 = outRec1->FirstLeft; - if (outRec1 == outRec2) return true; - } while (outRec1); - return false; -} -//------------------------------------------------------------------------------ - -OutRec* Clipper::GetOutRec(int Idx) -{ - OutRec* outrec = m_PolyOuts[Idx]; - while (outrec != m_PolyOuts[outrec->Idx]) - outrec = m_PolyOuts[outrec->Idx]; - return outrec; -} -//------------------------------------------------------------------------------ - -void Clipper::AppendPolygon(TEdge *e1, TEdge *e2) -{ - //get the start and ends of both output polygons ... - OutRec *outRec1 = m_PolyOuts[e1->OutIdx]; - OutRec *outRec2 = m_PolyOuts[e2->OutIdx]; - - OutRec *holeStateRec; - if (OutRec1RightOfOutRec2(outRec1, outRec2)) - holeStateRec = outRec2; - else if (OutRec1RightOfOutRec2(outRec2, outRec1)) - holeStateRec = outRec1; - else - holeStateRec = GetLowermostRec(outRec1, outRec2); - - //get the start and ends of both output polygons and - //join e2 poly onto e1 poly and delete pointers to e2 ... - - OutPt* p1_lft = outRec1->Pts; - OutPt* p1_rt = p1_lft->Prev; - OutPt* p2_lft = outRec2->Pts; - OutPt* p2_rt = p2_lft->Prev; - - //join e2 poly onto e1 poly and delete pointers to e2 ... - if( e1->Side == esLeft ) - { - if( e2->Side == esLeft ) - { - //z y x a b c - ReversePolyPtLinks(p2_lft); - p2_lft->Next = p1_lft; - p1_lft->Prev = p2_lft; - p1_rt->Next = p2_rt; - p2_rt->Prev = p1_rt; - outRec1->Pts = p2_rt; - } else - { - //x y z a b c - p2_rt->Next = p1_lft; - p1_lft->Prev = p2_rt; - p2_lft->Prev = p1_rt; - p1_rt->Next = p2_lft; - outRec1->Pts = p2_lft; - } - } else - { - if( e2->Side == esRight ) - { - //a b c z y x - ReversePolyPtLinks(p2_lft); - p1_rt->Next = p2_rt; - p2_rt->Prev = p1_rt; - p2_lft->Next = p1_lft; - p1_lft->Prev = p2_lft; - } else - { - //a b c x y z - p1_rt->Next = p2_lft; - p2_lft->Prev = p1_rt; - p1_lft->Prev = p2_rt; - p2_rt->Next = p1_lft; - } - } - - outRec1->BottomPt = 0; - if (holeStateRec == outRec2) - { - if (outRec2->FirstLeft != outRec1) - outRec1->FirstLeft = outRec2->FirstLeft; - outRec1->IsHole = outRec2->IsHole; - } - outRec2->Pts = 0; - outRec2->BottomPt = 0; - outRec2->FirstLeft = outRec1; - - int OKIdx = e1->OutIdx; - int ObsoleteIdx = e2->OutIdx; - - e1->OutIdx = Unassigned; //nb: safe because we only get here via AddLocalMaxPoly - e2->OutIdx = Unassigned; - - TEdge* e = m_ActiveEdges; - while( e ) - { - if( e->OutIdx == ObsoleteIdx ) - { - e->OutIdx = OKIdx; - e->Side = e1->Side; - break; - } - e = e->NextInAEL; - } - - outRec2->Idx = outRec1->Idx; -} -//------------------------------------------------------------------------------ - -OutPt* Clipper::AddOutPt(TEdge *e, const IntPoint &pt) -{ - if( e->OutIdx < 0 ) - { - OutRec *outRec = CreateOutRec(); - outRec->IsOpen = (e->WindDelta == 0); - OutPt* newOp = new OutPt; - outRec->Pts = newOp; - newOp->Idx = outRec->Idx; - newOp->Pt = pt; - newOp->Next = newOp; - newOp->Prev = newOp; - if (!outRec->IsOpen) - SetHoleState(e, outRec); - e->OutIdx = outRec->Idx; - return newOp; - } else - { - OutRec *outRec = m_PolyOuts[e->OutIdx]; - //OutRec.Pts is the 'Left-most' point & OutRec.Pts.Prev is the 'Right-most' - OutPt* op = outRec->Pts; - - bool ToFront = (e->Side == esLeft); - if (ToFront && (pt == op->Pt)) return op; - else if (!ToFront && (pt == op->Prev->Pt)) return op->Prev; - - OutPt* newOp = new OutPt; - newOp->Idx = outRec->Idx; - newOp->Pt = pt; - newOp->Next = op; - newOp->Prev = op->Prev; - newOp->Prev->Next = newOp; - op->Prev = newOp; - if (ToFront) outRec->Pts = newOp; - return newOp; - } -} -//------------------------------------------------------------------------------ - -OutPt* Clipper::GetLastOutPt(TEdge *e) -{ - OutRec *outRec = m_PolyOuts[e->OutIdx]; - if (e->Side == esLeft) - return outRec->Pts; - else - return outRec->Pts->Prev; -} -//------------------------------------------------------------------------------ - -void Clipper::ProcessHorizontals() -{ - TEdge* horzEdge; - while (PopEdgeFromSEL(horzEdge)) - ProcessHorizontal(horzEdge); -} -//------------------------------------------------------------------------------ - -inline bool IsMinima(TEdge *e) -{ - return e && (e->Prev->NextInLML != e) && (e->Next->NextInLML != e); -} -//------------------------------------------------------------------------------ - -inline bool IsMaxima(TEdge *e, const cInt Y) -{ - return e && e->Top.Y == Y && !e->NextInLML; -} -//------------------------------------------------------------------------------ - -inline bool IsIntermediate(TEdge *e, const cInt Y) -{ - return e->Top.Y == Y && e->NextInLML; -} -//------------------------------------------------------------------------------ - -TEdge *GetMaximaPair(TEdge *e) -{ - if ((e->Next->Top == e->Top) && !e->Next->NextInLML) - return e->Next; - else if ((e->Prev->Top == e->Top) && !e->Prev->NextInLML) - return e->Prev; - else return 0; -} -//------------------------------------------------------------------------------ - -TEdge *GetMaximaPairEx(TEdge *e) -{ - //as GetMaximaPair() but returns 0 if MaxPair isn't in AEL (unless it's horizontal) - TEdge* result = GetMaximaPair(e); - if (result && (result->OutIdx == Skip || - (result->NextInAEL == result->PrevInAEL && !IsHorizontal(*result)))) return 0; - return result; -} -//------------------------------------------------------------------------------ - -void Clipper::SwapPositionsInSEL(TEdge *Edge1, TEdge *Edge2) -{ - if( !( Edge1->NextInSEL ) && !( Edge1->PrevInSEL ) ) return; - if( !( Edge2->NextInSEL ) && !( Edge2->PrevInSEL ) ) return; - - if( Edge1->NextInSEL == Edge2 ) - { - TEdge* Next = Edge2->NextInSEL; - if( Next ) Next->PrevInSEL = Edge1; - TEdge* Prev = Edge1->PrevInSEL; - if( Prev ) Prev->NextInSEL = Edge2; - Edge2->PrevInSEL = Prev; - Edge2->NextInSEL = Edge1; - Edge1->PrevInSEL = Edge2; - Edge1->NextInSEL = Next; - } - else if( Edge2->NextInSEL == Edge1 ) - { - TEdge* Next = Edge1->NextInSEL; - if( Next ) Next->PrevInSEL = Edge2; - TEdge* Prev = Edge2->PrevInSEL; - if( Prev ) Prev->NextInSEL = Edge1; - Edge1->PrevInSEL = Prev; - Edge1->NextInSEL = Edge2; - Edge2->PrevInSEL = Edge1; - Edge2->NextInSEL = Next; - } - else - { - TEdge* Next = Edge1->NextInSEL; - TEdge* Prev = Edge1->PrevInSEL; - Edge1->NextInSEL = Edge2->NextInSEL; - if( Edge1->NextInSEL ) Edge1->NextInSEL->PrevInSEL = Edge1; - Edge1->PrevInSEL = Edge2->PrevInSEL; - if( Edge1->PrevInSEL ) Edge1->PrevInSEL->NextInSEL = Edge1; - Edge2->NextInSEL = Next; - if( Edge2->NextInSEL ) Edge2->NextInSEL->PrevInSEL = Edge2; - Edge2->PrevInSEL = Prev; - if( Edge2->PrevInSEL ) Edge2->PrevInSEL->NextInSEL = Edge2; - } - - if( !Edge1->PrevInSEL ) m_SortedEdges = Edge1; - else if( !Edge2->PrevInSEL ) m_SortedEdges = Edge2; -} -//------------------------------------------------------------------------------ - -TEdge* GetNextInAEL(TEdge *e, Direction dir) -{ - return dir == dLeftToRight ? e->NextInAEL : e->PrevInAEL; -} -//------------------------------------------------------------------------------ - -void GetHorzDirection(TEdge& HorzEdge, Direction& Dir, cInt& Left, cInt& Right) -{ - if (HorzEdge.Bot.X < HorzEdge.Top.X) - { - Left = HorzEdge.Bot.X; - Right = HorzEdge.Top.X; - Dir = dLeftToRight; - } else - { - Left = HorzEdge.Top.X; - Right = HorzEdge.Bot.X; - Dir = dRightToLeft; - } -} -//------------------------------------------------------------------------ - -/******************************************************************************* -* Notes: Horizontal edges (HEs) at scanline intersections (ie at the Top or * -* Bottom of a scanbeam) are processed as if layered. The order in which HEs * -* are processed doesn't matter. HEs intersect with other HE Bot.Xs only [#] * -* (or they could intersect with Top.Xs only, ie EITHER Bot.Xs OR Top.Xs), * -* and with other non-horizontal edges [*]. Once these intersections are * -* processed, intermediate HEs then 'promote' the Edge above (NextInLML) into * -* the AEL. These 'promoted' edges may in turn intersect [%] with other HEs. * -*******************************************************************************/ - -void Clipper::ProcessHorizontal(TEdge *horzEdge) -{ - Direction dir; - cInt horzLeft, horzRight; - bool IsOpen = (horzEdge->WindDelta == 0); - - GetHorzDirection(*horzEdge, dir, horzLeft, horzRight); - - TEdge* eLastHorz = horzEdge, *eMaxPair = 0; - while (eLastHorz->NextInLML && IsHorizontal(*eLastHorz->NextInLML)) - eLastHorz = eLastHorz->NextInLML; - if (!eLastHorz->NextInLML) - eMaxPair = GetMaximaPair(eLastHorz); - - MaximaList::const_iterator maxIt; - MaximaList::const_reverse_iterator maxRit; - if (m_Maxima.size() > 0) - { - //get the first maxima in range (X) ... - if (dir == dLeftToRight) - { - maxIt = m_Maxima.begin(); - while (maxIt != m_Maxima.end() && *maxIt <= horzEdge->Bot.X) maxIt++; - if (maxIt != m_Maxima.end() && *maxIt >= eLastHorz->Top.X) - maxIt = m_Maxima.end(); - } - else - { - maxRit = m_Maxima.rbegin(); - while (maxRit != m_Maxima.rend() && *maxRit > horzEdge->Bot.X) maxRit++; - if (maxRit != m_Maxima.rend() && *maxRit <= eLastHorz->Top.X) - maxRit = m_Maxima.rend(); - } - } - - OutPt* op1 = 0; - - for (;;) //loop through consec. horizontal edges - { - - bool IsLastHorz = (horzEdge == eLastHorz); - TEdge* e = GetNextInAEL(horzEdge, dir); - while(e) - { - - //this code block inserts extra coords into horizontal edges (in output - //polygons) whereever maxima touch these horizontal edges. This helps - //'simplifying' polygons (ie if the Simplify property is set). - if (m_Maxima.size() > 0) - { - if (dir == dLeftToRight) - { - while (maxIt != m_Maxima.end() && *maxIt < e->Curr.X) - { - if (horzEdge->OutIdx >= 0 && !IsOpen) - AddOutPt(horzEdge, IntPoint(*maxIt, horzEdge->Bot.Y)); - maxIt++; - } - } - else - { - while (maxRit != m_Maxima.rend() && *maxRit > e->Curr.X) - { - if (horzEdge->OutIdx >= 0 && !IsOpen) - AddOutPt(horzEdge, IntPoint(*maxRit, horzEdge->Bot.Y)); - maxRit++; - } - } - }; - - if ((dir == dLeftToRight && e->Curr.X > horzRight) || - (dir == dRightToLeft && e->Curr.X < horzLeft)) break; - - //Also break if we've got to the end of an intermediate horizontal edge ... - //nb: Smaller Dx's are to the right of larger Dx's ABOVE the horizontal. - if (e->Curr.X == horzEdge->Top.X && horzEdge->NextInLML && - e->Dx < horzEdge->NextInLML->Dx) break; - - if (horzEdge->OutIdx >= 0 && !IsOpen) //note: may be done multiple times - { - op1 = AddOutPt(horzEdge, e->Curr); - TEdge* eNextHorz = m_SortedEdges; - while (eNextHorz) - { - if (eNextHorz->OutIdx >= 0 && - HorzSegmentsOverlap(horzEdge->Bot.X, - horzEdge->Top.X, eNextHorz->Bot.X, eNextHorz->Top.X)) - { - OutPt* op2 = GetLastOutPt(eNextHorz); - AddJoin(op2, op1, eNextHorz->Top); - } - eNextHorz = eNextHorz->NextInSEL; - } - AddGhostJoin(op1, horzEdge->Bot); - } - - //OK, so far we're still in range of the horizontal Edge but make sure - //we're at the last of consec. horizontals when matching with eMaxPair - if(e == eMaxPair && IsLastHorz) - { - if (horzEdge->OutIdx >= 0) - AddLocalMaxPoly(horzEdge, eMaxPair, horzEdge->Top); - DeleteFromAEL(horzEdge); - DeleteFromAEL(eMaxPair); - return; - } - - if(dir == dLeftToRight) - { - IntPoint Pt = IntPoint(e->Curr.X, horzEdge->Curr.Y); - IntersectEdges(horzEdge, e, Pt); - } - else - { - IntPoint Pt = IntPoint(e->Curr.X, horzEdge->Curr.Y); - IntersectEdges( e, horzEdge, Pt); - } - TEdge* eNext = GetNextInAEL(e, dir); - SwapPositionsInAEL( horzEdge, e ); - e = eNext; - } //end while(e) - - //Break out of loop if HorzEdge.NextInLML is not also horizontal ... - if (!horzEdge->NextInLML || !IsHorizontal(*horzEdge->NextInLML)) break; - - UpdateEdgeIntoAEL(horzEdge); - if (horzEdge->OutIdx >= 0) AddOutPt(horzEdge, horzEdge->Bot); - GetHorzDirection(*horzEdge, dir, horzLeft, horzRight); - - } //end for (;;) - - if (horzEdge->OutIdx >= 0 && !op1) - { - op1 = GetLastOutPt(horzEdge); - TEdge* eNextHorz = m_SortedEdges; - while (eNextHorz) - { - if (eNextHorz->OutIdx >= 0 && - HorzSegmentsOverlap(horzEdge->Bot.X, - horzEdge->Top.X, eNextHorz->Bot.X, eNextHorz->Top.X)) - { - OutPt* op2 = GetLastOutPt(eNextHorz); - AddJoin(op2, op1, eNextHorz->Top); - } - eNextHorz = eNextHorz->NextInSEL; - } - AddGhostJoin(op1, horzEdge->Top); - } - - if (horzEdge->NextInLML) - { - if(horzEdge->OutIdx >= 0) - { - op1 = AddOutPt( horzEdge, horzEdge->Top); - UpdateEdgeIntoAEL(horzEdge); - if (horzEdge->WindDelta == 0) return; - //nb: HorzEdge is no longer horizontal here - TEdge* ePrev = horzEdge->PrevInAEL; - TEdge* eNext = horzEdge->NextInAEL; - if (ePrev && ePrev->Curr.X == horzEdge->Bot.X && - ePrev->Curr.Y == horzEdge->Bot.Y && ePrev->WindDelta != 0 && - (ePrev->OutIdx >= 0 && ePrev->Curr.Y > ePrev->Top.Y && - SlopesEqual(*horzEdge, *ePrev, m_UseFullRange))) - { - OutPt* op2 = AddOutPt(ePrev, horzEdge->Bot); - AddJoin(op1, op2, horzEdge->Top); - } - else if (eNext && eNext->Curr.X == horzEdge->Bot.X && - eNext->Curr.Y == horzEdge->Bot.Y && eNext->WindDelta != 0 && - eNext->OutIdx >= 0 && eNext->Curr.Y > eNext->Top.Y && - SlopesEqual(*horzEdge, *eNext, m_UseFullRange)) - { - OutPt* op2 = AddOutPt(eNext, horzEdge->Bot); - AddJoin(op1, op2, horzEdge->Top); - } - } - else - UpdateEdgeIntoAEL(horzEdge); - } - else - { - if (horzEdge->OutIdx >= 0) AddOutPt(horzEdge, horzEdge->Top); - DeleteFromAEL(horzEdge); - } -} -//------------------------------------------------------------------------------ - -bool Clipper::ProcessIntersections(const cInt topY) -{ - if( !m_ActiveEdges ) return true; - try { - BuildIntersectList(topY); - size_t IlSize = m_IntersectList.size(); - if (IlSize == 0) return true; - if (IlSize == 1 || FixupIntersectionOrder()) ProcessIntersectList(); - else return false; - } - catch(...) - { - m_SortedEdges = 0; - DisposeIntersectNodes(); - throw clipperException("ProcessIntersections error"); - } - m_SortedEdges = 0; - return true; -} -//------------------------------------------------------------------------------ - -void Clipper::DisposeIntersectNodes() -{ - for (size_t i = 0; i < m_IntersectList.size(); ++i ) - delete m_IntersectList[i]; - m_IntersectList.clear(); -} -//------------------------------------------------------------------------------ - -void Clipper::BuildIntersectList(const cInt topY) -{ - if ( !m_ActiveEdges ) return; - - //prepare for sorting ... - TEdge* e = m_ActiveEdges; - m_SortedEdges = e; - while( e ) - { - e->PrevInSEL = e->PrevInAEL; - e->NextInSEL = e->NextInAEL; - e->Curr.X = TopX( *e, topY ); - e = e->NextInAEL; - } - - //bubblesort ... - bool isModified; - do - { - isModified = false; - e = m_SortedEdges; - while( e->NextInSEL ) - { - TEdge *eNext = e->NextInSEL; - IntPoint Pt; - if(e->Curr.X > eNext->Curr.X) - { - IntersectPoint(*e, *eNext, Pt); - if (Pt.Y < topY) Pt = IntPoint(TopX(*e, topY), topY); - IntersectNode * newNode = new IntersectNode; - newNode->Edge1 = e; - newNode->Edge2 = eNext; - newNode->Pt = Pt; - m_IntersectList.push_back(newNode); - - SwapPositionsInSEL(e, eNext); - isModified = true; - } - else - e = eNext; - } - if( e->PrevInSEL ) e->PrevInSEL->NextInSEL = 0; - else break; - } - while ( isModified ); - m_SortedEdges = 0; //important -} -//------------------------------------------------------------------------------ - - -void Clipper::ProcessIntersectList() -{ - for (size_t i = 0; i < m_IntersectList.size(); ++i) - { - IntersectNode* iNode = m_IntersectList[i]; - { - IntersectEdges( iNode->Edge1, iNode->Edge2, iNode->Pt); - SwapPositionsInAEL( iNode->Edge1 , iNode->Edge2 ); - } - delete iNode; - } - m_IntersectList.clear(); -} -//------------------------------------------------------------------------------ - -bool IntersectListSort(IntersectNode* node1, IntersectNode* node2) -{ - return node2->Pt.Y < node1->Pt.Y; -} -//------------------------------------------------------------------------------ - -inline bool EdgesAdjacent(const IntersectNode &inode) -{ - return (inode.Edge1->NextInSEL == inode.Edge2) || - (inode.Edge1->PrevInSEL == inode.Edge2); -} -//------------------------------------------------------------------------------ - -bool Clipper::FixupIntersectionOrder() -{ - //pre-condition: intersections are sorted Bottom-most first. - //Now it's crucial that intersections are made only between adjacent edges, - //so to ensure this the order of intersections may need adjusting ... - CopyAELToSEL(); - std::sort(m_IntersectList.begin(), m_IntersectList.end(), IntersectListSort); - size_t cnt = m_IntersectList.size(); - for (size_t i = 0; i < cnt; ++i) - { - if (!EdgesAdjacent(*m_IntersectList[i])) - { - size_t j = i + 1; - while (j < cnt && !EdgesAdjacent(*m_IntersectList[j])) j++; - if (j == cnt) return false; - std::swap(m_IntersectList[i], m_IntersectList[j]); - } - SwapPositionsInSEL(m_IntersectList[i]->Edge1, m_IntersectList[i]->Edge2); - } - return true; -} -//------------------------------------------------------------------------------ - -void Clipper::DoMaxima(TEdge *e) -{ - TEdge* eMaxPair = GetMaximaPairEx(e); - if (!eMaxPair) - { - if (e->OutIdx >= 0) - AddOutPt(e, e->Top); - DeleteFromAEL(e); - return; - } - - TEdge* eNext = e->NextInAEL; - while(eNext && eNext != eMaxPair) - { - IntersectEdges(e, eNext, e->Top); - SwapPositionsInAEL(e, eNext); - eNext = e->NextInAEL; - } - - if(e->OutIdx == Unassigned && eMaxPair->OutIdx == Unassigned) - { - DeleteFromAEL(e); - DeleteFromAEL(eMaxPair); - } - else if( e->OutIdx >= 0 && eMaxPair->OutIdx >= 0 ) - { - if (e->OutIdx >= 0) AddLocalMaxPoly(e, eMaxPair, e->Top); - DeleteFromAEL(e); - DeleteFromAEL(eMaxPair); - } -#ifdef use_lines - else if (e->WindDelta == 0) - { - if (e->OutIdx >= 0) - { - AddOutPt(e, e->Top); - e->OutIdx = Unassigned; - } - DeleteFromAEL(e); - - if (eMaxPair->OutIdx >= 0) - { - AddOutPt(eMaxPair, e->Top); - eMaxPair->OutIdx = Unassigned; - } - DeleteFromAEL(eMaxPair); - } -#endif - else throw clipperException("DoMaxima error"); -} -//------------------------------------------------------------------------------ - -void Clipper::ProcessEdgesAtTopOfScanbeam(const cInt topY) -{ - TEdge* e = m_ActiveEdges; - while( e ) - { - //1. process maxima, treating them as if they're 'bent' horizontal edges, - // but exclude maxima with horizontal edges. nb: e can't be a horizontal. - bool IsMaximaEdge = IsMaxima(e, topY); - - if(IsMaximaEdge) - { - TEdge* eMaxPair = GetMaximaPairEx(e); - IsMaximaEdge = (!eMaxPair || !IsHorizontal(*eMaxPair)); - } - - if(IsMaximaEdge) - { - if (m_StrictSimple) m_Maxima.push_back(e->Top.X); - TEdge* ePrev = e->PrevInAEL; - DoMaxima(e); - if( !ePrev ) e = m_ActiveEdges; - else e = ePrev->NextInAEL; - } - else - { - //2. promote horizontal edges, otherwise update Curr.X and Curr.Y ... - if (IsIntermediate(e, topY) && IsHorizontal(*e->NextInLML)) - { - UpdateEdgeIntoAEL(e); - if (e->OutIdx >= 0) - AddOutPt(e, e->Bot); - AddEdgeToSEL(e); - } - else - { - e->Curr.X = TopX( *e, topY ); - e->Curr.Y = topY; - } - - //When StrictlySimple and 'e' is being touched by another edge, then - //make sure both edges have a vertex here ... - if (m_StrictSimple) - { - TEdge* ePrev = e->PrevInAEL; - if ((e->OutIdx >= 0) && (e->WindDelta != 0) && ePrev && (ePrev->OutIdx >= 0) && - (ePrev->Curr.X == e->Curr.X) && (ePrev->WindDelta != 0)) - { - IntPoint pt = e->Curr; -#ifdef use_xyz - SetZ(pt, *ePrev, *e); -#endif - OutPt* op = AddOutPt(ePrev, pt); - OutPt* op2 = AddOutPt(e, pt); - AddJoin(op, op2, pt); //StrictlySimple (type-3) join - } - } - - e = e->NextInAEL; - } - } - - //3. Process horizontals at the Top of the scanbeam ... - m_Maxima.sort(); - ProcessHorizontals(); - m_Maxima.clear(); - - //4. Promote intermediate vertices ... - e = m_ActiveEdges; - while(e) - { - if(IsIntermediate(e, topY)) - { - OutPt* op = 0; - if( e->OutIdx >= 0 ) - op = AddOutPt(e, e->Top); - UpdateEdgeIntoAEL(e); - - //if output polygons share an edge, they'll need joining later ... - TEdge* ePrev = e->PrevInAEL; - TEdge* eNext = e->NextInAEL; - if (ePrev && ePrev->Curr.X == e->Bot.X && - ePrev->Curr.Y == e->Bot.Y && op && - ePrev->OutIdx >= 0 && ePrev->Curr.Y > ePrev->Top.Y && - SlopesEqual(e->Curr, e->Top, ePrev->Curr, ePrev->Top, m_UseFullRange) && - (e->WindDelta != 0) && (ePrev->WindDelta != 0)) - { - OutPt* op2 = AddOutPt(ePrev, e->Bot); - AddJoin(op, op2, e->Top); - } - else if (eNext && eNext->Curr.X == e->Bot.X && - eNext->Curr.Y == e->Bot.Y && op && - eNext->OutIdx >= 0 && eNext->Curr.Y > eNext->Top.Y && - SlopesEqual(e->Curr, e->Top, eNext->Curr, eNext->Top, m_UseFullRange) && - (e->WindDelta != 0) && (eNext->WindDelta != 0)) - { - OutPt* op2 = AddOutPt(eNext, e->Bot); - AddJoin(op, op2, e->Top); - } - } - e = e->NextInAEL; - } -} -//------------------------------------------------------------------------------ - -void Clipper::FixupOutPolyline(OutRec &outrec) -{ - OutPt *pp = outrec.Pts; - OutPt *lastPP = pp->Prev; - while (pp != lastPP) - { - pp = pp->Next; - if (pp->Pt == pp->Prev->Pt) - { - if (pp == lastPP) lastPP = pp->Prev; - OutPt *tmpPP = pp->Prev; - tmpPP->Next = pp->Next; - pp->Next->Prev = tmpPP; - delete pp; - pp = tmpPP; - } - } - - if (pp == pp->Prev) - { - DisposeOutPts(pp); - outrec.Pts = 0; - return; - } -} -//------------------------------------------------------------------------------ - -void Clipper::FixupOutPolygon(OutRec &outrec) -{ - //FixupOutPolygon() - removes duplicate points and simplifies consecutive - //parallel edges by removing the middle vertex. - OutPt *lastOK = 0; - outrec.BottomPt = 0; - OutPt *pp = outrec.Pts; - bool preserveCol = m_PreserveCollinear || m_StrictSimple; - - for (;;) - { - if (pp->Prev == pp || pp->Prev == pp->Next) - { - DisposeOutPts(pp); - outrec.Pts = 0; - return; - } - - //test for duplicate points and collinear edges ... - if ((pp->Pt == pp->Next->Pt) || (pp->Pt == pp->Prev->Pt) || - (SlopesEqual(pp->Prev->Pt, pp->Pt, pp->Next->Pt, m_UseFullRange) && - (!preserveCol || !Pt2IsBetweenPt1AndPt3(pp->Prev->Pt, pp->Pt, pp->Next->Pt)))) - { - lastOK = 0; - OutPt *tmp = pp; - pp->Prev->Next = pp->Next; - pp->Next->Prev = pp->Prev; - pp = pp->Prev; - delete tmp; - } - else if (pp == lastOK) break; - else - { - if (!lastOK) lastOK = pp; - pp = pp->Next; - } - } - outrec.Pts = pp; -} -//------------------------------------------------------------------------------ - -int PointCount(OutPt *Pts) -{ - if (!Pts) return 0; - int result = 0; - OutPt* p = Pts; - do - { - result++; - p = p->Next; - } - while (p != Pts); - return result; -} -//------------------------------------------------------------------------------ - -void Clipper::BuildResult(Paths &polys) -{ - polys.reserve(m_PolyOuts.size()); - for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) - { - if (!m_PolyOuts[i]->Pts) continue; - Path pg; - OutPt* p = m_PolyOuts[i]->Pts->Prev; - int cnt = PointCount(p); - if (cnt < 2) continue; - pg.reserve(cnt); - for (int i = 0; i < cnt; ++i) - { - pg.push_back(p->Pt); - p = p->Prev; - } - polys.push_back(pg); - } -} -//------------------------------------------------------------------------------ - -void Clipper::BuildResult2(PolyTree& polytree) -{ - polytree.Clear(); - polytree.AllNodes.reserve(m_PolyOuts.size()); - //add each output polygon/contour to polytree ... - for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); i++) - { - OutRec* outRec = m_PolyOuts[i]; - int cnt = PointCount(outRec->Pts); - if ((outRec->IsOpen && cnt < 2) || (!outRec->IsOpen && cnt < 3)) continue; - FixHoleLinkage(*outRec); - PolyNode* pn = new PolyNode(); - //nb: polytree takes ownership of all the PolyNodes - polytree.AllNodes.push_back(pn); - outRec->PolyNd = pn; - pn->Parent = 0; - pn->Index = 0; - pn->Contour.reserve(cnt); - OutPt *op = outRec->Pts->Prev; - for (int j = 0; j < cnt; j++) - { - pn->Contour.push_back(op->Pt); - op = op->Prev; - } - } - - //fixup PolyNode links etc ... - polytree.Childs.reserve(m_PolyOuts.size()); - for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); i++) - { - OutRec* outRec = m_PolyOuts[i]; - if (!outRec->PolyNd) continue; - if (outRec->IsOpen) - { - outRec->PolyNd->m_IsOpen = true; - polytree.AddChild(*outRec->PolyNd); - } - else if (outRec->FirstLeft && outRec->FirstLeft->PolyNd) - outRec->FirstLeft->PolyNd->AddChild(*outRec->PolyNd); - else - polytree.AddChild(*outRec->PolyNd); - } -} -//------------------------------------------------------------------------------ - -void SwapIntersectNodes(IntersectNode &int1, IntersectNode &int2) -{ - //just swap the contents (because fIntersectNodes is a single-linked-list) - IntersectNode inode = int1; //gets a copy of Int1 - int1.Edge1 = int2.Edge1; - int1.Edge2 = int2.Edge2; - int1.Pt = int2.Pt; - int2.Edge1 = inode.Edge1; - int2.Edge2 = inode.Edge2; - int2.Pt = inode.Pt; -} -//------------------------------------------------------------------------------ - -inline bool E2InsertsBeforeE1(TEdge &e1, TEdge &e2) -{ - if (e2.Curr.X == e1.Curr.X) - { - if (e2.Top.Y > e1.Top.Y) - return e2.Top.X < TopX(e1, e2.Top.Y); - else return e1.Top.X > TopX(e2, e1.Top.Y); - } - else return e2.Curr.X < e1.Curr.X; -} -//------------------------------------------------------------------------------ - -bool GetOverlap(const cInt a1, const cInt a2, const cInt b1, const cInt b2, - cInt& Left, cInt& Right) -{ - if (a1 < a2) - { - if (b1 < b2) {Left = std::max(a1,b1); Right = std::min(a2,b2);} - else {Left = std::max(a1,b2); Right = std::min(a2,b1);} - } - else - { - if (b1 < b2) {Left = std::max(a2,b1); Right = std::min(a1,b2);} - else {Left = std::max(a2,b2); Right = std::min(a1,b1);} - } - return Left < Right; -} -//------------------------------------------------------------------------------ - -inline void UpdateOutPtIdxs(OutRec& outrec) -{ - OutPt* op = outrec.Pts; - do - { - op->Idx = outrec.Idx; - op = op->Prev; - } - while(op != outrec.Pts); -} -//------------------------------------------------------------------------------ - -void Clipper::InsertEdgeIntoAEL(TEdge *edge, TEdge* startEdge) -{ - if(!m_ActiveEdges) - { - edge->PrevInAEL = 0; - edge->NextInAEL = 0; - m_ActiveEdges = edge; - } - else if(!startEdge && E2InsertsBeforeE1(*m_ActiveEdges, *edge)) - { - edge->PrevInAEL = 0; - edge->NextInAEL = m_ActiveEdges; - m_ActiveEdges->PrevInAEL = edge; - m_ActiveEdges = edge; - } - else - { - if(!startEdge) startEdge = m_ActiveEdges; - while(startEdge->NextInAEL && - !E2InsertsBeforeE1(*startEdge->NextInAEL , *edge)) - startEdge = startEdge->NextInAEL; - edge->NextInAEL = startEdge->NextInAEL; - if(startEdge->NextInAEL) startEdge->NextInAEL->PrevInAEL = edge; - edge->PrevInAEL = startEdge; - startEdge->NextInAEL = edge; - } -} -//---------------------------------------------------------------------- - -OutPt* DupOutPt(OutPt* outPt, bool InsertAfter) -{ - OutPt* result = new OutPt; - result->Pt = outPt->Pt; - result->Idx = outPt->Idx; - if (InsertAfter) - { - result->Next = outPt->Next; - result->Prev = outPt; - outPt->Next->Prev = result; - outPt->Next = result; - } - else - { - result->Prev = outPt->Prev; - result->Next = outPt; - outPt->Prev->Next = result; - outPt->Prev = result; - } - return result; -} -//------------------------------------------------------------------------------ - -bool JoinHorz(OutPt* op1, OutPt* op1b, OutPt* op2, OutPt* op2b, - const IntPoint Pt, bool DiscardLeft) -{ - Direction Dir1 = (op1->Pt.X > op1b->Pt.X ? dRightToLeft : dLeftToRight); - Direction Dir2 = (op2->Pt.X > op2b->Pt.X ? dRightToLeft : dLeftToRight); - if (Dir1 == Dir2) return false; - - //When DiscardLeft, we want Op1b to be on the Left of Op1, otherwise we - //want Op1b to be on the Right. (And likewise with Op2 and Op2b.) - //So, to facilitate this while inserting Op1b and Op2b ... - //when DiscardLeft, make sure we're AT or RIGHT of Pt before adding Op1b, - //otherwise make sure we're AT or LEFT of Pt. (Likewise with Op2b.) - if (Dir1 == dLeftToRight) - { - while (op1->Next->Pt.X <= Pt.X && - op1->Next->Pt.X >= op1->Pt.X && op1->Next->Pt.Y == Pt.Y) - op1 = op1->Next; - if (DiscardLeft && (op1->Pt.X != Pt.X)) op1 = op1->Next; - op1b = DupOutPt(op1, !DiscardLeft); - if (op1b->Pt != Pt) - { - op1 = op1b; - op1->Pt = Pt; - op1b = DupOutPt(op1, !DiscardLeft); - } - } - else - { - while (op1->Next->Pt.X >= Pt.X && - op1->Next->Pt.X <= op1->Pt.X && op1->Next->Pt.Y == Pt.Y) - op1 = op1->Next; - if (!DiscardLeft && (op1->Pt.X != Pt.X)) op1 = op1->Next; - op1b = DupOutPt(op1, DiscardLeft); - if (op1b->Pt != Pt) - { - op1 = op1b; - op1->Pt = Pt; - op1b = DupOutPt(op1, DiscardLeft); - } - } - - if (Dir2 == dLeftToRight) - { - while (op2->Next->Pt.X <= Pt.X && - op2->Next->Pt.X >= op2->Pt.X && op2->Next->Pt.Y == Pt.Y) - op2 = op2->Next; - if (DiscardLeft && (op2->Pt.X != Pt.X)) op2 = op2->Next; - op2b = DupOutPt(op2, !DiscardLeft); - if (op2b->Pt != Pt) - { - op2 = op2b; - op2->Pt = Pt; - op2b = DupOutPt(op2, !DiscardLeft); - }; - } else - { - while (op2->Next->Pt.X >= Pt.X && - op2->Next->Pt.X <= op2->Pt.X && op2->Next->Pt.Y == Pt.Y) - op2 = op2->Next; - if (!DiscardLeft && (op2->Pt.X != Pt.X)) op2 = op2->Next; - op2b = DupOutPt(op2, DiscardLeft); - if (op2b->Pt != Pt) - { - op2 = op2b; - op2->Pt = Pt; - op2b = DupOutPt(op2, DiscardLeft); - }; - }; - - if ((Dir1 == dLeftToRight) == DiscardLeft) - { - op1->Prev = op2; - op2->Next = op1; - op1b->Next = op2b; - op2b->Prev = op1b; - } - else - { - op1->Next = op2; - op2->Prev = op1; - op1b->Prev = op2b; - op2b->Next = op1b; - } - return true; -} -//------------------------------------------------------------------------------ - -bool Clipper::JoinPoints(Join *j, OutRec* outRec1, OutRec* outRec2) -{ - OutPt *op1 = j->OutPt1, *op1b; - OutPt *op2 = j->OutPt2, *op2b; - - //There are 3 kinds of joins for output polygons ... - //1. Horizontal joins where Join.OutPt1 & Join.OutPt2 are vertices anywhere - //along (horizontal) collinear edges (& Join.OffPt is on the same horizontal). - //2. Non-horizontal joins where Join.OutPt1 & Join.OutPt2 are at the same - //location at the Bottom of the overlapping segment (& Join.OffPt is above). - //3. StrictSimple joins where edges touch but are not collinear and where - //Join.OutPt1, Join.OutPt2 & Join.OffPt all share the same point. - bool isHorizontal = (j->OutPt1->Pt.Y == j->OffPt.Y); - - if (isHorizontal && (j->OffPt == j->OutPt1->Pt) && - (j->OffPt == j->OutPt2->Pt)) - { - //Strictly Simple join ... - if (outRec1 != outRec2) return false; - op1b = j->OutPt1->Next; - while (op1b != op1 && (op1b->Pt == j->OffPt)) - op1b = op1b->Next; - bool reverse1 = (op1b->Pt.Y > j->OffPt.Y); - op2b = j->OutPt2->Next; - while (op2b != op2 && (op2b->Pt == j->OffPt)) - op2b = op2b->Next; - bool reverse2 = (op2b->Pt.Y > j->OffPt.Y); - if (reverse1 == reverse2) return false; - if (reverse1) - { - op1b = DupOutPt(op1, false); - op2b = DupOutPt(op2, true); - op1->Prev = op2; - op2->Next = op1; - op1b->Next = op2b; - op2b->Prev = op1b; - j->OutPt1 = op1; - j->OutPt2 = op1b; - return true; - } else - { - op1b = DupOutPt(op1, true); - op2b = DupOutPt(op2, false); - op1->Next = op2; - op2->Prev = op1; - op1b->Prev = op2b; - op2b->Next = op1b; - j->OutPt1 = op1; - j->OutPt2 = op1b; - return true; - } - } - else if (isHorizontal) - { - //treat horizontal joins differently to non-horizontal joins since with - //them we're not yet sure where the overlapping is. OutPt1.Pt & OutPt2.Pt - //may be anywhere along the horizontal edge. - op1b = op1; - while (op1->Prev->Pt.Y == op1->Pt.Y && op1->Prev != op1b && op1->Prev != op2) - op1 = op1->Prev; - while (op1b->Next->Pt.Y == op1b->Pt.Y && op1b->Next != op1 && op1b->Next != op2) - op1b = op1b->Next; - if (op1b->Next == op1 || op1b->Next == op2) return false; //a flat 'polygon' - - op2b = op2; - while (op2->Prev->Pt.Y == op2->Pt.Y && op2->Prev != op2b && op2->Prev != op1b) - op2 = op2->Prev; - while (op2b->Next->Pt.Y == op2b->Pt.Y && op2b->Next != op2 && op2b->Next != op1) - op2b = op2b->Next; - if (op2b->Next == op2 || op2b->Next == op1) return false; //a flat 'polygon' - - cInt Left, Right; - //Op1 --> Op1b & Op2 --> Op2b are the extremites of the horizontal edges - if (!GetOverlap(op1->Pt.X, op1b->Pt.X, op2->Pt.X, op2b->Pt.X, Left, Right)) - return false; - - //DiscardLeftSide: when overlapping edges are joined, a spike will created - //which needs to be cleaned up. However, we don't want Op1 or Op2 caught up - //on the discard Side as either may still be needed for other joins ... - IntPoint Pt; - bool DiscardLeftSide; - if (op1->Pt.X >= Left && op1->Pt.X <= Right) - { - Pt = op1->Pt; DiscardLeftSide = (op1->Pt.X > op1b->Pt.X); - } - else if (op2->Pt.X >= Left&& op2->Pt.X <= Right) - { - Pt = op2->Pt; DiscardLeftSide = (op2->Pt.X > op2b->Pt.X); - } - else if (op1b->Pt.X >= Left && op1b->Pt.X <= Right) - { - Pt = op1b->Pt; DiscardLeftSide = op1b->Pt.X > op1->Pt.X; - } - else - { - Pt = op2b->Pt; DiscardLeftSide = (op2b->Pt.X > op2->Pt.X); - } - j->OutPt1 = op1; j->OutPt2 = op2; - return JoinHorz(op1, op1b, op2, op2b, Pt, DiscardLeftSide); - } else - { - //nb: For non-horizontal joins ... - // 1. Jr.OutPt1.Pt.Y == Jr.OutPt2.Pt.Y - // 2. Jr.OutPt1.Pt > Jr.OffPt.Y - - //make sure the polygons are correctly oriented ... - op1b = op1->Next; - while ((op1b->Pt == op1->Pt) && (op1b != op1)) op1b = op1b->Next; - bool Reverse1 = ((op1b->Pt.Y > op1->Pt.Y) || - !SlopesEqual(op1->Pt, op1b->Pt, j->OffPt, m_UseFullRange)); - if (Reverse1) - { - op1b = op1->Prev; - while ((op1b->Pt == op1->Pt) && (op1b != op1)) op1b = op1b->Prev; - if ((op1b->Pt.Y > op1->Pt.Y) || - !SlopesEqual(op1->Pt, op1b->Pt, j->OffPt, m_UseFullRange)) return false; - }; - op2b = op2->Next; - while ((op2b->Pt == op2->Pt) && (op2b != op2))op2b = op2b->Next; - bool Reverse2 = ((op2b->Pt.Y > op2->Pt.Y) || - !SlopesEqual(op2->Pt, op2b->Pt, j->OffPt, m_UseFullRange)); - if (Reverse2) - { - op2b = op2->Prev; - while ((op2b->Pt == op2->Pt) && (op2b != op2)) op2b = op2b->Prev; - if ((op2b->Pt.Y > op2->Pt.Y) || - !SlopesEqual(op2->Pt, op2b->Pt, j->OffPt, m_UseFullRange)) return false; - } - - if ((op1b == op1) || (op2b == op2) || (op1b == op2b) || - ((outRec1 == outRec2) && (Reverse1 == Reverse2))) return false; - - if (Reverse1) - { - op1b = DupOutPt(op1, false); - op2b = DupOutPt(op2, true); - op1->Prev = op2; - op2->Next = op1; - op1b->Next = op2b; - op2b->Prev = op1b; - j->OutPt1 = op1; - j->OutPt2 = op1b; - return true; - } else - { - op1b = DupOutPt(op1, true); - op2b = DupOutPt(op2, false); - op1->Next = op2; - op2->Prev = op1; - op1b->Prev = op2b; - op2b->Next = op1b; - j->OutPt1 = op1; - j->OutPt2 = op1b; - return true; - } - } -} -//---------------------------------------------------------------------- - -static OutRec* ParseFirstLeft(OutRec* FirstLeft) -{ - while (FirstLeft && !FirstLeft->Pts) - FirstLeft = FirstLeft->FirstLeft; - return FirstLeft; -} -//------------------------------------------------------------------------------ - -void Clipper::FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec) -{ - //tests if NewOutRec contains the polygon before reassigning FirstLeft - for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) - { - OutRec* outRec = m_PolyOuts[i]; - OutRec* firstLeft = ParseFirstLeft(outRec->FirstLeft); - if (outRec->Pts && firstLeft == OldOutRec) - { - if (Poly2ContainsPoly1(outRec->Pts, NewOutRec->Pts)) - outRec->FirstLeft = NewOutRec; - } - } -} -//---------------------------------------------------------------------- - -void Clipper::FixupFirstLefts2(OutRec* InnerOutRec, OutRec* OuterOutRec) -{ - //A polygon has split into two such that one is now the inner of the other. - //It's possible that these polygons now wrap around other polygons, so check - //every polygon that's also contained by OuterOutRec's FirstLeft container - //(including 0) to see if they've become inner to the new inner polygon ... - OutRec* orfl = OuterOutRec->FirstLeft; - for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) - { - OutRec* outRec = m_PolyOuts[i]; - - if (!outRec->Pts || outRec == OuterOutRec || outRec == InnerOutRec) - continue; - OutRec* firstLeft = ParseFirstLeft(outRec->FirstLeft); - if (firstLeft != orfl && firstLeft != InnerOutRec && firstLeft != OuterOutRec) - continue; - if (Poly2ContainsPoly1(outRec->Pts, InnerOutRec->Pts)) - outRec->FirstLeft = InnerOutRec; - else if (Poly2ContainsPoly1(outRec->Pts, OuterOutRec->Pts)) - outRec->FirstLeft = OuterOutRec; - else if (outRec->FirstLeft == InnerOutRec || outRec->FirstLeft == OuterOutRec) - outRec->FirstLeft = orfl; - } -} -//---------------------------------------------------------------------- -void Clipper::FixupFirstLefts3(OutRec* OldOutRec, OutRec* NewOutRec) -{ - //reassigns FirstLeft WITHOUT testing if NewOutRec contains the polygon - for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) - { - OutRec* outRec = m_PolyOuts[i]; - OutRec* firstLeft = ParseFirstLeft(outRec->FirstLeft); - if (outRec->Pts && outRec->FirstLeft == OldOutRec) - outRec->FirstLeft = NewOutRec; - } -} -//---------------------------------------------------------------------- - -void Clipper::JoinCommonEdges() -{ - for (JoinList::size_type i = 0; i < m_Joins.size(); i++) - { - Join* join = m_Joins[i]; - - OutRec *outRec1 = GetOutRec(join->OutPt1->Idx); - OutRec *outRec2 = GetOutRec(join->OutPt2->Idx); - - if (!outRec1->Pts || !outRec2->Pts) continue; - if (outRec1->IsOpen || outRec2->IsOpen) continue; - - //get the polygon fragment with the correct hole state (FirstLeft) - //before calling JoinPoints() ... - OutRec *holeStateRec; - if (outRec1 == outRec2) holeStateRec = outRec1; - else if (OutRec1RightOfOutRec2(outRec1, outRec2)) holeStateRec = outRec2; - else if (OutRec1RightOfOutRec2(outRec2, outRec1)) holeStateRec = outRec1; - else holeStateRec = GetLowermostRec(outRec1, outRec2); - - if (!JoinPoints(join, outRec1, outRec2)) continue; - - if (outRec1 == outRec2) - { - //instead of joining two polygons, we've just created a new one by - //splitting one polygon into two. - outRec1->Pts = join->OutPt1; - outRec1->BottomPt = 0; - outRec2 = CreateOutRec(); - outRec2->Pts = join->OutPt2; - - //update all OutRec2.Pts Idx's ... - UpdateOutPtIdxs(*outRec2); - - if (Poly2ContainsPoly1(outRec2->Pts, outRec1->Pts)) - { - //outRec1 contains outRec2 ... - outRec2->IsHole = !outRec1->IsHole; - outRec2->FirstLeft = outRec1; - - if (m_UsingPolyTree) FixupFirstLefts2(outRec2, outRec1); - - if ((outRec2->IsHole ^ m_ReverseOutput) == (Area(*outRec2) > 0)) - ReversePolyPtLinks(outRec2->Pts); - - } else if (Poly2ContainsPoly1(outRec1->Pts, outRec2->Pts)) - { - //outRec2 contains outRec1 ... - outRec2->IsHole = outRec1->IsHole; - outRec1->IsHole = !outRec2->IsHole; - outRec2->FirstLeft = outRec1->FirstLeft; - outRec1->FirstLeft = outRec2; - - if (m_UsingPolyTree) FixupFirstLefts2(outRec1, outRec2); - - if ((outRec1->IsHole ^ m_ReverseOutput) == (Area(*outRec1) > 0)) - ReversePolyPtLinks(outRec1->Pts); - } - else - { - //the 2 polygons are completely separate ... - outRec2->IsHole = outRec1->IsHole; - outRec2->FirstLeft = outRec1->FirstLeft; - - //fixup FirstLeft pointers that may need reassigning to OutRec2 - if (m_UsingPolyTree) FixupFirstLefts1(outRec1, outRec2); - } - - } else - { - //joined 2 polygons together ... - - outRec2->Pts = 0; - outRec2->BottomPt = 0; - outRec2->Idx = outRec1->Idx; - - outRec1->IsHole = holeStateRec->IsHole; - if (holeStateRec == outRec2) - outRec1->FirstLeft = outRec2->FirstLeft; - outRec2->FirstLeft = outRec1; - - if (m_UsingPolyTree) FixupFirstLefts3(outRec2, outRec1); - } - } -} - -//------------------------------------------------------------------------------ -// ClipperOffset support functions ... -//------------------------------------------------------------------------------ - -DoublePoint GetUnitNormal(const IntPoint &pt1, const IntPoint &pt2) -{ - if(pt2.X == pt1.X && pt2.Y == pt1.Y) - return DoublePoint(0, 0); - - double Dx = (double)(pt2.X - pt1.X); - double dy = (double)(pt2.Y - pt1.Y); - double f = 1 *1.0/ std::sqrt( Dx*Dx + dy*dy ); - Dx *= f; - dy *= f; - return DoublePoint(dy, -Dx); -} - -//------------------------------------------------------------------------------ -// ClipperOffset class -//------------------------------------------------------------------------------ - -ClipperOffset::ClipperOffset(double miterLimit, double arcTolerance) -{ - this->MiterLimit = miterLimit; - this->ArcTolerance = arcTolerance; - m_lowest.X = -1; -} -//------------------------------------------------------------------------------ - -ClipperOffset::~ClipperOffset() -{ - Clear(); -} -//------------------------------------------------------------------------------ - -void ClipperOffset::Clear() -{ - for (int i = 0; i < m_polyNodes.ChildCount(); ++i) - delete m_polyNodes.Childs[i]; - m_polyNodes.Childs.clear(); - m_lowest.X = -1; -} -//------------------------------------------------------------------------------ - -void ClipperOffset::AddPath(const Path& path, JoinType joinType, EndType endType) -{ - int highI = (int)path.size() - 1; - if (highI < 0) return; - PolyNode* newNode = new PolyNode(); - newNode->m_jointype = joinType; - newNode->m_endtype = endType; - - //strip duplicate points from path and also get index to the lowest point ... - if (endType == etClosedLine || endType == etClosedPolygon) - while (highI > 0 && path[0] == path[highI]) highI--; - newNode->Contour.reserve(highI + 1); - newNode->Contour.push_back(path[0]); - int j = 0, k = 0; - for (int i = 1; i <= highI; i++) - if (newNode->Contour[j] != path[i]) - { - j++; - newNode->Contour.push_back(path[i]); - if (path[i].Y > newNode->Contour[k].Y || - (path[i].Y == newNode->Contour[k].Y && - path[i].X < newNode->Contour[k].X)) k = j; - } - if (endType == etClosedPolygon && j < 2) - { - delete newNode; - return; - } - m_polyNodes.AddChild(*newNode); - - //if this path's lowest pt is lower than all the others then update m_lowest - if (endType != etClosedPolygon) return; - if (m_lowest.X < 0) - m_lowest = IntPoint(m_polyNodes.ChildCount() - 1, k); - else - { - IntPoint ip = m_polyNodes.Childs[(int)m_lowest.X]->Contour[(int)m_lowest.Y]; - if (newNode->Contour[k].Y > ip.Y || - (newNode->Contour[k].Y == ip.Y && - newNode->Contour[k].X < ip.X)) - m_lowest = IntPoint(m_polyNodes.ChildCount() - 1, k); - } -} -//------------------------------------------------------------------------------ - -void ClipperOffset::AddPaths(const Paths& paths, JoinType joinType, EndType endType) -{ - for (Paths::size_type i = 0; i < paths.size(); ++i) - AddPath(paths[i], joinType, endType); -} -//------------------------------------------------------------------------------ - -void ClipperOffset::FixOrientations() -{ - //fixup orientations of all closed paths if the orientation of the - //closed path with the lowermost vertex is wrong ... - if (m_lowest.X >= 0 && - !Orientation(m_polyNodes.Childs[(int)m_lowest.X]->Contour)) - { - for (int i = 0; i < m_polyNodes.ChildCount(); ++i) - { - PolyNode& node = *m_polyNodes.Childs[i]; - if (node.m_endtype == etClosedPolygon || - (node.m_endtype == etClosedLine && Orientation(node.Contour))) - ReversePath(node.Contour); - } - } else - { - for (int i = 0; i < m_polyNodes.ChildCount(); ++i) - { - PolyNode& node = *m_polyNodes.Childs[i]; - if (node.m_endtype == etClosedLine && !Orientation(node.Contour)) - ReversePath(node.Contour); - } - } -} -//------------------------------------------------------------------------------ - -void ClipperOffset::Execute(Paths& solution, double delta) -{ - solution.clear(); - FixOrientations(); - DoOffset(delta); - - //now clean up 'corners' ... - Clipper clpr; - clpr.AddPaths(m_destPolys, ptSubject, true); - if (delta > 0) - { - clpr.Execute(ctUnion, solution, pftPositive, pftPositive); - } - else - { - IntRect r = clpr.GetBounds(); - Path outer(4); - outer[0] = IntPoint(r.left - 10, r.bottom + 10); - outer[1] = IntPoint(r.right + 10, r.bottom + 10); - outer[2] = IntPoint(r.right + 10, r.top - 10); - outer[3] = IntPoint(r.left - 10, r.top - 10); - - clpr.AddPath(outer, ptSubject, true); - clpr.ReverseSolution(true); - clpr.Execute(ctUnion, solution, pftNegative, pftNegative); - if (solution.size() > 0) solution.erase(solution.begin()); - } -} -//------------------------------------------------------------------------------ - -void ClipperOffset::Execute(PolyTree& solution, double delta) -{ - solution.Clear(); - FixOrientations(); - DoOffset(delta); - - //now clean up 'corners' ... - Clipper clpr; - clpr.AddPaths(m_destPolys, ptSubject, true); - if (delta > 0) - { - clpr.Execute(ctUnion, solution, pftPositive, pftPositive); - } - else - { - IntRect r = clpr.GetBounds(); - Path outer(4); - outer[0] = IntPoint(r.left - 10, r.bottom + 10); - outer[1] = IntPoint(r.right + 10, r.bottom + 10); - outer[2] = IntPoint(r.right + 10, r.top - 10); - outer[3] = IntPoint(r.left - 10, r.top - 10); - - clpr.AddPath(outer, ptSubject, true); - clpr.ReverseSolution(true); - clpr.Execute(ctUnion, solution, pftNegative, pftNegative); - //remove the outer PolyNode rectangle ... - if (solution.ChildCount() == 1 && solution.Childs[0]->ChildCount() > 0) - { - PolyNode* outerNode = solution.Childs[0]; - solution.Childs.reserve(outerNode->ChildCount()); - solution.Childs[0] = outerNode->Childs[0]; - solution.Childs[0]->Parent = outerNode->Parent; - for (int i = 1; i < outerNode->ChildCount(); ++i) - solution.AddChild(*outerNode->Childs[i]); - } - else - solution.Clear(); - } -} -//------------------------------------------------------------------------------ - -void ClipperOffset::DoOffset(double delta) -{ - m_destPolys.clear(); - m_delta = delta; - - //if Zero offset, just copy any CLOSED polygons to m_p and return ... - if (NEAR_ZERO(delta)) - { - m_destPolys.reserve(m_polyNodes.ChildCount()); - for (int i = 0; i < m_polyNodes.ChildCount(); i++) - { - PolyNode& node = *m_polyNodes.Childs[i]; - if (node.m_endtype == etClosedPolygon) - m_destPolys.push_back(node.Contour); - } - return; - } - - //see offset_triginometry3.svg in the documentation folder ... - if (MiterLimit > 2) m_miterLim = 2/(MiterLimit * MiterLimit); - else m_miterLim = 0.5; - - double y; - if (ArcTolerance <= 0.0) y = def_arc_tolerance; - else if (ArcTolerance > std::fabs(delta) * def_arc_tolerance) - y = std::fabs(delta) * def_arc_tolerance; - else y = ArcTolerance; - //see offset_triginometry2.svg in the documentation folder ... - double steps = pi / std::acos(1 - y / std::fabs(delta)); - if (steps > std::fabs(delta) * pi) - steps = std::fabs(delta) * pi; //ie excessive precision check - m_sin = std::sin(two_pi / steps); - m_cos = std::cos(two_pi / steps); - m_StepsPerRad = steps / two_pi; - if (delta < 0.0) m_sin = -m_sin; - - m_destPolys.reserve(m_polyNodes.ChildCount() * 2); - for (int i = 0; i < m_polyNodes.ChildCount(); i++) - { - PolyNode& node = *m_polyNodes.Childs[i]; - m_srcPoly = node.Contour; - - int len = (int)m_srcPoly.size(); - if (len == 0 || (delta <= 0 && (len < 3 || node.m_endtype != etClosedPolygon))) - continue; - - m_destPoly.clear(); - if (len == 1) - { - if (node.m_jointype == jtRound) - { - double X = 1.0, Y = 0.0; - for (cInt j = 1; j <= steps; j++) - { - m_destPoly.push_back(IntPoint( - Round(m_srcPoly[0].X + X * delta), - Round(m_srcPoly[0].Y + Y * delta))); - double X2 = X; - X = X * m_cos - m_sin * Y; - Y = X2 * m_sin + Y * m_cos; - } - } - else - { - double X = -1.0, Y = -1.0; - for (int j = 0; j < 4; ++j) - { - m_destPoly.push_back(IntPoint( - Round(m_srcPoly[0].X + X * delta), - Round(m_srcPoly[0].Y + Y * delta))); - if (X < 0) X = 1; - else if (Y < 0) Y = 1; - else X = -1; - } - } - m_destPolys.push_back(m_destPoly); - continue; - } - //build m_normals ... - m_normals.clear(); - m_normals.reserve(len); - for (int j = 0; j < len - 1; ++j) - m_normals.push_back(GetUnitNormal(m_srcPoly[j], m_srcPoly[j + 1])); - if (node.m_endtype == etClosedLine || node.m_endtype == etClosedPolygon) - m_normals.push_back(GetUnitNormal(m_srcPoly[len - 1], m_srcPoly[0])); - else - m_normals.push_back(DoublePoint(m_normals[len - 2])); - - if (node.m_endtype == etClosedPolygon) - { - int k = len - 1; - for (int j = 0; j < len; ++j) - OffsetPoint(j, k, node.m_jointype); - m_destPolys.push_back(m_destPoly); - } - else if (node.m_endtype == etClosedLine) - { - int k = len - 1; - for (int j = 0; j < len; ++j) - OffsetPoint(j, k, node.m_jointype); - m_destPolys.push_back(m_destPoly); - m_destPoly.clear(); - //re-build m_normals ... - DoublePoint n = m_normals[len -1]; - for (int j = len - 1; j > 0; j--) - m_normals[j] = DoublePoint(-m_normals[j - 1].X, -m_normals[j - 1].Y); - m_normals[0] = DoublePoint(-n.X, -n.Y); - k = 0; - for (int j = len - 1; j >= 0; j--) - OffsetPoint(j, k, node.m_jointype); - m_destPolys.push_back(m_destPoly); - } - else - { - int k = 0; - for (int j = 1; j < len - 1; ++j) - OffsetPoint(j, k, node.m_jointype); - - IntPoint pt1; - if (node.m_endtype == etOpenButt) - { - int j = len - 1; - pt1 = IntPoint((cInt)Round(m_srcPoly[j].X + m_normals[j].X * - delta), (cInt)Round(m_srcPoly[j].Y + m_normals[j].Y * delta)); - m_destPoly.push_back(pt1); - pt1 = IntPoint((cInt)Round(m_srcPoly[j].X - m_normals[j].X * - delta), (cInt)Round(m_srcPoly[j].Y - m_normals[j].Y * delta)); - m_destPoly.push_back(pt1); - } - else - { - int j = len - 1; - k = len - 2; - m_sinA = 0; - m_normals[j] = DoublePoint(-m_normals[j].X, -m_normals[j].Y); - if (node.m_endtype == etOpenSquare) - DoSquare(j, k); - else - DoRound(j, k); - } - - //re-build m_normals ... - for (int j = len - 1; j > 0; j--) - m_normals[j] = DoublePoint(-m_normals[j - 1].X, -m_normals[j - 1].Y); - m_normals[0] = DoublePoint(-m_normals[1].X, -m_normals[1].Y); - - k = len - 1; - for (int j = k - 1; j > 0; --j) OffsetPoint(j, k, node.m_jointype); - - if (node.m_endtype == etOpenButt) - { - pt1 = IntPoint((cInt)Round(m_srcPoly[0].X - m_normals[0].X * delta), - (cInt)Round(m_srcPoly[0].Y - m_normals[0].Y * delta)); - m_destPoly.push_back(pt1); - pt1 = IntPoint((cInt)Round(m_srcPoly[0].X + m_normals[0].X * delta), - (cInt)Round(m_srcPoly[0].Y + m_normals[0].Y * delta)); - m_destPoly.push_back(pt1); - } - else - { - k = 1; - m_sinA = 0; - if (node.m_endtype == etOpenSquare) - DoSquare(0, 1); - else - DoRound(0, 1); - } - m_destPolys.push_back(m_destPoly); - } - } -} -//------------------------------------------------------------------------------ - -void ClipperOffset::OffsetPoint(int j, int& k, JoinType jointype) -{ - //cross product ... - m_sinA = (m_normals[k].X * m_normals[j].Y - m_normals[j].X * m_normals[k].Y); - if (std::fabs(m_sinA * m_delta) < 1.0) - { - //dot product ... - double cosA = (m_normals[k].X * m_normals[j].X + m_normals[j].Y * m_normals[k].Y ); - if (cosA > 0) // angle => 0 degrees - { - m_destPoly.push_back(IntPoint(Round(m_srcPoly[j].X + m_normals[k].X * m_delta), - Round(m_srcPoly[j].Y + m_normals[k].Y * m_delta))); - return; - } - //else angle => 180 degrees - } - else if (m_sinA > 1.0) m_sinA = 1.0; - else if (m_sinA < -1.0) m_sinA = -1.0; - - if (m_sinA * m_delta < 0) - { - m_destPoly.push_back(IntPoint(Round(m_srcPoly[j].X + m_normals[k].X * m_delta), - Round(m_srcPoly[j].Y + m_normals[k].Y * m_delta))); - m_destPoly.push_back(m_srcPoly[j]); - m_destPoly.push_back(IntPoint(Round(m_srcPoly[j].X + m_normals[j].X * m_delta), - Round(m_srcPoly[j].Y + m_normals[j].Y * m_delta))); - } - else - switch (jointype) - { - case jtMiter: - { - double r = 1 + (m_normals[j].X * m_normals[k].X + - m_normals[j].Y * m_normals[k].Y); - if (r >= m_miterLim) DoMiter(j, k, r); else DoSquare(j, k); - break; - } - case jtSquare: DoSquare(j, k); break; - case jtRound: DoRound(j, k); break; - } - k = j; -} -//------------------------------------------------------------------------------ - -void ClipperOffset::DoSquare(int j, int k) -{ - double dx = std::tan(std::atan2(m_sinA, - m_normals[k].X * m_normals[j].X + m_normals[k].Y * m_normals[j].Y) / 4); - m_destPoly.push_back(IntPoint( - Round(m_srcPoly[j].X + m_delta * (m_normals[k].X - m_normals[k].Y * dx)), - Round(m_srcPoly[j].Y + m_delta * (m_normals[k].Y + m_normals[k].X * dx)))); - m_destPoly.push_back(IntPoint( - Round(m_srcPoly[j].X + m_delta * (m_normals[j].X + m_normals[j].Y * dx)), - Round(m_srcPoly[j].Y + m_delta * (m_normals[j].Y - m_normals[j].X * dx)))); -} -//------------------------------------------------------------------------------ - -void ClipperOffset::DoMiter(int j, int k, double r) -{ - double q = m_delta / r; - m_destPoly.push_back(IntPoint(Round(m_srcPoly[j].X + (m_normals[k].X + m_normals[j].X) * q), - Round(m_srcPoly[j].Y + (m_normals[k].Y + m_normals[j].Y) * q))); -} -//------------------------------------------------------------------------------ - -void ClipperOffset::DoRound(int j, int k) -{ - double a = std::atan2(m_sinA, - m_normals[k].X * m_normals[j].X + m_normals[k].Y * m_normals[j].Y); - int steps = std::max((int)Round(m_StepsPerRad * std::fabs(a)), 1); - - double X = m_normals[k].X, Y = m_normals[k].Y, X2; - for (int i = 0; i < steps; ++i) - { - m_destPoly.push_back(IntPoint( - Round(m_srcPoly[j].X + X * m_delta), - Round(m_srcPoly[j].Y + Y * m_delta))); - X2 = X; - X = X * m_cos - m_sin * Y; - Y = X2 * m_sin + Y * m_cos; - } - m_destPoly.push_back(IntPoint( - Round(m_srcPoly[j].X + m_normals[j].X * m_delta), - Round(m_srcPoly[j].Y + m_normals[j].Y * m_delta))); -} - -//------------------------------------------------------------------------------ -// Miscellaneous public functions -//------------------------------------------------------------------------------ - -void Clipper::DoSimplePolygons() -{ - PolyOutList::size_type i = 0; - while (i < m_PolyOuts.size()) - { - OutRec* outrec = m_PolyOuts[i++]; - OutPt* op = outrec->Pts; - if (!op || outrec->IsOpen) continue; - do //for each Pt in Polygon until duplicate found do ... - { - OutPt* op2 = op->Next; - while (op2 != outrec->Pts) - { - if ((op->Pt == op2->Pt) && op2->Next != op && op2->Prev != op) - { - //split the polygon into two ... - OutPt* op3 = op->Prev; - OutPt* op4 = op2->Prev; - op->Prev = op4; - op4->Next = op; - op2->Prev = op3; - op3->Next = op2; - - outrec->Pts = op; - OutRec* outrec2 = CreateOutRec(); - outrec2->Pts = op2; - UpdateOutPtIdxs(*outrec2); - if (Poly2ContainsPoly1(outrec2->Pts, outrec->Pts)) - { - //OutRec2 is contained by OutRec1 ... - outrec2->IsHole = !outrec->IsHole; - outrec2->FirstLeft = outrec; - if (m_UsingPolyTree) FixupFirstLefts2(outrec2, outrec); - } - else - if (Poly2ContainsPoly1(outrec->Pts, outrec2->Pts)) - { - //OutRec1 is contained by OutRec2 ... - outrec2->IsHole = outrec->IsHole; - outrec->IsHole = !outrec2->IsHole; - outrec2->FirstLeft = outrec->FirstLeft; - outrec->FirstLeft = outrec2; - if (m_UsingPolyTree) FixupFirstLefts2(outrec, outrec2); - } - else - { - //the 2 polygons are separate ... - outrec2->IsHole = outrec->IsHole; - outrec2->FirstLeft = outrec->FirstLeft; - if (m_UsingPolyTree) FixupFirstLefts1(outrec, outrec2); - } - op2 = op; //ie get ready for the Next iteration - } - op2 = op2->Next; - } - op = op->Next; - } - while (op != outrec->Pts); - } -} -//------------------------------------------------------------------------------ - -void ReversePath(Path& p) -{ - std::reverse(p.begin(), p.end()); -} -//------------------------------------------------------------------------------ - -void ReversePaths(Paths& p) -{ - for (Paths::size_type i = 0; i < p.size(); ++i) - ReversePath(p[i]); -} -//------------------------------------------------------------------------------ - -void SimplifyPolygon(const Path &in_poly, Paths &out_polys, PolyFillType fillType) -{ - Clipper c; - c.StrictlySimple(true); - c.AddPath(in_poly, ptSubject, true); - c.Execute(ctUnion, out_polys, fillType, fillType); -} -//------------------------------------------------------------------------------ - -void SimplifyPolygons(const Paths &in_polys, Paths &out_polys, PolyFillType fillType) -{ - Clipper c; - c.StrictlySimple(true); - c.AddPaths(in_polys, ptSubject, true); - c.Execute(ctUnion, out_polys, fillType, fillType); -} -//------------------------------------------------------------------------------ - -void SimplifyPolygons(Paths &polys, PolyFillType fillType) -{ - SimplifyPolygons(polys, polys, fillType); -} -//------------------------------------------------------------------------------ - -inline double DistanceSqrd(const IntPoint& pt1, const IntPoint& pt2) -{ - double Dx = ((double)pt1.X - pt2.X); - double dy = ((double)pt1.Y - pt2.Y); - return (Dx*Dx + dy*dy); -} -//------------------------------------------------------------------------------ - -double DistanceFromLineSqrd( - const IntPoint& pt, const IntPoint& ln1, const IntPoint& ln2) -{ - //The equation of a line in general form (Ax + By + C = 0) - //given 2 points (x,y) & (x,y) is ... - //(y - y)x + (x - x)y + (y - y)x - (x - x)y = 0 - //A = (y - y); B = (x - x); C = (y - y)x - (x - x)y - //perpendicular distance of point (x,y) = (Ax + By + C)/Sqrt(A + B) - //see http://en.wikipedia.org/wiki/Perpendicular_distance - double A = double(ln1.Y - ln2.Y); - double B = double(ln2.X - ln1.X); - double C = A * ln1.X + B * ln1.Y; - C = A * pt.X + B * pt.Y - C; - return (C * C) / (A * A + B * B); -} -//--------------------------------------------------------------------------- - -bool SlopesNearCollinear(const IntPoint& pt1, - const IntPoint& pt2, const IntPoint& pt3, double distSqrd) -{ - //this function is more accurate when the point that's geometrically - //between the other 2 points is the one that's tested for distance. - //ie makes it more likely to pick up 'spikes' ... - if (Abs(pt1.X - pt2.X) > Abs(pt1.Y - pt2.Y)) - { - if ((pt1.X > pt2.X) == (pt1.X < pt3.X)) - return DistanceFromLineSqrd(pt1, pt2, pt3) < distSqrd; - else if ((pt2.X > pt1.X) == (pt2.X < pt3.X)) - return DistanceFromLineSqrd(pt2, pt1, pt3) < distSqrd; - else - return DistanceFromLineSqrd(pt3, pt1, pt2) < distSqrd; - } - else - { - if ((pt1.Y > pt2.Y) == (pt1.Y < pt3.Y)) - return DistanceFromLineSqrd(pt1, pt2, pt3) < distSqrd; - else if ((pt2.Y > pt1.Y) == (pt2.Y < pt3.Y)) - return DistanceFromLineSqrd(pt2, pt1, pt3) < distSqrd; - else - return DistanceFromLineSqrd(pt3, pt1, pt2) < distSqrd; - } -} -//------------------------------------------------------------------------------ - -bool PointsAreClose(IntPoint pt1, IntPoint pt2, double distSqrd) -{ - double Dx = (double)pt1.X - pt2.X; - double dy = (double)pt1.Y - pt2.Y; - return ((Dx * Dx) + (dy * dy) <= distSqrd); -} -//------------------------------------------------------------------------------ - -OutPt* ExcludeOp(OutPt* op) -{ - OutPt* result = op->Prev; - result->Next = op->Next; - op->Next->Prev = result; - result->Idx = 0; - return result; -} -//------------------------------------------------------------------------------ - -void CleanPolygon(const Path& in_poly, Path& out_poly, double distance) -{ - //distance = proximity in units/pixels below which vertices - //will be stripped. Default ~= sqrt(2). - - size_t size = in_poly.size(); - - if (size == 0) - { - out_poly.clear(); - return; - } - - OutPt* outPts = new OutPt[size]; - for (size_t i = 0; i < size; ++i) - { - outPts[i].Pt = in_poly[i]; - outPts[i].Next = &outPts[(i + 1) % size]; - outPts[i].Next->Prev = &outPts[i]; - outPts[i].Idx = 0; - } - - double distSqrd = distance * distance; - OutPt* op = &outPts[0]; - while (op->Idx == 0 && op->Next != op->Prev) - { - if (PointsAreClose(op->Pt, op->Prev->Pt, distSqrd)) - { - op = ExcludeOp(op); - size--; - } - else if (PointsAreClose(op->Prev->Pt, op->Next->Pt, distSqrd)) - { - ExcludeOp(op->Next); - op = ExcludeOp(op); - size -= 2; - } - else if (SlopesNearCollinear(op->Prev->Pt, op->Pt, op->Next->Pt, distSqrd)) - { - op = ExcludeOp(op); - size--; - } - else - { - op->Idx = 1; - op = op->Next; - } - } - - if (size < 3) size = 0; - out_poly.resize(size); - for (size_t i = 0; i < size; ++i) - { - out_poly[i] = op->Pt; - op = op->Next; - } - delete [] outPts; -} -//------------------------------------------------------------------------------ - -void CleanPolygon(Path& poly, double distance) -{ - CleanPolygon(poly, poly, distance); -} -//------------------------------------------------------------------------------ - -void CleanPolygons(const Paths& in_polys, Paths& out_polys, double distance) -{ - out_polys.resize(in_polys.size()); - for (Paths::size_type i = 0; i < in_polys.size(); ++i) - CleanPolygon(in_polys[i], out_polys[i], distance); -} -//------------------------------------------------------------------------------ - -void CleanPolygons(Paths& polys, double distance) -{ - CleanPolygons(polys, polys, distance); -} -//------------------------------------------------------------------------------ - -void Minkowski(const Path& poly, const Path& path, - Paths& solution, bool isSum, bool isClosed) -{ - int delta = (isClosed ? 1 : 0); - size_t polyCnt = poly.size(); - size_t pathCnt = path.size(); - Paths pp; - pp.reserve(pathCnt); - if (isSum) - for (size_t i = 0; i < pathCnt; ++i) - { - Path p; - p.reserve(polyCnt); - for (size_t j = 0; j < poly.size(); ++j) - p.push_back(IntPoint(path[i].X + poly[j].X, path[i].Y + poly[j].Y)); - pp.push_back(p); - } - else - for (size_t i = 0; i < pathCnt; ++i) - { - Path p; - p.reserve(polyCnt); - for (size_t j = 0; j < poly.size(); ++j) - p.push_back(IntPoint(path[i].X - poly[j].X, path[i].Y - poly[j].Y)); - pp.push_back(p); - } - - solution.clear(); - solution.reserve((pathCnt + delta) * (polyCnt + 1)); - for (size_t i = 0; i < pathCnt - 1 + delta; ++i) - for (size_t j = 0; j < polyCnt; ++j) - { - Path quad; - quad.reserve(4); - quad.push_back(pp[i % pathCnt][j % polyCnt]); - quad.push_back(pp[(i + 1) % pathCnt][j % polyCnt]); - quad.push_back(pp[(i + 1) % pathCnt][(j + 1) % polyCnt]); - quad.push_back(pp[i % pathCnt][(j + 1) % polyCnt]); - if (!Orientation(quad)) ReversePath(quad); - solution.push_back(quad); - } -} -//------------------------------------------------------------------------------ - -void MinkowskiSum(const Path& pattern, const Path& path, Paths& solution, bool pathIsClosed) -{ - Minkowski(pattern, path, solution, true, pathIsClosed); - Clipper c; - c.AddPaths(solution, ptSubject, true); - c.Execute(ctUnion, solution, pftNonZero, pftNonZero); -} -//------------------------------------------------------------------------------ - -void TranslatePath(const Path& input, Path& output, const IntPoint delta) -{ - //precondition: input != output - output.resize(input.size()); - for (size_t i = 0; i < input.size(); ++i) - output[i] = IntPoint(input[i].X + delta.X, input[i].Y + delta.Y); -} -//------------------------------------------------------------------------------ - -void MinkowskiSum(const Path& pattern, const Paths& paths, Paths& solution, bool pathIsClosed) -{ - Clipper c; - for (size_t i = 0; i < paths.size(); ++i) - { - Paths tmp; - Minkowski(pattern, paths[i], tmp, true, pathIsClosed); - c.AddPaths(tmp, ptSubject, true); - if (pathIsClosed) - { - Path tmp2; - TranslatePath(paths[i], tmp2, pattern[0]); - c.AddPath(tmp2, ptClip, true); - } - } - c.Execute(ctUnion, solution, pftNonZero, pftNonZero); -} -//------------------------------------------------------------------------------ - -void MinkowskiDiff(const Path& poly1, const Path& poly2, Paths& solution) -{ - Minkowski(poly1, poly2, solution, false, true); - Clipper c; - c.AddPaths(solution, ptSubject, true); - c.Execute(ctUnion, solution, pftNonZero, pftNonZero); -} -//------------------------------------------------------------------------------ - -enum NodeType {ntAny, ntOpen, ntClosed}; - -void AddPolyNodeToPaths(const PolyNode& polynode, NodeType nodetype, Paths& paths) -{ - bool match = true; - if (nodetype == ntClosed) match = !polynode.IsOpen(); - else if (nodetype == ntOpen) return; - - if (!polynode.Contour.empty() && match) - paths.push_back(polynode.Contour); - for (int i = 0; i < polynode.ChildCount(); ++i) - AddPolyNodeToPaths(*polynode.Childs[i], nodetype, paths); -} -//------------------------------------------------------------------------------ - -void PolyTreeToPaths(const PolyTree& polytree, Paths& paths) -{ - paths.resize(0); - paths.reserve(polytree.Total()); - AddPolyNodeToPaths(polytree, ntAny, paths); -} -//------------------------------------------------------------------------------ - -void ClosedPathsFromPolyTree(const PolyTree& polytree, Paths& paths) -{ - paths.resize(0); - paths.reserve(polytree.Total()); - AddPolyNodeToPaths(polytree, ntClosed, paths); -} -//------------------------------------------------------------------------------ - -void OpenPathsFromPolyTree(PolyTree& polytree, Paths& paths) -{ - paths.resize(0); - paths.reserve(polytree.Total()); - //Open paths are top level only, so ... - for (int i = 0; i < polytree.ChildCount(); ++i) - if (polytree.Childs[i]->IsOpen()) - paths.push_back(polytree.Childs[i]->Contour); -} -//------------------------------------------------------------------------------ - -std::ostream& operator <<(std::ostream &s, const IntPoint &p) -{ - s << "(" << p.X << "," << p.Y << ")"; - return s; -} -//------------------------------------------------------------------------------ - -std::ostream& operator <<(std::ostream &s, const Path &p) -{ - if (p.empty()) return s; - Path::size_type last = p.size() -1; - for (Path::size_type i = 0; i < last; i++) - s << "(" << p[i].X << "," << p[i].Y << "), "; - s << "(" << p[last].X << "," << p[last].Y << ")\n"; - return s; -} -//------------------------------------------------------------------------------ - -std::ostream& operator <<(std::ostream &s, const Paths &p) -{ - for (Paths::size_type i = 0; i < p.size(); i++) - s << p[i]; - s << "\n"; - return s; -} -//------------------------------------------------------------------------------ - -} //ClipperLib namespace diff --git a/ptocr/postprocess/lanms/include/clipper/clipper.hpp b/ptocr/postprocess/lanms/include/clipper/clipper.hpp deleted file mode 100644 index 1c38371..0000000 --- a/ptocr/postprocess/lanms/include/clipper/clipper.hpp +++ /dev/null @@ -1,404 +0,0 @@ -/******************************************************************************* -* * -* Author : Angus Johnson * -* Version : 6.4.0 * -* Date : 2 July 2015 * -* Website : http://www.angusj.com * -* Copyright : Angus Johnson 2010-2015 * -* * -* License: * -* Use, modification & distribution is subject to Boost Software License Ver 1. * -* http://www.boost.org/LICENSE_1_0.txt * -* * -* Attributions: * -* The code in this library is an extension of Bala Vatti's clipping algorithm: * -* "A generic solution to polygon clipping" * -* Communications of the ACM, Vol 35, Issue 7 (July 1992) pp 56-63. * -* http://portal.acm.org/citation.cfm?id=129906 * -* * -* Computer graphics and geometric modeling: implementation and algorithms * -* By Max K. Agoston * -* Springer; 1 edition (January 4, 2005) * -* http://books.google.com/books?q=vatti+clipping+agoston * -* * -* See also: * -* "Polygon Offsetting by Computing Winding Numbers" * -* Paper no. DETC2005-85513 pp. 565-575 * -* ASME 2005 International Design Engineering Technical Conferences * -* and Computers and Information in Engineering Conference (IDETC/CIE2005) * -* September 24-28, 2005 , Long Beach, California, USA * -* http://www.me.berkeley.edu/~mcmains/pubs/DAC05OffsetPolygon.pdf * -* * -*******************************************************************************/ - -#ifndef clipper_hpp -#define clipper_hpp - -#define CLIPPER_VERSION "6.2.6" - -//use_int32: When enabled 32bit ints are used instead of 64bit ints. This -//improve performance but coordinate values are limited to the range +/- 46340 -//#define use_int32 - -//use_xyz: adds a Z member to IntPoint. Adds a minor cost to perfomance. -//#define use_xyz - -//use_lines: Enables line clipping. Adds a very minor cost to performance. -#define use_lines - -//use_deprecated: Enables temporary support for the obsolete functions -//#define use_deprecated - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace ClipperLib { - -enum ClipType { ctIntersection, ctUnion, ctDifference, ctXor }; -enum PolyType { ptSubject, ptClip }; -//By far the most widely used winding rules for polygon filling are -//EvenOdd & NonZero (GDI, GDI+, XLib, OpenGL, Cairo, AGG, Quartz, SVG, Gr32) -//Others rules include Positive, Negative and ABS_GTR_EQ_TWO (only in OpenGL) -//see http://glprogramming.com/red/chapter11.html -enum PolyFillType { pftEvenOdd, pftNonZero, pftPositive, pftNegative }; - -#ifdef use_int32 - typedef int cInt; - static cInt const loRange = 0x7FFF; - static cInt const hiRange = 0x7FFF; -#else - typedef signed long long cInt; - static cInt const loRange = 0x3FFFFFFF; - static cInt const hiRange = 0x3FFFFFFFFFFFFFFFLL; - typedef signed long long long64; //used by Int128 class - typedef unsigned long long ulong64; - -#endif - -struct IntPoint { - cInt X; - cInt Y; -#ifdef use_xyz - cInt Z; - IntPoint(cInt x = 0, cInt y = 0, cInt z = 0): X(x), Y(y), Z(z) {}; -#else - IntPoint(cInt x = 0, cInt y = 0): X(x), Y(y) {}; -#endif - - friend inline bool operator== (const IntPoint& a, const IntPoint& b) - { - return a.X == b.X && a.Y == b.Y; - } - friend inline bool operator!= (const IntPoint& a, const IntPoint& b) - { - return a.X != b.X || a.Y != b.Y; - } -}; -//------------------------------------------------------------------------------ - -typedef std::vector< IntPoint > Path; -typedef std::vector< Path > Paths; - -inline Path& operator <<(Path& poly, const IntPoint& p) {poly.push_back(p); return poly;} -inline Paths& operator <<(Paths& polys, const Path& p) {polys.push_back(p); return polys;} - -std::ostream& operator <<(std::ostream &s, const IntPoint &p); -std::ostream& operator <<(std::ostream &s, const Path &p); -std::ostream& operator <<(std::ostream &s, const Paths &p); - -struct DoublePoint -{ - double X; - double Y; - DoublePoint(double x = 0, double y = 0) : X(x), Y(y) {} - DoublePoint(IntPoint ip) : X((double)ip.X), Y((double)ip.Y) {} -}; -//------------------------------------------------------------------------------ - -#ifdef use_xyz -typedef void (*ZFillCallback)(IntPoint& e1bot, IntPoint& e1top, IntPoint& e2bot, IntPoint& e2top, IntPoint& pt); -#endif - -enum InitOptions {ioReverseSolution = 1, ioStrictlySimple = 2, ioPreserveCollinear = 4}; -enum JoinType {jtSquare, jtRound, jtMiter}; -enum EndType {etClosedPolygon, etClosedLine, etOpenButt, etOpenSquare, etOpenRound}; - -class PolyNode; -typedef std::vector< PolyNode* > PolyNodes; - -class PolyNode -{ -public: - PolyNode(); - virtual ~PolyNode(){}; - Path Contour; - PolyNodes Childs; - PolyNode* Parent; - PolyNode* GetNext() const; - bool IsHole() const; - bool IsOpen() const; - int ChildCount() const; -private: - unsigned Index; //node index in Parent.Childs - bool m_IsOpen; - JoinType m_jointype; - EndType m_endtype; - PolyNode* GetNextSiblingUp() const; - void AddChild(PolyNode& child); - friend class Clipper; //to access Index - friend class ClipperOffset; -}; - -class PolyTree: public PolyNode -{ -public: - ~PolyTree(){Clear();}; - PolyNode* GetFirst() const; - void Clear(); - int Total() const; -private: - PolyNodes AllNodes; - friend class Clipper; //to access AllNodes -}; - -bool Orientation(const Path &poly); -double Area(const Path &poly); -int PointInPolygon(const IntPoint &pt, const Path &path); - -void SimplifyPolygon(const Path &in_poly, Paths &out_polys, PolyFillType fillType = pftEvenOdd); -void SimplifyPolygons(const Paths &in_polys, Paths &out_polys, PolyFillType fillType = pftEvenOdd); -void SimplifyPolygons(Paths &polys, PolyFillType fillType = pftEvenOdd); - -void CleanPolygon(const Path& in_poly, Path& out_poly, double distance = 1.415); -void CleanPolygon(Path& poly, double distance = 1.415); -void CleanPolygons(const Paths& in_polys, Paths& out_polys, double distance = 1.415); -void CleanPolygons(Paths& polys, double distance = 1.415); - -void MinkowskiSum(const Path& pattern, const Path& path, Paths& solution, bool pathIsClosed); -void MinkowskiSum(const Path& pattern, const Paths& paths, Paths& solution, bool pathIsClosed); -void MinkowskiDiff(const Path& poly1, const Path& poly2, Paths& solution); - -void PolyTreeToPaths(const PolyTree& polytree, Paths& paths); -void ClosedPathsFromPolyTree(const PolyTree& polytree, Paths& paths); -void OpenPathsFromPolyTree(PolyTree& polytree, Paths& paths); - -void ReversePath(Path& p); -void ReversePaths(Paths& p); - -struct IntRect { cInt left; cInt top; cInt right; cInt bottom; }; - -//enums that are used internally ... -enum EdgeSide { esLeft = 1, esRight = 2}; - -//forward declarations (for stuff used internally) ... -struct TEdge; -struct IntersectNode; -struct LocalMinimum; -struct OutPt; -struct OutRec; -struct Join; - -typedef std::vector < OutRec* > PolyOutList; -typedef std::vector < TEdge* > EdgeList; -typedef std::vector < Join* > JoinList; -typedef std::vector < IntersectNode* > IntersectList; - -//------------------------------------------------------------------------------ - -//ClipperBase is the ancestor to the Clipper class. It should not be -//instantiated directly. This class simply abstracts the conversion of sets of -//polygon coordinates into edge objects that are stored in a LocalMinima list. -class ClipperBase -{ -public: - ClipperBase(); - virtual ~ClipperBase(); - virtual bool AddPath(const Path &pg, PolyType PolyTyp, bool Closed); - bool AddPaths(const Paths &ppg, PolyType PolyTyp, bool Closed); - virtual void Clear(); - IntRect GetBounds(); - bool PreserveCollinear() {return m_PreserveCollinear;}; - void PreserveCollinear(bool value) {m_PreserveCollinear = value;}; -protected: - void DisposeLocalMinimaList(); - TEdge* AddBoundsToLML(TEdge *e, bool IsClosed); - virtual void Reset(); - TEdge* ProcessBound(TEdge* E, bool IsClockwise); - void InsertScanbeam(const cInt Y); - bool PopScanbeam(cInt &Y); - bool LocalMinimaPending(); - bool PopLocalMinima(cInt Y, const LocalMinimum *&locMin); - OutRec* CreateOutRec(); - void DisposeAllOutRecs(); - void DisposeOutRec(PolyOutList::size_type index); - void SwapPositionsInAEL(TEdge *edge1, TEdge *edge2); - void DeleteFromAEL(TEdge *e); - void UpdateEdgeIntoAEL(TEdge *&e); - - typedef std::vector MinimaList; - MinimaList::iterator m_CurrentLM; - MinimaList m_MinimaList; - - bool m_UseFullRange; - EdgeList m_edges; - bool m_PreserveCollinear; - bool m_HasOpenPaths; - PolyOutList m_PolyOuts; - TEdge *m_ActiveEdges; - - typedef std::priority_queue ScanbeamList; - ScanbeamList m_Scanbeam; -}; -//------------------------------------------------------------------------------ - -class Clipper : public virtual ClipperBase -{ -public: - Clipper(int initOptions = 0); - bool Execute(ClipType clipType, - Paths &solution, - PolyFillType fillType = pftEvenOdd); - bool Execute(ClipType clipType, - Paths &solution, - PolyFillType subjFillType, - PolyFillType clipFillType); - bool Execute(ClipType clipType, - PolyTree &polytree, - PolyFillType fillType = pftEvenOdd); - bool Execute(ClipType clipType, - PolyTree &polytree, - PolyFillType subjFillType, - PolyFillType clipFillType); - bool ReverseSolution() { return m_ReverseOutput; }; - void ReverseSolution(bool value) {m_ReverseOutput = value;}; - bool StrictlySimple() {return m_StrictSimple;}; - void StrictlySimple(bool value) {m_StrictSimple = value;}; - //set the callback function for z value filling on intersections (otherwise Z is 0) -#ifdef use_xyz - void ZFillFunction(ZFillCallback zFillFunc); -#endif -protected: - virtual bool ExecuteInternal(); -private: - JoinList m_Joins; - JoinList m_GhostJoins; - IntersectList m_IntersectList; - ClipType m_ClipType; - typedef std::list MaximaList; - MaximaList m_Maxima; - TEdge *m_SortedEdges; - bool m_ExecuteLocked; - PolyFillType m_ClipFillType; - PolyFillType m_SubjFillType; - bool m_ReverseOutput; - bool m_UsingPolyTree; - bool m_StrictSimple; -#ifdef use_xyz - ZFillCallback m_ZFill; //custom callback -#endif - void SetWindingCount(TEdge& edge); - bool IsEvenOddFillType(const TEdge& edge) const; - bool IsEvenOddAltFillType(const TEdge& edge) const; - void InsertLocalMinimaIntoAEL(const cInt botY); - void InsertEdgeIntoAEL(TEdge *edge, TEdge* startEdge); - void AddEdgeToSEL(TEdge *edge); - bool PopEdgeFromSEL(TEdge *&edge); - void CopyAELToSEL(); - void DeleteFromSEL(TEdge *e); - void SwapPositionsInSEL(TEdge *edge1, TEdge *edge2); - bool IsContributing(const TEdge& edge) const; - bool IsTopHorz(const cInt XPos); - void DoMaxima(TEdge *e); - void ProcessHorizontals(); - void ProcessHorizontal(TEdge *horzEdge); - void AddLocalMaxPoly(TEdge *e1, TEdge *e2, const IntPoint &pt); - OutPt* AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &pt); - OutRec* GetOutRec(int idx); - void AppendPolygon(TEdge *e1, TEdge *e2); - void IntersectEdges(TEdge *e1, TEdge *e2, IntPoint &pt); - OutPt* AddOutPt(TEdge *e, const IntPoint &pt); - OutPt* GetLastOutPt(TEdge *e); - bool ProcessIntersections(const cInt topY); - void BuildIntersectList(const cInt topY); - void ProcessIntersectList(); - void ProcessEdgesAtTopOfScanbeam(const cInt topY); - void BuildResult(Paths& polys); - void BuildResult2(PolyTree& polytree); - void SetHoleState(TEdge *e, OutRec *outrec); - void DisposeIntersectNodes(); - bool FixupIntersectionOrder(); - void FixupOutPolygon(OutRec &outrec); - void FixupOutPolyline(OutRec &outrec); - bool IsHole(TEdge *e); - bool FindOwnerFromSplitRecs(OutRec &outRec, OutRec *&currOrfl); - void FixHoleLinkage(OutRec &outrec); - void AddJoin(OutPt *op1, OutPt *op2, const IntPoint offPt); - void ClearJoins(); - void ClearGhostJoins(); - void AddGhostJoin(OutPt *op, const IntPoint offPt); - bool JoinPoints(Join *j, OutRec* outRec1, OutRec* outRec2); - void JoinCommonEdges(); - void DoSimplePolygons(); - void FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec); - void FixupFirstLefts2(OutRec* InnerOutRec, OutRec* OuterOutRec); - void FixupFirstLefts3(OutRec* OldOutRec, OutRec* NewOutRec); -#ifdef use_xyz - void SetZ(IntPoint& pt, TEdge& e1, TEdge& e2); -#endif -}; -//------------------------------------------------------------------------------ - -class ClipperOffset -{ -public: - ClipperOffset(double miterLimit = 2.0, double roundPrecision = 0.25); - ~ClipperOffset(); - void AddPath(const Path& path, JoinType joinType, EndType endType); - void AddPaths(const Paths& paths, JoinType joinType, EndType endType); - void Execute(Paths& solution, double delta); - void Execute(PolyTree& solution, double delta); - void Clear(); - double MiterLimit; - double ArcTolerance; -private: - Paths m_destPolys; - Path m_srcPoly; - Path m_destPoly; - std::vector m_normals; - double m_delta, m_sinA, m_sin, m_cos; - double m_miterLim, m_StepsPerRad; - IntPoint m_lowest; - PolyNode m_polyNodes; - - void FixOrientations(); - void DoOffset(double delta); - void OffsetPoint(int j, int& k, JoinType jointype); - void DoSquare(int j, int k); - void DoMiter(int j, int k, double r); - void DoRound(int j, int k); -}; -//------------------------------------------------------------------------------ - -class clipperException : public std::exception -{ - public: - clipperException(const char* description): m_descr(description) {} - virtual ~clipperException() throw() {} - virtual const char* what() const throw() {return m_descr.c_str();} - private: - std::string m_descr; -}; -//------------------------------------------------------------------------------ - -} //ClipperLib namespace - -#endif //clipper_hpp - - diff --git a/ptocr/postprocess/lanms/include/pybind11/attr.h b/ptocr/postprocess/lanms/include/pybind11/attr.h deleted file mode 100644 index b4137cb..0000000 --- a/ptocr/postprocess/lanms/include/pybind11/attr.h +++ /dev/null @@ -1,471 +0,0 @@ -/* - pybind11/attr.h: Infrastructure for processing custom - type and function attributes - - Copyright (c) 2016 Wenzel Jakob - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#include "cast.h" - -NAMESPACE_BEGIN(pybind11) - -/// \addtogroup annotations -/// @{ - -/// Annotation for methods -struct is_method { handle class_; is_method(const handle &c) : class_(c) { } }; - -/// Annotation for operators -struct is_operator { }; - -/// Annotation for parent scope -struct scope { handle value; scope(const handle &s) : value(s) { } }; - -/// Annotation for documentation -struct doc { const char *value; doc(const char *value) : value(value) { } }; - -/// Annotation for function names -struct name { const char *value; name(const char *value) : value(value) { } }; - -/// Annotation indicating that a function is an overload associated with a given "sibling" -struct sibling { handle value; sibling(const handle &value) : value(value.ptr()) { } }; - -/// Annotation indicating that a class derives from another given type -template struct base { - PYBIND11_DEPRECATED("base() was deprecated in favor of specifying 'T' as a template argument to class_") - base() { } -}; - -/// Keep patient alive while nurse lives -template struct keep_alive { }; - -/// Annotation indicating that a class is involved in a multiple inheritance relationship -struct multiple_inheritance { }; - -/// Annotation which enables dynamic attributes, i.e. adds `__dict__` to a class -struct dynamic_attr { }; - -/// Annotation which enables the buffer protocol for a type -struct buffer_protocol { }; - -/// Annotation which requests that a special metaclass is created for a type -struct metaclass { - handle value; - - PYBIND11_DEPRECATED("py::metaclass() is no longer required. It's turned on by default now.") - metaclass() {} - - /// Override pybind11's default metaclass - explicit metaclass(handle value) : value(value) { } -}; - -/// Annotation to mark enums as an arithmetic type -struct arithmetic { }; - -/** \rst - A call policy which places one or more guard variables (``Ts...``) around the function call. - - For example, this definition: - - .. code-block:: cpp - - m.def("foo", foo, py::call_guard()); - - is equivalent to the following pseudocode: - - .. code-block:: cpp - - m.def("foo", [](args...) { - T scope_guard; - return foo(args...); // forwarded arguments - }); - \endrst */ -template struct call_guard; - -template <> struct call_guard<> { using type = detail::void_type; }; - -template -struct call_guard { - static_assert(std::is_default_constructible::value, - "The guard type must be default constructible"); - - using type = T; -}; - -template -struct call_guard { - struct type { - T guard{}; // Compose multiple guard types with left-to-right default-constructor order - typename call_guard::type next{}; - }; -}; - -/// @} annotations - -NAMESPACE_BEGIN(detail) -/* Forward declarations */ -enum op_id : int; -enum op_type : int; -struct undefined_t; -template struct op_; -template struct init; -template struct init_alias; -inline void keep_alive_impl(size_t Nurse, size_t Patient, function_call &call, handle ret); - -/// Internal data structure which holds metadata about a keyword argument -struct argument_record { - const char *name; ///< Argument name - const char *descr; ///< Human-readable version of the argument value - handle value; ///< Associated Python object - bool convert : 1; ///< True if the argument is allowed to convert when loading - bool none : 1; ///< True if None is allowed when loading - - argument_record(const char *name, const char *descr, handle value, bool convert, bool none) - : name(name), descr(descr), value(value), convert(convert), none(none) { } -}; - -/// Internal data structure which holds metadata about a bound function (signature, overloads, etc.) -struct function_record { - function_record() - : is_constructor(false), is_stateless(false), is_operator(false), - has_args(false), has_kwargs(false), is_method(false) { } - - /// Function name - char *name = nullptr; /* why no C++ strings? They generate heavier code.. */ - - // User-specified documentation string - char *doc = nullptr; - - /// Human-readable version of the function signature - char *signature = nullptr; - - /// List of registered keyword arguments - std::vector args; - - /// Pointer to lambda function which converts arguments and performs the actual call - handle (*impl) (function_call &) = nullptr; - - /// Storage for the wrapped function pointer and captured data, if any - void *data[3] = { }; - - /// Pointer to custom destructor for 'data' (if needed) - void (*free_data) (function_record *ptr) = nullptr; - - /// Return value policy associated with this function - return_value_policy policy = return_value_policy::automatic; - - /// True if name == '__init__' - bool is_constructor : 1; - - /// True if this is a stateless function pointer - bool is_stateless : 1; - - /// True if this is an operator (__add__), etc. - bool is_operator : 1; - - /// True if the function has a '*args' argument - bool has_args : 1; - - /// True if the function has a '**kwargs' argument - bool has_kwargs : 1; - - /// True if this is a method - bool is_method : 1; - - /// Number of arguments (including py::args and/or py::kwargs, if present) - std::uint16_t nargs; - - /// Python method object - PyMethodDef *def = nullptr; - - /// Python handle to the parent scope (a class or a module) - handle scope; - - /// Python handle to the sibling function representing an overload chain - handle sibling; - - /// Pointer to next overload - function_record *next = nullptr; -}; - -/// Special data structure which (temporarily) holds metadata about a bound class -struct type_record { - PYBIND11_NOINLINE type_record() - : multiple_inheritance(false), dynamic_attr(false), buffer_protocol(false) { } - - /// Handle to the parent scope - handle scope; - - /// Name of the class - const char *name = nullptr; - - // Pointer to RTTI type_info data structure - const std::type_info *type = nullptr; - - /// How large is the underlying C++ type? - size_t type_size = 0; - - /// How large is the type's holder? - size_t holder_size = 0; - - /// The global operator new can be overridden with a class-specific variant - void *(*operator_new)(size_t) = ::operator new; - - /// Function pointer to class_<..>::init_instance - void (*init_instance)(instance *, const void *) = nullptr; - - /// Function pointer to class_<..>::dealloc - void (*dealloc)(const detail::value_and_holder &) = nullptr; - - /// List of base classes of the newly created type - list bases; - - /// Optional docstring - const char *doc = nullptr; - - /// Custom metaclass (optional) - handle metaclass; - - /// Multiple inheritance marker - bool multiple_inheritance : 1; - - /// Does the class manage a __dict__? - bool dynamic_attr : 1; - - /// Does the class implement the buffer protocol? - bool buffer_protocol : 1; - - /// Is the default (unique_ptr) holder type used? - bool default_holder : 1; - - PYBIND11_NOINLINE void add_base(const std::type_info &base, void *(*caster)(void *)) { - auto base_info = detail::get_type_info(base, false); - if (!base_info) { - std::string tname(base.name()); - detail::clean_type_id(tname); - pybind11_fail("generic_type: type \"" + std::string(name) + - "\" referenced unknown base type \"" + tname + "\""); - } - - if (default_holder != base_info->default_holder) { - std::string tname(base.name()); - detail::clean_type_id(tname); - pybind11_fail("generic_type: type \"" + std::string(name) + "\" " + - (default_holder ? "does not have" : "has") + - " a non-default holder type while its base \"" + tname + "\" " + - (base_info->default_holder ? "does not" : "does")); - } - - bases.append((PyObject *) base_info->type); - - if (base_info->type->tp_dictoffset != 0) - dynamic_attr = true; - - if (caster) - base_info->implicit_casts.emplace_back(type, caster); - } -}; - -inline function_call::function_call(function_record &f, handle p) : - func(f), parent(p) { - args.reserve(f.nargs); - args_convert.reserve(f.nargs); -} - -/** - * Partial template specializations to process custom attributes provided to - * cpp_function_ and class_. These are either used to initialize the respective - * fields in the type_record and function_record data structures or executed at - * runtime to deal with custom call policies (e.g. keep_alive). - */ -template struct process_attribute; - -template struct process_attribute_default { - /// Default implementation: do nothing - static void init(const T &, function_record *) { } - static void init(const T &, type_record *) { } - static void precall(function_call &) { } - static void postcall(function_call &, handle) { } -}; - -/// Process an attribute specifying the function's name -template <> struct process_attribute : process_attribute_default { - static void init(const name &n, function_record *r) { r->name = const_cast(n.value); } -}; - -/// Process an attribute specifying the function's docstring -template <> struct process_attribute : process_attribute_default { - static void init(const doc &n, function_record *r) { r->doc = const_cast(n.value); } -}; - -/// Process an attribute specifying the function's docstring (provided as a C-style string) -template <> struct process_attribute : process_attribute_default { - static void init(const char *d, function_record *r) { r->doc = const_cast(d); } - static void init(const char *d, type_record *r) { r->doc = const_cast(d); } -}; -template <> struct process_attribute : process_attribute { }; - -/// Process an attribute indicating the function's return value policy -template <> struct process_attribute : process_attribute_default { - static void init(const return_value_policy &p, function_record *r) { r->policy = p; } -}; - -/// Process an attribute which indicates that this is an overloaded function associated with a given sibling -template <> struct process_attribute : process_attribute_default { - static void init(const sibling &s, function_record *r) { r->sibling = s.value; } -}; - -/// Process an attribute which indicates that this function is a method -template <> struct process_attribute : process_attribute_default { - static void init(const is_method &s, function_record *r) { r->is_method = true; r->scope = s.class_; } -}; - -/// Process an attribute which indicates the parent scope of a method -template <> struct process_attribute : process_attribute_default { - static void init(const scope &s, function_record *r) { r->scope = s.value; } -}; - -/// Process an attribute which indicates that this function is an operator -template <> struct process_attribute : process_attribute_default { - static void init(const is_operator &, function_record *r) { r->is_operator = true; } -}; - -/// Process a keyword argument attribute (*without* a default value) -template <> struct process_attribute : process_attribute_default { - static void init(const arg &a, function_record *r) { - if (r->is_method && r->args.empty()) - r->args.emplace_back("self", nullptr, handle(), true /*convert*/, false /*none not allowed*/); - r->args.emplace_back(a.name, nullptr, handle(), !a.flag_noconvert, a.flag_none); - } -}; - -/// Process a keyword argument attribute (*with* a default value) -template <> struct process_attribute : process_attribute_default { - static void init(const arg_v &a, function_record *r) { - if (r->is_method && r->args.empty()) - r->args.emplace_back("self", nullptr /*descr*/, handle() /*parent*/, true /*convert*/, false /*none not allowed*/); - - if (!a.value) { -#if !defined(NDEBUG) - std::string descr("'"); - if (a.name) descr += std::string(a.name) + ": "; - descr += a.type + "'"; - if (r->is_method) { - if (r->name) - descr += " in method '" + (std::string) str(r->scope) + "." + (std::string) r->name + "'"; - else - descr += " in method of '" + (std::string) str(r->scope) + "'"; - } else if (r->name) { - descr += " in function '" + (std::string) r->name + "'"; - } - pybind11_fail("arg(): could not convert default argument " - + descr + " into a Python object (type not registered yet?)"); -#else - pybind11_fail("arg(): could not convert default argument " - "into a Python object (type not registered yet?). " - "Compile in debug mode for more information."); -#endif - } - r->args.emplace_back(a.name, a.descr, a.value.inc_ref(), !a.flag_noconvert, a.flag_none); - } -}; - -/// Process a parent class attribute. Single inheritance only (class_ itself already guarantees that) -template -struct process_attribute::value>> : process_attribute_default { - static void init(const handle &h, type_record *r) { r->bases.append(h); } -}; - -/// Process a parent class attribute (deprecated, does not support multiple inheritance) -template -struct process_attribute> : process_attribute_default> { - static void init(const base &, type_record *r) { r->add_base(typeid(T), nullptr); } -}; - -/// Process a multiple inheritance attribute -template <> -struct process_attribute : process_attribute_default { - static void init(const multiple_inheritance &, type_record *r) { r->multiple_inheritance = true; } -}; - -template <> -struct process_attribute : process_attribute_default { - static void init(const dynamic_attr &, type_record *r) { r->dynamic_attr = true; } -}; - -template <> -struct process_attribute : process_attribute_default { - static void init(const buffer_protocol &, type_record *r) { r->buffer_protocol = true; } -}; - -template <> -struct process_attribute : process_attribute_default { - static void init(const metaclass &m, type_record *r) { r->metaclass = m.value; } -}; - - -/// Process an 'arithmetic' attribute for enums (does nothing here) -template <> -struct process_attribute : process_attribute_default {}; - -template -struct process_attribute> : process_attribute_default> { }; - -/** - * Process a keep_alive call policy -- invokes keep_alive_impl during the - * pre-call handler if both Nurse, Patient != 0 and use the post-call handler - * otherwise - */ -template struct process_attribute> : public process_attribute_default> { - template = 0> - static void precall(function_call &call) { keep_alive_impl(Nurse, Patient, call, handle()); } - template = 0> - static void postcall(function_call &, handle) { } - template = 0> - static void precall(function_call &) { } - template = 0> - static void postcall(function_call &call, handle ret) { keep_alive_impl(Nurse, Patient, call, ret); } -}; - -/// Recursively iterate over variadic template arguments -template struct process_attributes { - static void init(const Args&... args, function_record *r) { - int unused[] = { 0, (process_attribute::type>::init(args, r), 0) ... }; - ignore_unused(unused); - } - static void init(const Args&... args, type_record *r) { - int unused[] = { 0, (process_attribute::type>::init(args, r), 0) ... }; - ignore_unused(unused); - } - static void precall(function_call &call) { - int unused[] = { 0, (process_attribute::type>::precall(call), 0) ... }; - ignore_unused(unused); - } - static void postcall(function_call &call, handle fn_ret) { - int unused[] = { 0, (process_attribute::type>::postcall(call, fn_ret), 0) ... }; - ignore_unused(unused); - } -}; - -template -using is_call_guard = is_instantiation; - -/// Extract the ``type`` from the first `call_guard` in `Extras...` (or `void_type` if none found) -template -using extract_guard_t = typename exactly_one_t, Extra...>::type; - -/// Check the number of named arguments at compile time -template ::value...), - size_t self = constexpr_sum(std::is_same::value...)> -constexpr bool expected_num_args(size_t nargs, bool has_args, bool has_kwargs) { - return named == 0 || (self + named + has_args + has_kwargs) == nargs; -} - -NAMESPACE_END(detail) -NAMESPACE_END(pybind11) diff --git a/ptocr/postprocess/lanms/include/pybind11/buffer_info.h b/ptocr/postprocess/lanms/include/pybind11/buffer_info.h deleted file mode 100644 index 6d1167d..0000000 --- a/ptocr/postprocess/lanms/include/pybind11/buffer_info.h +++ /dev/null @@ -1,108 +0,0 @@ -/* - pybind11/buffer_info.h: Python buffer object interface - - Copyright (c) 2016 Wenzel Jakob - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#include "common.h" - -NAMESPACE_BEGIN(pybind11) - -/// Information record describing a Python buffer object -struct buffer_info { - void *ptr = nullptr; // Pointer to the underlying storage - ssize_t itemsize = 0; // Size of individual items in bytes - ssize_t size = 0; // Total number of entries - std::string format; // For homogeneous buffers, this should be set to format_descriptor::format() - ssize_t ndim = 0; // Number of dimensions - std::vector shape; // Shape of the tensor (1 entry per dimension) - std::vector strides; // Number of entries between adjacent entries (for each per dimension) - - buffer_info() { } - - buffer_info(void *ptr, ssize_t itemsize, const std::string &format, ssize_t ndim, - detail::any_container shape_in, detail::any_container strides_in) - : ptr(ptr), itemsize(itemsize), size(1), format(format), ndim(ndim), - shape(std::move(shape_in)), strides(std::move(strides_in)) { - if (ndim != (ssize_t) shape.size() || ndim != (ssize_t) strides.size()) - pybind11_fail("buffer_info: ndim doesn't match shape and/or strides length"); - for (size_t i = 0; i < (size_t) ndim; ++i) - size *= shape[i]; - } - - template - buffer_info(T *ptr, detail::any_container shape_in, detail::any_container strides_in) - : buffer_info(private_ctr_tag(), ptr, sizeof(T), format_descriptor::format(), static_cast(shape_in->size()), std::move(shape_in), std::move(strides_in)) { } - - buffer_info(void *ptr, ssize_t itemsize, const std::string &format, ssize_t size) - : buffer_info(ptr, itemsize, format, 1, {size}, {itemsize}) { } - - template - buffer_info(T *ptr, ssize_t size) - : buffer_info(ptr, sizeof(T), format_descriptor::format(), size) { } - - explicit buffer_info(Py_buffer *view, bool ownview = true) - : buffer_info(view->buf, view->itemsize, view->format, view->ndim, - {view->shape, view->shape + view->ndim}, {view->strides, view->strides + view->ndim}) { - this->view = view; - this->ownview = ownview; - } - - buffer_info(const buffer_info &) = delete; - buffer_info& operator=(const buffer_info &) = delete; - - buffer_info(buffer_info &&other) { - (*this) = std::move(other); - } - - buffer_info& operator=(buffer_info &&rhs) { - ptr = rhs.ptr; - itemsize = rhs.itemsize; - size = rhs.size; - format = std::move(rhs.format); - ndim = rhs.ndim; - shape = std::move(rhs.shape); - strides = std::move(rhs.strides); - std::swap(view, rhs.view); - std::swap(ownview, rhs.ownview); - return *this; - } - - ~buffer_info() { - if (view && ownview) { PyBuffer_Release(view); delete view; } - } - -private: - struct private_ctr_tag { }; - - buffer_info(private_ctr_tag, void *ptr, ssize_t itemsize, const std::string &format, ssize_t ndim, - detail::any_container &&shape_in, detail::any_container &&strides_in) - : buffer_info(ptr, itemsize, format, ndim, std::move(shape_in), std::move(strides_in)) { } - - Py_buffer *view = nullptr; - bool ownview = false; -}; - -NAMESPACE_BEGIN(detail) - -template struct compare_buffer_info { - static bool compare(const buffer_info& b) { - return b.format == format_descriptor::format() && b.itemsize == (ssize_t) sizeof(T); - } -}; - -template struct compare_buffer_info::value>> { - static bool compare(const buffer_info& b) { - return (size_t) b.itemsize == sizeof(T) && (b.format == format_descriptor::value || - ((sizeof(T) == sizeof(long)) && b.format == (std::is_unsigned::value ? "L" : "l")) || - ((sizeof(T) == sizeof(size_t)) && b.format == (std::is_unsigned::value ? "N" : "n"))); - } -}; - -NAMESPACE_END(detail) -NAMESPACE_END(pybind11) diff --git a/ptocr/postprocess/lanms/include/pybind11/cast.h b/ptocr/postprocess/lanms/include/pybind11/cast.h deleted file mode 100644 index 5db03e2..0000000 --- a/ptocr/postprocess/lanms/include/pybind11/cast.h +++ /dev/null @@ -1,2058 +0,0 @@ -/* - pybind11/cast.h: Partial template specializations to cast between - C++ and Python types - - Copyright (c) 2016 Wenzel Jakob - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#include "pytypes.h" -#include "typeid.h" -#include "descr.h" -#include -#include -#include - -#if defined(PYBIND11_CPP17) -# if defined(__has_include) -# if __has_include() -# define PYBIND11_HAS_STRING_VIEW -# endif -# elif defined(_MSC_VER) -# define PYBIND11_HAS_STRING_VIEW -# endif -#endif -#ifdef PYBIND11_HAS_STRING_VIEW -#include -#endif - -NAMESPACE_BEGIN(pybind11) -NAMESPACE_BEGIN(detail) -// Forward declarations: -inline PyTypeObject *make_static_property_type(); -inline PyTypeObject *make_default_metaclass(); -inline PyObject *make_object_base_type(PyTypeObject *metaclass); -struct value_and_holder; - -/// Additional type information which does not fit into the PyTypeObject -struct type_info { - PyTypeObject *type; - const std::type_info *cpptype; - size_t type_size, holder_size_in_ptrs; - void *(*operator_new)(size_t); - void (*init_instance)(instance *, const void *); - void (*dealloc)(const value_and_holder &v_h); - std::vector implicit_conversions; - std::vector> implicit_casts; - std::vector *direct_conversions; - buffer_info *(*get_buffer)(PyObject *, void *) = nullptr; - void *get_buffer_data = nullptr; - /* A simple type never occurs as a (direct or indirect) parent - * of a class that makes use of multiple inheritance */ - bool simple_type : 1; - /* True if there is no multiple inheritance in this type's inheritance tree */ - bool simple_ancestors : 1; - /* for base vs derived holder_type checks */ - bool default_holder : 1; -}; - -// Store the static internals pointer in a version-specific function so that we're guaranteed it -// will be distinct for modules compiled for different pybind11 versions. Without this, some -// compilers (i.e. gcc) can use the same static pointer storage location across different .so's, -// even though the `get_internals()` function itself is local to each shared object. -template -internals *&get_internals_ptr() { static internals *internals_ptr = nullptr; return internals_ptr; } - -PYBIND11_NOINLINE inline internals &get_internals() { - internals *&internals_ptr = get_internals_ptr(); - if (internals_ptr) - return *internals_ptr; - handle builtins(PyEval_GetBuiltins()); - const char *id = PYBIND11_INTERNALS_ID; - if (builtins.contains(id) && isinstance(builtins[id])) { - internals_ptr = *static_cast(capsule(builtins[id])); - } else { - internals_ptr = new internals(); - #if defined(WITH_THREAD) - PyEval_InitThreads(); - PyThreadState *tstate = PyThreadState_Get(); - internals_ptr->tstate = PyThread_create_key(); - PyThread_set_key_value(internals_ptr->tstate, tstate); - internals_ptr->istate = tstate->interp; - #endif - builtins[id] = capsule(&internals_ptr); - internals_ptr->registered_exception_translators.push_front( - [](std::exception_ptr p) -> void { - try { - if (p) std::rethrow_exception(p); - } catch (error_already_set &e) { e.restore(); return; - } catch (const builtin_exception &e) { e.set_error(); return; - } catch (const std::bad_alloc &e) { PyErr_SetString(PyExc_MemoryError, e.what()); return; - } catch (const std::domain_error &e) { PyErr_SetString(PyExc_ValueError, e.what()); return; - } catch (const std::invalid_argument &e) { PyErr_SetString(PyExc_ValueError, e.what()); return; - } catch (const std::length_error &e) { PyErr_SetString(PyExc_ValueError, e.what()); return; - } catch (const std::out_of_range &e) { PyErr_SetString(PyExc_IndexError, e.what()); return; - } catch (const std::range_error &e) { PyErr_SetString(PyExc_ValueError, e.what()); return; - } catch (const std::exception &e) { PyErr_SetString(PyExc_RuntimeError, e.what()); return; - } catch (...) { - PyErr_SetString(PyExc_RuntimeError, "Caught an unknown exception!"); - return; - } - } - ); - internals_ptr->static_property_type = make_static_property_type(); - internals_ptr->default_metaclass = make_default_metaclass(); - internals_ptr->instance_base = make_object_base_type(internals_ptr->default_metaclass); - } - return *internals_ptr; -} - -/// A life support system for temporary objects created by `type_caster::load()`. -/// Adding a patient will keep it alive up until the enclosing function returns. -class loader_life_support { -public: - /// A new patient frame is created when a function is entered - loader_life_support() { - get_internals().loader_patient_stack.push_back(nullptr); - } - - /// ... and destroyed after it returns - ~loader_life_support() { - auto &stack = get_internals().loader_patient_stack; - if (stack.empty()) - pybind11_fail("loader_life_support: internal error"); - - auto ptr = stack.back(); - stack.pop_back(); - Py_CLEAR(ptr); - - // A heuristic to reduce the stack's capacity (e.g. after long recursive calls) - if (stack.capacity() > 16 && stack.size() != 0 && stack.capacity() / stack.size() > 2) - stack.shrink_to_fit(); - } - - /// This can only be used inside a pybind11-bound function, either by `argument_loader` - /// at argument preparation time or by `py::cast()` at execution time. - PYBIND11_NOINLINE static void add_patient(handle h) { - auto &stack = get_internals().loader_patient_stack; - if (stack.empty()) - throw cast_error("When called outside a bound function, py::cast() cannot " - "do Python -> C++ conversions which require the creation " - "of temporary values"); - - auto &list_ptr = stack.back(); - if (list_ptr == nullptr) { - list_ptr = PyList_New(1); - if (!list_ptr) - pybind11_fail("loader_life_support: error allocating list"); - PyList_SET_ITEM(list_ptr, 0, h.inc_ref().ptr()); - } else { - auto result = PyList_Append(list_ptr, h.ptr()); - if (result == -1) - pybind11_fail("loader_life_support: error adding patient"); - } - } -}; - -// Gets the cache entry for the given type, creating it if necessary. The return value is the pair -// returned by emplace, i.e. an iterator for the entry and a bool set to `true` if the entry was -// just created. -inline std::pair all_type_info_get_cache(PyTypeObject *type); - -// Populates a just-created cache entry. -PYBIND11_NOINLINE inline void all_type_info_populate(PyTypeObject *t, std::vector &bases) { - std::vector check; - for (handle parent : reinterpret_borrow(t->tp_bases)) - check.push_back((PyTypeObject *) parent.ptr()); - - auto const &type_dict = get_internals().registered_types_py; - for (size_t i = 0; i < check.size(); i++) { - auto type = check[i]; - // Ignore Python2 old-style class super type: - if (!PyType_Check((PyObject *) type)) continue; - - // Check `type` in the current set of registered python types: - auto it = type_dict.find(type); - if (it != type_dict.end()) { - // We found a cache entry for it, so it's either pybind-registered or has pre-computed - // pybind bases, but we have to make sure we haven't already seen the type(s) before: we - // want to follow Python/virtual C++ rules that there should only be one instance of a - // common base. - for (auto *tinfo : it->second) { - // NB: Could use a second set here, rather than doing a linear search, but since - // having a large number of immediate pybind11-registered types seems fairly - // unlikely, that probably isn't worthwhile. - bool found = false; - for (auto *known : bases) { - if (known == tinfo) { found = true; break; } - } - if (!found) bases.push_back(tinfo); - } - } - else if (type->tp_bases) { - // It's some python type, so keep follow its bases classes to look for one or more - // registered types - if (i + 1 == check.size()) { - // When we're at the end, we can pop off the current element to avoid growing - // `check` when adding just one base (which is typical--.e. when there is no - // multiple inheritance) - check.pop_back(); - i--; - } - for (handle parent : reinterpret_borrow(type->tp_bases)) - check.push_back((PyTypeObject *) parent.ptr()); - } - } -} - -/** - * Extracts vector of type_info pointers of pybind-registered roots of the given Python type. Will - * be just 1 pybind type for the Python type of a pybind-registered class, or for any Python-side - * derived class that uses single inheritance. Will contain as many types as required for a Python - * class that uses multiple inheritance to inherit (directly or indirectly) from multiple - * pybind-registered classes. Will be empty if neither the type nor any base classes are - * pybind-registered. - * - * The value is cached for the lifetime of the Python type. - */ -inline const std::vector &all_type_info(PyTypeObject *type) { - auto ins = all_type_info_get_cache(type); - if (ins.second) - // New cache entry: populate it - all_type_info_populate(type, ins.first->second); - - return ins.first->second; -} - -/** - * Gets a single pybind11 type info for a python type. Returns nullptr if neither the type nor any - * ancestors are pybind11-registered. Throws an exception if there are multiple bases--use - * `all_type_info` instead if you want to support multiple bases. - */ -PYBIND11_NOINLINE inline detail::type_info* get_type_info(PyTypeObject *type) { - auto &bases = all_type_info(type); - if (bases.size() == 0) - return nullptr; - if (bases.size() > 1) - pybind11_fail("pybind11::detail::get_type_info: type has multiple pybind11-registered bases"); - return bases.front(); -} - -PYBIND11_NOINLINE inline detail::type_info *get_type_info(const std::type_info &tp, - bool throw_if_missing = false) { - auto &types = get_internals().registered_types_cpp; - - auto it = types.find(std::type_index(tp)); - if (it != types.end()) - return (detail::type_info *) it->second; - if (throw_if_missing) { - std::string tname = tp.name(); - detail::clean_type_id(tname); - pybind11_fail("pybind11::detail::get_type_info: unable to find type info for \"" + tname + "\""); - } - return nullptr; -} - -PYBIND11_NOINLINE inline handle get_type_handle(const std::type_info &tp, bool throw_if_missing) { - detail::type_info *type_info = get_type_info(tp, throw_if_missing); - return handle(type_info ? ((PyObject *) type_info->type) : nullptr); -} - -struct value_and_holder { - instance *inst; - size_t index; - const detail::type_info *type; - void **vh; - - value_and_holder(instance *i, const detail::type_info *type, size_t vpos, size_t index) : - inst{i}, index{index}, type{type}, - vh{inst->simple_layout ? inst->simple_value_holder : &inst->nonsimple.values_and_holders[vpos]} - {} - - // Used for past-the-end iterator - value_and_holder(size_t index) : index{index} {} - - template V *&value_ptr() const { - return reinterpret_cast(vh[0]); - } - // True if this `value_and_holder` has a non-null value pointer - explicit operator bool() const { return value_ptr(); } - - template H &holder() const { - return reinterpret_cast(vh[1]); - } - bool holder_constructed() const { - return inst->simple_layout - ? inst->simple_holder_constructed - : inst->nonsimple.status[index] & instance::status_holder_constructed; - } - void set_holder_constructed() { - if (inst->simple_layout) - inst->simple_holder_constructed = true; - else - inst->nonsimple.status[index] |= instance::status_holder_constructed; - } - bool instance_registered() const { - return inst->simple_layout - ? inst->simple_instance_registered - : inst->nonsimple.status[index] & instance::status_instance_registered; - } - void set_instance_registered() { - if (inst->simple_layout) - inst->simple_instance_registered = true; - else - inst->nonsimple.status[index] |= instance::status_instance_registered; - } -}; - -// Container for accessing and iterating over an instance's values/holders -struct values_and_holders { -private: - instance *inst; - using type_vec = std::vector; - const type_vec &tinfo; - -public: - values_and_holders(instance *inst) : inst{inst}, tinfo(all_type_info(Py_TYPE(inst))) {} - - struct iterator { - private: - instance *inst; - const type_vec *types; - value_and_holder curr; - friend struct values_and_holders; - iterator(instance *inst, const type_vec *tinfo) - : inst{inst}, types{tinfo}, - curr(inst /* instance */, - types->empty() ? nullptr : (*types)[0] /* type info */, - 0, /* vpos: (non-simple types only): the first vptr comes first */ - 0 /* index */) - {} - // Past-the-end iterator: - iterator(size_t end) : curr(end) {} - public: - bool operator==(const iterator &other) { return curr.index == other.curr.index; } - bool operator!=(const iterator &other) { return curr.index != other.curr.index; } - iterator &operator++() { - if (!inst->simple_layout) - curr.vh += 1 + (*types)[curr.index]->holder_size_in_ptrs; - ++curr.index; - curr.type = curr.index < types->size() ? (*types)[curr.index] : nullptr; - return *this; - } - value_and_holder &operator*() { return curr; } - value_and_holder *operator->() { return &curr; } - }; - - iterator begin() { return iterator(inst, &tinfo); } - iterator end() { return iterator(tinfo.size()); } - - iterator find(const type_info *find_type) { - auto it = begin(), endit = end(); - while (it != endit && it->type != find_type) ++it; - return it; - } - - size_t size() { return tinfo.size(); } -}; - -/** - * Extracts C++ value and holder pointer references from an instance (which may contain multiple - * values/holders for python-side multiple inheritance) that match the given type. Throws an error - * if the given type (or ValueType, if omitted) is not a pybind11 base of the given instance. If - * `find_type` is omitted (or explicitly specified as nullptr) the first value/holder are returned, - * regardless of type (and the resulting .type will be nullptr). - * - * The returned object should be short-lived: in particular, it must not outlive the called-upon - * instance. - */ -PYBIND11_NOINLINE inline value_and_holder instance::get_value_and_holder(const type_info *find_type /*= nullptr default in common.h*/) { - // Optimize common case: - if (!find_type || Py_TYPE(this) == find_type->type) - return value_and_holder(this, find_type, 0, 0); - - detail::values_and_holders vhs(this); - auto it = vhs.find(find_type); - if (it != vhs.end()) - return *it; - -#if defined(NDEBUG) - pybind11_fail("pybind11::detail::instance::get_value_and_holder: " - "type is not a pybind11 base of the given instance " - "(compile in debug mode for type details)"); -#else - pybind11_fail("pybind11::detail::instance::get_value_and_holder: `" + - std::string(find_type->type->tp_name) + "' is not a pybind11 base of the given `" + - std::string(Py_TYPE(this)->tp_name) + "' instance"); -#endif -} - -PYBIND11_NOINLINE inline void instance::allocate_layout() { - auto &tinfo = all_type_info(Py_TYPE(this)); - - const size_t n_types = tinfo.size(); - - if (n_types == 0) - pybind11_fail("instance allocation failed: new instance has no pybind11-registered base types"); - - simple_layout = - n_types == 1 && tinfo.front()->holder_size_in_ptrs <= instance_simple_holder_in_ptrs(); - - // Simple path: no python-side multiple inheritance, and a small-enough holder - if (simple_layout) { - simple_value_holder[0] = nullptr; - simple_holder_constructed = false; - simple_instance_registered = false; - } - else { // multiple base types or a too-large holder - // Allocate space to hold: [v1*][h1][v2*][h2]...[bb...] where [vN*] is a value pointer, - // [hN] is the (uninitialized) holder instance for value N, and [bb...] is a set of bool - // values that tracks whether each associated holder has been initialized. Each [block] is - // padded, if necessary, to an integer multiple of sizeof(void *). - size_t space = 0; - for (auto t : tinfo) { - space += 1; // value pointer - space += t->holder_size_in_ptrs; // holder instance - } - size_t flags_at = space; - space += size_in_ptrs(n_types); // status bytes (holder_constructed and instance_registered) - - // Allocate space for flags, values, and holders, and initialize it to 0 (flags and values, - // in particular, need to be 0). Use Python's memory allocation functions: in Python 3.6 - // they default to using pymalloc, which is designed to be efficient for small allocations - // like the one we're doing here; in earlier versions (and for larger allocations) they are - // just wrappers around malloc. -#if PY_VERSION_HEX >= 0x03050000 - nonsimple.values_and_holders = (void **) PyMem_Calloc(space, sizeof(void *)); - if (!nonsimple.values_and_holders) throw std::bad_alloc(); -#else - nonsimple.values_and_holders = (void **) PyMem_New(void *, space); - if (!nonsimple.values_and_holders) throw std::bad_alloc(); - std::memset(nonsimple.values_and_holders, 0, space * sizeof(void *)); -#endif - nonsimple.status = reinterpret_cast(&nonsimple.values_and_holders[flags_at]); - } - owned = true; -} - -PYBIND11_NOINLINE inline void instance::deallocate_layout() { - if (!simple_layout) - PyMem_Free(nonsimple.values_and_holders); -} - -PYBIND11_NOINLINE inline bool isinstance_generic(handle obj, const std::type_info &tp) { - handle type = detail::get_type_handle(tp, false); - if (!type) - return false; - return isinstance(obj, type); -} - -PYBIND11_NOINLINE inline std::string error_string() { - if (!PyErr_Occurred()) { - PyErr_SetString(PyExc_RuntimeError, "Unknown internal error occurred"); - return "Unknown internal error occurred"; - } - - error_scope scope; // Preserve error state - - std::string errorString; - if (scope.type) { - errorString += handle(scope.type).attr("__name__").cast(); - errorString += ": "; - } - if (scope.value) - errorString += (std::string) str(scope.value); - - PyErr_NormalizeException(&scope.type, &scope.value, &scope.trace); - -#if PY_MAJOR_VERSION >= 3 - if (scope.trace != nullptr) - PyException_SetTraceback(scope.value, scope.trace); -#endif - -#if !defined(PYPY_VERSION) - if (scope.trace) { - PyTracebackObject *trace = (PyTracebackObject *) scope.trace; - - /* Get the deepest trace possible */ - while (trace->tb_next) - trace = trace->tb_next; - - PyFrameObject *frame = trace->tb_frame; - errorString += "\n\nAt:\n"; - while (frame) { - int lineno = PyFrame_GetLineNumber(frame); - errorString += - " " + handle(frame->f_code->co_filename).cast() + - "(" + std::to_string(lineno) + "): " + - handle(frame->f_code->co_name).cast() + "\n"; - frame = frame->f_back; - } - trace = trace->tb_next; - } -#endif - - return errorString; -} - -PYBIND11_NOINLINE inline handle get_object_handle(const void *ptr, const detail::type_info *type ) { - auto &instances = get_internals().registered_instances; - auto range = instances.equal_range(ptr); - for (auto it = range.first; it != range.second; ++it) { - for (auto vh : values_and_holders(it->second)) { - if (vh.type == type) - return handle((PyObject *) it->second); - } - } - return handle(); -} - -inline PyThreadState *get_thread_state_unchecked() { -#if defined(PYPY_VERSION) - return PyThreadState_GET(); -#elif PY_VERSION_HEX < 0x03000000 - return _PyThreadState_Current; -#elif PY_VERSION_HEX < 0x03050000 - return (PyThreadState*) _Py_atomic_load_relaxed(&_PyThreadState_Current); -#elif PY_VERSION_HEX < 0x03050200 - return (PyThreadState*) _PyThreadState_Current.value; -#else - return _PyThreadState_UncheckedGet(); -#endif -} - -// Forward declarations -inline void keep_alive_impl(handle nurse, handle patient); -inline PyObject *make_new_instance(PyTypeObject *type, bool allocate_value = true); - -class type_caster_generic { -public: - PYBIND11_NOINLINE type_caster_generic(const std::type_info &type_info) - : typeinfo(get_type_info(type_info)) { } - - bool load(handle src, bool convert) { - return load_impl(src, convert); - } - - PYBIND11_NOINLINE static handle cast(const void *_src, return_value_policy policy, handle parent, - const detail::type_info *tinfo, - void *(*copy_constructor)(const void *), - void *(*move_constructor)(const void *), - const void *existing_holder = nullptr) { - if (!tinfo) // no type info: error will be set already - return handle(); - - void *src = const_cast(_src); - if (src == nullptr) - return none().release(); - - auto it_instances = get_internals().registered_instances.equal_range(src); - for (auto it_i = it_instances.first; it_i != it_instances.second; ++it_i) { - for (auto instance_type : detail::all_type_info(Py_TYPE(it_i->second))) { - if (instance_type && instance_type == tinfo) - return handle((PyObject *) it_i->second).inc_ref(); - } - } - - auto inst = reinterpret_steal(make_new_instance(tinfo->type, false /* don't allocate value */)); - auto wrapper = reinterpret_cast(inst.ptr()); - wrapper->owned = false; - void *&valueptr = values_and_holders(wrapper).begin()->value_ptr(); - - switch (policy) { - case return_value_policy::automatic: - case return_value_policy::take_ownership: - valueptr = src; - wrapper->owned = true; - break; - - case return_value_policy::automatic_reference: - case return_value_policy::reference: - valueptr = src; - wrapper->owned = false; - break; - - case return_value_policy::copy: - if (copy_constructor) - valueptr = copy_constructor(src); - else - throw cast_error("return_value_policy = copy, but the " - "object is non-copyable!"); - wrapper->owned = true; - break; - - case return_value_policy::move: - if (move_constructor) - valueptr = move_constructor(src); - else if (copy_constructor) - valueptr = copy_constructor(src); - else - throw cast_error("return_value_policy = move, but the " - "object is neither movable nor copyable!"); - wrapper->owned = true; - break; - - case return_value_policy::reference_internal: - valueptr = src; - wrapper->owned = false; - keep_alive_impl(inst, parent); - break; - - default: - throw cast_error("unhandled return_value_policy: should not happen!"); - } - - tinfo->init_instance(wrapper, existing_holder); - - return inst.release(); - } - -protected: - - // Base methods for generic caster; there are overridden in copyable_holder_caster - void load_value(const value_and_holder &v_h) { - value = v_h.value_ptr(); - } - bool try_implicit_casts(handle src, bool convert) { - for (auto &cast : typeinfo->implicit_casts) { - type_caster_generic sub_caster(*cast.first); - if (sub_caster.load(src, convert)) { - value = cast.second(sub_caster.value); - return true; - } - } - return false; - } - bool try_direct_conversions(handle src) { - for (auto &converter : *typeinfo->direct_conversions) { - if (converter(src.ptr(), value)) - return true; - } - return false; - } - void check_holder_compat() {} - - // Implementation of `load`; this takes the type of `this` so that it can dispatch the relevant - // bits of code between here and copyable_holder_caster where the two classes need different - // logic (without having to resort to virtual inheritance). - template - PYBIND11_NOINLINE bool load_impl(handle src, bool convert) { - if (!src || !typeinfo) - return false; - if (src.is_none()) { - // Defer accepting None to other overloads (if we aren't in convert mode): - if (!convert) return false; - value = nullptr; - return true; - } - - auto &this_ = static_cast(*this); - this_.check_holder_compat(); - - PyTypeObject *srctype = Py_TYPE(src.ptr()); - - // Case 1: If src is an exact type match for the target type then we can reinterpret_cast - // the instance's value pointer to the target type: - if (srctype == typeinfo->type) { - this_.load_value(reinterpret_cast(src.ptr())->get_value_and_holder()); - return true; - } - // Case 2: We have a derived class - else if (PyType_IsSubtype(srctype, typeinfo->type)) { - auto &bases = all_type_info(srctype); - bool no_cpp_mi = typeinfo->simple_type; - - // Case 2a: the python type is a Python-inherited derived class that inherits from just - // one simple (no MI) pybind11 class, or is an exact match, so the C++ instance is of - // the right type and we can use reinterpret_cast. - // (This is essentially the same as case 2b, but because not using multiple inheritance - // is extremely common, we handle it specially to avoid the loop iterator and type - // pointer lookup overhead) - if (bases.size() == 1 && (no_cpp_mi || bases.front()->type == typeinfo->type)) { - this_.load_value(reinterpret_cast(src.ptr())->get_value_and_holder()); - return true; - } - // Case 2b: the python type inherits from multiple C++ bases. Check the bases to see if - // we can find an exact match (or, for a simple C++ type, an inherited match); if so, we - // can safely reinterpret_cast to the relevant pointer. - else if (bases.size() > 1) { - for (auto base : bases) { - if (no_cpp_mi ? PyType_IsSubtype(base->type, typeinfo->type) : base->type == typeinfo->type) { - this_.load_value(reinterpret_cast(src.ptr())->get_value_and_holder(base)); - return true; - } - } - } - - // Case 2c: C++ multiple inheritance is involved and we couldn't find an exact type match - // in the registered bases, above, so try implicit casting (needed for proper C++ casting - // when MI is involved). - if (this_.try_implicit_casts(src, convert)) - return true; - } - - // Perform an implicit conversion - if (convert) { - for (auto &converter : typeinfo->implicit_conversions) { - auto temp = reinterpret_steal(converter(src.ptr(), typeinfo->type)); - if (load_impl(temp, false)) { - loader_life_support::add_patient(temp); - return true; - } - } - if (this_.try_direct_conversions(src)) - return true; - } - return false; - } - - - // Called to do type lookup and wrap the pointer and type in a pair when a dynamic_cast - // isn't needed or can't be used. If the type is unknown, sets the error and returns a pair - // with .second = nullptr. (p.first = nullptr is not an error: it becomes None). - PYBIND11_NOINLINE static std::pair src_and_type( - const void *src, const std::type_info &cast_type, const std::type_info *rtti_type = nullptr) { - auto &internals = get_internals(); - auto it = internals.registered_types_cpp.find(std::type_index(cast_type)); - if (it != internals.registered_types_cpp.end()) - return {src, (const type_info *) it->second}; - - // Not found, set error: - std::string tname = rtti_type ? rtti_type->name() : cast_type.name(); - detail::clean_type_id(tname); - std::string msg = "Unregistered type : " + tname; - PyErr_SetString(PyExc_TypeError, msg.c_str()); - return {nullptr, nullptr}; - } - - const type_info *typeinfo = nullptr; - void *value = nullptr; -}; - -/** - * Determine suitable casting operator for pointer-or-lvalue-casting type casters. The type caster - * needs to provide `operator T*()` and `operator T&()` operators. - * - * If the type supports moving the value away via an `operator T&&() &&` method, it should use - * `movable_cast_op_type` instead. - */ -template -using cast_op_type = - conditional_t>::value, - typename std::add_pointer>::type, - typename std::add_lvalue_reference>::type>; - -/** - * Determine suitable casting operator for a type caster with a movable value. Such a type caster - * needs to provide `operator T*()`, `operator T&()`, and `operator T&&() &&`. The latter will be - * called in appropriate contexts where the value can be moved rather than copied. - * - * These operator are automatically provided when using the PYBIND11_TYPE_CASTER macro. - */ -template -using movable_cast_op_type = - conditional_t::type>::value, - typename std::add_pointer>::type, - conditional_t::value, - typename std::add_rvalue_reference>::type, - typename std::add_lvalue_reference>::type>>; - -// std::is_copy_constructible isn't quite enough: it lets std::vector (and similar) through when -// T is non-copyable, but code containing such a copy constructor fails to actually compile. -template struct is_copy_constructible : std::is_copy_constructible {}; - -// Specialization for types that appear to be copy constructible but also look like stl containers -// (we specifically check for: has `value_type` and `reference` with `reference = value_type&`): if -// so, copy constructability depends on whether the value_type is copy constructible. -template struct is_copy_constructible, - std::is_same - >::value>> : is_copy_constructible {}; - -#if !defined(PYBIND11_CPP17) -// Likewise for std::pair before C++17 (which mandates that the copy constructor not exist when the -// two types aren't themselves copy constructible). -template struct is_copy_constructible> - : all_of, is_copy_constructible> {}; -#endif - -/// Generic type caster for objects stored on the heap -template class type_caster_base : public type_caster_generic { - using itype = intrinsic_t; -public: - static PYBIND11_DESCR name() { return type_descr(_()); } - - type_caster_base() : type_caster_base(typeid(type)) { } - explicit type_caster_base(const std::type_info &info) : type_caster_generic(info) { } - - static handle cast(const itype &src, return_value_policy policy, handle parent) { - if (policy == return_value_policy::automatic || policy == return_value_policy::automatic_reference) - policy = return_value_policy::copy; - return cast(&src, policy, parent); - } - - static handle cast(itype &&src, return_value_policy, handle parent) { - return cast(&src, return_value_policy::move, parent); - } - - // Returns a (pointer, type_info) pair taking care of necessary RTTI type lookup for a - // polymorphic type. If the instance isn't derived, returns the non-RTTI base version. - template ::value, int> = 0> - static std::pair src_and_type(const itype *src) { - const void *vsrc = src; - auto &internals = get_internals(); - auto &cast_type = typeid(itype); - const std::type_info *instance_type = nullptr; - if (vsrc) { - instance_type = &typeid(*src); - if (!same_type(cast_type, *instance_type)) { - // This is a base pointer to a derived type; if it is a pybind11-registered type, we - // can get the correct derived pointer (which may be != base pointer) by a - // dynamic_cast to most derived type: - auto it = internals.registered_types_cpp.find(std::type_index(*instance_type)); - if (it != internals.registered_types_cpp.end()) - return {dynamic_cast(src), (const type_info *) it->second}; - } - } - // Otherwise we have either a nullptr, an `itype` pointer, or an unknown derived pointer, so - // don't do a cast - return type_caster_generic::src_and_type(vsrc, cast_type, instance_type); - } - - // Non-polymorphic type, so no dynamic casting; just call the generic version directly - template ::value, int> = 0> - static std::pair src_and_type(const itype *src) { - return type_caster_generic::src_and_type(src, typeid(itype)); - } - - static handle cast(const itype *src, return_value_policy policy, handle parent) { - auto st = src_and_type(src); - return type_caster_generic::cast( - st.first, policy, parent, st.second, - make_copy_constructor(src), make_move_constructor(src)); - } - - static handle cast_holder(const itype *src, const void *holder) { - auto st = src_and_type(src); - return type_caster_generic::cast( - st.first, return_value_policy::take_ownership, {}, st.second, - nullptr, nullptr, holder); - } - - template using cast_op_type = cast_op_type; - - operator itype*() { return (type *) value; } - operator itype&() { if (!value) throw reference_cast_error(); return *((itype *) value); } - -protected: - using Constructor = void *(*)(const void *); - - /* Only enabled when the types are {copy,move}-constructible *and* when the type - does not have a private operator new implementation. */ - template ::value>> - static auto make_copy_constructor(const T *x) -> decltype(new T(*x), Constructor{}) { - return [](const void *arg) -> void * { - return new T(*reinterpret_cast(arg)); - }; - } - - template ::value>> - static auto make_move_constructor(const T *x) -> decltype(new T(std::move(*const_cast(x))), Constructor{}) { - return [](const void *arg) -> void * { - return new T(std::move(*const_cast(reinterpret_cast(arg)))); - }; - } - - static Constructor make_copy_constructor(...) { return nullptr; } - static Constructor make_move_constructor(...) { return nullptr; } -}; - -template class type_caster : public type_caster_base { }; -template using make_caster = type_caster>; - -// Shortcut for calling a caster's `cast_op_type` cast operator for casting a type_caster to a T -template typename make_caster::template cast_op_type cast_op(make_caster &caster) { - return caster.operator typename make_caster::template cast_op_type(); -} -template typename make_caster::template cast_op_type::type> -cast_op(make_caster &&caster) { - return std::move(caster).operator - typename make_caster::template cast_op_type::type>(); -} - -template class type_caster> { -private: - using caster_t = make_caster; - caster_t subcaster; - using subcaster_cast_op_type = typename caster_t::template cast_op_type; - static_assert(std::is_same::type &, subcaster_cast_op_type>::value, - "std::reference_wrapper caster requires T to have a caster with an `T &` operator"); -public: - bool load(handle src, bool convert) { return subcaster.load(src, convert); } - static PYBIND11_DESCR name() { return caster_t::name(); } - static handle cast(const std::reference_wrapper &src, return_value_policy policy, handle parent) { - // It is definitely wrong to take ownership of this pointer, so mask that rvp - if (policy == return_value_policy::take_ownership || policy == return_value_policy::automatic) - policy = return_value_policy::automatic_reference; - return caster_t::cast(&src.get(), policy, parent); - } - template using cast_op_type = std::reference_wrapper; - operator std::reference_wrapper() { return subcaster.operator subcaster_cast_op_type&(); } -}; - -#define PYBIND11_TYPE_CASTER(type, py_name) \ - protected: \ - type value; \ - public: \ - static PYBIND11_DESCR name() { return type_descr(py_name); } \ - template >::value, int> = 0> \ - static handle cast(T_ *src, return_value_policy policy, handle parent) { \ - if (!src) return none().release(); \ - if (policy == return_value_policy::take_ownership) { \ - auto h = cast(std::move(*src), policy, parent); delete src; return h; \ - } else { \ - return cast(*src, policy, parent); \ - } \ - } \ - operator type*() { return &value; } \ - operator type&() { return value; } \ - operator type&&() && { return std::move(value); } \ - template using cast_op_type = pybind11::detail::movable_cast_op_type - - -template using is_std_char_type = any_of< - std::is_same, /* std::string */ - std::is_same, /* std::u16string */ - std::is_same, /* std::u32string */ - std::is_same /* std::wstring */ ->; - -template -struct type_caster::value && !is_std_char_type::value>> { - using _py_type_0 = conditional_t; - using _py_type_1 = conditional_t::value, _py_type_0, typename std::make_unsigned<_py_type_0>::type>; - using py_type = conditional_t::value, double, _py_type_1>; -public: - - bool load(handle src, bool convert) { - py_type py_value; - - if (!src) - return false; - - if (std::is_floating_point::value) { - if (convert || PyFloat_Check(src.ptr())) - py_value = (py_type) PyFloat_AsDouble(src.ptr()); - else - return false; - } else if (PyFloat_Check(src.ptr())) { - return false; - } else if (std::is_unsigned::value) { - py_value = as_unsigned(src.ptr()); - } else { // signed integer: - py_value = sizeof(T) <= sizeof(long) - ? (py_type) PyLong_AsLong(src.ptr()) - : (py_type) PYBIND11_LONG_AS_LONGLONG(src.ptr()); - } - - bool py_err = py_value == (py_type) -1 && PyErr_Occurred(); - if (py_err || (std::is_integral::value && sizeof(py_type) != sizeof(T) && - (py_value < (py_type) std::numeric_limits::min() || - py_value > (py_type) std::numeric_limits::max()))) { - bool type_error = py_err && PyErr_ExceptionMatches( -#if PY_VERSION_HEX < 0x03000000 && !defined(PYPY_VERSION) - PyExc_SystemError -#else - PyExc_TypeError -#endif - ); - PyErr_Clear(); - if (type_error && convert && PyNumber_Check(src.ptr())) { - auto tmp = reinterpret_borrow(std::is_floating_point::value - ? PyNumber_Float(src.ptr()) - : PyNumber_Long(src.ptr())); - PyErr_Clear(); - return load(tmp, false); - } - return false; - } - - value = (T) py_value; - return true; - } - - static handle cast(T src, return_value_policy /* policy */, handle /* parent */) { - if (std::is_floating_point::value) { - return PyFloat_FromDouble((double) src); - } else if (sizeof(T) <= sizeof(long)) { - if (std::is_signed::value) - return PyLong_FromLong((long) src); - else - return PyLong_FromUnsignedLong((unsigned long) src); - } else { - if (std::is_signed::value) - return PyLong_FromLongLong((long long) src); - else - return PyLong_FromUnsignedLongLong((unsigned long long) src); - } - } - - PYBIND11_TYPE_CASTER(T, _::value>("int", "float")); -}; - -template struct void_caster { -public: - bool load(handle src, bool) { - if (src && src.is_none()) - return true; - return false; - } - static handle cast(T, return_value_policy /* policy */, handle /* parent */) { - return none().inc_ref(); - } - PYBIND11_TYPE_CASTER(T, _("None")); -}; - -template <> class type_caster : public void_caster {}; - -template <> class type_caster : public type_caster { -public: - using type_caster::cast; - - bool load(handle h, bool) { - if (!h) { - return false; - } else if (h.is_none()) { - value = nullptr; - return true; - } - - /* Check if this is a capsule */ - if (isinstance(h)) { - value = reinterpret_borrow(h); - return true; - } - - /* Check if this is a C++ type */ - auto &bases = all_type_info((PyTypeObject *) h.get_type().ptr()); - if (bases.size() == 1) { // Only allowing loading from a single-value type - value = values_and_holders(reinterpret_cast(h.ptr())).begin()->value_ptr(); - return true; - } - - /* Fail */ - return false; - } - - static handle cast(const void *ptr, return_value_policy /* policy */, handle /* parent */) { - if (ptr) - return capsule(ptr).release(); - else - return none().inc_ref(); - } - - template using cast_op_type = void*&; - operator void *&() { return value; } - static PYBIND11_DESCR name() { return type_descr(_("capsule")); } -private: - void *value = nullptr; -}; - -template <> class type_caster : public void_caster { }; - -template <> class type_caster { -public: - bool load(handle src, bool convert) { - if (!src) return false; - else if (src.ptr() == Py_True) { value = true; return true; } - else if (src.ptr() == Py_False) { value = false; return true; } - else if (convert || !strcmp("numpy.bool_", Py_TYPE(src.ptr())->tp_name)) { - // (allow non-implicit conversion for numpy booleans) - - Py_ssize_t res = -1; - if (src.is_none()) { - res = 0; // None is implicitly converted to False - } - #if defined(PYPY_VERSION) - // On PyPy, check that "__bool__" (or "__nonzero__" on Python 2.7) attr exists - else if (hasattr(src, PYBIND11_BOOL_ATTR)) { - res = PyObject_IsTrue(src.ptr()); - } - #else - // Alternate approach for CPython: this does the same as the above, but optimized - // using the CPython API so as to avoid an unneeded attribute lookup. - else if (auto tp_as_number = src.ptr()->ob_type->tp_as_number) { - if (PYBIND11_NB_BOOL(tp_as_number)) { - res = (*PYBIND11_NB_BOOL(tp_as_number))(src.ptr()); - } - } - #endif - if (res == 0 || res == 1) { - value = (bool) res; - return true; - } - } - return false; - } - static handle cast(bool src, return_value_policy /* policy */, handle /* parent */) { - return handle(src ? Py_True : Py_False).inc_ref(); - } - PYBIND11_TYPE_CASTER(bool, _("bool")); -}; - -// Helper class for UTF-{8,16,32} C++ stl strings: -template struct string_caster { - using CharT = typename StringType::value_type; - - // Simplify life by being able to assume standard char sizes (the standard only guarantees - // minimums, but Python requires exact sizes) - static_assert(!std::is_same::value || sizeof(CharT) == 1, "Unsupported char size != 1"); - static_assert(!std::is_same::value || sizeof(CharT) == 2, "Unsupported char16_t size != 2"); - static_assert(!std::is_same::value || sizeof(CharT) == 4, "Unsupported char32_t size != 4"); - // wchar_t can be either 16 bits (Windows) or 32 (everywhere else) - static_assert(!std::is_same::value || sizeof(CharT) == 2 || sizeof(CharT) == 4, - "Unsupported wchar_t size != 2/4"); - static constexpr size_t UTF_N = 8 * sizeof(CharT); - - bool load(handle src, bool) { -#if PY_MAJOR_VERSION < 3 - object temp; -#endif - handle load_src = src; - if (!src) { - return false; - } else if (!PyUnicode_Check(load_src.ptr())) { -#if PY_MAJOR_VERSION >= 3 - return load_bytes(load_src); -#else - if (sizeof(CharT) == 1) { - return load_bytes(load_src); - } - - // The below is a guaranteed failure in Python 3 when PyUnicode_Check returns false - if (!PYBIND11_BYTES_CHECK(load_src.ptr())) - return false; - - temp = reinterpret_steal(PyUnicode_FromObject(load_src.ptr())); - if (!temp) { PyErr_Clear(); return false; } - load_src = temp; -#endif - } - - object utfNbytes = reinterpret_steal(PyUnicode_AsEncodedString( - load_src.ptr(), UTF_N == 8 ? "utf-8" : UTF_N == 16 ? "utf-16" : "utf-32", nullptr)); - if (!utfNbytes) { PyErr_Clear(); return false; } - - const CharT *buffer = reinterpret_cast(PYBIND11_BYTES_AS_STRING(utfNbytes.ptr())); - size_t length = (size_t) PYBIND11_BYTES_SIZE(utfNbytes.ptr()) / sizeof(CharT); - if (UTF_N > 8) { buffer++; length--; } // Skip BOM for UTF-16/32 - value = StringType(buffer, length); - - // If we're loading a string_view we need to keep the encoded Python object alive: - if (IsView) - loader_life_support::add_patient(utfNbytes); - - return true; - } - - static handle cast(const StringType &src, return_value_policy /* policy */, handle /* parent */) { - const char *buffer = reinterpret_cast(src.data()); - ssize_t nbytes = ssize_t(src.size() * sizeof(CharT)); - handle s = decode_utfN(buffer, nbytes); - if (!s) throw error_already_set(); - return s; - } - - PYBIND11_TYPE_CASTER(StringType, _(PYBIND11_STRING_NAME)); - -private: - static handle decode_utfN(const char *buffer, ssize_t nbytes) { -#if !defined(PYPY_VERSION) - return - UTF_N == 8 ? PyUnicode_DecodeUTF8(buffer, nbytes, nullptr) : - UTF_N == 16 ? PyUnicode_DecodeUTF16(buffer, nbytes, nullptr, nullptr) : - PyUnicode_DecodeUTF32(buffer, nbytes, nullptr, nullptr); -#else - // PyPy seems to have multiple problems related to PyUnicode_UTF*: the UTF8 version - // sometimes segfaults for unknown reasons, while the UTF16 and 32 versions require a - // non-const char * arguments, which is also a nuissance, so bypass the whole thing by just - // passing the encoding as a string value, which works properly: - return PyUnicode_Decode(buffer, nbytes, UTF_N == 8 ? "utf-8" : UTF_N == 16 ? "utf-16" : "utf-32", nullptr); -#endif - } - - // When loading into a std::string or char*, accept a bytes object as-is (i.e. - // without any encoding/decoding attempt). For other C++ char sizes this is a no-op. - // which supports loading a unicode from a str, doesn't take this path. - template - bool load_bytes(enable_if_t src) { - if (PYBIND11_BYTES_CHECK(src.ptr())) { - // We were passed a Python 3 raw bytes; accept it into a std::string or char* - // without any encoding attempt. - const char *bytes = PYBIND11_BYTES_AS_STRING(src.ptr()); - if (bytes) { - value = StringType(bytes, (size_t) PYBIND11_BYTES_SIZE(src.ptr())); - return true; - } - } - - return false; - } - - template - bool load_bytes(enable_if_t) { return false; } -}; - -template -struct type_caster, enable_if_t::value>> - : string_caster> {}; - -#ifdef PYBIND11_HAS_STRING_VIEW -template -struct type_caster, enable_if_t::value>> - : string_caster, true> {}; -#endif - -// Type caster for C-style strings. We basically use a std::string type caster, but also add the -// ability to use None as a nullptr char* (which the string caster doesn't allow). -template struct type_caster::value>> { - using StringType = std::basic_string; - using StringCaster = type_caster; - StringCaster str_caster; - bool none = false; -public: - bool load(handle src, bool convert) { - if (!src) return false; - if (src.is_none()) { - // Defer accepting None to other overloads (if we aren't in convert mode): - if (!convert) return false; - none = true; - return true; - } - return str_caster.load(src, convert); - } - - static handle cast(const CharT *src, return_value_policy policy, handle parent) { - if (src == nullptr) return pybind11::none().inc_ref(); - return StringCaster::cast(StringType(src), policy, parent); - } - - static handle cast(CharT src, return_value_policy policy, handle parent) { - if (std::is_same::value) { - handle s = PyUnicode_DecodeLatin1((const char *) &src, 1, nullptr); - if (!s) throw error_already_set(); - return s; - } - return StringCaster::cast(StringType(1, src), policy, parent); - } - - operator CharT*() { return none ? nullptr : const_cast(static_cast(str_caster).c_str()); } - operator CharT() { - if (none) - throw value_error("Cannot convert None to a character"); - - auto &value = static_cast(str_caster); - size_t str_len = value.size(); - if (str_len == 0) - throw value_error("Cannot convert empty string to a character"); - - // If we're in UTF-8 mode, we have two possible failures: one for a unicode character that - // is too high, and one for multiple unicode characters (caught later), so we need to figure - // out how long the first encoded character is in bytes to distinguish between these two - // errors. We also allow want to allow unicode characters U+0080 through U+00FF, as those - // can fit into a single char value. - if (StringCaster::UTF_N == 8 && str_len > 1 && str_len <= 4) { - unsigned char v0 = static_cast(value[0]); - size_t char0_bytes = !(v0 & 0x80) ? 1 : // low bits only: 0-127 - (v0 & 0xE0) == 0xC0 ? 2 : // 0b110xxxxx - start of 2-byte sequence - (v0 & 0xF0) == 0xE0 ? 3 : // 0b1110xxxx - start of 3-byte sequence - 4; // 0b11110xxx - start of 4-byte sequence - - if (char0_bytes == str_len) { - // If we have a 128-255 value, we can decode it into a single char: - if (char0_bytes == 2 && (v0 & 0xFC) == 0xC0) { // 0x110000xx 0x10xxxxxx - return static_cast(((v0 & 3) << 6) + (static_cast(value[1]) & 0x3F)); - } - // Otherwise we have a single character, but it's > U+00FF - throw value_error("Character code point not in range(0x100)"); - } - } - - // UTF-16 is much easier: we can only have a surrogate pair for values above U+FFFF, thus a - // surrogate pair with total length 2 instantly indicates a range error (but not a "your - // string was too long" error). - else if (StringCaster::UTF_N == 16 && str_len == 2) { - char16_t v0 = static_cast(value[0]); - if (v0 >= 0xD800 && v0 < 0xE000) - throw value_error("Character code point not in range(0x10000)"); - } - - if (str_len != 1) - throw value_error("Expected a character, but multi-character string found"); - - return value[0]; - } - - static PYBIND11_DESCR name() { return type_descr(_(PYBIND11_STRING_NAME)); } - template using cast_op_type = remove_reference_t>; -}; - -// Base implementation for std::tuple and std::pair -template class Tuple, typename... Ts> class tuple_caster { - using type = Tuple; - static constexpr auto size = sizeof...(Ts); - using indices = make_index_sequence; -public: - - bool load(handle src, bool convert) { - if (!isinstance(src)) - return false; - const auto seq = reinterpret_borrow(src); - if (seq.size() != size) - return false; - return load_impl(seq, convert, indices{}); - } - - template - static handle cast(T &&src, return_value_policy policy, handle parent) { - return cast_impl(std::forward(src), policy, parent, indices{}); - } - - static PYBIND11_DESCR name() { - return type_descr(_("Tuple[") + detail::concat(make_caster::name()...) + _("]")); - } - - template using cast_op_type = type; - - operator type() & { return implicit_cast(indices{}); } - operator type() && { return std::move(*this).implicit_cast(indices{}); } - -protected: - template - type implicit_cast(index_sequence) & { return type(cast_op(std::get(subcasters))...); } - template - type implicit_cast(index_sequence) && { return type(cast_op(std::move(std::get(subcasters)))...); } - - static constexpr bool load_impl(const sequence &, bool, index_sequence<>) { return true; } - - template - bool load_impl(const sequence &seq, bool convert, index_sequence) { - for (bool r : {std::get(subcasters).load(seq[Is], convert)...}) - if (!r) - return false; - return true; - } - - /* Implementation: Convert a C++ tuple into a Python tuple */ - template - static handle cast_impl(T &&src, return_value_policy policy, handle parent, index_sequence) { - std::array entries{{ - reinterpret_steal(make_caster::cast(std::get(std::forward(src)), policy, parent))... - }}; - for (const auto &entry: entries) - if (!entry) - return handle(); - tuple result(size); - int counter = 0; - for (auto & entry: entries) - PyTuple_SET_ITEM(result.ptr(), counter++, entry.release().ptr()); - return result.release(); - } - - Tuple...> subcasters; -}; - -template class type_caster> - : public tuple_caster {}; - -template class type_caster> - : public tuple_caster {}; - -/// Helper class which abstracts away certain actions. Users can provide specializations for -/// custom holders, but it's only necessary if the type has a non-standard interface. -template -struct holder_helper { - static auto get(const T &p) -> decltype(p.get()) { return p.get(); } -}; - -/// Type caster for holder types like std::shared_ptr, etc. -template -struct copyable_holder_caster : public type_caster_base { -public: - using base = type_caster_base; - static_assert(std::is_base_of>::value, - "Holder classes are only supported for custom types"); - using base::base; - using base::cast; - using base::typeinfo; - using base::value; - - bool load(handle src, bool convert) { - return base::template load_impl>(src, convert); - } - - explicit operator type*() { return this->value; } - explicit operator type&() { return *(this->value); } - explicit operator holder_type*() { return &holder; } - - // Workaround for Intel compiler bug - // see pybind11 issue 94 - #if defined(__ICC) || defined(__INTEL_COMPILER) - operator holder_type&() { return holder; } - #else - explicit operator holder_type&() { return holder; } - #endif - - static handle cast(const holder_type &src, return_value_policy, handle) { - const auto *ptr = holder_helper::get(src); - return type_caster_base::cast_holder(ptr, &src); - } - -protected: - friend class type_caster_generic; - void check_holder_compat() { - if (typeinfo->default_holder) - throw cast_error("Unable to load a custom holder type from a default-holder instance"); - } - - bool load_value(const value_and_holder &v_h) { - if (v_h.holder_constructed()) { - value = v_h.value_ptr(); - holder = v_h.holder(); - return true; - } else { - throw cast_error("Unable to cast from non-held to held instance (T& to Holder) " -#if defined(NDEBUG) - "(compile in debug mode for type information)"); -#else - "of type '" + type_id() + "''"); -#endif - } - } - - template ::value, int> = 0> - bool try_implicit_casts(handle, bool) { return false; } - - template ::value, int> = 0> - bool try_implicit_casts(handle src, bool convert) { - for (auto &cast : typeinfo->implicit_casts) { - copyable_holder_caster sub_caster(*cast.first); - if (sub_caster.load(src, convert)) { - value = cast.second(sub_caster.value); - holder = holder_type(sub_caster.holder, (type *) value); - return true; - } - } - return false; - } - - static bool try_direct_conversions(handle) { return false; } - - - holder_type holder; -}; - -/// Specialize for the common std::shared_ptr, so users don't need to -template -class type_caster> : public copyable_holder_caster> { }; - -template -struct move_only_holder_caster { - static_assert(std::is_base_of, type_caster>::value, - "Holder classes are only supported for custom types"); - - static handle cast(holder_type &&src, return_value_policy, handle) { - auto *ptr = holder_helper::get(src); - return type_caster_base::cast_holder(ptr, &src); - } - static PYBIND11_DESCR name() { return type_caster_base::name(); } -}; - -template -class type_caster> - : public move_only_holder_caster> { }; - -template -using type_caster_holder = conditional_t::value, - copyable_holder_caster, - move_only_holder_caster>; - -template struct always_construct_holder { static constexpr bool value = Value; }; - -/// Create a specialization for custom holder types (silently ignores std::shared_ptr) -#define PYBIND11_DECLARE_HOLDER_TYPE(type, holder_type, ...) \ - namespace pybind11 { namespace detail { \ - template \ - struct always_construct_holder : always_construct_holder { }; \ - template \ - class type_caster::value>> \ - : public type_caster_holder { }; \ - }} - -// PYBIND11_DECLARE_HOLDER_TYPE holder types: -template struct is_holder_type : - std::is_base_of, detail::type_caster> {}; -// Specialization for always-supported unique_ptr holders: -template struct is_holder_type> : - std::true_type {}; - -template struct handle_type_name { static PYBIND11_DESCR name() { return _(); } }; -template <> struct handle_type_name { static PYBIND11_DESCR name() { return _(PYBIND11_BYTES_NAME); } }; -template <> struct handle_type_name { static PYBIND11_DESCR name() { return _("*args"); } }; -template <> struct handle_type_name { static PYBIND11_DESCR name() { return _("**kwargs"); } }; - -template -struct pyobject_caster { - template ::value, int> = 0> - bool load(handle src, bool /* convert */) { value = src; return static_cast(value); } - - template ::value, int> = 0> - bool load(handle src, bool /* convert */) { - if (!isinstance(src)) - return false; - value = reinterpret_borrow(src); - return true; - } - - static handle cast(const handle &src, return_value_policy /* policy */, handle /* parent */) { - return src.inc_ref(); - } - PYBIND11_TYPE_CASTER(type, handle_type_name::name()); -}; - -template -class type_caster::value>> : public pyobject_caster { }; - -// Our conditions for enabling moving are quite restrictive: -// At compile time: -// - T needs to be a non-const, non-pointer, non-reference type -// - type_caster::operator T&() must exist -// - the type must be move constructible (obviously) -// At run-time: -// - if the type is non-copy-constructible, the object must be the sole owner of the type (i.e. it -// must have ref_count() == 1)h -// If any of the above are not satisfied, we fall back to copying. -template using move_is_plain_type = satisfies_none_of; -template struct move_always : std::false_type {}; -template struct move_always, - negation>, - std::is_move_constructible, - std::is_same>().operator T&()), T&> ->::value>> : std::true_type {}; -template struct move_if_unreferenced : std::false_type {}; -template struct move_if_unreferenced, - negation>, - std::is_move_constructible, - std::is_same>().operator T&()), T&> ->::value>> : std::true_type {}; -template using move_never = none_of, move_if_unreferenced>; - -// Detect whether returning a `type` from a cast on type's type_caster is going to result in a -// reference or pointer to a local variable of the type_caster. Basically, only -// non-reference/pointer `type`s and reference/pointers from a type_caster_generic are safe; -// everything else returns a reference/pointer to a local variable. -template using cast_is_temporary_value_reference = bool_constant< - (std::is_reference::value || std::is_pointer::value) && - !std::is_base_of>::value ->; - -// When a value returned from a C++ function is being cast back to Python, we almost always want to -// force `policy = move`, regardless of the return value policy the function/method was declared -// with. Some classes (most notably Eigen::Ref and related) need to avoid this, and so can do so by -// specializing this struct. -template struct return_value_policy_override { - static return_value_policy policy(return_value_policy p) { - return !std::is_lvalue_reference::value && !std::is_pointer::value - ? return_value_policy::move : p; - } -}; - -// Basic python -> C++ casting; throws if casting fails -template type_caster &load_type(type_caster &conv, const handle &handle) { - if (!conv.load(handle, true)) { -#if defined(NDEBUG) - throw cast_error("Unable to cast Python instance to C++ type (compile in debug mode for details)"); -#else - throw cast_error("Unable to cast Python instance of type " + - (std::string) str(handle.get_type()) + " to C++ type '" + type_id() + "''"); -#endif - } - return conv; -} -// Wrapper around the above that also constructs and returns a type_caster -template make_caster load_type(const handle &handle) { - make_caster conv; - load_type(conv, handle); - return conv; -} - -NAMESPACE_END(detail) - -// pytype -> C++ type -template ::value, int> = 0> -T cast(const handle &handle) { - using namespace detail; - static_assert(!cast_is_temporary_value_reference::value, - "Unable to cast type to reference: value is local to type caster"); - return cast_op(load_type(handle)); -} - -// pytype -> pytype (calls converting constructor) -template ::value, int> = 0> -T cast(const handle &handle) { return T(reinterpret_borrow(handle)); } - -// C++ type -> py::object -template ::value, int> = 0> -object cast(const T &value, return_value_policy policy = return_value_policy::automatic_reference, - handle parent = handle()) { - if (policy == return_value_policy::automatic) - policy = std::is_pointer::value ? return_value_policy::take_ownership : return_value_policy::copy; - else if (policy == return_value_policy::automatic_reference) - policy = std::is_pointer::value ? return_value_policy::reference : return_value_policy::copy; - return reinterpret_steal(detail::make_caster::cast(value, policy, parent)); -} - -template T handle::cast() const { return pybind11::cast(*this); } -template <> inline void handle::cast() const { return; } - -template -detail::enable_if_t::value, T> move(object &&obj) { - if (obj.ref_count() > 1) -#if defined(NDEBUG) - throw cast_error("Unable to cast Python instance to C++ rvalue: instance has multiple references" - " (compile in debug mode for details)"); -#else - throw cast_error("Unable to move from Python " + (std::string) str(obj.get_type()) + - " instance to C++ " + type_id() + " instance: instance has multiple references"); -#endif - - // Move into a temporary and return that, because the reference may be a local value of `conv` - T ret = std::move(detail::load_type(obj).operator T&()); - return ret; -} - -// Calling cast() on an rvalue calls pybind::cast with the object rvalue, which does: -// - If we have to move (because T has no copy constructor), do it. This will fail if the moved -// object has multiple references, but trying to copy will fail to compile. -// - If both movable and copyable, check ref count: if 1, move; otherwise copy -// - Otherwise (not movable), copy. -template detail::enable_if_t::value, T> cast(object &&object) { - return move(std::move(object)); -} -template detail::enable_if_t::value, T> cast(object &&object) { - if (object.ref_count() > 1) - return cast(object); - else - return move(std::move(object)); -} -template detail::enable_if_t::value, T> cast(object &&object) { - return cast(object); -} - -template T object::cast() const & { return pybind11::cast(*this); } -template T object::cast() && { return pybind11::cast(std::move(*this)); } -template <> inline void object::cast() const & { return; } -template <> inline void object::cast() && { return; } - -NAMESPACE_BEGIN(detail) - -// Declared in pytypes.h: -template ::value, int>> -object object_or_cast(T &&o) { return pybind11::cast(std::forward(o)); } - -struct overload_unused {}; // Placeholder type for the unneeded (and dead code) static variable in the OVERLOAD_INT macro -template using overload_caster_t = conditional_t< - cast_is_temporary_value_reference::value, make_caster, overload_unused>; - -// Trampoline use: for reference/pointer types to value-converted values, we do a value cast, then -// store the result in the given variable. For other types, this is a no-op. -template enable_if_t::value, T> cast_ref(object &&o, make_caster &caster) { - return cast_op(load_type(caster, o)); -} -template enable_if_t::value, T> cast_ref(object &&, overload_unused &) { - pybind11_fail("Internal error: cast_ref fallback invoked"); } - -// Trampoline use: Having a pybind11::cast with an invalid reference type is going to static_assert, even -// though if it's in dead code, so we provide a "trampoline" to pybind11::cast that only does anything in -// cases where pybind11::cast is valid. -template enable_if_t::value, T> cast_safe(object &&o) { - return pybind11::cast(std::move(o)); } -template enable_if_t::value, T> cast_safe(object &&) { - pybind11_fail("Internal error: cast_safe fallback invoked"); } -template <> inline void cast_safe(object &&) {} - -NAMESPACE_END(detail) - -template tuple make_tuple(Args&&... args_) { - constexpr size_t size = sizeof...(Args); - std::array args { - { reinterpret_steal(detail::make_caster::cast( - std::forward(args_), policy, nullptr))... } - }; - for (size_t i = 0; i < args.size(); i++) { - if (!args[i]) { -#if defined(NDEBUG) - throw cast_error("make_tuple(): unable to convert arguments to Python object (compile in debug mode for details)"); -#else - std::array argtypes { {type_id()...} }; - throw cast_error("make_tuple(): unable to convert argument of type '" + - argtypes[i] + "' to Python object"); -#endif - } - } - tuple result(size); - int counter = 0; - for (auto &arg_value : args) - PyTuple_SET_ITEM(result.ptr(), counter++, arg_value.release().ptr()); - return result; -} - -/// \ingroup annotations -/// Annotation for arguments -struct arg { - /// Constructs an argument with the name of the argument; if null or omitted, this is a positional argument. - constexpr explicit arg(const char *name = nullptr) : name(name), flag_noconvert(false), flag_none(true) { } - /// Assign a value to this argument - template arg_v operator=(T &&value) const; - /// Indicate that the type should not be converted in the type caster - arg &noconvert(bool flag = true) { flag_noconvert = flag; return *this; } - /// Indicates that the argument should/shouldn't allow None (e.g. for nullable pointer args) - arg &none(bool flag = true) { flag_none = flag; return *this; } - - const char *name; ///< If non-null, this is a named kwargs argument - bool flag_noconvert : 1; ///< If set, do not allow conversion (requires a supporting type caster!) - bool flag_none : 1; ///< If set (the default), allow None to be passed to this argument -}; - -/// \ingroup annotations -/// Annotation for arguments with values -struct arg_v : arg { -private: - template - arg_v(arg &&base, T &&x, const char *descr = nullptr) - : arg(base), - value(reinterpret_steal( - detail::make_caster::cast(x, return_value_policy::automatic, {}) - )), - descr(descr) -#if !defined(NDEBUG) - , type(type_id()) -#endif - { } - -public: - /// Direct construction with name, default, and description - template - arg_v(const char *name, T &&x, const char *descr = nullptr) - : arg_v(arg(name), std::forward(x), descr) { } - - /// Called internally when invoking `py::arg("a") = value` - template - arg_v(const arg &base, T &&x, const char *descr = nullptr) - : arg_v(arg(base), std::forward(x), descr) { } - - /// Same as `arg::noconvert()`, but returns *this as arg_v&, not arg& - arg_v &noconvert(bool flag = true) { arg::noconvert(flag); return *this; } - - /// Same as `arg::nonone()`, but returns *this as arg_v&, not arg& - arg_v &none(bool flag = true) { arg::none(flag); return *this; } - - /// The default value - object value; - /// The (optional) description of the default value - const char *descr; -#if !defined(NDEBUG) - /// The C++ type name of the default value (only available when compiled in debug mode) - std::string type; -#endif -}; - -template -arg_v arg::operator=(T &&value) const { return {std::move(*this), std::forward(value)}; } - -/// Alias for backward compatibility -- to be removed in version 2.0 -template using arg_t = arg_v; - -inline namespace literals { -/** \rst - String literal version of `arg` - \endrst */ -constexpr arg operator"" _a(const char *name, size_t) { return arg(name); } -} - -NAMESPACE_BEGIN(detail) - -// forward declaration (definition in attr.h) -struct function_record; - -/// Internal data associated with a single function call -struct function_call { - function_call(function_record &f, handle p); // Implementation in attr.h - - /// The function data: - const function_record &func; - - /// Arguments passed to the function: - std::vector args; - - /// The `convert` value the arguments should be loaded with - std::vector args_convert; - - /// The parent, if any - handle parent; -}; - - -/// Helper class which loads arguments for C++ functions called from Python -template -class argument_loader { - using indices = make_index_sequence; - - template using argument_is_args = std::is_same, args>; - template using argument_is_kwargs = std::is_same, kwargs>; - // Get args/kwargs argument positions relative to the end of the argument list: - static constexpr auto args_pos = constexpr_first() - (int) sizeof...(Args), - kwargs_pos = constexpr_first() - (int) sizeof...(Args); - - static constexpr bool args_kwargs_are_last = kwargs_pos >= - 1 && args_pos >= kwargs_pos - 1; - - static_assert(args_kwargs_are_last, "py::args/py::kwargs are only permitted as the last argument(s) of a function"); - -public: - static constexpr bool has_kwargs = kwargs_pos < 0; - static constexpr bool has_args = args_pos < 0; - - static PYBIND11_DESCR arg_names() { return detail::concat(make_caster::name()...); } - - bool load_args(function_call &call) { - return load_impl_sequence(call, indices{}); - } - - template - enable_if_t::value, Return> call(Func &&f) && { - return std::move(*this).template call_impl(std::forward(f), indices{}, Guard{}); - } - - template - enable_if_t::value, void_type> call(Func &&f) && { - std::move(*this).template call_impl(std::forward(f), indices{}, Guard{}); - return void_type(); - } - -private: - - static bool load_impl_sequence(function_call &, index_sequence<>) { return true; } - - template - bool load_impl_sequence(function_call &call, index_sequence) { - for (bool r : {std::get(argcasters).load(call.args[Is], call.args_convert[Is])...}) - if (!r) - return false; - return true; - } - - template - Return call_impl(Func &&f, index_sequence, Guard &&) { - return std::forward(f)(cast_op(std::move(std::get(argcasters)))...); - } - - std::tuple...> argcasters; -}; - -/// Helper class which collects only positional arguments for a Python function call. -/// A fancier version below can collect any argument, but this one is optimal for simple calls. -template -class simple_collector { -public: - template - explicit simple_collector(Ts &&...values) - : m_args(pybind11::make_tuple(std::forward(values)...)) { } - - const tuple &args() const & { return m_args; } - dict kwargs() const { return {}; } - - tuple args() && { return std::move(m_args); } - - /// Call a Python function and pass the collected arguments - object call(PyObject *ptr) const { - PyObject *result = PyObject_CallObject(ptr, m_args.ptr()); - if (!result) - throw error_already_set(); - return reinterpret_steal(result); - } - -private: - tuple m_args; -}; - -/// Helper class which collects positional, keyword, * and ** arguments for a Python function call -template -class unpacking_collector { -public: - template - explicit unpacking_collector(Ts &&...values) { - // Tuples aren't (easily) resizable so a list is needed for collection, - // but the actual function call strictly requires a tuple. - auto args_list = list(); - int _[] = { 0, (process(args_list, std::forward(values)), 0)... }; - ignore_unused(_); - - m_args = std::move(args_list); - } - - const tuple &args() const & { return m_args; } - const dict &kwargs() const & { return m_kwargs; } - - tuple args() && { return std::move(m_args); } - dict kwargs() && { return std::move(m_kwargs); } - - /// Call a Python function and pass the collected arguments - object call(PyObject *ptr) const { - PyObject *result = PyObject_Call(ptr, m_args.ptr(), m_kwargs.ptr()); - if (!result) - throw error_already_set(); - return reinterpret_steal(result); - } - -private: - template - void process(list &args_list, T &&x) { - auto o = reinterpret_steal(detail::make_caster::cast(std::forward(x), policy, {})); - if (!o) { -#if defined(NDEBUG) - argument_cast_error(); -#else - argument_cast_error(std::to_string(args_list.size()), type_id()); -#endif - } - args_list.append(o); - } - - void process(list &args_list, detail::args_proxy ap) { - for (const auto &a : ap) - args_list.append(a); - } - - void process(list &/*args_list*/, arg_v a) { - if (!a.name) -#if defined(NDEBUG) - nameless_argument_error(); -#else - nameless_argument_error(a.type); -#endif - - if (m_kwargs.contains(a.name)) { -#if defined(NDEBUG) - multiple_values_error(); -#else - multiple_values_error(a.name); -#endif - } - if (!a.value) { -#if defined(NDEBUG) - argument_cast_error(); -#else - argument_cast_error(a.name, a.type); -#endif - } - m_kwargs[a.name] = a.value; - } - - void process(list &/*args_list*/, detail::kwargs_proxy kp) { - if (!kp) - return; - for (const auto &k : reinterpret_borrow(kp)) { - if (m_kwargs.contains(k.first)) { -#if defined(NDEBUG) - multiple_values_error(); -#else - multiple_values_error(str(k.first)); -#endif - } - m_kwargs[k.first] = k.second; - } - } - - [[noreturn]] static void nameless_argument_error() { - throw type_error("Got kwargs without a name; only named arguments " - "may be passed via py::arg() to a python function call. " - "(compile in debug mode for details)"); - } - [[noreturn]] static void nameless_argument_error(std::string type) { - throw type_error("Got kwargs without a name of type '" + type + "'; only named " - "arguments may be passed via py::arg() to a python function call. "); - } - [[noreturn]] static void multiple_values_error() { - throw type_error("Got multiple values for keyword argument " - "(compile in debug mode for details)"); - } - - [[noreturn]] static void multiple_values_error(std::string name) { - throw type_error("Got multiple values for keyword argument '" + name + "'"); - } - - [[noreturn]] static void argument_cast_error() { - throw cast_error("Unable to convert call argument to Python object " - "(compile in debug mode for details)"); - } - - [[noreturn]] static void argument_cast_error(std::string name, std::string type) { - throw cast_error("Unable to convert call argument '" + name - + "' of type '" + type + "' to Python object"); - } - -private: - tuple m_args; - dict m_kwargs; -}; - -/// Collect only positional arguments for a Python function call -template ...>::value>> -simple_collector collect_arguments(Args &&...args) { - return simple_collector(std::forward(args)...); -} - -/// Collect all arguments, including keywords and unpacking (only instantiated when needed) -template ...>::value>> -unpacking_collector collect_arguments(Args &&...args) { - // Following argument order rules for generalized unpacking according to PEP 448 - static_assert( - constexpr_last() < constexpr_first() - && constexpr_last() < constexpr_first(), - "Invalid function call: positional args must precede keywords and ** unpacking; " - "* unpacking must precede ** unpacking" - ); - return unpacking_collector(std::forward(args)...); -} - -template -template -object object_api::operator()(Args &&...args) const { - return detail::collect_arguments(std::forward(args)...).call(derived().ptr()); -} - -template -template -object object_api::call(Args &&...args) const { - return operator()(std::forward(args)...); -} - -NAMESPACE_END(detail) - -#define PYBIND11_MAKE_OPAQUE(Type) \ - namespace pybind11 { namespace detail { \ - template<> class type_caster : public type_caster_base { }; \ - }} - -NAMESPACE_END(pybind11) diff --git a/ptocr/postprocess/lanms/include/pybind11/chrono.h b/ptocr/postprocess/lanms/include/pybind11/chrono.h deleted file mode 100644 index 8a41d08..0000000 --- a/ptocr/postprocess/lanms/include/pybind11/chrono.h +++ /dev/null @@ -1,162 +0,0 @@ -/* - pybind11/chrono.h: Transparent conversion between std::chrono and python's datetime - - Copyright (c) 2016 Trent Houliston and - Wenzel Jakob - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#include "pybind11.h" -#include -#include -#include -#include - -// Backport the PyDateTime_DELTA functions from Python3.3 if required -#ifndef PyDateTime_DELTA_GET_DAYS -#define PyDateTime_DELTA_GET_DAYS(o) (((PyDateTime_Delta*)o)->days) -#endif -#ifndef PyDateTime_DELTA_GET_SECONDS -#define PyDateTime_DELTA_GET_SECONDS(o) (((PyDateTime_Delta*)o)->seconds) -#endif -#ifndef PyDateTime_DELTA_GET_MICROSECONDS -#define PyDateTime_DELTA_GET_MICROSECONDS(o) (((PyDateTime_Delta*)o)->microseconds) -#endif - -NAMESPACE_BEGIN(pybind11) -NAMESPACE_BEGIN(detail) - -template class duration_caster { -public: - typedef typename type::rep rep; - typedef typename type::period period; - - typedef std::chrono::duration> days; - - bool load(handle src, bool) { - using namespace std::chrono; - - // Lazy initialise the PyDateTime import - if (!PyDateTimeAPI) { PyDateTime_IMPORT; } - - if (!src) return false; - // If invoked with datetime.delta object - if (PyDelta_Check(src.ptr())) { - value = type(duration_cast>( - days(PyDateTime_DELTA_GET_DAYS(src.ptr())) - + seconds(PyDateTime_DELTA_GET_SECONDS(src.ptr())) - + microseconds(PyDateTime_DELTA_GET_MICROSECONDS(src.ptr())))); - return true; - } - // If invoked with a float we assume it is seconds and convert - else if (PyFloat_Check(src.ptr())) { - value = type(duration_cast>(duration(PyFloat_AsDouble(src.ptr())))); - return true; - } - else return false; - } - - // If this is a duration just return it back - static const std::chrono::duration& get_duration(const std::chrono::duration &src) { - return src; - } - - // If this is a time_point get the time_since_epoch - template static std::chrono::duration get_duration(const std::chrono::time_point> &src) { - return src.time_since_epoch(); - } - - static handle cast(const type &src, return_value_policy /* policy */, handle /* parent */) { - using namespace std::chrono; - - // Use overloaded function to get our duration from our source - // Works out if it is a duration or time_point and get the duration - auto d = get_duration(src); - - // Lazy initialise the PyDateTime import - if (!PyDateTimeAPI) { PyDateTime_IMPORT; } - - // Declare these special duration types so the conversions happen with the correct primitive types (int) - using dd_t = duration>; - using ss_t = duration>; - using us_t = duration; - - auto dd = duration_cast(d); - auto subd = d - dd; - auto ss = duration_cast(subd); - auto us = duration_cast(subd - ss); - return PyDelta_FromDSU(dd.count(), ss.count(), us.count()); - } - - PYBIND11_TYPE_CASTER(type, _("datetime.timedelta")); -}; - -// This is for casting times on the system clock into datetime.datetime instances -template class type_caster> { -public: - typedef std::chrono::time_point type; - bool load(handle src, bool) { - using namespace std::chrono; - - // Lazy initialise the PyDateTime import - if (!PyDateTimeAPI) { PyDateTime_IMPORT; } - - if (!src) return false; - if (PyDateTime_Check(src.ptr())) { - std::tm cal; - cal.tm_sec = PyDateTime_DATE_GET_SECOND(src.ptr()); - cal.tm_min = PyDateTime_DATE_GET_MINUTE(src.ptr()); - cal.tm_hour = PyDateTime_DATE_GET_HOUR(src.ptr()); - cal.tm_mday = PyDateTime_GET_DAY(src.ptr()); - cal.tm_mon = PyDateTime_GET_MONTH(src.ptr()) - 1; - cal.tm_year = PyDateTime_GET_YEAR(src.ptr()) - 1900; - cal.tm_isdst = -1; - - value = system_clock::from_time_t(std::mktime(&cal)) + microseconds(PyDateTime_DATE_GET_MICROSECOND(src.ptr())); - return true; - } - else return false; - } - - static handle cast(const std::chrono::time_point &src, return_value_policy /* policy */, handle /* parent */) { - using namespace std::chrono; - - // Lazy initialise the PyDateTime import - if (!PyDateTimeAPI) { PyDateTime_IMPORT; } - - std::time_t tt = system_clock::to_time_t(src); - // this function uses static memory so it's best to copy it out asap just in case - // otherwise other code that is using localtime may break this (not just python code) - std::tm localtime = *std::localtime(&tt); - - // Declare these special duration types so the conversions happen with the correct primitive types (int) - using us_t = duration; - - return PyDateTime_FromDateAndTime(localtime.tm_year + 1900, - localtime.tm_mon + 1, - localtime.tm_mday, - localtime.tm_hour, - localtime.tm_min, - localtime.tm_sec, - (duration_cast(src.time_since_epoch() % seconds(1))).count()); - } - PYBIND11_TYPE_CASTER(type, _("datetime.datetime")); -}; - -// Other clocks that are not the system clock are not measured as datetime.datetime objects -// since they are not measured on calendar time. So instead we just make them timedeltas -// Or if they have passed us a time as a float we convert that -template class type_caster> -: public duration_caster> { -}; - -template class type_caster> -: public duration_caster> { -}; - -NAMESPACE_END(detail) -NAMESPACE_END(pybind11) diff --git a/ptocr/postprocess/lanms/include/pybind11/class_support.h b/ptocr/postprocess/lanms/include/pybind11/class_support.h deleted file mode 100644 index 8e18c4c..0000000 --- a/ptocr/postprocess/lanms/include/pybind11/class_support.h +++ /dev/null @@ -1,603 +0,0 @@ -/* - pybind11/class_support.h: Python C API implementation details for py::class_ - - Copyright (c) 2017 Wenzel Jakob - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#include "attr.h" - -NAMESPACE_BEGIN(pybind11) -NAMESPACE_BEGIN(detail) - -inline PyTypeObject *type_incref(PyTypeObject *type) { - Py_INCREF(type); - return type; -} - -#if !defined(PYPY_VERSION) - -/// `pybind11_static_property.__get__()`: Always pass the class instead of the instance. -extern "C" inline PyObject *pybind11_static_get(PyObject *self, PyObject * /*ob*/, PyObject *cls) { - return PyProperty_Type.tp_descr_get(self, cls, cls); -} - -/// `pybind11_static_property.__set__()`: Just like the above `__get__()`. -extern "C" inline int pybind11_static_set(PyObject *self, PyObject *obj, PyObject *value) { - PyObject *cls = PyType_Check(obj) ? obj : (PyObject *) Py_TYPE(obj); - return PyProperty_Type.tp_descr_set(self, cls, value); -} - -/** A `static_property` is the same as a `property` but the `__get__()` and `__set__()` - methods are modified to always use the object type instead of a concrete instance. - Return value: New reference. */ -inline PyTypeObject *make_static_property_type() { - constexpr auto *name = "pybind11_static_property"; - auto name_obj = reinterpret_steal(PYBIND11_FROM_STRING(name)); - - /* Danger zone: from now (and until PyType_Ready), make sure to - issue no Python C API calls which could potentially invoke the - garbage collector (the GC will call type_traverse(), which will in - turn find the newly constructed type in an invalid state) */ - auto heap_type = (PyHeapTypeObject *) PyType_Type.tp_alloc(&PyType_Type, 0); - if (!heap_type) - pybind11_fail("make_static_property_type(): error allocating type!"); - - heap_type->ht_name = name_obj.inc_ref().ptr(); -#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 3 - heap_type->ht_qualname = name_obj.inc_ref().ptr(); -#endif - - auto type = &heap_type->ht_type; - type->tp_name = name; - type->tp_base = type_incref(&PyProperty_Type); - type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE; - type->tp_descr_get = pybind11_static_get; - type->tp_descr_set = pybind11_static_set; - - if (PyType_Ready(type) < 0) - pybind11_fail("make_static_property_type(): failure in PyType_Ready()!"); - - setattr((PyObject *) type, "__module__", str("pybind11_builtins")); - - return type; -} - -#else // PYPY - -/** PyPy has some issues with the above C API, so we evaluate Python code instead. - This function will only be called once so performance isn't really a concern. - Return value: New reference. */ -inline PyTypeObject *make_static_property_type() { - auto d = dict(); - PyObject *result = PyRun_String(R"(\ - class pybind11_static_property(property): - def __get__(self, obj, cls): - return property.__get__(self, cls, cls) - - def __set__(self, obj, value): - cls = obj if isinstance(obj, type) else type(obj) - property.__set__(self, cls, value) - )", Py_file_input, d.ptr(), d.ptr() - ); - if (result == nullptr) - throw error_already_set(); - Py_DECREF(result); - return (PyTypeObject *) d["pybind11_static_property"].cast().release().ptr(); -} - -#endif // PYPY - -/** Types with static properties need to handle `Type.static_prop = x` in a specific way. - By default, Python replaces the `static_property` itself, but for wrapped C++ types - we need to call `static_property.__set__()` in order to propagate the new value to - the underlying C++ data structure. */ -extern "C" inline int pybind11_meta_setattro(PyObject* obj, PyObject* name, PyObject* value) { - // Use `_PyType_Lookup()` instead of `PyObject_GetAttr()` in order to get the raw - // descriptor (`property`) instead of calling `tp_descr_get` (`property.__get__()`). - PyObject *descr = _PyType_Lookup((PyTypeObject *) obj, name); - - // The following assignment combinations are possible: - // 1. `Type.static_prop = value` --> descr_set: `Type.static_prop.__set__(value)` - // 2. `Type.static_prop = other_static_prop` --> setattro: replace existing `static_prop` - // 3. `Type.regular_attribute = value` --> setattro: regular attribute assignment - const auto static_prop = (PyObject *) get_internals().static_property_type; - const auto call_descr_set = descr && PyObject_IsInstance(descr, static_prop) - && !PyObject_IsInstance(value, static_prop); - if (call_descr_set) { - // Call `static_property.__set__()` instead of replacing the `static_property`. -#if !defined(PYPY_VERSION) - return Py_TYPE(descr)->tp_descr_set(descr, obj, value); -#else - if (PyObject *result = PyObject_CallMethod(descr, "__set__", "OO", obj, value)) { - Py_DECREF(result); - return 0; - } else { - return -1; - } -#endif - } else { - // Replace existing attribute. - return PyType_Type.tp_setattro(obj, name, value); - } -} - -#if PY_MAJOR_VERSION >= 3 -/** - * Python 3's PyInstanceMethod_Type hides itself via its tp_descr_get, which prevents aliasing - * methods via cls.attr("m2") = cls.attr("m1"): instead the tp_descr_get returns a plain function, - * when called on a class, or a PyMethod, when called on an instance. Override that behaviour here - * to do a special case bypass for PyInstanceMethod_Types. - */ -extern "C" inline PyObject *pybind11_meta_getattro(PyObject *obj, PyObject *name) { - PyObject *descr = _PyType_Lookup((PyTypeObject *) obj, name); - if (descr && PyInstanceMethod_Check(descr)) { - Py_INCREF(descr); - return descr; - } - else { - return PyType_Type.tp_getattro(obj, name); - } -} -#endif - -/** This metaclass is assigned by default to all pybind11 types and is required in order - for static properties to function correctly. Users may override this using `py::metaclass`. - Return value: New reference. */ -inline PyTypeObject* make_default_metaclass() { - constexpr auto *name = "pybind11_type"; - auto name_obj = reinterpret_steal(PYBIND11_FROM_STRING(name)); - - /* Danger zone: from now (and until PyType_Ready), make sure to - issue no Python C API calls which could potentially invoke the - garbage collector (the GC will call type_traverse(), which will in - turn find the newly constructed type in an invalid state) */ - auto heap_type = (PyHeapTypeObject *) PyType_Type.tp_alloc(&PyType_Type, 0); - if (!heap_type) - pybind11_fail("make_default_metaclass(): error allocating metaclass!"); - - heap_type->ht_name = name_obj.inc_ref().ptr(); -#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 3 - heap_type->ht_qualname = name_obj.inc_ref().ptr(); -#endif - - auto type = &heap_type->ht_type; - type->tp_name = name; - type->tp_base = type_incref(&PyType_Type); - type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE; - - type->tp_setattro = pybind11_meta_setattro; -#if PY_MAJOR_VERSION >= 3 - type->tp_getattro = pybind11_meta_getattro; -#endif - - if (PyType_Ready(type) < 0) - pybind11_fail("make_default_metaclass(): failure in PyType_Ready()!"); - - setattr((PyObject *) type, "__module__", str("pybind11_builtins")); - - return type; -} - -/// For multiple inheritance types we need to recursively register/deregister base pointers for any -/// base classes with pointers that are difference from the instance value pointer so that we can -/// correctly recognize an offset base class pointer. This calls a function with any offset base ptrs. -inline void traverse_offset_bases(void *valueptr, const detail::type_info *tinfo, instance *self, - bool (*f)(void * /*parentptr*/, instance * /*self*/)) { - for (handle h : reinterpret_borrow(tinfo->type->tp_bases)) { - if (auto parent_tinfo = get_type_info((PyTypeObject *) h.ptr())) { - for (auto &c : parent_tinfo->implicit_casts) { - if (c.first == tinfo->cpptype) { - auto *parentptr = c.second(valueptr); - if (parentptr != valueptr) - f(parentptr, self); - traverse_offset_bases(parentptr, parent_tinfo, self, f); - break; - } - } - } - } -} - -inline bool register_instance_impl(void *ptr, instance *self) { - get_internals().registered_instances.emplace(ptr, self); - return true; // unused, but gives the same signature as the deregister func -} -inline bool deregister_instance_impl(void *ptr, instance *self) { - auto ®istered_instances = get_internals().registered_instances; - auto range = registered_instances.equal_range(ptr); - for (auto it = range.first; it != range.second; ++it) { - if (Py_TYPE(self) == Py_TYPE(it->second)) { - registered_instances.erase(it); - return true; - } - } - return false; -} - -inline void register_instance(instance *self, void *valptr, const type_info *tinfo) { - register_instance_impl(valptr, self); - if (!tinfo->simple_ancestors) - traverse_offset_bases(valptr, tinfo, self, register_instance_impl); -} - -inline bool deregister_instance(instance *self, void *valptr, const type_info *tinfo) { - bool ret = deregister_instance_impl(valptr, self); - if (!tinfo->simple_ancestors) - traverse_offset_bases(valptr, tinfo, self, deregister_instance_impl); - return ret; -} - -/// Instance creation function for all pybind11 types. It only allocates space for the C++ object -/// (or multiple objects, for Python-side inheritance from multiple pybind11 types), but doesn't -/// call the constructor -- an `__init__` function must do that (followed by an `init_instance` -/// to set up the holder and register the instance). -inline PyObject *make_new_instance(PyTypeObject *type, bool allocate_value /*= true (in cast.h)*/) { -#if defined(PYPY_VERSION) - // PyPy gets tp_basicsize wrong (issue 2482) under multiple inheritance when the first inherited - // object is a a plain Python type (i.e. not derived from an extension type). Fix it. - ssize_t instance_size = static_cast(sizeof(instance)); - if (type->tp_basicsize < instance_size) { - type->tp_basicsize = instance_size; - } -#endif - PyObject *self = type->tp_alloc(type, 0); - auto inst = reinterpret_cast(self); - // Allocate the value/holder internals: - inst->allocate_layout(); - - inst->owned = true; - // Allocate (if requested) the value pointers; otherwise leave them as nullptr - if (allocate_value) { - for (auto &v_h : values_and_holders(inst)) { - void *&vptr = v_h.value_ptr(); - vptr = v_h.type->operator_new(v_h.type->type_size); - } - } - - return self; -} - -/// Instance creation function for all pybind11 types. It only allocates space for the -/// C++ object, but doesn't call the constructor -- an `__init__` function must do that. -extern "C" inline PyObject *pybind11_object_new(PyTypeObject *type, PyObject *, PyObject *) { - return make_new_instance(type); -} - -/// An `__init__` function constructs the C++ object. Users should provide at least one -/// of these using `py::init` or directly with `.def(__init__, ...)`. Otherwise, the -/// following default function will be used which simply throws an exception. -extern "C" inline int pybind11_object_init(PyObject *self, PyObject *, PyObject *) { - PyTypeObject *type = Py_TYPE(self); - std::string msg; -#if defined(PYPY_VERSION) - msg += handle((PyObject *) type).attr("__module__").cast() + "."; -#endif - msg += type->tp_name; - msg += ": No constructor defined!"; - PyErr_SetString(PyExc_TypeError, msg.c_str()); - return -1; -} - -inline void add_patient(PyObject *nurse, PyObject *patient) { - auto &internals = get_internals(); - auto instance = reinterpret_cast(nurse); - instance->has_patients = true; - Py_INCREF(patient); - internals.patients[nurse].push_back(patient); -} - -inline void clear_patients(PyObject *self) { - auto instance = reinterpret_cast(self); - auto &internals = get_internals(); - auto pos = internals.patients.find(self); - assert(pos != internals.patients.end()); - // Clearing the patients can cause more Python code to run, which - // can invalidate the iterator. Extract the vector of patients - // from the unordered_map first. - auto patients = std::move(pos->second); - internals.patients.erase(pos); - instance->has_patients = false; - for (PyObject *&patient : patients) - Py_CLEAR(patient); -} - -/// Clears all internal data from the instance and removes it from registered instances in -/// preparation for deallocation. -inline void clear_instance(PyObject *self) { - auto instance = reinterpret_cast(self); - - // Deallocate any values/holders, if present: - for (auto &v_h : values_and_holders(instance)) { - if (v_h) { - - // We have to deregister before we call dealloc because, for virtual MI types, we still - // need to be able to get the parent pointers. - if (v_h.instance_registered() && !deregister_instance(instance, v_h.value_ptr(), v_h.type)) - pybind11_fail("pybind11_object_dealloc(): Tried to deallocate unregistered instance!"); - - if (instance->owned || v_h.holder_constructed()) - v_h.type->dealloc(v_h); - } - } - // Deallocate the value/holder layout internals: - instance->deallocate_layout(); - - if (instance->weakrefs) - PyObject_ClearWeakRefs(self); - - PyObject **dict_ptr = _PyObject_GetDictPtr(self); - if (dict_ptr) - Py_CLEAR(*dict_ptr); - - if (instance->has_patients) - clear_patients(self); -} - -/// Instance destructor function for all pybind11 types. It calls `type_info.dealloc` -/// to destroy the C++ object itself, while the rest is Python bookkeeping. -extern "C" inline void pybind11_object_dealloc(PyObject *self) { - clear_instance(self); - Py_TYPE(self)->tp_free(self); -} - -/** Create the type which can be used as a common base for all classes. This is - needed in order to satisfy Python's requirements for multiple inheritance. - Return value: New reference. */ -inline PyObject *make_object_base_type(PyTypeObject *metaclass) { - constexpr auto *name = "pybind11_object"; - auto name_obj = reinterpret_steal(PYBIND11_FROM_STRING(name)); - - /* Danger zone: from now (and until PyType_Ready), make sure to - issue no Python C API calls which could potentially invoke the - garbage collector (the GC will call type_traverse(), which will in - turn find the newly constructed type in an invalid state) */ - auto heap_type = (PyHeapTypeObject *) metaclass->tp_alloc(metaclass, 0); - if (!heap_type) - pybind11_fail("make_object_base_type(): error allocating type!"); - - heap_type->ht_name = name_obj.inc_ref().ptr(); -#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 3 - heap_type->ht_qualname = name_obj.inc_ref().ptr(); -#endif - - auto type = &heap_type->ht_type; - type->tp_name = name; - type->tp_base = type_incref(&PyBaseObject_Type); - type->tp_basicsize = static_cast(sizeof(instance)); - type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE; - - type->tp_new = pybind11_object_new; - type->tp_init = pybind11_object_init; - type->tp_dealloc = pybind11_object_dealloc; - - /* Support weak references (needed for the keep_alive feature) */ - type->tp_weaklistoffset = offsetof(instance, weakrefs); - - if (PyType_Ready(type) < 0) - pybind11_fail("PyType_Ready failed in make_object_base_type():" + error_string()); - - setattr((PyObject *) type, "__module__", str("pybind11_builtins")); - - assert(!PyType_HasFeature(type, Py_TPFLAGS_HAVE_GC)); - return (PyObject *) heap_type; -} - -/// dynamic_attr: Support for `d = instance.__dict__`. -extern "C" inline PyObject *pybind11_get_dict(PyObject *self, void *) { - PyObject *&dict = *_PyObject_GetDictPtr(self); - if (!dict) - dict = PyDict_New(); - Py_XINCREF(dict); - return dict; -} - -/// dynamic_attr: Support for `instance.__dict__ = dict()`. -extern "C" inline int pybind11_set_dict(PyObject *self, PyObject *new_dict, void *) { - if (!PyDict_Check(new_dict)) { - PyErr_Format(PyExc_TypeError, "__dict__ must be set to a dictionary, not a '%.200s'", - Py_TYPE(new_dict)->tp_name); - return -1; - } - PyObject *&dict = *_PyObject_GetDictPtr(self); - Py_INCREF(new_dict); - Py_CLEAR(dict); - dict = new_dict; - return 0; -} - -/// dynamic_attr: Allow the garbage collector to traverse the internal instance `__dict__`. -extern "C" inline int pybind11_traverse(PyObject *self, visitproc visit, void *arg) { - PyObject *&dict = *_PyObject_GetDictPtr(self); - Py_VISIT(dict); - return 0; -} - -/// dynamic_attr: Allow the GC to clear the dictionary. -extern "C" inline int pybind11_clear(PyObject *self) { - PyObject *&dict = *_PyObject_GetDictPtr(self); - Py_CLEAR(dict); - return 0; -} - -/// Give instances of this type a `__dict__` and opt into garbage collection. -inline void enable_dynamic_attributes(PyHeapTypeObject *heap_type) { - auto type = &heap_type->ht_type; -#if defined(PYPY_VERSION) - pybind11_fail(std::string(type->tp_name) + ": dynamic attributes are " - "currently not supported in " - "conjunction with PyPy!"); -#endif - type->tp_flags |= Py_TPFLAGS_HAVE_GC; - type->tp_dictoffset = type->tp_basicsize; // place dict at the end - type->tp_basicsize += (ssize_t)sizeof(PyObject *); // and allocate enough space for it - type->tp_traverse = pybind11_traverse; - type->tp_clear = pybind11_clear; - - static PyGetSetDef getset[] = { - {const_cast("__dict__"), pybind11_get_dict, pybind11_set_dict, nullptr, nullptr}, - {nullptr, nullptr, nullptr, nullptr, nullptr} - }; - type->tp_getset = getset; -} - -/// buffer_protocol: Fill in the view as specified by flags. -extern "C" inline int pybind11_getbuffer(PyObject *obj, Py_buffer *view, int flags) { - // Look for a `get_buffer` implementation in this type's info or any bases (following MRO). - type_info *tinfo = nullptr; - for (auto type : reinterpret_borrow(Py_TYPE(obj)->tp_mro)) { - tinfo = get_type_info((PyTypeObject *) type.ptr()); - if (tinfo && tinfo->get_buffer) - break; - } - if (view == nullptr || obj == nullptr || !tinfo || !tinfo->get_buffer) { - if (view) - view->obj = nullptr; - PyErr_SetString(PyExc_BufferError, "pybind11_getbuffer(): Internal error"); - return -1; - } - std::memset(view, 0, sizeof(Py_buffer)); - buffer_info *info = tinfo->get_buffer(obj, tinfo->get_buffer_data); - view->obj = obj; - view->ndim = 1; - view->internal = info; - view->buf = info->ptr; - view->itemsize = info->itemsize; - view->len = view->itemsize; - for (auto s : info->shape) - view->len *= s; - if ((flags & PyBUF_FORMAT) == PyBUF_FORMAT) - view->format = const_cast(info->format.c_str()); - if ((flags & PyBUF_STRIDES) == PyBUF_STRIDES) { - view->ndim = (int) info->ndim; - view->strides = &info->strides[0]; - view->shape = &info->shape[0]; - } - Py_INCREF(view->obj); - return 0; -} - -/// buffer_protocol: Release the resources of the buffer. -extern "C" inline void pybind11_releasebuffer(PyObject *, Py_buffer *view) { - delete (buffer_info *) view->internal; -} - -/// Give this type a buffer interface. -inline void enable_buffer_protocol(PyHeapTypeObject *heap_type) { - heap_type->ht_type.tp_as_buffer = &heap_type->as_buffer; -#if PY_MAJOR_VERSION < 3 - heap_type->ht_type.tp_flags |= Py_TPFLAGS_HAVE_NEWBUFFER; -#endif - - heap_type->as_buffer.bf_getbuffer = pybind11_getbuffer; - heap_type->as_buffer.bf_releasebuffer = pybind11_releasebuffer; -} - -/** Create a brand new Python type according to the `type_record` specification. - Return value: New reference. */ -inline PyObject* make_new_python_type(const type_record &rec) { - auto name = reinterpret_steal(PYBIND11_FROM_STRING(rec.name)); - -#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 3 - auto ht_qualname = name; - if (rec.scope && hasattr(rec.scope, "__qualname__")) { - ht_qualname = reinterpret_steal( - PyUnicode_FromFormat("%U.%U", rec.scope.attr("__qualname__").ptr(), name.ptr())); - } -#endif - - object module; - if (rec.scope) { - if (hasattr(rec.scope, "__module__")) - module = rec.scope.attr("__module__"); - else if (hasattr(rec.scope, "__name__")) - module = rec.scope.attr("__name__"); - } - -#if !defined(PYPY_VERSION) - const auto full_name = module ? str(module).cast() + "." + rec.name - : std::string(rec.name); -#else - const auto full_name = std::string(rec.name); -#endif - - char *tp_doc = nullptr; - if (rec.doc && options::show_user_defined_docstrings()) { - /* Allocate memory for docstring (using PyObject_MALLOC, since - Python will free this later on) */ - size_t size = strlen(rec.doc) + 1; - tp_doc = (char *) PyObject_MALLOC(size); - memcpy((void *) tp_doc, rec.doc, size); - } - - auto &internals = get_internals(); - auto bases = tuple(rec.bases); - auto base = (bases.size() == 0) ? internals.instance_base - : bases[0].ptr(); - - /* Danger zone: from now (and until PyType_Ready), make sure to - issue no Python C API calls which could potentially invoke the - garbage collector (the GC will call type_traverse(), which will in - turn find the newly constructed type in an invalid state) */ - auto metaclass = rec.metaclass.ptr() ? (PyTypeObject *) rec.metaclass.ptr() - : internals.default_metaclass; - - auto heap_type = (PyHeapTypeObject *) metaclass->tp_alloc(metaclass, 0); - if (!heap_type) - pybind11_fail(std::string(rec.name) + ": Unable to create type object!"); - - heap_type->ht_name = name.release().ptr(); -#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 3 - heap_type->ht_qualname = ht_qualname.release().ptr(); -#endif - - auto type = &heap_type->ht_type; - type->tp_name = strdup(full_name.c_str()); - type->tp_doc = tp_doc; - type->tp_base = type_incref((PyTypeObject *)base); - type->tp_basicsize = static_cast(sizeof(instance)); - if (bases.size() > 0) - type->tp_bases = bases.release().ptr(); - - /* Don't inherit base __init__ */ - type->tp_init = pybind11_object_init; - - /* Supported protocols */ - type->tp_as_number = &heap_type->as_number; - type->tp_as_sequence = &heap_type->as_sequence; - type->tp_as_mapping = &heap_type->as_mapping; - - /* Flags */ - type->tp_flags |= Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE; -#if PY_MAJOR_VERSION < 3 - type->tp_flags |= Py_TPFLAGS_CHECKTYPES; -#endif - - if (rec.dynamic_attr) - enable_dynamic_attributes(heap_type); - - if (rec.buffer_protocol) - enable_buffer_protocol(heap_type); - - if (PyType_Ready(type) < 0) - pybind11_fail(std::string(rec.name) + ": PyType_Ready failed (" + error_string() + ")!"); - - assert(rec.dynamic_attr ? PyType_HasFeature(type, Py_TPFLAGS_HAVE_GC) - : !PyType_HasFeature(type, Py_TPFLAGS_HAVE_GC)); - - /* Register type with the parent scope */ - if (rec.scope) - setattr(rec.scope, rec.name, (PyObject *) type); - - if (module) // Needed by pydoc - setattr((PyObject *) type, "__module__", module); - - return (PyObject *) type; -} - -NAMESPACE_END(detail) -NAMESPACE_END(pybind11) diff --git a/ptocr/postprocess/lanms/include/pybind11/common.h b/ptocr/postprocess/lanms/include/pybind11/common.h deleted file mode 100644 index 240f6d8..0000000 --- a/ptocr/postprocess/lanms/include/pybind11/common.h +++ /dev/null @@ -1,857 +0,0 @@ -/* - pybind11/common.h -- Basic macros - - Copyright (c) 2016 Wenzel Jakob - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#if !defined(NAMESPACE_BEGIN) -# define NAMESPACE_BEGIN(name) namespace name { -#endif -#if !defined(NAMESPACE_END) -# define NAMESPACE_END(name) } -#endif - -#if !defined(_MSC_VER) && !defined(__INTEL_COMPILER) -# if __cplusplus >= 201402L -# define PYBIND11_CPP14 -# if __cplusplus > 201402L /* Temporary: should be updated to >= the final C++17 value once known */ -# define PYBIND11_CPP17 -# endif -# endif -#elif defined(_MSC_VER) -// MSVC sets _MSVC_LANG rather than __cplusplus (supposedly until the standard is fully implemented) -# if _MSVC_LANG >= 201402L -# define PYBIND11_CPP14 -# if _MSVC_LANG > 201402L && _MSC_VER >= 1910 -# define PYBIND11_CPP17 -# endif -# endif -#endif - -// Compiler version assertions -#if defined(__INTEL_COMPILER) -# if __INTEL_COMPILER < 1500 -# error pybind11 requires Intel C++ compiler v15 or newer -# endif -#elif defined(__clang__) && !defined(__apple_build_version__) -# if __clang_major__ < 3 || (__clang_major__ == 3 && __clang_minor__ < 3) -# error pybind11 requires clang 3.3 or newer -# endif -#elif defined(__clang__) -// Apple changes clang version macros to its Xcode version; the first Xcode release based on -// (upstream) clang 3.3 was Xcode 5: -# if __clang_major__ < 5 -# error pybind11 requires Xcode/clang 5.0 or newer -# endif -#elif defined(__GNUG__) -# if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 8) -# error pybind11 requires gcc 4.8 or newer -# endif -#elif defined(_MSC_VER) -// Pybind hits various compiler bugs in 2015u2 and earlier, and also makes use of some stl features -// (e.g. std::negation) added in 2015u3: -# if _MSC_FULL_VER < 190024210 -# error pybind11 requires MSVC 2015 update 3 or newer -# endif -#endif - -#if !defined(PYBIND11_EXPORT) -# if defined(WIN32) || defined(_WIN32) -# define PYBIND11_EXPORT __declspec(dllexport) -# else -# define PYBIND11_EXPORT __attribute__ ((visibility("default"))) -# endif -#endif - -#if defined(_MSC_VER) -# define PYBIND11_NOINLINE __declspec(noinline) -#else -# define PYBIND11_NOINLINE __attribute__ ((noinline)) -#endif - -#if defined(PYBIND11_CPP14) -# define PYBIND11_DEPRECATED(reason) [[deprecated(reason)]] -#else -# define PYBIND11_DEPRECATED(reason) __attribute__((deprecated(reason))) -#endif - -#define PYBIND11_VERSION_MAJOR 2 -#define PYBIND11_VERSION_MINOR 2 -#define PYBIND11_VERSION_PATCH dev0 - -/// Include Python header, disable linking to pythonX_d.lib on Windows in debug mode -#if defined(_MSC_VER) -# if (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION < 4) -# define HAVE_ROUND 1 -# endif -# pragma warning(push) -# pragma warning(disable: 4510 4610 4512 4005) -# if defined(_DEBUG) -# define PYBIND11_DEBUG_MARKER -# undef _DEBUG -# endif -#endif - -#include -#include -#include - -#if defined(_WIN32) && (defined(min) || defined(max)) -# error Macro clash with min and max -- define NOMINMAX when compiling your program on Windows -#endif - -#if defined(isalnum) -# undef isalnum -# undef isalpha -# undef islower -# undef isspace -# undef isupper -# undef tolower -# undef toupper -#endif - -#if defined(_MSC_VER) -# if defined(PYBIND11_DEBUG_MARKER) -# define _DEBUG -# undef PYBIND11_DEBUG_MARKER -# endif -# pragma warning(pop) -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if PY_MAJOR_VERSION >= 3 /// Compatibility macros for various Python versions -#define PYBIND11_INSTANCE_METHOD_NEW(ptr, class_) PyInstanceMethod_New(ptr) -#define PYBIND11_INSTANCE_METHOD_CHECK PyInstanceMethod_Check -#define PYBIND11_INSTANCE_METHOD_GET_FUNCTION PyInstanceMethod_GET_FUNCTION -#define PYBIND11_BYTES_CHECK PyBytes_Check -#define PYBIND11_BYTES_FROM_STRING PyBytes_FromString -#define PYBIND11_BYTES_FROM_STRING_AND_SIZE PyBytes_FromStringAndSize -#define PYBIND11_BYTES_AS_STRING_AND_SIZE PyBytes_AsStringAndSize -#define PYBIND11_BYTES_AS_STRING PyBytes_AsString -#define PYBIND11_BYTES_SIZE PyBytes_Size -#define PYBIND11_LONG_CHECK(o) PyLong_Check(o) -#define PYBIND11_LONG_AS_LONGLONG(o) PyLong_AsLongLong(o) -#define PYBIND11_BYTES_NAME "bytes" -#define PYBIND11_STRING_NAME "str" -#define PYBIND11_SLICE_OBJECT PyObject -#define PYBIND11_FROM_STRING PyUnicode_FromString -#define PYBIND11_STR_TYPE ::pybind11::str -#define PYBIND11_BOOL_ATTR "__bool__" -#define PYBIND11_NB_BOOL(ptr) ((ptr)->nb_bool) -#define PYBIND11_PLUGIN_IMPL(name) \ - extern "C" PYBIND11_EXPORT PyObject *PyInit_##name() - -#else -#define PYBIND11_INSTANCE_METHOD_NEW(ptr, class_) PyMethod_New(ptr, nullptr, class_) -#define PYBIND11_INSTANCE_METHOD_CHECK PyMethod_Check -#define PYBIND11_INSTANCE_METHOD_GET_FUNCTION PyMethod_GET_FUNCTION -#define PYBIND11_BYTES_CHECK PyString_Check -#define PYBIND11_BYTES_FROM_STRING PyString_FromString -#define PYBIND11_BYTES_FROM_STRING_AND_SIZE PyString_FromStringAndSize -#define PYBIND11_BYTES_AS_STRING_AND_SIZE PyString_AsStringAndSize -#define PYBIND11_BYTES_AS_STRING PyString_AsString -#define PYBIND11_BYTES_SIZE PyString_Size -#define PYBIND11_LONG_CHECK(o) (PyInt_Check(o) || PyLong_Check(o)) -#define PYBIND11_LONG_AS_LONGLONG(o) (PyInt_Check(o) ? (long long) PyLong_AsLong(o) : PyLong_AsLongLong(o)) -#define PYBIND11_BYTES_NAME "str" -#define PYBIND11_STRING_NAME "unicode" -#define PYBIND11_SLICE_OBJECT PySliceObject -#define PYBIND11_FROM_STRING PyString_FromString -#define PYBIND11_STR_TYPE ::pybind11::bytes -#define PYBIND11_BOOL_ATTR "__nonzero__" -#define PYBIND11_NB_BOOL(ptr) ((ptr)->nb_nonzero) -#define PYBIND11_PLUGIN_IMPL(name) \ - static PyObject *pybind11_init_wrapper(); \ - extern "C" PYBIND11_EXPORT void init##name() { \ - (void)pybind11_init_wrapper(); \ - } \ - PyObject *pybind11_init_wrapper() -#endif - -#if PY_VERSION_HEX >= 0x03050000 && PY_VERSION_HEX < 0x03050200 -extern "C" { - struct _Py_atomic_address { void *value; }; - PyAPI_DATA(_Py_atomic_address) _PyThreadState_Current; -} -#endif - -#define PYBIND11_TRY_NEXT_OVERLOAD ((PyObject *) 1) // special failure return code -#define PYBIND11_STRINGIFY(x) #x -#define PYBIND11_TOSTRING(x) PYBIND11_STRINGIFY(x) -#define PYBIND11_INTERNALS_ID "__pybind11_" \ - PYBIND11_TOSTRING(PYBIND11_VERSION_MAJOR) "_" PYBIND11_TOSTRING(PYBIND11_VERSION_MINOR) "__" - -/** \rst - ***Deprecated in favor of PYBIND11_MODULE*** - - This macro creates the entry point that will be invoked when the Python interpreter - imports a plugin library. Please create a `module` in the function body and return - the pointer to its underlying Python object at the end. - - .. code-block:: cpp - - PYBIND11_PLUGIN(example) { - pybind11::module m("example", "pybind11 example plugin"); - /// Set up bindings here - return m.ptr(); - } -\endrst */ -#define PYBIND11_PLUGIN(name) \ - PYBIND11_DEPRECATED("PYBIND11_PLUGIN is deprecated, use PYBIND11_MODULE") \ - static PyObject *pybind11_init(); \ - PYBIND11_PLUGIN_IMPL(name) { \ - int major, minor; \ - if (sscanf(Py_GetVersion(), "%i.%i", &major, &minor) != 2) { \ - PyErr_SetString(PyExc_ImportError, "Can't parse Python version."); \ - return nullptr; \ - } else if (major != PY_MAJOR_VERSION || minor != PY_MINOR_VERSION) { \ - PyErr_Format(PyExc_ImportError, \ - "Python version mismatch: module was compiled for " \ - "version %i.%i, while the interpreter is running " \ - "version %i.%i.", PY_MAJOR_VERSION, PY_MINOR_VERSION, \ - major, minor); \ - return nullptr; \ - } \ - try { \ - return pybind11_init(); \ - } catch (pybind11::error_already_set &e) { \ - PyErr_SetString(PyExc_ImportError, e.what()); \ - return nullptr; \ - } catch (const std::exception &e) { \ - PyErr_SetString(PyExc_ImportError, e.what()); \ - return nullptr; \ - } \ - } \ - PyObject *pybind11_init() - -/** \rst - This macro creates the entry point that will be invoked when the Python interpreter - imports an extension module. The module name is given as the fist argument and it - should not be in quotes. The second macro argument defines a variable of type - `py::module` which can be used to initialize the module. - - .. code-block:: cpp - - PYBIND11_MODULE(example, m) { - m.doc() = "pybind11 example module"; - - // Add bindings here - m.def("foo", []() { - return "Hello, World!"; - }); - } -\endrst */ -#define PYBIND11_MODULE(name, variable) \ - static void pybind11_init_##name(pybind11::module &); \ - PYBIND11_PLUGIN_IMPL(name) { \ - int major, minor; \ - if (sscanf(Py_GetVersion(), "%i.%i", &major, &minor) != 2) { \ - PyErr_SetString(PyExc_ImportError, "Can't parse Python version."); \ - return nullptr; \ - } else if (major != PY_MAJOR_VERSION || minor != PY_MINOR_VERSION) { \ - PyErr_Format(PyExc_ImportError, \ - "Python version mismatch: module was compiled for " \ - "version %i.%i, while the interpreter is running " \ - "version %i.%i.", PY_MAJOR_VERSION, PY_MINOR_VERSION, \ - major, minor); \ - return nullptr; \ - } \ - auto m = pybind11::module(#name); \ - try { \ - pybind11_init_##name(m); \ - return m.ptr(); \ - } catch (pybind11::error_already_set &e) { \ - PyErr_SetString(PyExc_ImportError, e.what()); \ - return nullptr; \ - } catch (const std::exception &e) { \ - PyErr_SetString(PyExc_ImportError, e.what()); \ - return nullptr; \ - } \ - } \ - void pybind11_init_##name(pybind11::module &variable) - - -NAMESPACE_BEGIN(pybind11) - -using ssize_t = Py_ssize_t; -using size_t = std::size_t; - -/// Approach used to cast a previously unknown C++ instance into a Python object -enum class return_value_policy : uint8_t { - /** This is the default return value policy, which falls back to the policy - return_value_policy::take_ownership when the return value is a pointer. - Otherwise, it uses return_value::move or return_value::copy for rvalue - and lvalue references, respectively. See below for a description of what - all of these different policies do. */ - automatic = 0, - - /** As above, but use policy return_value_policy::reference when the return - value is a pointer. This is the default conversion policy for function - arguments when calling Python functions manually from C++ code (i.e. via - handle::operator()). You probably won't need to use this. */ - automatic_reference, - - /** Reference an existing object (i.e. do not create a new copy) and take - ownership. Python will call the destructor and delete operator when the - object’s reference count reaches zero. Undefined behavior ensues when - the C++ side does the same.. */ - take_ownership, - - /** Create a new copy of the returned object, which will be owned by - Python. This policy is comparably safe because the lifetimes of the two - instances are decoupled. */ - copy, - - /** Use std::move to move the return value contents into a new instance - that will be owned by Python. This policy is comparably safe because the - lifetimes of the two instances (move source and destination) are - decoupled. */ - move, - - /** Reference an existing object, but do not take ownership. The C++ side - is responsible for managing the object’s lifetime and deallocating it - when it is no longer used. Warning: undefined behavior will ensue when - the C++ side deletes an object that is still referenced and used by - Python. */ - reference, - - /** This policy only applies to methods and properties. It references the - object without taking ownership similar to the above - return_value_policy::reference policy. In contrast to that policy, the - function or property’s implicit this argument (called the parent) is - considered to be the the owner of the return value (the child). - pybind11 then couples the lifetime of the parent to the child via a - reference relationship that ensures that the parent cannot be garbage - collected while Python is still using the child. More advanced - variations of this scheme are also possible using combinations of - return_value_policy::reference and the keep_alive call policy */ - reference_internal -}; - -NAMESPACE_BEGIN(detail) - -inline static constexpr int log2(size_t n, int k = 0) { return (n <= 1) ? k : log2(n >> 1, k + 1); } - -// Returns the size as a multiple of sizeof(void *), rounded up. -inline static constexpr size_t size_in_ptrs(size_t s) { return 1 + ((s - 1) >> log2(sizeof(void *))); } - -/** - * The space to allocate for simple layout instance holders (see below) in multiple of the size of - * a pointer (e.g. 2 means 16 bytes on 64-bit architectures). The default is the minimum required - * to holder either a std::unique_ptr or std::shared_ptr (which is almost always - * sizeof(std::shared_ptr)). - */ -constexpr size_t instance_simple_holder_in_ptrs() { - static_assert(sizeof(std::shared_ptr) >= sizeof(std::unique_ptr), - "pybind assumes std::shared_ptrs are at least as big as std::unique_ptrs"); - return size_in_ptrs(sizeof(std::shared_ptr)); -} - -// Forward declarations -struct type_info; -struct value_and_holder; - -/// The 'instance' type which needs to be standard layout (need to be able to use 'offsetof') -struct instance { - PyObject_HEAD - /// Storage for pointers and holder; see simple_layout, below, for a description - union { - void *simple_value_holder[1 + instance_simple_holder_in_ptrs()]; - struct { - void **values_and_holders; - uint8_t *status; - } nonsimple; - }; - /// Weak references (needed for keep alive): - PyObject *weakrefs; - /// If true, the pointer is owned which means we're free to manage it with a holder. - bool owned : 1; - /** - * An instance has two possible value/holder layouts. - * - * Simple layout (when this flag is true), means the `simple_value_holder` is set with a pointer - * and the holder object governing that pointer, i.e. [val1*][holder]. This layout is applied - * whenever there is no python-side multiple inheritance of bound C++ types *and* the type's - * holder will fit in the default space (which is large enough to hold either a std::unique_ptr - * or std::shared_ptr). - * - * Non-simple layout applies when using custom holders that require more space than `shared_ptr` - * (which is typically the size of two pointers), or when multiple inheritance is used on the - * python side. Non-simple layout allocates the required amount of memory to have multiple - * bound C++ classes as parents. Under this layout, `nonsimple.values_and_holders` is set to a - * pointer to allocated space of the required space to hold a a sequence of value pointers and - * holders followed `status`, a set of bit flags (1 byte each), i.e. - * [val1*][holder1][val2*][holder2]...[bb...] where each [block] is rounded up to a multiple of - * `sizeof(void *)`. `nonsimple.holder_constructed` is, for convenience, a pointer to the - * beginning of the [bb...] block (but not independently allocated). - * - * Status bits indicate whether the associated holder is constructed (& - * status_holder_constructed) and whether the value pointer is registered (& - * status_instance_registered) in `registered_instances`. - */ - bool simple_layout : 1; - /// For simple layout, tracks whether the holder has been constructed - bool simple_holder_constructed : 1; - /// For simple layout, tracks whether the instance is registered in `registered_instances` - bool simple_instance_registered : 1; - /// If true, get_internals().patients has an entry for this object - bool has_patients : 1; - - /// Initializes all of the above type/values/holders data - void allocate_layout(); - - /// Destroys/deallocates all of the above - void deallocate_layout(); - - /// Returns the value_and_holder wrapper for the given type (or the first, if `find_type` - /// omitted) - value_and_holder get_value_and_holder(const type_info *find_type = nullptr); - - /// Bit values for the non-simple status flags - static constexpr uint8_t status_holder_constructed = 1; - static constexpr uint8_t status_instance_registered = 2; -}; - -static_assert(std::is_standard_layout::value, "Internal error: `pybind11::detail::instance` is not standard layout!"); - -struct overload_hash { - inline size_t operator()(const std::pair& v) const { - size_t value = std::hash()(v.first); - value ^= std::hash()(v.second) + 0x9e3779b9 + (value<<6) + (value>>2); - return value; - } -}; - -// Python loads modules by default with dlopen with the RTLD_LOCAL flag; under libc++ and possibly -// other stls, this means `typeid(A)` from one module won't equal `typeid(A)` from another module -// even when `A` is the same, non-hidden-visibility type (e.g. from a common include). Under -// stdlibc++, this doesn't happen: equality and the type_index hash are based on the type name, -// which works. If not under a known-good stl, provide our own name-based hasher and equality -// functions that use the type name. -#if defined(__GLIBCXX__) -inline bool same_type(const std::type_info &lhs, const std::type_info &rhs) { return lhs == rhs; } -using type_hash = std::hash; -using type_equal_to = std::equal_to; -#else -inline bool same_type(const std::type_info &lhs, const std::type_info &rhs) { - return lhs.name() == rhs.name() || - std::strcmp(lhs.name(), rhs.name()) == 0; -} -struct type_hash { - size_t operator()(const std::type_index &t) const { - size_t hash = 5381; - const char *ptr = t.name(); - while (auto c = static_cast(*ptr++)) - hash = (hash * 33) ^ c; - return hash; - } -}; -struct type_equal_to { - bool operator()(const std::type_index &lhs, const std::type_index &rhs) const { - return lhs.name() == rhs.name() || - std::strcmp(lhs.name(), rhs.name()) == 0; - } -}; -#endif - -template -using type_map = std::unordered_map; - -/// Internal data structure used to track registered instances and types -struct internals { - type_map registered_types_cpp; // std::type_index -> type_info - std::unordered_map> registered_types_py; // PyTypeObject* -> base type_info(s) - std::unordered_multimap registered_instances; // void * -> instance* - std::unordered_set, overload_hash> inactive_overload_cache; - type_map> direct_conversions; - std::unordered_map> patients; - std::forward_list registered_exception_translators; - std::unordered_map shared_data; // Custom data to be shared across extensions - std::vector loader_patient_stack; // Used by `loader_life_support` - PyTypeObject *static_property_type; - PyTypeObject *default_metaclass; - PyObject *instance_base; -#if defined(WITH_THREAD) - decltype(PyThread_create_key()) tstate = 0; // Usually an int but a long on Cygwin64 with Python 3.x - PyInterpreterState *istate = nullptr; -#endif -}; - -/// Return a reference to the current 'internals' information -inline internals &get_internals(); - -/// from __cpp_future__ import (convenient aliases from C++14/17) -#if defined(PYBIND11_CPP14) && (!defined(_MSC_VER) || _MSC_VER >= 1910) -using std::enable_if_t; -using std::conditional_t; -using std::remove_cv_t; -using std::remove_reference_t; -#else -template using enable_if_t = typename std::enable_if::type; -template using conditional_t = typename std::conditional::type; -template using remove_cv_t = typename std::remove_cv::type; -template using remove_reference_t = typename std::remove_reference::type; -#endif - -/// Index sequences -#if defined(PYBIND11_CPP14) -using std::index_sequence; -using std::make_index_sequence; -#else -template struct index_sequence { }; -template struct make_index_sequence_impl : make_index_sequence_impl { }; -template struct make_index_sequence_impl <0, S...> { typedef index_sequence type; }; -template using make_index_sequence = typename make_index_sequence_impl::type; -#endif - -/// Make an index sequence of the indices of true arguments -template struct select_indices_impl { using type = ISeq; }; -template struct select_indices_impl, I, B, Bs...> - : select_indices_impl, index_sequence>, I + 1, Bs...> {}; -template using select_indices = typename select_indices_impl, 0, Bs...>::type; - -/// Backports of std::bool_constant and std::negation to accomodate older compilers -template using bool_constant = std::integral_constant; -template struct negation : bool_constant { }; - -template struct void_t_impl { using type = void; }; -template using void_t = typename void_t_impl::type; - -/// Compile-time all/any/none of that check the boolean value of all template types -#ifdef __cpp_fold_expressions -template using all_of = bool_constant<(Ts::value && ...)>; -template using any_of = bool_constant<(Ts::value || ...)>; -#elif !defined(_MSC_VER) -template struct bools {}; -template using all_of = std::is_same< - bools, - bools>; -template using any_of = negation...>>; -#else -// MSVC has trouble with the above, but supports std::conjunction, which we can use instead (albeit -// at a slight loss of compilation efficiency). -template using all_of = std::conjunction; -template using any_of = std::disjunction; -#endif -template using none_of = negation>; - -template class... Predicates> using satisfies_all_of = all_of...>; -template class... Predicates> using satisfies_any_of = any_of...>; -template class... Predicates> using satisfies_none_of = none_of...>; - -/// Strip the class from a method type -template struct remove_class { }; -template struct remove_class { typedef R type(A...); }; -template struct remove_class { typedef R type(A...); }; - -/// Helper template to strip away type modifiers -template struct intrinsic_type { typedef T type; }; -template struct intrinsic_type { typedef typename intrinsic_type::type type; }; -template struct intrinsic_type { typedef typename intrinsic_type::type type; }; -template struct intrinsic_type { typedef typename intrinsic_type::type type; }; -template struct intrinsic_type { typedef typename intrinsic_type::type type; }; -template struct intrinsic_type { typedef typename intrinsic_type::type type; }; -template struct intrinsic_type { typedef typename intrinsic_type::type type; }; -template using intrinsic_t = typename intrinsic_type::type; - -/// Helper type to replace 'void' in some expressions -struct void_type { }; - -/// Helper template which holds a list of types -template struct type_list { }; - -/// Compile-time integer sum -#ifdef __cpp_fold_expressions -template constexpr size_t constexpr_sum(Ts... ns) { return (0 + ... + size_t{ns}); } -#else -constexpr size_t constexpr_sum() { return 0; } -template -constexpr size_t constexpr_sum(T n, Ts... ns) { return size_t{n} + constexpr_sum(ns...); } -#endif - -NAMESPACE_BEGIN(constexpr_impl) -/// Implementation details for constexpr functions -constexpr int first(int i) { return i; } -template -constexpr int first(int i, T v, Ts... vs) { return v ? i : first(i + 1, vs...); } - -constexpr int last(int /*i*/, int result) { return result; } -template -constexpr int last(int i, int result, T v, Ts... vs) { return last(i + 1, v ? i : result, vs...); } -NAMESPACE_END(constexpr_impl) - -/// Return the index of the first type in Ts which satisfies Predicate. Returns sizeof...(Ts) if -/// none match. -template class Predicate, typename... Ts> -constexpr int constexpr_first() { return constexpr_impl::first(0, Predicate::value...); } - -/// Return the index of the last type in Ts which satisfies Predicate, or -1 if none match. -template class Predicate, typename... Ts> -constexpr int constexpr_last() { return constexpr_impl::last(0, -1, Predicate::value...); } - -/// Return the Nth element from the parameter pack -template -struct pack_element { using type = typename pack_element::type; }; -template -struct pack_element<0, T, Ts...> { using type = T; }; - -/// Return the one and only type which matches the predicate, or Default if none match. -/// If more than one type matches the predicate, fail at compile-time. -template class Predicate, typename Default, typename... Ts> -struct exactly_one { - static constexpr auto found = constexpr_sum(Predicate::value...); - static_assert(found <= 1, "Found more than one type matching the predicate"); - - static constexpr auto index = found ? constexpr_first() : 0; - using type = conditional_t::type, Default>; -}; -template class P, typename Default> -struct exactly_one { using type = Default; }; - -template class Predicate, typename Default, typename... Ts> -using exactly_one_t = typename exactly_one::type; - -/// Defer the evaluation of type T until types Us are instantiated -template struct deferred_type { using type = T; }; -template using deferred_t = typename deferred_type::type; - -/// Like is_base_of, but requires a strict base (i.e. `is_strict_base_of::value == false`, -/// unlike `std::is_base_of`) -template using is_strict_base_of = bool_constant< - std::is_base_of::value && !std::is_same::value>; - -template class Base> -struct is_template_base_of_impl { - template static std::true_type check(Base *); - static std::false_type check(...); -}; - -/// Check if a template is the base of a type. For example: -/// `is_template_base_of` is true if `struct T : Base {}` where U can be anything -template class Base, typename T> -#if !defined(_MSC_VER) -using is_template_base_of = decltype(is_template_base_of_impl::check((remove_cv_t*)nullptr)); -#else // MSVC2015 has trouble with decltype in template aliases -struct is_template_base_of : decltype(is_template_base_of_impl::check((remove_cv_t*)nullptr)) { }; -#endif - -/// Check if T is an instantiation of the template `Class`. For example: -/// `is_instantiation` is true if `T == shared_ptr` where U can be anything. -template class Class, typename T> -struct is_instantiation : std::false_type { }; -template class Class, typename... Us> -struct is_instantiation> : std::true_type { }; - -/// Check if T is std::shared_ptr where U can be anything -template using is_shared_ptr = is_instantiation; - -/// Check if T looks like an input iterator -template struct is_input_iterator : std::false_type {}; -template -struct is_input_iterator()), decltype(++std::declval())>> - : std::true_type {}; - -/// Ignore that a variable is unused in compiler warnings -inline void ignore_unused(const int *) { } - -/// Apply a function over each element of a parameter pack -#ifdef __cpp_fold_expressions -#define PYBIND11_EXPAND_SIDE_EFFECTS(PATTERN) (((PATTERN), void()), ...) -#else -using expand_side_effects = bool[]; -#define PYBIND11_EXPAND_SIDE_EFFECTS(PATTERN) pybind11::detail::expand_side_effects{ ((PATTERN), void(), false)..., false } -#endif - -NAMESPACE_END(detail) - -/// Returns a named pointer that is shared among all extension modules (using the same -/// pybind11 version) running in the current interpreter. Names starting with underscores -/// are reserved for internal usage. Returns `nullptr` if no matching entry was found. -inline PYBIND11_NOINLINE void* get_shared_data(const std::string& name) { - auto& internals = detail::get_internals(); - auto it = internals.shared_data.find(name); - return it != internals.shared_data.end() ? it->second : nullptr; -} - -/// Set the shared data that can be later recovered by `get_shared_data()`. -inline PYBIND11_NOINLINE void *set_shared_data(const std::string& name, void *data) { - detail::get_internals().shared_data[name] = data; - return data; -} - -/// Returns a typed reference to a shared data entry (by using `get_shared_data()`) if -/// such entry exists. Otherwise, a new object of default-constructible type `T` is -/// added to the shared data under the given name and a reference to it is returned. -template T& get_or_create_shared_data(const std::string& name) { - auto& internals = detail::get_internals(); - auto it = internals.shared_data.find(name); - T* ptr = (T*) (it != internals.shared_data.end() ? it->second : nullptr); - if (!ptr) { - ptr = new T(); - internals.shared_data[name] = ptr; - } - return *ptr; -} - -/// C++ bindings of builtin Python exceptions -class builtin_exception : public std::runtime_error { -public: - using std::runtime_error::runtime_error; - /// Set the error using the Python C API - virtual void set_error() const = 0; -}; - -#define PYBIND11_RUNTIME_EXCEPTION(name, type) \ - class name : public builtin_exception { public: \ - using builtin_exception::builtin_exception; \ - name() : name("") { } \ - void set_error() const override { PyErr_SetString(type, what()); } \ - }; - -PYBIND11_RUNTIME_EXCEPTION(stop_iteration, PyExc_StopIteration) -PYBIND11_RUNTIME_EXCEPTION(index_error, PyExc_IndexError) -PYBIND11_RUNTIME_EXCEPTION(key_error, PyExc_KeyError) -PYBIND11_RUNTIME_EXCEPTION(value_error, PyExc_ValueError) -PYBIND11_RUNTIME_EXCEPTION(type_error, PyExc_TypeError) -PYBIND11_RUNTIME_EXCEPTION(cast_error, PyExc_RuntimeError) /// Thrown when pybind11::cast or handle::call fail due to a type casting error -PYBIND11_RUNTIME_EXCEPTION(reference_cast_error, PyExc_RuntimeError) /// Used internally - -[[noreturn]] PYBIND11_NOINLINE inline void pybind11_fail(const char *reason) { throw std::runtime_error(reason); } -[[noreturn]] PYBIND11_NOINLINE inline void pybind11_fail(const std::string &reason) { throw std::runtime_error(reason); } - -template struct format_descriptor { }; - -NAMESPACE_BEGIN(detail) -// Returns the index of the given type in the type char array below, and in the list in numpy.h -// The order here is: bool; 8 ints ((signed,unsigned)x(8,16,32,64)bits); float,double,long double; -// complex float,double,long double. Note that the long double types only participate when long -// double is actually longer than double (it isn't under MSVC). -// NB: not only the string below but also complex.h and numpy.h rely on this order. -template struct is_fmt_numeric { static constexpr bool value = false; }; -template struct is_fmt_numeric::value>> { - static constexpr bool value = true; - static constexpr int index = std::is_same::value ? 0 : 1 + ( - std::is_integral::value ? detail::log2(sizeof(T))*2 + std::is_unsigned::value : 8 + ( - std::is_same::value ? 1 : std::is_same::value ? 2 : 0)); -}; -NAMESPACE_END(detail) - -template struct format_descriptor::value>> { - static constexpr const char c = "?bBhHiIqQfdg"[detail::is_fmt_numeric::index]; - static constexpr const char value[2] = { c, '\0' }; - static std::string format() { return std::string(1, c); } -}; - -template constexpr const char format_descriptor< - T, detail::enable_if_t::value>>::value[2]; - -/// RAII wrapper that temporarily clears any Python error state -struct error_scope { - PyObject *type, *value, *trace; - error_scope() { PyErr_Fetch(&type, &value, &trace); } - ~error_scope() { PyErr_Restore(type, value, trace); } -}; - -/// Dummy destructor wrapper that can be used to expose classes with a private destructor -struct nodelete { template void operator()(T*) { } }; - -// overload_cast requires variable templates: C++14 -#if defined(PYBIND11_CPP14) -#define PYBIND11_OVERLOAD_CAST 1 - -NAMESPACE_BEGIN(detail) -template -struct overload_cast_impl { - template - constexpr auto operator()(Return (*pf)(Args...)) const noexcept - -> decltype(pf) { return pf; } - - template - constexpr auto operator()(Return (Class::*pmf)(Args...), std::false_type = {}) const noexcept - -> decltype(pmf) { return pmf; } - - template - constexpr auto operator()(Return (Class::*pmf)(Args...) const, std::true_type) const noexcept - -> decltype(pmf) { return pmf; } -}; -NAMESPACE_END(detail) - -/// Syntax sugar for resolving overloaded function pointers: -/// - regular: static_cast(&Class::func) -/// - sweet: overload_cast(&Class::func) -template -static constexpr detail::overload_cast_impl overload_cast = {}; -// MSVC 2015 only accepts this particular initialization syntax for this variable template. - -/// Const member function selector for overload_cast -/// - regular: static_cast(&Class::func) -/// - sweet: overload_cast(&Class::func, const_) -static constexpr auto const_ = std::true_type{}; - -#else // no overload_cast: providing something that static_assert-fails: -template struct overload_cast { - static_assert(detail::deferred_t::value, - "pybind11::overload_cast<...> requires compiling in C++14 mode"); -}; -#endif // overload_cast - -NAMESPACE_BEGIN(detail) - -// Adaptor for converting arbitrary container arguments into a vector; implicitly convertible from -// any standard container (or C-style array) supporting std::begin/std::end, any singleton -// arithmetic type (if T is arithmetic), or explicitly constructible from an iterator pair. -template -class any_container { - std::vector v; -public: - any_container() = default; - - // Can construct from a pair of iterators - template ::value>> - any_container(It first, It last) : v(first, last) { } - - // Implicit conversion constructor from any arbitrary container type with values convertible to T - template ())), T>::value>> - any_container(const Container &c) : any_container(std::begin(c), std::end(c)) { } - - // initializer_list's aren't deducible, so don't get matched by the above template; we need this - // to explicitly allow implicit conversion from one: - template ::value>> - any_container(const std::initializer_list &c) : any_container(c.begin(), c.end()) { } - - // Avoid copying if given an rvalue vector of the correct type. - any_container(std::vector &&v) : v(std::move(v)) { } - - // Moves the vector out of an rvalue any_container - operator std::vector &&() && { return std::move(v); } - - // Dereferencing obtains a reference to the underlying vector - std::vector &operator*() { return v; } - const std::vector &operator*() const { return v; } - - // -> lets you call methods on the underlying vector - std::vector *operator->() { return &v; } - const std::vector *operator->() const { return &v; } -}; - -NAMESPACE_END(detail) - - - -NAMESPACE_END(pybind11) diff --git a/ptocr/postprocess/lanms/include/pybind11/complex.h b/ptocr/postprocess/lanms/include/pybind11/complex.h deleted file mode 100644 index 7d422e2..0000000 --- a/ptocr/postprocess/lanms/include/pybind11/complex.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - pybind11/complex.h: Complex number support - - Copyright (c) 2016 Wenzel Jakob - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#include "pybind11.h" -#include - -/// glibc defines I as a macro which breaks things, e.g., boost template names -#ifdef I -# undef I -#endif - -NAMESPACE_BEGIN(pybind11) - -template struct format_descriptor, detail::enable_if_t::value>> { - static constexpr const char c = format_descriptor::c; - static constexpr const char value[3] = { 'Z', c, '\0' }; - static std::string format() { return std::string(value); } -}; - -template constexpr const char format_descriptor< - std::complex, detail::enable_if_t::value>>::value[3]; - -NAMESPACE_BEGIN(detail) - -template struct is_fmt_numeric, detail::enable_if_t::value>> { - static constexpr bool value = true; - static constexpr int index = is_fmt_numeric::index + 3; -}; - -template class type_caster> { -public: - bool load(handle src, bool convert) { - if (!src) - return false; - if (!convert && !PyComplex_Check(src.ptr())) - return false; - Py_complex result = PyComplex_AsCComplex(src.ptr()); - if (result.real == -1.0 && PyErr_Occurred()) { - PyErr_Clear(); - return false; - } - value = std::complex((T) result.real, (T) result.imag); - return true; - } - - static handle cast(const std::complex &src, return_value_policy /* policy */, handle /* parent */) { - return PyComplex_FromDoubles((double) src.real(), (double) src.imag()); - } - - PYBIND11_TYPE_CASTER(std::complex, _("complex")); -}; -NAMESPACE_END(detail) -NAMESPACE_END(pybind11) diff --git a/ptocr/postprocess/lanms/include/pybind11/descr.h b/ptocr/postprocess/lanms/include/pybind11/descr.h deleted file mode 100644 index 23a099c..0000000 --- a/ptocr/postprocess/lanms/include/pybind11/descr.h +++ /dev/null @@ -1,185 +0,0 @@ -/* - pybind11/descr.h: Helper type for concatenating type signatures - either at runtime (C++11) or compile time (C++14) - - Copyright (c) 2016 Wenzel Jakob - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#include "common.h" - -NAMESPACE_BEGIN(pybind11) -NAMESPACE_BEGIN(detail) - -/* Concatenate type signatures at compile time using C++14 */ -#if defined(PYBIND11_CPP14) && !defined(_MSC_VER) -#define PYBIND11_CONSTEXPR_DESCR - -template class descr { - template friend class descr; -public: - constexpr descr(char const (&text) [Size1+1], const std::type_info * const (&types)[Size2+1]) - : descr(text, types, - make_index_sequence(), - make_index_sequence()) { } - - constexpr const char *text() const { return m_text; } - constexpr const std::type_info * const * types() const { return m_types; } - - template - constexpr descr operator+(const descr &other) const { - return concat(other, - make_index_sequence(), - make_index_sequence(), - make_index_sequence(), - make_index_sequence()); - } - -protected: - template - constexpr descr( - char const (&text) [Size1+1], - const std::type_info * const (&types) [Size2+1], - index_sequence, index_sequence) - : m_text{text[Indices1]..., '\0'}, - m_types{types[Indices2]..., nullptr } {} - - template - constexpr descr - concat(const descr &other, - index_sequence, index_sequence, - index_sequence, index_sequence) const { - return descr( - { m_text[Indices1]..., other.m_text[OtherIndices1]..., '\0' }, - { m_types[Indices2]..., other.m_types[OtherIndices2]..., nullptr } - ); - } - -protected: - char m_text[Size1 + 1]; - const std::type_info * m_types[Size2 + 1]; -}; - -template constexpr descr _(char const(&text)[Size]) { - return descr(text, { nullptr }); -} - -template struct int_to_str : int_to_str { }; -template struct int_to_str<0, Digits...> { - static constexpr auto digits = descr({ ('0' + Digits)..., '\0' }, { nullptr }); -}; - -// Ternary description (like std::conditional) -template -constexpr enable_if_t> _(char const(&text1)[Size1], char const(&)[Size2]) { - return _(text1); -} -template -constexpr enable_if_t> _(char const(&)[Size1], char const(&text2)[Size2]) { - return _(text2); -} -template -constexpr enable_if_t> _(descr d, descr) { return d; } -template -constexpr enable_if_t> _(descr, descr d) { return d; } - -template auto constexpr _() -> decltype(int_to_str::digits) { - return int_to_str::digits; -} - -template constexpr descr<1, 1> _() { - return descr<1, 1>({ '%', '\0' }, { &typeid(Type), nullptr }); -} - -inline constexpr descr<0, 0> concat() { return _(""); } -template auto constexpr concat(descr descr) { return descr; } -template auto constexpr concat(descr descr, Args&&... args) { return descr + _(", ") + concat(args...); } -template auto constexpr type_descr(descr descr) { return _("{") + descr + _("}"); } - -#define PYBIND11_DESCR constexpr auto - -#else /* Simpler C++11 implementation based on run-time memory allocation and copying */ - -class descr { -public: - PYBIND11_NOINLINE descr(const char *text, const std::type_info * const * types) { - size_t nChars = len(text), nTypes = len(types); - m_text = new char[nChars]; - m_types = new const std::type_info *[nTypes]; - memcpy(m_text, text, nChars * sizeof(char)); - memcpy(m_types, types, nTypes * sizeof(const std::type_info *)); - } - - PYBIND11_NOINLINE descr operator+(descr &&d2) && { - descr r; - - size_t nChars1 = len(m_text), nTypes1 = len(m_types); - size_t nChars2 = len(d2.m_text), nTypes2 = len(d2.m_types); - - r.m_text = new char[nChars1 + nChars2 - 1]; - r.m_types = new const std::type_info *[nTypes1 + nTypes2 - 1]; - memcpy(r.m_text, m_text, (nChars1-1) * sizeof(char)); - memcpy(r.m_text + nChars1 - 1, d2.m_text, nChars2 * sizeof(char)); - memcpy(r.m_types, m_types, (nTypes1-1) * sizeof(std::type_info *)); - memcpy(r.m_types + nTypes1 - 1, d2.m_types, nTypes2 * sizeof(std::type_info *)); - - delete[] m_text; delete[] m_types; - delete[] d2.m_text; delete[] d2.m_types; - - return r; - } - - char *text() { return m_text; } - const std::type_info * * types() { return m_types; } - -protected: - PYBIND11_NOINLINE descr() { } - - template static size_t len(const T *ptr) { // return length including null termination - const T *it = ptr; - while (*it++ != (T) 0) - ; - return static_cast(it - ptr); - } - - const std::type_info **m_types = nullptr; - char *m_text = nullptr; -}; - -/* The 'PYBIND11_NOINLINE inline' combinations below are intentional to get the desired linkage while producing as little object code as possible */ - -PYBIND11_NOINLINE inline descr _(const char *text) { - const std::type_info *types[1] = { nullptr }; - return descr(text, types); -} - -template PYBIND11_NOINLINE enable_if_t _(const char *text1, const char *) { return _(text1); } -template PYBIND11_NOINLINE enable_if_t _(char const *, const char *text2) { return _(text2); } -template PYBIND11_NOINLINE enable_if_t _(descr d, descr) { return d; } -template PYBIND11_NOINLINE enable_if_t _(descr, descr d) { return d; } - -template PYBIND11_NOINLINE descr _() { - const std::type_info *types[2] = { &typeid(Type), nullptr }; - return descr("%", types); -} - -template PYBIND11_NOINLINE descr _() { - const std::type_info *types[1] = { nullptr }; - return descr(std::to_string(Size).c_str(), types); -} - -PYBIND11_NOINLINE inline descr concat() { return _(""); } -PYBIND11_NOINLINE inline descr concat(descr &&d) { return d; } -template PYBIND11_NOINLINE descr concat(descr &&d, Args&&... args) { return std::move(d) + _(", ") + concat(std::forward(args)...); } -PYBIND11_NOINLINE inline descr type_descr(descr&& d) { return _("{") + std::move(d) + _("}"); } - -#define PYBIND11_DESCR ::pybind11::detail::descr -#endif - -NAMESPACE_END(detail) -NAMESPACE_END(pybind11) diff --git a/ptocr/postprocess/lanms/include/pybind11/eigen.h b/ptocr/postprocess/lanms/include/pybind11/eigen.h deleted file mode 100644 index fc07051..0000000 --- a/ptocr/postprocess/lanms/include/pybind11/eigen.h +++ /dev/null @@ -1,610 +0,0 @@ -/* - pybind11/eigen.h: Transparent conversion for dense and sparse Eigen matrices - - Copyright (c) 2016 Wenzel Jakob - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#include "numpy.h" - -#if defined(__INTEL_COMPILER) -# pragma warning(disable: 1682) // implicit conversion of a 64-bit integral type to a smaller integral type (potential portability problem) -#elif defined(__GNUG__) || defined(__clang__) -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wconversion" -# pragma GCC diagnostic ignored "-Wdeprecated-declarations" -# if __GNUC__ >= 7 -# pragma GCC diagnostic ignored "-Wint-in-bool-context" -# endif -#endif - -#include -#include - -#if defined(_MSC_VER) -# pragma warning(push) -# pragma warning(disable: 4127) // warning C4127: Conditional expression is constant -#endif - -// Eigen prior to 3.2.7 doesn't have proper move constructors--but worse, some classes get implicit -// move constructors that break things. We could detect this an explicitly copy, but an extra copy -// of matrices seems highly undesirable. -static_assert(EIGEN_VERSION_AT_LEAST(3,2,7), "Eigen support in pybind11 requires Eigen >= 3.2.7"); - -NAMESPACE_BEGIN(pybind11) - -// Provide a convenience alias for easier pass-by-ref usage with fully dynamic strides: -using EigenDStride = Eigen::Stride; -template using EigenDRef = Eigen::Ref; -template using EigenDMap = Eigen::Map; - -NAMESPACE_BEGIN(detail) - -#if EIGEN_VERSION_AT_LEAST(3,3,0) -using EigenIndex = Eigen::Index; -#else -using EigenIndex = EIGEN_DEFAULT_DENSE_INDEX_TYPE; -#endif - -// Matches Eigen::Map, Eigen::Ref, blocks, etc: -template using is_eigen_dense_map = all_of, std::is_base_of, T>>; -template using is_eigen_mutable_map = std::is_base_of, T>; -template using is_eigen_dense_plain = all_of>, is_template_base_of>; -template using is_eigen_sparse = is_template_base_of; -// Test for objects inheriting from EigenBase that aren't captured by the above. This -// basically covers anything that can be assigned to a dense matrix but that don't have a typical -// matrix data layout that can be copied from their .data(). For example, DiagonalMatrix and -// SelfAdjointView fall into this category. -template using is_eigen_other = all_of< - is_template_base_of, - negation, is_eigen_dense_plain, is_eigen_sparse>> ->; - -// Captures numpy/eigen conformability status (returned by EigenProps::conformable()): -template struct EigenConformable { - bool conformable = false; - EigenIndex rows = 0, cols = 0; - EigenDStride stride{0, 0}; // Only valid if negativestrides is false! - bool negativestrides = false; // If true, do not use stride! - - EigenConformable(bool fits = false) : conformable{fits} {} - // Matrix type: - EigenConformable(EigenIndex r, EigenIndex c, - EigenIndex rstride, EigenIndex cstride) : - conformable{true}, rows{r}, cols{c} { - // TODO: when Eigen bug #747 is fixed, remove the tests for non-negativity. http://eigen.tuxfamily.org/bz/show_bug.cgi?id=747 - if (rstride < 0 || cstride < 0) { - negativestrides = true; - } else { - stride = {EigenRowMajor ? rstride : cstride /* outer stride */, - EigenRowMajor ? cstride : rstride /* inner stride */ }; - } - } - // Vector type: - EigenConformable(EigenIndex r, EigenIndex c, EigenIndex stride) - : EigenConformable(r, c, r == 1 ? c*stride : stride, c == 1 ? r : r*stride) {} - - template bool stride_compatible() const { - // To have compatible strides, we need (on both dimensions) one of fully dynamic strides, - // matching strides, or a dimension size of 1 (in which case the stride value is irrelevant) - return - !negativestrides && - (props::inner_stride == Eigen::Dynamic || props::inner_stride == stride.inner() || - (EigenRowMajor ? cols : rows) == 1) && - (props::outer_stride == Eigen::Dynamic || props::outer_stride == stride.outer() || - (EigenRowMajor ? rows : cols) == 1); - } - operator bool() const { return conformable; } -}; - -template struct eigen_extract_stride { using type = Type; }; -template -struct eigen_extract_stride> { using type = StrideType; }; -template -struct eigen_extract_stride> { using type = StrideType; }; - -// Helper struct for extracting information from an Eigen type -template struct EigenProps { - using Type = Type_; - using Scalar = typename Type::Scalar; - using StrideType = typename eigen_extract_stride::type; - static constexpr EigenIndex - rows = Type::RowsAtCompileTime, - cols = Type::ColsAtCompileTime, - size = Type::SizeAtCompileTime; - static constexpr bool - row_major = Type::IsRowMajor, - vector = Type::IsVectorAtCompileTime, // At least one dimension has fixed size 1 - fixed_rows = rows != Eigen::Dynamic, - fixed_cols = cols != Eigen::Dynamic, - fixed = size != Eigen::Dynamic, // Fully-fixed size - dynamic = !fixed_rows && !fixed_cols; // Fully-dynamic size - - template using if_zero = std::integral_constant; - static constexpr EigenIndex inner_stride = if_zero::value, - outer_stride = if_zero::value; - static constexpr bool dynamic_stride = inner_stride == Eigen::Dynamic && outer_stride == Eigen::Dynamic; - static constexpr bool requires_row_major = !dynamic_stride && !vector && (row_major ? inner_stride : outer_stride) == 1; - static constexpr bool requires_col_major = !dynamic_stride && !vector && (row_major ? outer_stride : inner_stride) == 1; - - // Takes an input array and determines whether we can make it fit into the Eigen type. If - // the array is a vector, we attempt to fit it into either an Eigen 1xN or Nx1 vector - // (preferring the latter if it will fit in either, i.e. for a fully dynamic matrix type). - static EigenConformable conformable(const array &a) { - const auto dims = a.ndim(); - if (dims < 1 || dims > 2) - return false; - - if (dims == 2) { // Matrix type: require exact match (or dynamic) - - EigenIndex - np_rows = a.shape(0), - np_cols = a.shape(1), - np_rstride = a.strides(0) / static_cast(sizeof(Scalar)), - np_cstride = a.strides(1) / static_cast(sizeof(Scalar)); - if ((fixed_rows && np_rows != rows) || (fixed_cols && np_cols != cols)) - return false; - - return {np_rows, np_cols, np_rstride, np_cstride}; - } - - // Otherwise we're storing an n-vector. Only one of the strides will be used, but whichever - // is used, we want the (single) numpy stride value. - const EigenIndex n = a.shape(0), - stride = a.strides(0) / static_cast(sizeof(Scalar)); - - if (vector) { // Eigen type is a compile-time vector - if (fixed && size != n) - return false; // Vector size mismatch - return {rows == 1 ? 1 : n, cols == 1 ? 1 : n, stride}; - } - else if (fixed) { - // The type has a fixed size, but is not a vector: abort - return false; - } - else if (fixed_cols) { - // Since this isn't a vector, cols must be != 1. We allow this only if it exactly - // equals the number of elements (rows is Dynamic, and so 1 row is allowed). - if (cols != n) return false; - return {1, n, stride}; - } - else { - // Otherwise it's either fully dynamic, or column dynamic; both become a column vector - if (fixed_rows && rows != n) return false; - return {n, 1, stride}; - } - } - - static PYBIND11_DESCR descriptor() { - constexpr bool show_writeable = is_eigen_dense_map::value && is_eigen_mutable_map::value; - constexpr bool show_order = is_eigen_dense_map::value; - constexpr bool show_c_contiguous = show_order && requires_row_major; - constexpr bool show_f_contiguous = !show_c_contiguous && show_order && requires_col_major; - - return type_descr(_("numpy.ndarray[") + npy_format_descriptor::name() + - _("[") + _(_<(size_t) rows>(), _("m")) + - _(", ") + _(_<(size_t) cols>(), _("n")) + - _("]") + - // For a reference type (e.g. Ref) we have other constraints that might need to be - // satisfied: writeable=True (for a mutable reference), and, depending on the map's stride - // options, possibly f_contiguous or c_contiguous. We include them in the descriptor output - // to provide some hint as to why a TypeError is occurring (otherwise it can be confusing to - // see that a function accepts a 'numpy.ndarray[float64[3,2]]' and an error message that you - // *gave* a numpy.ndarray of the right type and dimensions. - _(", flags.writeable", "") + - _(", flags.c_contiguous", "") + - _(", flags.f_contiguous", "") + - _("]") - ); - } -}; - -// Casts an Eigen type to numpy array. If given a base, the numpy array references the src data, -// otherwise it'll make a copy. writeable lets you turn off the writeable flag for the array. -template handle eigen_array_cast(typename props::Type const &src, handle base = handle(), bool writeable = true) { - constexpr ssize_t elem_size = sizeof(typename props::Scalar); - array a; - if (props::vector) - a = array({ src.size() }, { elem_size * src.innerStride() }, src.data(), base); - else - a = array({ src.rows(), src.cols() }, { elem_size * src.rowStride(), elem_size * src.colStride() }, - src.data(), base); - - if (!writeable) - array_proxy(a.ptr())->flags &= ~detail::npy_api::NPY_ARRAY_WRITEABLE_; - - return a.release(); -} - -// Takes an lvalue ref to some Eigen type and a (python) base object, creating a numpy array that -// reference the Eigen object's data with `base` as the python-registered base class (if omitted, -// the base will be set to None, and lifetime management is up to the caller). The numpy array is -// non-writeable if the given type is const. -template -handle eigen_ref_array(Type &src, handle parent = none()) { - // none here is to get past array's should-we-copy detection, which currently always - // copies when there is no base. Setting the base to None should be harmless. - return eigen_array_cast(src, parent, !std::is_const::value); -} - -// Takes a pointer to some dense, plain Eigen type, builds a capsule around it, then returns a numpy -// array that references the encapsulated data with a python-side reference to the capsule to tie -// its destruction to that of any dependent python objects. Const-ness is determined by whether or -// not the Type of the pointer given is const. -template ::value>> -handle eigen_encapsulate(Type *src) { - capsule base(src, [](void *o) { delete static_cast(o); }); - return eigen_ref_array(*src, base); -} - -// Type caster for regular, dense matrix types (e.g. MatrixXd), but not maps/refs/etc. of dense -// types. -template -struct type_caster::value>> { - using Scalar = typename Type::Scalar; - using props = EigenProps; - - bool load(handle src, bool convert) { - // If we're in no-convert mode, only load if given an array of the correct type - if (!convert && !isinstance>(src)) - return false; - - // Coerce into an array, but don't do type conversion yet; the copy below handles it. - auto buf = array::ensure(src); - - if (!buf) - return false; - - auto dims = buf.ndim(); - if (dims < 1 || dims > 2) - return false; - - auto fits = props::conformable(buf); - if (!fits) - return false; - - // Allocate the new type, then build a numpy reference into it - value = Type(fits.rows, fits.cols); - auto ref = reinterpret_steal(eigen_ref_array(value)); - if (dims == 1) ref = ref.squeeze(); - - int result = detail::npy_api::get().PyArray_CopyInto_(ref.ptr(), buf.ptr()); - - if (result < 0) { // Copy failed! - PyErr_Clear(); - return false; - } - - return true; - } - -private: - - // Cast implementation - template - static handle cast_impl(CType *src, return_value_policy policy, handle parent) { - switch (policy) { - case return_value_policy::take_ownership: - case return_value_policy::automatic: - return eigen_encapsulate(src); - case return_value_policy::move: - return eigen_encapsulate(new CType(std::move(*src))); - case return_value_policy::copy: - return eigen_array_cast(*src); - case return_value_policy::reference: - case return_value_policy::automatic_reference: - return eigen_ref_array(*src); - case return_value_policy::reference_internal: - return eigen_ref_array(*src, parent); - default: - throw cast_error("unhandled return_value_policy: should not happen!"); - }; - } - -public: - - // Normal returned non-reference, non-const value: - static handle cast(Type &&src, return_value_policy /* policy */, handle parent) { - return cast_impl(&src, return_value_policy::move, parent); - } - // If you return a non-reference const, we mark the numpy array readonly: - static handle cast(const Type &&src, return_value_policy /* policy */, handle parent) { - return cast_impl(&src, return_value_policy::move, parent); - } - // lvalue reference return; default (automatic) becomes copy - static handle cast(Type &src, return_value_policy policy, handle parent) { - if (policy == return_value_policy::automatic || policy == return_value_policy::automatic_reference) - policy = return_value_policy::copy; - return cast_impl(&src, policy, parent); - } - // const lvalue reference return; default (automatic) becomes copy - static handle cast(const Type &src, return_value_policy policy, handle parent) { - if (policy == return_value_policy::automatic || policy == return_value_policy::automatic_reference) - policy = return_value_policy::copy; - return cast(&src, policy, parent); - } - // non-const pointer return - static handle cast(Type *src, return_value_policy policy, handle parent) { - return cast_impl(src, policy, parent); - } - // const pointer return - static handle cast(const Type *src, return_value_policy policy, handle parent) { - return cast_impl(src, policy, parent); - } - - static PYBIND11_DESCR name() { return props::descriptor(); } - - operator Type*() { return &value; } - operator Type&() { return value; } - operator Type&&() && { return std::move(value); } - template using cast_op_type = movable_cast_op_type; - -private: - Type value; -}; - -// Eigen Ref/Map classes have slightly different policy requirements, meaning we don't want to force -// `move` when a Ref/Map rvalue is returned; we treat Ref<> sort of like a pointer (we care about -// the underlying data, not the outer shell). -template -struct return_value_policy_override::value>> { - static return_value_policy policy(return_value_policy p) { return p; } -}; - -// Base class for casting reference/map/block/etc. objects back to python. -template struct eigen_map_caster { -private: - using props = EigenProps; - -public: - - // Directly referencing a ref/map's data is a bit dangerous (whatever the map/ref points to has - // to stay around), but we'll allow it under the assumption that you know what you're doing (and - // have an appropriate keep_alive in place). We return a numpy array pointing directly at the - // ref's data (The numpy array ends up read-only if the ref was to a const matrix type.) Note - // that this means you need to ensure you don't destroy the object in some other way (e.g. with - // an appropriate keep_alive, or with a reference to a statically allocated matrix). - static handle cast(const MapType &src, return_value_policy policy, handle parent) { - switch (policy) { - case return_value_policy::copy: - return eigen_array_cast(src); - case return_value_policy::reference_internal: - return eigen_array_cast(src, parent, is_eigen_mutable_map::value); - case return_value_policy::reference: - case return_value_policy::automatic: - case return_value_policy::automatic_reference: - return eigen_array_cast(src, none(), is_eigen_mutable_map::value); - default: - // move, take_ownership don't make any sense for a ref/map: - pybind11_fail("Invalid return_value_policy for Eigen Map/Ref/Block type"); - } - } - - static PYBIND11_DESCR name() { return props::descriptor(); } - - // Explicitly delete these: support python -> C++ conversion on these (i.e. these can be return - // types but not bound arguments). We still provide them (with an explicitly delete) so that - // you end up here if you try anyway. - bool load(handle, bool) = delete; - operator MapType() = delete; - template using cast_op_type = MapType; -}; - -// We can return any map-like object (but can only load Refs, specialized next): -template struct type_caster::value>> - : eigen_map_caster {}; - -// Loader for Ref<...> arguments. See the documentation for info on how to make this work without -// copying (it requires some extra effort in many cases). -template -struct type_caster< - Eigen::Ref, - enable_if_t>::value> -> : public eigen_map_caster> { -private: - using Type = Eigen::Ref; - using props = EigenProps; - using Scalar = typename props::Scalar; - using MapType = Eigen::Map; - using Array = array_t; - static constexpr bool need_writeable = is_eigen_mutable_map::value; - // Delay construction (these have no default constructor) - std::unique_ptr map; - std::unique_ptr ref; - // Our array. When possible, this is just a numpy array pointing to the source data, but - // sometimes we can't avoid copying (e.g. input is not a numpy array at all, has an incompatible - // layout, or is an array of a type that needs to be converted). Using a numpy temporary - // (rather than an Eigen temporary) saves an extra copy when we need both type conversion and - // storage order conversion. (Note that we refuse to use this temporary copy when loading an - // argument for a Ref with M non-const, i.e. a read-write reference). - Array copy_or_ref; -public: - bool load(handle src, bool convert) { - // First check whether what we have is already an array of the right type. If not, we can't - // avoid a copy (because the copy is also going to do type conversion). - bool need_copy = !isinstance(src); - - EigenConformable fits; - if (!need_copy) { - // We don't need a converting copy, but we also need to check whether the strides are - // compatible with the Ref's stride requirements - Array aref = reinterpret_borrow(src); - - if (aref && (!need_writeable || aref.writeable())) { - fits = props::conformable(aref); - if (!fits) return false; // Incompatible dimensions - if (!fits.template stride_compatible()) - need_copy = true; - else - copy_or_ref = std::move(aref); - } - else { - need_copy = true; - } - } - - if (need_copy) { - // We need to copy: If we need a mutable reference, or we're not supposed to convert - // (either because we're in the no-convert overload pass, or because we're explicitly - // instructed not to copy (via `py::arg().noconvert()`) we have to fail loading. - if (!convert || need_writeable) return false; - - Array copy = Array::ensure(src); - if (!copy) return false; - fits = props::conformable(copy); - if (!fits || !fits.template stride_compatible()) - return false; - copy_or_ref = std::move(copy); - loader_life_support::add_patient(copy_or_ref); - } - - ref.reset(); - map.reset(new MapType(data(copy_or_ref), fits.rows, fits.cols, make_stride(fits.stride.outer(), fits.stride.inner()))); - ref.reset(new Type(*map)); - - return true; - } - - operator Type*() { return ref.get(); } - operator Type&() { return *ref; } - template using cast_op_type = pybind11::detail::cast_op_type<_T>; - -private: - template ::value, int> = 0> - Scalar *data(Array &a) { return a.mutable_data(); } - - template ::value, int> = 0> - const Scalar *data(Array &a) { return a.data(); } - - // Attempt to figure out a constructor of `Stride` that will work. - // If both strides are fixed, use a default constructor: - template using stride_ctor_default = bool_constant< - S::InnerStrideAtCompileTime != Eigen::Dynamic && S::OuterStrideAtCompileTime != Eigen::Dynamic && - std::is_default_constructible::value>; - // Otherwise, if there is a two-index constructor, assume it is (outer,inner) like - // Eigen::Stride, and use it: - template using stride_ctor_dual = bool_constant< - !stride_ctor_default::value && std::is_constructible::value>; - // Otherwise, if there is a one-index constructor, and just one of the strides is dynamic, use - // it (passing whichever stride is dynamic). - template using stride_ctor_outer = bool_constant< - !any_of, stride_ctor_dual>::value && - S::OuterStrideAtCompileTime == Eigen::Dynamic && S::InnerStrideAtCompileTime != Eigen::Dynamic && - std::is_constructible::value>; - template using stride_ctor_inner = bool_constant< - !any_of, stride_ctor_dual>::value && - S::InnerStrideAtCompileTime == Eigen::Dynamic && S::OuterStrideAtCompileTime != Eigen::Dynamic && - std::is_constructible::value>; - - template ::value, int> = 0> - static S make_stride(EigenIndex, EigenIndex) { return S(); } - template ::value, int> = 0> - static S make_stride(EigenIndex outer, EigenIndex inner) { return S(outer, inner); } - template ::value, int> = 0> - static S make_stride(EigenIndex outer, EigenIndex) { return S(outer); } - template ::value, int> = 0> - static S make_stride(EigenIndex, EigenIndex inner) { return S(inner); } - -}; - -// type_caster for special matrix types (e.g. DiagonalMatrix), which are EigenBase, but not -// EigenDense (i.e. they don't have a data(), at least not with the usual matrix layout). -// load() is not supported, but we can cast them into the python domain by first copying to a -// regular Eigen::Matrix, then casting that. -template -struct type_caster::value>> { -protected: - using Matrix = Eigen::Matrix; - using props = EigenProps; -public: - static handle cast(const Type &src, return_value_policy /* policy */, handle /* parent */) { - handle h = eigen_encapsulate(new Matrix(src)); - return h; - } - static handle cast(const Type *src, return_value_policy policy, handle parent) { return cast(*src, policy, parent); } - - static PYBIND11_DESCR name() { return props::descriptor(); } - - // Explicitly delete these: support python -> C++ conversion on these (i.e. these can be return - // types but not bound arguments). We still provide them (with an explicitly delete) so that - // you end up here if you try anyway. - bool load(handle, bool) = delete; - operator Type() = delete; - template using cast_op_type = Type; -}; - -template -struct type_caster::value>> { - typedef typename Type::Scalar Scalar; - typedef remove_reference_t().outerIndexPtr())> StorageIndex; - typedef typename Type::Index Index; - static constexpr bool rowMajor = Type::IsRowMajor; - - bool load(handle src, bool) { - if (!src) - return false; - - auto obj = reinterpret_borrow(src); - object sparse_module = module::import("scipy.sparse"); - object matrix_type = sparse_module.attr( - rowMajor ? "csr_matrix" : "csc_matrix"); - - if (!obj.get_type().is(matrix_type)) { - try { - obj = matrix_type(obj); - } catch (const error_already_set &) { - return false; - } - } - - auto values = array_t((object) obj.attr("data")); - auto innerIndices = array_t((object) obj.attr("indices")); - auto outerIndices = array_t((object) obj.attr("indptr")); - auto shape = pybind11::tuple((pybind11::object) obj.attr("shape")); - auto nnz = obj.attr("nnz").cast(); - - if (!values || !innerIndices || !outerIndices) - return false; - - value = Eigen::MappedSparseMatrix( - shape[0].cast(), shape[1].cast(), nnz, - outerIndices.mutable_data(), innerIndices.mutable_data(), values.mutable_data()); - - return true; - } - - static handle cast(const Type &src, return_value_policy /* policy */, handle /* parent */) { - const_cast(src).makeCompressed(); - - object matrix_type = module::import("scipy.sparse").attr( - rowMajor ? "csr_matrix" : "csc_matrix"); - - array data(src.nonZeros(), src.valuePtr()); - array outerIndices((rowMajor ? src.rows() : src.cols()) + 1, src.outerIndexPtr()); - array innerIndices(src.nonZeros(), src.innerIndexPtr()); - - return matrix_type( - std::make_tuple(data, innerIndices, outerIndices), - std::make_pair(src.rows(), src.cols()) - ).release(); - } - - PYBIND11_TYPE_CASTER(Type, _<(Type::IsRowMajor) != 0>("scipy.sparse.csr_matrix[", "scipy.sparse.csc_matrix[") - + npy_format_descriptor::name() + _("]")); -}; - -NAMESPACE_END(detail) -NAMESPACE_END(pybind11) - -#if defined(__GNUG__) || defined(__clang__) -# pragma GCC diagnostic pop -#elif defined(_MSC_VER) -# pragma warning(pop) -#endif diff --git a/ptocr/postprocess/lanms/include/pybind11/embed.h b/ptocr/postprocess/lanms/include/pybind11/embed.h deleted file mode 100644 index 0eb656b..0000000 --- a/ptocr/postprocess/lanms/include/pybind11/embed.h +++ /dev/null @@ -1,194 +0,0 @@ -/* - pybind11/embed.h: Support for embedding the interpreter - - Copyright (c) 2017 Wenzel Jakob - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#include "pybind11.h" -#include "eval.h" - -#if defined(PYPY_VERSION) -# error Embedding the interpreter is not supported with PyPy -#endif - -#if PY_MAJOR_VERSION >= 3 -# define PYBIND11_EMBEDDED_MODULE_IMPL(name) \ - extern "C" PyObject *pybind11_init_impl_##name() { \ - return pybind11_init_wrapper_##name(); \ - } -#else -# define PYBIND11_EMBEDDED_MODULE_IMPL(name) \ - extern "C" void pybind11_init_impl_##name() { \ - pybind11_init_wrapper_##name(); \ - } -#endif - -/** \rst - Add a new module to the table of builtins for the interpreter. Must be - defined in global scope. The first macro parameter is the name of the - module (without quotes). The second parameter is the variable which will - be used as the interface to add functions and classes to the module. - - .. code-block:: cpp - - PYBIND11_EMBEDDED_MODULE(example, m) { - // ... initialize functions and classes here - m.def("foo", []() { - return "Hello, World!"; - }); - } - \endrst */ -#define PYBIND11_EMBEDDED_MODULE(name, variable) \ - static void pybind11_init_##name(pybind11::module &); \ - static PyObject *pybind11_init_wrapper_##name() { \ - auto m = pybind11::module(#name); \ - try { \ - pybind11_init_##name(m); \ - return m.ptr(); \ - } catch (pybind11::error_already_set &e) { \ - PyErr_SetString(PyExc_ImportError, e.what()); \ - return nullptr; \ - } catch (const std::exception &e) { \ - PyErr_SetString(PyExc_ImportError, e.what()); \ - return nullptr; \ - } \ - } \ - PYBIND11_EMBEDDED_MODULE_IMPL(name) \ - pybind11::detail::embedded_module name(#name, pybind11_init_impl_##name); \ - void pybind11_init_##name(pybind11::module &variable) - - -NAMESPACE_BEGIN(pybind11) -NAMESPACE_BEGIN(detail) - -/// Python 2.7/3.x compatible version of `PyImport_AppendInittab` and error checks. -struct embedded_module { -#if PY_MAJOR_VERSION >= 3 - using init_t = PyObject *(*)(); -#else - using init_t = void (*)(); -#endif - embedded_module(const char *name, init_t init) { - if (Py_IsInitialized()) - pybind11_fail("Can't add new modules after the interpreter has been initialized"); - - auto result = PyImport_AppendInittab(name, init); - if (result == -1) - pybind11_fail("Insufficient memory to add a new module"); - } -}; - -NAMESPACE_END(detail) - -/** \rst - Initialize the Python interpreter. No other pybind11 or CPython API functions can be - called before this is done; with the exception of `PYBIND11_EMBEDDED_MODULE`. The - optional parameter can be used to skip the registration of signal handlers (see the - Python documentation for details). Calling this function again after the interpreter - has already been initialized is a fatal error. - \endrst */ -inline void initialize_interpreter(bool init_signal_handlers = true) { - if (Py_IsInitialized()) - pybind11_fail("The interpreter is already running"); - - Py_InitializeEx(init_signal_handlers ? 1 : 0); - - // Make .py files in the working directory available by default - auto sys_path = reinterpret_borrow(module::import("sys").attr("path")); - sys_path.append("."); -} - -/** \rst - Shut down the Python interpreter. No pybind11 or CPython API functions can be called - after this. In addition, pybind11 objects must not outlive the interpreter: - - .. code-block:: cpp - - { // BAD - py::initialize_interpreter(); - auto hello = py::str("Hello, World!"); - py::finalize_interpreter(); - } // <-- BOOM, hello's destructor is called after interpreter shutdown - - { // GOOD - py::initialize_interpreter(); - { // scoped - auto hello = py::str("Hello, World!"); - } // <-- OK, hello is cleaned up properly - py::finalize_interpreter(); - } - - { // BETTER - py::scoped_interpreter guard{}; - auto hello = py::str("Hello, World!"); - } - - .. warning:: - - The interpreter can be restarted by calling `initialize_interpreter` again. - Modules created using pybind11 can be safely re-initialized. However, Python - itself cannot completely unload binary extension modules and there are several - caveats with regard to interpreter restarting. All the details can be found - in the CPython documentation. In short, not all interpreter memory may be - freed, either due to reference cycles or user-created global data. - - \endrst */ -inline void finalize_interpreter() { - handle builtins(PyEval_GetBuiltins()); - const char *id = PYBIND11_INTERNALS_ID; - - // Get the internals pointer (without creating it if it doesn't exist). It's possible for the - // internals to be created during Py_Finalize() (e.g. if a py::capsule calls `get_internals()` - // during destruction), so we get the pointer-pointer here and check it after Py_Finalize(). - detail::internals **internals_ptr_ptr = &detail::get_internals_ptr(); - // It could also be stashed in builtins, so look there too: - if (builtins.contains(id) && isinstance(builtins[id])) - internals_ptr_ptr = capsule(builtins[id]); - - Py_Finalize(); - - if (internals_ptr_ptr) { - delete *internals_ptr_ptr; - *internals_ptr_ptr = nullptr; - } -} - -/** \rst - Scope guard version of `initialize_interpreter` and `finalize_interpreter`. - This a move-only guard and only a single instance can exist. - - .. code-block:: cpp - - #include - - int main() { - py::scoped_interpreter guard{}; - py::print(Hello, World!); - } // <-- interpreter shutdown - \endrst */ -class scoped_interpreter { -public: - scoped_interpreter(bool init_signal_handlers = true) { - initialize_interpreter(init_signal_handlers); - } - - scoped_interpreter(const scoped_interpreter &) = delete; - scoped_interpreter(scoped_interpreter &&other) noexcept { other.is_valid = false; } - scoped_interpreter &operator=(const scoped_interpreter &) = delete; - scoped_interpreter &operator=(scoped_interpreter &&) = delete; - - ~scoped_interpreter() { - if (is_valid) - finalize_interpreter(); - } - -private: - bool is_valid = true; -}; - -NAMESPACE_END(pybind11) diff --git a/ptocr/postprocess/lanms/include/pybind11/eval.h b/ptocr/postprocess/lanms/include/pybind11/eval.h deleted file mode 100644 index 165003b..0000000 --- a/ptocr/postprocess/lanms/include/pybind11/eval.h +++ /dev/null @@ -1,117 +0,0 @@ -/* - pybind11/exec.h: Support for evaluating Python expressions and statements - from strings and files - - Copyright (c) 2016 Klemens Morgenstern and - Wenzel Jakob - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#include "pybind11.h" - -NAMESPACE_BEGIN(pybind11) - -enum eval_mode { - /// Evaluate a string containing an isolated expression - eval_expr, - - /// Evaluate a string containing a single statement. Returns \c none - eval_single_statement, - - /// Evaluate a string containing a sequence of statement. Returns \c none - eval_statements -}; - -template -object eval(str expr, object global = globals(), object local = object()) { - if (!local) - local = global; - - /* PyRun_String does not accept a PyObject / encoding specifier, - this seems to be the only alternative */ - std::string buffer = "# -*- coding: utf-8 -*-\n" + (std::string) expr; - - int start; - switch (mode) { - case eval_expr: start = Py_eval_input; break; - case eval_single_statement: start = Py_single_input; break; - case eval_statements: start = Py_file_input; break; - default: pybind11_fail("invalid evaluation mode"); - } - - PyObject *result = PyRun_String(buffer.c_str(), start, global.ptr(), local.ptr()); - if (!result) - throw error_already_set(); - return reinterpret_steal(result); -} - -template -object eval(const char (&s)[N], object global = globals(), object local = object()) { - /* Support raw string literals by removing common leading whitespace */ - auto expr = (s[0] == '\n') ? str(module::import("textwrap").attr("dedent")(s)) - : str(s); - return eval(expr, global, local); -} - -inline void exec(str expr, object global = globals(), object local = object()) { - eval(expr, global, local); -} - -template -void exec(const char (&s)[N], object global = globals(), object local = object()) { - eval(s, global, local); -} - -template -object eval_file(str fname, object global = globals(), object local = object()) { - if (!local) - local = global; - - int start; - switch (mode) { - case eval_expr: start = Py_eval_input; break; - case eval_single_statement: start = Py_single_input; break; - case eval_statements: start = Py_file_input; break; - default: pybind11_fail("invalid evaluation mode"); - } - - int closeFile = 1; - std::string fname_str = (std::string) fname; -#if PY_VERSION_HEX >= 0x03040000 - FILE *f = _Py_fopen_obj(fname.ptr(), "r"); -#elif PY_VERSION_HEX >= 0x03000000 - FILE *f = _Py_fopen(fname.ptr(), "r"); -#else - /* No unicode support in open() :( */ - auto fobj = reinterpret_steal(PyFile_FromString( - const_cast(fname_str.c_str()), - const_cast("r"))); - FILE *f = nullptr; - if (fobj) - f = PyFile_AsFile(fobj.ptr()); - closeFile = 0; -#endif - if (!f) { - PyErr_Clear(); - pybind11_fail("File \"" + fname_str + "\" could not be opened!"); - } - -#if PY_VERSION_HEX < 0x03000000 && defined(PYPY_VERSION) - PyObject *result = PyRun_File(f, fname_str.c_str(), start, global.ptr(), - local.ptr()); - (void) closeFile; -#else - PyObject *result = PyRun_FileEx(f, fname_str.c_str(), start, global.ptr(), - local.ptr(), closeFile); -#endif - - if (!result) - throw error_already_set(); - return reinterpret_steal(result); -} - -NAMESPACE_END(pybind11) diff --git a/ptocr/postprocess/lanms/include/pybind11/functional.h b/ptocr/postprocess/lanms/include/pybind11/functional.h deleted file mode 100644 index fdb6b33..0000000 --- a/ptocr/postprocess/lanms/include/pybind11/functional.h +++ /dev/null @@ -1,85 +0,0 @@ -/* - pybind11/functional.h: std::function<> support - - Copyright (c) 2016 Wenzel Jakob - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#include "pybind11.h" -#include - -NAMESPACE_BEGIN(pybind11) -NAMESPACE_BEGIN(detail) - -template -struct type_caster> { - using type = std::function; - using retval_type = conditional_t::value, void_type, Return>; - using function_type = Return (*) (Args...); - -public: - bool load(handle src, bool convert) { - if (src.is_none()) { - // Defer accepting None to other overloads (if we aren't in convert mode): - if (!convert) return false; - return true; - } - - if (!isinstance(src)) - return false; - - auto func = reinterpret_borrow(src); - - /* - When passing a C++ function as an argument to another C++ - function via Python, every function call would normally involve - a full C++ -> Python -> C++ roundtrip, which can be prohibitive. - Here, we try to at least detect the case where the function is - stateless (i.e. function pointer or lambda function without - captured variables), in which case the roundtrip can be avoided. - */ - if (auto cfunc = func.cpp_function()) { - auto c = reinterpret_borrow(PyCFunction_GET_SELF(cfunc.ptr())); - auto rec = (function_record *) c; - - if (rec && rec->is_stateless && - same_type(typeid(function_type), *reinterpret_cast(rec->data[1]))) { - struct capture { function_type f; }; - value = ((capture *) &rec->data)->f; - return true; - } - } - - value = [func](Args... args) -> Return { - gil_scoped_acquire acq; - object retval(func(std::forward(args)...)); - /* Visual studio 2015 parser issue: need parentheses around this expression */ - return (retval.template cast()); - }; - return true; - } - - template - static handle cast(Func &&f_, return_value_policy policy, handle /* parent */) { - if (!f_) - return none().inc_ref(); - - auto result = f_.template target(); - if (result) - return cpp_function(*result, policy).release(); - else - return cpp_function(std::forward(f_), policy).release(); - } - - PYBIND11_TYPE_CASTER(type, _("Callable[[") + - argument_loader::arg_names() + _("], ") + - make_caster::name() + - _("]")); -}; - -NAMESPACE_END(detail) -NAMESPACE_END(pybind11) diff --git a/ptocr/postprocess/lanms/include/pybind11/numpy.h b/ptocr/postprocess/lanms/include/pybind11/numpy.h deleted file mode 100644 index 388e212..0000000 --- a/ptocr/postprocess/lanms/include/pybind11/numpy.h +++ /dev/null @@ -1,1598 +0,0 @@ -/* - pybind11/numpy.h: Basic NumPy support, vectorize() wrapper - - Copyright (c) 2016 Wenzel Jakob - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#include "pybind11.h" -#include "complex.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if defined(_MSC_VER) -# pragma warning(push) -# pragma warning(disable: 4127) // warning C4127: Conditional expression is constant -#endif - -/* This will be true on all flat address space platforms and allows us to reduce the - whole npy_intp / ssize_t / Py_intptr_t business down to just ssize_t for all size - and dimension types (e.g. shape, strides, indexing), instead of inflicting this - upon the library user. */ -static_assert(sizeof(ssize_t) == sizeof(Py_intptr_t), "ssize_t != Py_intptr_t"); - -NAMESPACE_BEGIN(pybind11) - -class array; // Forward declaration - -NAMESPACE_BEGIN(detail) -template struct npy_format_descriptor; - -struct PyArrayDescr_Proxy { - PyObject_HEAD - PyObject *typeobj; - char kind; - char type; - char byteorder; - char flags; - int type_num; - int elsize; - int alignment; - char *subarray; - PyObject *fields; - PyObject *names; -}; - -struct PyArray_Proxy { - PyObject_HEAD - char *data; - int nd; - ssize_t *dimensions; - ssize_t *strides; - PyObject *base; - PyObject *descr; - int flags; -}; - -struct PyVoidScalarObject_Proxy { - PyObject_VAR_HEAD - char *obval; - PyArrayDescr_Proxy *descr; - int flags; - PyObject *base; -}; - -struct numpy_type_info { - PyObject* dtype_ptr; - std::string format_str; -}; - -struct numpy_internals { - std::unordered_map registered_dtypes; - - numpy_type_info *get_type_info(const std::type_info& tinfo, bool throw_if_missing = true) { - auto it = registered_dtypes.find(std::type_index(tinfo)); - if (it != registered_dtypes.end()) - return &(it->second); - if (throw_if_missing) - pybind11_fail(std::string("NumPy type info missing for ") + tinfo.name()); - return nullptr; - } - - template numpy_type_info *get_type_info(bool throw_if_missing = true) { - return get_type_info(typeid(typename std::remove_cv::type), throw_if_missing); - } -}; - -inline PYBIND11_NOINLINE void load_numpy_internals(numpy_internals* &ptr) { - ptr = &get_or_create_shared_data("_numpy_internals"); -} - -inline numpy_internals& get_numpy_internals() { - static numpy_internals* ptr = nullptr; - if (!ptr) - load_numpy_internals(ptr); - return *ptr; -} - -struct npy_api { - enum constants { - NPY_ARRAY_C_CONTIGUOUS_ = 0x0001, - NPY_ARRAY_F_CONTIGUOUS_ = 0x0002, - NPY_ARRAY_OWNDATA_ = 0x0004, - NPY_ARRAY_FORCECAST_ = 0x0010, - NPY_ARRAY_ENSUREARRAY_ = 0x0040, - NPY_ARRAY_ALIGNED_ = 0x0100, - NPY_ARRAY_WRITEABLE_ = 0x0400, - NPY_BOOL_ = 0, - NPY_BYTE_, NPY_UBYTE_, - NPY_SHORT_, NPY_USHORT_, - NPY_INT_, NPY_UINT_, - NPY_LONG_, NPY_ULONG_, - NPY_LONGLONG_, NPY_ULONGLONG_, - NPY_FLOAT_, NPY_DOUBLE_, NPY_LONGDOUBLE_, - NPY_CFLOAT_, NPY_CDOUBLE_, NPY_CLONGDOUBLE_, - NPY_OBJECT_ = 17, - NPY_STRING_, NPY_UNICODE_, NPY_VOID_ - }; - - typedef struct { - Py_intptr_t *ptr; - int len; - } PyArray_Dims; - - static npy_api& get() { - static npy_api api = lookup(); - return api; - } - - bool PyArray_Check_(PyObject *obj) const { - return (bool) PyObject_TypeCheck(obj, PyArray_Type_); - } - bool PyArrayDescr_Check_(PyObject *obj) const { - return (bool) PyObject_TypeCheck(obj, PyArrayDescr_Type_); - } - - unsigned int (*PyArray_GetNDArrayCFeatureVersion_)(); - PyObject *(*PyArray_DescrFromType_)(int); - PyObject *(*PyArray_NewFromDescr_) - (PyTypeObject *, PyObject *, int, Py_intptr_t *, - Py_intptr_t *, void *, int, PyObject *); - PyObject *(*PyArray_DescrNewFromType_)(int); - int (*PyArray_CopyInto_)(PyObject *, PyObject *); - PyObject *(*PyArray_NewCopy_)(PyObject *, int); - PyTypeObject *PyArray_Type_; - PyTypeObject *PyVoidArrType_Type_; - PyTypeObject *PyArrayDescr_Type_; - PyObject *(*PyArray_DescrFromScalar_)(PyObject *); - PyObject *(*PyArray_FromAny_) (PyObject *, PyObject *, int, int, int, PyObject *); - int (*PyArray_DescrConverter_) (PyObject *, PyObject **); - bool (*PyArray_EquivTypes_) (PyObject *, PyObject *); - int (*PyArray_GetArrayParamsFromObject_)(PyObject *, PyObject *, char, PyObject **, int *, - Py_ssize_t *, PyObject **, PyObject *); - PyObject *(*PyArray_Squeeze_)(PyObject *); - int (*PyArray_SetBaseObject_)(PyObject *, PyObject *); - PyObject* (*PyArray_Resize_)(PyObject*, PyArray_Dims*, int, int); -private: - enum functions { - API_PyArray_GetNDArrayCFeatureVersion = 211, - API_PyArray_Type = 2, - API_PyArrayDescr_Type = 3, - API_PyVoidArrType_Type = 39, - API_PyArray_DescrFromType = 45, - API_PyArray_DescrFromScalar = 57, - API_PyArray_FromAny = 69, - API_PyArray_Resize = 80, - API_PyArray_CopyInto = 82, - API_PyArray_NewCopy = 85, - API_PyArray_NewFromDescr = 94, - API_PyArray_DescrNewFromType = 9, - API_PyArray_DescrConverter = 174, - API_PyArray_EquivTypes = 182, - API_PyArray_GetArrayParamsFromObject = 278, - API_PyArray_Squeeze = 136, - API_PyArray_SetBaseObject = 282 - }; - - static npy_api lookup() { - module m = module::import("numpy.core.multiarray"); - auto c = m.attr("_ARRAY_API"); -#if PY_MAJOR_VERSION >= 3 - void **api_ptr = (void **) PyCapsule_GetPointer(c.ptr(), NULL); -#else - void **api_ptr = (void **) PyCObject_AsVoidPtr(c.ptr()); -#endif - npy_api api; -#define DECL_NPY_API(Func) api.Func##_ = (decltype(api.Func##_)) api_ptr[API_##Func]; - DECL_NPY_API(PyArray_GetNDArrayCFeatureVersion); - if (api.PyArray_GetNDArrayCFeatureVersion_() < 0x7) - pybind11_fail("pybind11 numpy support requires numpy >= 1.7.0"); - DECL_NPY_API(PyArray_Type); - DECL_NPY_API(PyVoidArrType_Type); - DECL_NPY_API(PyArrayDescr_Type); - DECL_NPY_API(PyArray_DescrFromType); - DECL_NPY_API(PyArray_DescrFromScalar); - DECL_NPY_API(PyArray_FromAny); - DECL_NPY_API(PyArray_Resize); - DECL_NPY_API(PyArray_CopyInto); - DECL_NPY_API(PyArray_NewCopy); - DECL_NPY_API(PyArray_NewFromDescr); - DECL_NPY_API(PyArray_DescrNewFromType); - DECL_NPY_API(PyArray_DescrConverter); - DECL_NPY_API(PyArray_EquivTypes); - DECL_NPY_API(PyArray_GetArrayParamsFromObject); - DECL_NPY_API(PyArray_Squeeze); - DECL_NPY_API(PyArray_SetBaseObject); -#undef DECL_NPY_API - return api; - } -}; - -inline PyArray_Proxy* array_proxy(void* ptr) { - return reinterpret_cast(ptr); -} - -inline const PyArray_Proxy* array_proxy(const void* ptr) { - return reinterpret_cast(ptr); -} - -inline PyArrayDescr_Proxy* array_descriptor_proxy(PyObject* ptr) { - return reinterpret_cast(ptr); -} - -inline const PyArrayDescr_Proxy* array_descriptor_proxy(const PyObject* ptr) { - return reinterpret_cast(ptr); -} - -inline bool check_flags(const void* ptr, int flag) { - return (flag == (array_proxy(ptr)->flags & flag)); -} - -template struct is_std_array : std::false_type { }; -template struct is_std_array> : std::true_type { }; -template struct is_complex : std::false_type { }; -template struct is_complex> : std::true_type { }; - -template struct array_info_scalar { - typedef T type; - static constexpr bool is_array = false; - static constexpr bool is_empty = false; - static PYBIND11_DESCR extents() { return _(""); } - static void append_extents(list& /* shape */) { } -}; -// Computes underlying type and a comma-separated list of extents for array -// types (any mix of std::array and built-in arrays). An array of char is -// treated as scalar because it gets special handling. -template struct array_info : array_info_scalar { }; -template struct array_info> { - using type = typename array_info::type; - static constexpr bool is_array = true; - static constexpr bool is_empty = (N == 0) || array_info::is_empty; - static constexpr size_t extent = N; - - // appends the extents to shape - static void append_extents(list& shape) { - shape.append(N); - array_info::append_extents(shape); - } - - template::is_array, int> = 0> - static PYBIND11_DESCR extents() { - return _(); - } - - template::is_array, int> = 0> - static PYBIND11_DESCR extents() { - return concat(_(), array_info::extents()); - } -}; -// For numpy we have special handling for arrays of characters, so we don't include -// the size in the array extents. -template struct array_info : array_info_scalar { }; -template struct array_info> : array_info_scalar> { }; -template struct array_info : array_info> { }; -template using remove_all_extents_t = typename array_info::type; - -template using is_pod_struct = all_of< - std::is_standard_layout, // since we're accessing directly in memory we need a standard layout type -#if !defined(__GNUG__) || defined(__clang__) || __GNUC__ >= 5 - std::is_trivially_copyable, -#else - // GCC 4 doesn't implement is_trivially_copyable, so approximate it - std::is_trivially_destructible, - satisfies_any_of, -#endif - satisfies_none_of ->; - -template ssize_t byte_offset_unsafe(const Strides &) { return 0; } -template -ssize_t byte_offset_unsafe(const Strides &strides, ssize_t i, Ix... index) { - return i * strides[Dim] + byte_offset_unsafe(strides, index...); -} - -/** - * Proxy class providing unsafe, unchecked const access to array data. This is constructed through - * the `unchecked()` method of `array` or the `unchecked()` method of `array_t`. `Dims` - * will be -1 for dimensions determined at runtime. - */ -template -class unchecked_reference { -protected: - static constexpr bool Dynamic = Dims < 0; - const unsigned char *data_; - // Storing the shape & strides in local variables (i.e. these arrays) allows the compiler to - // make large performance gains on big, nested loops, but requires compile-time dimensions - conditional_t> - shape_, strides_; - const ssize_t dims_; - - friend class pybind11::array; - // Constructor for compile-time dimensions: - template - unchecked_reference(const void *data, const ssize_t *shape, const ssize_t *strides, enable_if_t) - : data_{reinterpret_cast(data)}, dims_{Dims} { - for (size_t i = 0; i < (size_t) dims_; i++) { - shape_[i] = shape[i]; - strides_[i] = strides[i]; - } - } - // Constructor for runtime dimensions: - template - unchecked_reference(const void *data, const ssize_t *shape, const ssize_t *strides, enable_if_t dims) - : data_{reinterpret_cast(data)}, shape_{shape}, strides_{strides}, dims_{dims} {} - -public: - /** - * Unchecked const reference access to data at the given indices. For a compile-time known - * number of dimensions, this requires the correct number of arguments; for run-time - * dimensionality, this is not checked (and so is up to the caller to use safely). - */ - template const T &operator()(Ix... index) const { - static_assert(sizeof...(Ix) == Dims || Dynamic, - "Invalid number of indices for unchecked array reference"); - return *reinterpret_cast(data_ + byte_offset_unsafe(strides_, ssize_t(index)...)); - } - /** - * Unchecked const reference access to data; this operator only participates if the reference - * is to a 1-dimensional array. When present, this is exactly equivalent to `obj(index)`. - */ - template > - const T &operator[](ssize_t index) const { return operator()(index); } - - /// Pointer access to the data at the given indices. - template const T *data(Ix... ix) const { return &operator()(ssize_t(ix)...); } - - /// Returns the item size, i.e. sizeof(T) - constexpr static ssize_t itemsize() { return sizeof(T); } - - /// Returns the shape (i.e. size) of dimension `dim` - ssize_t shape(ssize_t dim) const { return shape_[(size_t) dim]; } - - /// Returns the number of dimensions of the array - ssize_t ndim() const { return dims_; } - - /// Returns the total number of elements in the referenced array, i.e. the product of the shapes - template - enable_if_t size() const { - return std::accumulate(shape_.begin(), shape_.end(), (ssize_t) 1, std::multiplies()); - } - template - enable_if_t size() const { - return std::accumulate(shape_, shape_ + ndim(), (ssize_t) 1, std::multiplies()); - } - - /// Returns the total number of bytes used by the referenced data. Note that the actual span in - /// memory may be larger if the referenced array has non-contiguous strides (e.g. for a slice). - ssize_t nbytes() const { - return size() * itemsize(); - } -}; - -template -class unchecked_mutable_reference : public unchecked_reference { - friend class pybind11::array; - using ConstBase = unchecked_reference; - using ConstBase::ConstBase; - using ConstBase::Dynamic; -public: - /// Mutable, unchecked access to data at the given indices. - template T& operator()(Ix... index) { - static_assert(sizeof...(Ix) == Dims || Dynamic, - "Invalid number of indices for unchecked array reference"); - return const_cast(ConstBase::operator()(index...)); - } - /** - * Mutable, unchecked access data at the given index; this operator only participates if the - * reference is to a 1-dimensional array (or has runtime dimensions). When present, this is - * exactly equivalent to `obj(index)`. - */ - template > - T &operator[](ssize_t index) { return operator()(index); } - - /// Mutable pointer access to the data at the given indices. - template T *mutable_data(Ix... ix) { return &operator()(ssize_t(ix)...); } -}; - -template -struct type_caster> { - static_assert(Dim == 0 && Dim > 0 /* always fail */, "unchecked array proxy object is not castable"); -}; -template -struct type_caster> : type_caster> {}; - -NAMESPACE_END(detail) - -class dtype : public object { -public: - PYBIND11_OBJECT_DEFAULT(dtype, object, detail::npy_api::get().PyArrayDescr_Check_); - - explicit dtype(const buffer_info &info) { - dtype descr(_dtype_from_pep3118()(PYBIND11_STR_TYPE(info.format))); - // If info.itemsize == 0, use the value calculated from the format string - m_ptr = descr.strip_padding(info.itemsize ? info.itemsize : descr.itemsize()).release().ptr(); - } - - explicit dtype(const std::string &format) { - m_ptr = from_args(pybind11::str(format)).release().ptr(); - } - - dtype(const char *format) : dtype(std::string(format)) { } - - dtype(list names, list formats, list offsets, ssize_t itemsize) { - dict args; - args["names"] = names; - args["formats"] = formats; - args["offsets"] = offsets; - args["itemsize"] = pybind11::int_(itemsize); - m_ptr = from_args(args).release().ptr(); - } - - /// This is essentially the same as calling numpy.dtype(args) in Python. - static dtype from_args(object args) { - PyObject *ptr = nullptr; - if (!detail::npy_api::get().PyArray_DescrConverter_(args.release().ptr(), &ptr) || !ptr) - throw error_already_set(); - return reinterpret_steal(ptr); - } - - /// Return dtype associated with a C++ type. - template static dtype of() { - return detail::npy_format_descriptor::type>::dtype(); - } - - /// Size of the data type in bytes. - ssize_t itemsize() const { - return detail::array_descriptor_proxy(m_ptr)->elsize; - } - - /// Returns true for structured data types. - bool has_fields() const { - return detail::array_descriptor_proxy(m_ptr)->names != nullptr; - } - - /// Single-character type code. - char kind() const { - return detail::array_descriptor_proxy(m_ptr)->kind; - } - -private: - static object _dtype_from_pep3118() { - static PyObject *obj = module::import("numpy.core._internal") - .attr("_dtype_from_pep3118").cast().release().ptr(); - return reinterpret_borrow(obj); - } - - dtype strip_padding(ssize_t itemsize) { - // Recursively strip all void fields with empty names that are generated for - // padding fields (as of NumPy v1.11). - if (!has_fields()) - return *this; - - struct field_descr { PYBIND11_STR_TYPE name; object format; pybind11::int_ offset; }; - std::vector field_descriptors; - - for (auto field : attr("fields").attr("items")()) { - auto spec = field.cast(); - auto name = spec[0].cast(); - auto format = spec[1].cast()[0].cast(); - auto offset = spec[1].cast()[1].cast(); - if (!len(name) && format.kind() == 'V') - continue; - field_descriptors.push_back({(PYBIND11_STR_TYPE) name, format.strip_padding(format.itemsize()), offset}); - } - - std::sort(field_descriptors.begin(), field_descriptors.end(), - [](const field_descr& a, const field_descr& b) { - return a.offset.cast() < b.offset.cast(); - }); - - list names, formats, offsets; - for (auto& descr : field_descriptors) { - names.append(descr.name); - formats.append(descr.format); - offsets.append(descr.offset); - } - return dtype(names, formats, offsets, itemsize); - } -}; - -class array : public buffer { -public: - PYBIND11_OBJECT_CVT(array, buffer, detail::npy_api::get().PyArray_Check_, raw_array) - - enum { - c_style = detail::npy_api::NPY_ARRAY_C_CONTIGUOUS_, - f_style = detail::npy_api::NPY_ARRAY_F_CONTIGUOUS_, - forcecast = detail::npy_api::NPY_ARRAY_FORCECAST_ - }; - - array() : array({{0}}, static_cast(nullptr)) {} - - using ShapeContainer = detail::any_container; - using StridesContainer = detail::any_container; - - // Constructs an array taking shape/strides from arbitrary container types - array(const pybind11::dtype &dt, ShapeContainer shape, StridesContainer strides, - const void *ptr = nullptr, handle base = handle()) { - - if (strides->empty()) - *strides = c_strides(*shape, dt.itemsize()); - - auto ndim = shape->size(); - if (ndim != strides->size()) - pybind11_fail("NumPy: shape ndim doesn't match strides ndim"); - auto descr = dt; - - int flags = 0; - if (base && ptr) { - if (isinstance(base)) - /* Copy flags from base (except ownership bit) */ - flags = reinterpret_borrow(base).flags() & ~detail::npy_api::NPY_ARRAY_OWNDATA_; - else - /* Writable by default, easy to downgrade later on if needed */ - flags = detail::npy_api::NPY_ARRAY_WRITEABLE_; - } - - auto &api = detail::npy_api::get(); - auto tmp = reinterpret_steal(api.PyArray_NewFromDescr_( - api.PyArray_Type_, descr.release().ptr(), (int) ndim, shape->data(), strides->data(), - const_cast(ptr), flags, nullptr)); - if (!tmp) - throw error_already_set(); - if (ptr) { - if (base) { - api.PyArray_SetBaseObject_(tmp.ptr(), base.inc_ref().ptr()); - } else { - tmp = reinterpret_steal(api.PyArray_NewCopy_(tmp.ptr(), -1 /* any order */)); - } - } - m_ptr = tmp.release().ptr(); - } - - array(const pybind11::dtype &dt, ShapeContainer shape, const void *ptr = nullptr, handle base = handle()) - : array(dt, std::move(shape), {}, ptr, base) { } - - template ::value && !std::is_same::value>> - array(const pybind11::dtype &dt, T count, const void *ptr = nullptr, handle base = handle()) - : array(dt, {{count}}, ptr, base) { } - - template - array(ShapeContainer shape, StridesContainer strides, const T *ptr, handle base = handle()) - : array(pybind11::dtype::of(), std::move(shape), std::move(strides), ptr, base) { } - - template - array(ShapeContainer shape, const T *ptr, handle base = handle()) - : array(std::move(shape), {}, ptr, base) { } - - template - explicit array(ssize_t count, const T *ptr, handle base = handle()) : array({count}, {}, ptr, base) { } - - explicit array(const buffer_info &info) - : array(pybind11::dtype(info), info.shape, info.strides, info.ptr) { } - - /// Array descriptor (dtype) - pybind11::dtype dtype() const { - return reinterpret_borrow(detail::array_proxy(m_ptr)->descr); - } - - /// Total number of elements - ssize_t size() const { - return std::accumulate(shape(), shape() + ndim(), (ssize_t) 1, std::multiplies()); - } - - /// Byte size of a single element - ssize_t itemsize() const { - return detail::array_descriptor_proxy(detail::array_proxy(m_ptr)->descr)->elsize; - } - - /// Total number of bytes - ssize_t nbytes() const { - return size() * itemsize(); - } - - /// Number of dimensions - ssize_t ndim() const { - return detail::array_proxy(m_ptr)->nd; - } - - /// Base object - object base() const { - return reinterpret_borrow(detail::array_proxy(m_ptr)->base); - } - - /// Dimensions of the array - const ssize_t* shape() const { - return detail::array_proxy(m_ptr)->dimensions; - } - - /// Dimension along a given axis - ssize_t shape(ssize_t dim) const { - if (dim >= ndim()) - fail_dim_check(dim, "invalid axis"); - return shape()[dim]; - } - - /// Strides of the array - const ssize_t* strides() const { - return detail::array_proxy(m_ptr)->strides; - } - - /// Stride along a given axis - ssize_t strides(ssize_t dim) const { - if (dim >= ndim()) - fail_dim_check(dim, "invalid axis"); - return strides()[dim]; - } - - /// Return the NumPy array flags - int flags() const { - return detail::array_proxy(m_ptr)->flags; - } - - /// If set, the array is writeable (otherwise the buffer is read-only) - bool writeable() const { - return detail::check_flags(m_ptr, detail::npy_api::NPY_ARRAY_WRITEABLE_); - } - - /// If set, the array owns the data (will be freed when the array is deleted) - bool owndata() const { - return detail::check_flags(m_ptr, detail::npy_api::NPY_ARRAY_OWNDATA_); - } - - /// Pointer to the contained data. If index is not provided, points to the - /// beginning of the buffer. May throw if the index would lead to out of bounds access. - template const void* data(Ix... index) const { - return static_cast(detail::array_proxy(m_ptr)->data + offset_at(index...)); - } - - /// Mutable pointer to the contained data. If index is not provided, points to the - /// beginning of the buffer. May throw if the index would lead to out of bounds access. - /// May throw if the array is not writeable. - template void* mutable_data(Ix... index) { - check_writeable(); - return static_cast(detail::array_proxy(m_ptr)->data + offset_at(index...)); - } - - /// Byte offset from beginning of the array to a given index (full or partial). - /// May throw if the index would lead to out of bounds access. - template ssize_t offset_at(Ix... index) const { - if ((ssize_t) sizeof...(index) > ndim()) - fail_dim_check(sizeof...(index), "too many indices for an array"); - return byte_offset(ssize_t(index)...); - } - - ssize_t offset_at() const { return 0; } - - /// Item count from beginning of the array to a given index (full or partial). - /// May throw if the index would lead to out of bounds access. - template ssize_t index_at(Ix... index) const { - return offset_at(index...) / itemsize(); - } - - /** - * Returns a proxy object that provides access to the array's data without bounds or - * dimensionality checking. Will throw if the array is missing the `writeable` flag. Use with - * care: the array must not be destroyed or reshaped for the duration of the returned object, - * and the caller must take care not to access invalid dimensions or dimension indices. - */ - template detail::unchecked_mutable_reference mutable_unchecked() { - if (Dims >= 0 && ndim() != Dims) - throw std::domain_error("array has incorrect number of dimensions: " + std::to_string(ndim()) + - "; expected " + std::to_string(Dims)); - return detail::unchecked_mutable_reference(mutable_data(), shape(), strides(), ndim()); - } - - /** - * Returns a proxy object that provides const access to the array's data without bounds or - * dimensionality checking. Unlike `mutable_unchecked()`, this does not require that the - * underlying array have the `writable` flag. Use with care: the array must not be destroyed or - * reshaped for the duration of the returned object, and the caller must take care not to access - * invalid dimensions or dimension indices. - */ - template detail::unchecked_reference unchecked() const { - if (Dims >= 0 && ndim() != Dims) - throw std::domain_error("array has incorrect number of dimensions: " + std::to_string(ndim()) + - "; expected " + std::to_string(Dims)); - return detail::unchecked_reference(data(), shape(), strides(), ndim()); - } - - /// Return a new view with all of the dimensions of length 1 removed - array squeeze() { - auto& api = detail::npy_api::get(); - return reinterpret_steal(api.PyArray_Squeeze_(m_ptr)); - } - - /// Resize array to given shape - /// If refcheck is true and more that one reference exist to this array - /// then resize will succeed only if it makes a reshape, i.e. original size doesn't change - void resize(ShapeContainer new_shape, bool refcheck = true) { - detail::npy_api::PyArray_Dims d = { - new_shape->data(), int(new_shape->size()) - }; - // try to resize, set ordering param to -1 cause it's not used anyway - object new_array = reinterpret_steal( - detail::npy_api::get().PyArray_Resize_(m_ptr, &d, int(refcheck), -1) - ); - if (!new_array) throw error_already_set(); - if (isinstance(new_array)) { *this = std::move(new_array); } - } - - /// Ensure that the argument is a NumPy array - /// In case of an error, nullptr is returned and the Python error is cleared. - static array ensure(handle h, int ExtraFlags = 0) { - auto result = reinterpret_steal(raw_array(h.ptr(), ExtraFlags)); - if (!result) - PyErr_Clear(); - return result; - } - -protected: - template friend struct detail::npy_format_descriptor; - - void fail_dim_check(ssize_t dim, const std::string& msg) const { - throw index_error(msg + ": " + std::to_string(dim) + - " (ndim = " + std::to_string(ndim()) + ")"); - } - - template ssize_t byte_offset(Ix... index) const { - check_dimensions(index...); - return detail::byte_offset_unsafe(strides(), ssize_t(index)...); - } - - void check_writeable() const { - if (!writeable()) - throw std::domain_error("array is not writeable"); - } - - // Default, C-style strides - static std::vector c_strides(const std::vector &shape, ssize_t itemsize) { - auto ndim = shape.size(); - std::vector strides(ndim, itemsize); - for (size_t i = ndim - 1; i > 0; --i) - strides[i - 1] = strides[i] * shape[i]; - return strides; - } - - // F-style strides; default when constructing an array_t with `ExtraFlags & f_style` - static std::vector f_strides(const std::vector &shape, ssize_t itemsize) { - auto ndim = shape.size(); - std::vector strides(ndim, itemsize); - for (size_t i = 1; i < ndim; ++i) - strides[i] = strides[i - 1] * shape[i - 1]; - return strides; - } - - template void check_dimensions(Ix... index) const { - check_dimensions_impl(ssize_t(0), shape(), ssize_t(index)...); - } - - void check_dimensions_impl(ssize_t, const ssize_t*) const { } - - template void check_dimensions_impl(ssize_t axis, const ssize_t* shape, ssize_t i, Ix... index) const { - if (i >= *shape) { - throw index_error(std::string("index ") + std::to_string(i) + - " is out of bounds for axis " + std::to_string(axis) + - " with size " + std::to_string(*shape)); - } - check_dimensions_impl(axis + 1, shape + 1, index...); - } - - /// Create array from any object -- always returns a new reference - static PyObject *raw_array(PyObject *ptr, int ExtraFlags = 0) { - if (ptr == nullptr) { - PyErr_SetString(PyExc_ValueError, "cannot create a pybind11::array from a nullptr"); - return nullptr; - } - return detail::npy_api::get().PyArray_FromAny_( - ptr, nullptr, 0, 0, detail::npy_api::NPY_ARRAY_ENSUREARRAY_ | ExtraFlags, nullptr); - } -}; - -template class array_t : public array { -private: - struct private_ctor {}; - // Delegating constructor needed when both moving and accessing in the same constructor - array_t(private_ctor, ShapeContainer &&shape, StridesContainer &&strides, const T *ptr, handle base) - : array(std::move(shape), std::move(strides), ptr, base) {} -public: - static_assert(!detail::array_info::is_array, "Array types cannot be used with array_t"); - - using value_type = T; - - array_t() : array(0, static_cast(nullptr)) {} - array_t(handle h, borrowed_t) : array(h, borrowed_t{}) { } - array_t(handle h, stolen_t) : array(h, stolen_t{}) { } - - PYBIND11_DEPRECATED("Use array_t::ensure() instead") - array_t(handle h, bool is_borrowed) : array(raw_array_t(h.ptr()), stolen_t{}) { - if (!m_ptr) PyErr_Clear(); - if (!is_borrowed) Py_XDECREF(h.ptr()); - } - - array_t(const object &o) : array(raw_array_t(o.ptr()), stolen_t{}) { - if (!m_ptr) throw error_already_set(); - } - - explicit array_t(const buffer_info& info) : array(info) { } - - array_t(ShapeContainer shape, StridesContainer strides, const T *ptr = nullptr, handle base = handle()) - : array(std::move(shape), std::move(strides), ptr, base) { } - - explicit array_t(ShapeContainer shape, const T *ptr = nullptr, handle base = handle()) - : array_t(private_ctor{}, std::move(shape), - ExtraFlags & f_style ? f_strides(*shape, itemsize()) : c_strides(*shape, itemsize()), - ptr, base) { } - - explicit array_t(size_t count, const T *ptr = nullptr, handle base = handle()) - : array({count}, {}, ptr, base) { } - - constexpr ssize_t itemsize() const { - return sizeof(T); - } - - template ssize_t index_at(Ix... index) const { - return offset_at(index...) / itemsize(); - } - - template const T* data(Ix... index) const { - return static_cast(array::data(index...)); - } - - template T* mutable_data(Ix... index) { - return static_cast(array::mutable_data(index...)); - } - - // Reference to element at a given index - template const T& at(Ix... index) const { - if (sizeof...(index) != ndim()) - fail_dim_check(sizeof...(index), "index dimension mismatch"); - return *(static_cast(array::data()) + byte_offset(ssize_t(index)...) / itemsize()); - } - - // Mutable reference to element at a given index - template T& mutable_at(Ix... index) { - if (sizeof...(index) != ndim()) - fail_dim_check(sizeof...(index), "index dimension mismatch"); - return *(static_cast(array::mutable_data()) + byte_offset(ssize_t(index)...) / itemsize()); - } - - /** - * Returns a proxy object that provides access to the array's data without bounds or - * dimensionality checking. Will throw if the array is missing the `writeable` flag. Use with - * care: the array must not be destroyed or reshaped for the duration of the returned object, - * and the caller must take care not to access invalid dimensions or dimension indices. - */ - template detail::unchecked_mutable_reference mutable_unchecked() { - return array::mutable_unchecked(); - } - - /** - * Returns a proxy object that provides const access to the array's data without bounds or - * dimensionality checking. Unlike `unchecked()`, this does not require that the underlying - * array have the `writable` flag. Use with care: the array must not be destroyed or reshaped - * for the duration of the returned object, and the caller must take care not to access invalid - * dimensions or dimension indices. - */ - template detail::unchecked_reference unchecked() const { - return array::unchecked(); - } - - /// Ensure that the argument is a NumPy array of the correct dtype (and if not, try to convert - /// it). In case of an error, nullptr is returned and the Python error is cleared. - static array_t ensure(handle h) { - auto result = reinterpret_steal(raw_array_t(h.ptr())); - if (!result) - PyErr_Clear(); - return result; - } - - static bool check_(handle h) { - const auto &api = detail::npy_api::get(); - return api.PyArray_Check_(h.ptr()) - && api.PyArray_EquivTypes_(detail::array_proxy(h.ptr())->descr, dtype::of().ptr()); - } - -protected: - /// Create array from any object -- always returns a new reference - static PyObject *raw_array_t(PyObject *ptr) { - if (ptr == nullptr) { - PyErr_SetString(PyExc_ValueError, "cannot create a pybind11::array_t from a nullptr"); - return nullptr; - } - return detail::npy_api::get().PyArray_FromAny_( - ptr, dtype::of().release().ptr(), 0, 0, - detail::npy_api::NPY_ARRAY_ENSUREARRAY_ | ExtraFlags, nullptr); - } -}; - -template -struct format_descriptor::value>> { - static std::string format() { - return detail::npy_format_descriptor::type>::format(); - } -}; - -template struct format_descriptor { - static std::string format() { return std::to_string(N) + "s"; } -}; -template struct format_descriptor> { - static std::string format() { return std::to_string(N) + "s"; } -}; - -template -struct format_descriptor::value>> { - static std::string format() { - return format_descriptor< - typename std::remove_cv::type>::type>::format(); - } -}; - -template -struct format_descriptor::is_array>> { - static std::string format() { - using detail::_; - PYBIND11_DESCR extents = _("(") + detail::array_info::extents() + _(")"); - return extents.text() + format_descriptor>::format(); - } -}; - -NAMESPACE_BEGIN(detail) -template -struct pyobject_caster> { - using type = array_t; - - bool load(handle src, bool convert) { - if (!convert && !type::check_(src)) - return false; - value = type::ensure(src); - return static_cast(value); - } - - static handle cast(const handle &src, return_value_policy /* policy */, handle /* parent */) { - return src.inc_ref(); - } - PYBIND11_TYPE_CASTER(type, handle_type_name::name()); -}; - -template -struct compare_buffer_info::value>> { - static bool compare(const buffer_info& b) { - return npy_api::get().PyArray_EquivTypes_(dtype::of().ptr(), dtype(b).ptr()); - } -}; - -template struct npy_format_descriptor::value>> { -private: - // NB: the order here must match the one in common.h - constexpr static const int values[15] = { - npy_api::NPY_BOOL_, - npy_api::NPY_BYTE_, npy_api::NPY_UBYTE_, npy_api::NPY_SHORT_, npy_api::NPY_USHORT_, - npy_api::NPY_INT_, npy_api::NPY_UINT_, npy_api::NPY_LONGLONG_, npy_api::NPY_ULONGLONG_, - npy_api::NPY_FLOAT_, npy_api::NPY_DOUBLE_, npy_api::NPY_LONGDOUBLE_, - npy_api::NPY_CFLOAT_, npy_api::NPY_CDOUBLE_, npy_api::NPY_CLONGDOUBLE_ - }; - -public: - static constexpr int value = values[detail::is_fmt_numeric::index]; - - static pybind11::dtype dtype() { - if (auto ptr = npy_api::get().PyArray_DescrFromType_(value)) - return reinterpret_borrow(ptr); - pybind11_fail("Unsupported buffer format!"); - } - template ::value, int> = 0> - static PYBIND11_DESCR name() { - return _::value>(_("bool"), - _::value>("int", "uint") + _()); - } - template ::value, int> = 0> - static PYBIND11_DESCR name() { - return _::value || std::is_same::value>( - _("float") + _(), _("longdouble")); - } - template ::value, int> = 0> - static PYBIND11_DESCR name() { - return _::value || std::is_same::value>( - _("complex") + _(), _("longcomplex")); - } -}; - -#define PYBIND11_DECL_CHAR_FMT \ - static PYBIND11_DESCR name() { return _("S") + _(); } \ - static pybind11::dtype dtype() { return pybind11::dtype(std::string("S") + std::to_string(N)); } -template struct npy_format_descriptor { PYBIND11_DECL_CHAR_FMT }; -template struct npy_format_descriptor> { PYBIND11_DECL_CHAR_FMT }; -#undef PYBIND11_DECL_CHAR_FMT - -template struct npy_format_descriptor::is_array>> { -private: - using base_descr = npy_format_descriptor::type>; -public: - static_assert(!array_info::is_empty, "Zero-sized arrays are not supported"); - - static PYBIND11_DESCR name() { return _("(") + array_info::extents() + _(")") + base_descr::name(); } - static pybind11::dtype dtype() { - list shape; - array_info::append_extents(shape); - return pybind11::dtype::from_args(pybind11::make_tuple(base_descr::dtype(), shape)); - } -}; - -template struct npy_format_descriptor::value>> { -private: - using base_descr = npy_format_descriptor::type>; -public: - static PYBIND11_DESCR name() { return base_descr::name(); } - static pybind11::dtype dtype() { return base_descr::dtype(); } -}; - -struct field_descriptor { - const char *name; - ssize_t offset; - ssize_t size; - std::string format; - dtype descr; -}; - -inline PYBIND11_NOINLINE void register_structured_dtype( - const std::initializer_list& fields, - const std::type_info& tinfo, ssize_t itemsize, - bool (*direct_converter)(PyObject *, void *&)) { - - auto& numpy_internals = get_numpy_internals(); - if (numpy_internals.get_type_info(tinfo, false)) - pybind11_fail("NumPy: dtype is already registered"); - - list names, formats, offsets; - for (auto field : fields) { - if (!field.descr) - pybind11_fail(std::string("NumPy: unsupported field dtype: `") + - field.name + "` @ " + tinfo.name()); - names.append(PYBIND11_STR_TYPE(field.name)); - formats.append(field.descr); - offsets.append(pybind11::int_(field.offset)); - } - auto dtype_ptr = pybind11::dtype(names, formats, offsets, itemsize).release().ptr(); - - // There is an existing bug in NumPy (as of v1.11): trailing bytes are - // not encoded explicitly into the format string. This will supposedly - // get fixed in v1.12; for further details, see these: - // - https://github.com/numpy/numpy/issues/7797 - // - https://github.com/numpy/numpy/pull/7798 - // Because of this, we won't use numpy's logic to generate buffer format - // strings and will just do it ourselves. - std::vector ordered_fields(fields); - std::sort(ordered_fields.begin(), ordered_fields.end(), - [](const field_descriptor &a, const field_descriptor &b) { return a.offset < b.offset; }); - ssize_t offset = 0; - std::ostringstream oss; - // mark the structure as unaligned with '^', because numpy and C++ don't - // always agree about alignment (particularly for complex), and we're - // explicitly listing all our padding. This depends on none of the fields - // overriding the endianness. Putting the ^ in front of individual fields - // isn't guaranteed to work due to https://github.com/numpy/numpy/issues/9049 - oss << "^T{"; - for (auto& field : ordered_fields) { - if (field.offset > offset) - oss << (field.offset - offset) << 'x'; - oss << field.format << ':' << field.name << ':'; - offset = field.offset + field.size; - } - if (itemsize > offset) - oss << (itemsize - offset) << 'x'; - oss << '}'; - auto format_str = oss.str(); - - // Sanity check: verify that NumPy properly parses our buffer format string - auto& api = npy_api::get(); - auto arr = array(buffer_info(nullptr, itemsize, format_str, 1)); - if (!api.PyArray_EquivTypes_(dtype_ptr, arr.dtype().ptr())) - pybind11_fail("NumPy: invalid buffer descriptor!"); - - auto tindex = std::type_index(tinfo); - numpy_internals.registered_dtypes[tindex] = { dtype_ptr, format_str }; - get_internals().direct_conversions[tindex].push_back(direct_converter); -} - -template struct npy_format_descriptor { - static_assert(is_pod_struct::value, "Attempt to use a non-POD or unimplemented POD type as a numpy dtype"); - - static PYBIND11_DESCR name() { return make_caster::name(); } - - static pybind11::dtype dtype() { - return reinterpret_borrow(dtype_ptr()); - } - - static std::string format() { - static auto format_str = get_numpy_internals().get_type_info(true)->format_str; - return format_str; - } - - static void register_dtype(const std::initializer_list& fields) { - register_structured_dtype(fields, typeid(typename std::remove_cv::type), - sizeof(T), &direct_converter); - } - -private: - static PyObject* dtype_ptr() { - static PyObject* ptr = get_numpy_internals().get_type_info(true)->dtype_ptr; - return ptr; - } - - static bool direct_converter(PyObject *obj, void*& value) { - auto& api = npy_api::get(); - if (!PyObject_TypeCheck(obj, api.PyVoidArrType_Type_)) - return false; - if (auto descr = reinterpret_steal(api.PyArray_DescrFromScalar_(obj))) { - if (api.PyArray_EquivTypes_(dtype_ptr(), descr.ptr())) { - value = ((PyVoidScalarObject_Proxy *) obj)->obval; - return true; - } - } - return false; - } -}; - -#ifdef __CLION_IDE__ // replace heavy macro with dummy code for the IDE (doesn't affect code) -# define PYBIND11_NUMPY_DTYPE(Type, ...) ((void)0) -# define PYBIND11_NUMPY_DTYPE_EX(Type, ...) ((void)0) -#else - -#define PYBIND11_FIELD_DESCRIPTOR_EX(T, Field, Name) \ - ::pybind11::detail::field_descriptor { \ - Name, offsetof(T, Field), sizeof(decltype(std::declval().Field)), \ - ::pybind11::format_descriptor().Field)>::format(), \ - ::pybind11::detail::npy_format_descriptor().Field)>::dtype() \ - } - -// Extract name, offset and format descriptor for a struct field -#define PYBIND11_FIELD_DESCRIPTOR(T, Field) PYBIND11_FIELD_DESCRIPTOR_EX(T, Field, #Field) - -// The main idea of this macro is borrowed from https://github.com/swansontec/map-macro -// (C) William Swanson, Paul Fultz -#define PYBIND11_EVAL0(...) __VA_ARGS__ -#define PYBIND11_EVAL1(...) PYBIND11_EVAL0 (PYBIND11_EVAL0 (PYBIND11_EVAL0 (__VA_ARGS__))) -#define PYBIND11_EVAL2(...) PYBIND11_EVAL1 (PYBIND11_EVAL1 (PYBIND11_EVAL1 (__VA_ARGS__))) -#define PYBIND11_EVAL3(...) PYBIND11_EVAL2 (PYBIND11_EVAL2 (PYBIND11_EVAL2 (__VA_ARGS__))) -#define PYBIND11_EVAL4(...) PYBIND11_EVAL3 (PYBIND11_EVAL3 (PYBIND11_EVAL3 (__VA_ARGS__))) -#define PYBIND11_EVAL(...) PYBIND11_EVAL4 (PYBIND11_EVAL4 (PYBIND11_EVAL4 (__VA_ARGS__))) -#define PYBIND11_MAP_END(...) -#define PYBIND11_MAP_OUT -#define PYBIND11_MAP_COMMA , -#define PYBIND11_MAP_GET_END() 0, PYBIND11_MAP_END -#define PYBIND11_MAP_NEXT0(test, next, ...) next PYBIND11_MAP_OUT -#define PYBIND11_MAP_NEXT1(test, next) PYBIND11_MAP_NEXT0 (test, next, 0) -#define PYBIND11_MAP_NEXT(test, next) PYBIND11_MAP_NEXT1 (PYBIND11_MAP_GET_END test, next) -#ifdef _MSC_VER // MSVC is not as eager to expand macros, hence this workaround -#define PYBIND11_MAP_LIST_NEXT1(test, next) \ - PYBIND11_EVAL0 (PYBIND11_MAP_NEXT0 (test, PYBIND11_MAP_COMMA next, 0)) -#else -#define PYBIND11_MAP_LIST_NEXT1(test, next) \ - PYBIND11_MAP_NEXT0 (test, PYBIND11_MAP_COMMA next, 0) -#endif -#define PYBIND11_MAP_LIST_NEXT(test, next) \ - PYBIND11_MAP_LIST_NEXT1 (PYBIND11_MAP_GET_END test, next) -#define PYBIND11_MAP_LIST0(f, t, x, peek, ...) \ - f(t, x) PYBIND11_MAP_LIST_NEXT (peek, PYBIND11_MAP_LIST1) (f, t, peek, __VA_ARGS__) -#define PYBIND11_MAP_LIST1(f, t, x, peek, ...) \ - f(t, x) PYBIND11_MAP_LIST_NEXT (peek, PYBIND11_MAP_LIST0) (f, t, peek, __VA_ARGS__) -// PYBIND11_MAP_LIST(f, t, a1, a2, ...) expands to f(t, a1), f(t, a2), ... -#define PYBIND11_MAP_LIST(f, t, ...) \ - PYBIND11_EVAL (PYBIND11_MAP_LIST1 (f, t, __VA_ARGS__, (), 0)) - -#define PYBIND11_NUMPY_DTYPE(Type, ...) \ - ::pybind11::detail::npy_format_descriptor::register_dtype \ - ({PYBIND11_MAP_LIST (PYBIND11_FIELD_DESCRIPTOR, Type, __VA_ARGS__)}) - -#ifdef _MSC_VER -#define PYBIND11_MAP2_LIST_NEXT1(test, next) \ - PYBIND11_EVAL0 (PYBIND11_MAP_NEXT0 (test, PYBIND11_MAP_COMMA next, 0)) -#else -#define PYBIND11_MAP2_LIST_NEXT1(test, next) \ - PYBIND11_MAP_NEXT0 (test, PYBIND11_MAP_COMMA next, 0) -#endif -#define PYBIND11_MAP2_LIST_NEXT(test, next) \ - PYBIND11_MAP2_LIST_NEXT1 (PYBIND11_MAP_GET_END test, next) -#define PYBIND11_MAP2_LIST0(f, t, x1, x2, peek, ...) \ - f(t, x1, x2) PYBIND11_MAP2_LIST_NEXT (peek, PYBIND11_MAP2_LIST1) (f, t, peek, __VA_ARGS__) -#define PYBIND11_MAP2_LIST1(f, t, x1, x2, peek, ...) \ - f(t, x1, x2) PYBIND11_MAP2_LIST_NEXT (peek, PYBIND11_MAP2_LIST0) (f, t, peek, __VA_ARGS__) -// PYBIND11_MAP2_LIST(f, t, a1, a2, ...) expands to f(t, a1, a2), f(t, a3, a4), ... -#define PYBIND11_MAP2_LIST(f, t, ...) \ - PYBIND11_EVAL (PYBIND11_MAP2_LIST1 (f, t, __VA_ARGS__, (), 0)) - -#define PYBIND11_NUMPY_DTYPE_EX(Type, ...) \ - ::pybind11::detail::npy_format_descriptor::register_dtype \ - ({PYBIND11_MAP2_LIST (PYBIND11_FIELD_DESCRIPTOR_EX, Type, __VA_ARGS__)}) - -#endif // __CLION_IDE__ - -template -using array_iterator = typename std::add_pointer::type; - -template -array_iterator array_begin(const buffer_info& buffer) { - return array_iterator(reinterpret_cast(buffer.ptr)); -} - -template -array_iterator array_end(const buffer_info& buffer) { - return array_iterator(reinterpret_cast(buffer.ptr) + buffer.size); -} - -class common_iterator { -public: - using container_type = std::vector; - using value_type = container_type::value_type; - using size_type = container_type::size_type; - - common_iterator() : p_ptr(0), m_strides() {} - - common_iterator(void* ptr, const container_type& strides, const container_type& shape) - : p_ptr(reinterpret_cast(ptr)), m_strides(strides.size()) { - m_strides.back() = static_cast(strides.back()); - for (size_type i = m_strides.size() - 1; i != 0; --i) { - size_type j = i - 1; - value_type s = static_cast(shape[i]); - m_strides[j] = strides[j] + m_strides[i] - strides[i] * s; - } - } - - void increment(size_type dim) { - p_ptr += m_strides[dim]; - } - - void* data() const { - return p_ptr; - } - -private: - char* p_ptr; - container_type m_strides; -}; - -template class multi_array_iterator { -public: - using container_type = std::vector; - - multi_array_iterator(const std::array &buffers, - const container_type &shape) - : m_shape(shape.size()), m_index(shape.size(), 0), - m_common_iterator() { - - // Manual copy to avoid conversion warning if using std::copy - for (size_t i = 0; i < shape.size(); ++i) - m_shape[i] = shape[i]; - - container_type strides(shape.size()); - for (size_t i = 0; i < N; ++i) - init_common_iterator(buffers[i], shape, m_common_iterator[i], strides); - } - - multi_array_iterator& operator++() { - for (size_t j = m_index.size(); j != 0; --j) { - size_t i = j - 1; - if (++m_index[i] != m_shape[i]) { - increment_common_iterator(i); - break; - } else { - m_index[i] = 0; - } - } - return *this; - } - - template T* data() const { - return reinterpret_cast(m_common_iterator[K].data()); - } - -private: - - using common_iter = common_iterator; - - void init_common_iterator(const buffer_info &buffer, - const container_type &shape, - common_iter &iterator, - container_type &strides) { - auto buffer_shape_iter = buffer.shape.rbegin(); - auto buffer_strides_iter = buffer.strides.rbegin(); - auto shape_iter = shape.rbegin(); - auto strides_iter = strides.rbegin(); - - while (buffer_shape_iter != buffer.shape.rend()) { - if (*shape_iter == *buffer_shape_iter) - *strides_iter = *buffer_strides_iter; - else - *strides_iter = 0; - - ++buffer_shape_iter; - ++buffer_strides_iter; - ++shape_iter; - ++strides_iter; - } - - std::fill(strides_iter, strides.rend(), 0); - iterator = common_iter(buffer.ptr, strides, shape); - } - - void increment_common_iterator(size_t dim) { - for (auto &iter : m_common_iterator) - iter.increment(dim); - } - - container_type m_shape; - container_type m_index; - std::array m_common_iterator; -}; - -enum class broadcast_trivial { non_trivial, c_trivial, f_trivial }; - -// Populates the shape and number of dimensions for the set of buffers. Returns a broadcast_trivial -// enum value indicating whether the broadcast is "trivial"--that is, has each buffer being either a -// singleton or a full-size, C-contiguous (`c_trivial`) or Fortran-contiguous (`f_trivial`) storage -// buffer; returns `non_trivial` otherwise. -template -broadcast_trivial broadcast(const std::array &buffers, ssize_t &ndim, std::vector &shape) { - ndim = std::accumulate(buffers.begin(), buffers.end(), ssize_t(0), [](ssize_t res, const buffer_info &buf) { - return std::max(res, buf.ndim); - }); - - shape.clear(); - shape.resize((size_t) ndim, 1); - - // Figure out the output size, and make sure all input arrays conform (i.e. are either size 1 or - // the full size). - for (size_t i = 0; i < N; ++i) { - auto res_iter = shape.rbegin(); - auto end = buffers[i].shape.rend(); - for (auto shape_iter = buffers[i].shape.rbegin(); shape_iter != end; ++shape_iter, ++res_iter) { - const auto &dim_size_in = *shape_iter; - auto &dim_size_out = *res_iter; - - // Each input dimension can either be 1 or `n`, but `n` values must match across buffers - if (dim_size_out == 1) - dim_size_out = dim_size_in; - else if (dim_size_in != 1 && dim_size_in != dim_size_out) - pybind11_fail("pybind11::vectorize: incompatible size/dimension of inputs!"); - } - } - - bool trivial_broadcast_c = true; - bool trivial_broadcast_f = true; - for (size_t i = 0; i < N && (trivial_broadcast_c || trivial_broadcast_f); ++i) { - if (buffers[i].size == 1) - continue; - - // Require the same number of dimensions: - if (buffers[i].ndim != ndim) - return broadcast_trivial::non_trivial; - - // Require all dimensions be full-size: - if (!std::equal(buffers[i].shape.cbegin(), buffers[i].shape.cend(), shape.cbegin())) - return broadcast_trivial::non_trivial; - - // Check for C contiguity (but only if previous inputs were also C contiguous) - if (trivial_broadcast_c) { - ssize_t expect_stride = buffers[i].itemsize; - auto end = buffers[i].shape.crend(); - for (auto shape_iter = buffers[i].shape.crbegin(), stride_iter = buffers[i].strides.crbegin(); - trivial_broadcast_c && shape_iter != end; ++shape_iter, ++stride_iter) { - if (expect_stride == *stride_iter) - expect_stride *= *shape_iter; - else - trivial_broadcast_c = false; - } - } - - // Check for Fortran contiguity (if previous inputs were also F contiguous) - if (trivial_broadcast_f) { - ssize_t expect_stride = buffers[i].itemsize; - auto end = buffers[i].shape.cend(); - for (auto shape_iter = buffers[i].shape.cbegin(), stride_iter = buffers[i].strides.cbegin(); - trivial_broadcast_f && shape_iter != end; ++shape_iter, ++stride_iter) { - if (expect_stride == *stride_iter) - expect_stride *= *shape_iter; - else - trivial_broadcast_f = false; - } - } - } - - return - trivial_broadcast_c ? broadcast_trivial::c_trivial : - trivial_broadcast_f ? broadcast_trivial::f_trivial : - broadcast_trivial::non_trivial; -} - -template -struct vectorize_arg { - static_assert(!std::is_rvalue_reference::value, "Functions with rvalue reference arguments cannot be vectorized"); - // The wrapped function gets called with this type: - using call_type = remove_reference_t; - // Is this a vectorized argument? - static constexpr bool vectorize = - satisfies_any_of::value && - satisfies_none_of::value && - (!std::is_reference::value || - (std::is_lvalue_reference::value && std::is_const::value)); - // Accept this type: an array for vectorized types, otherwise the type as-is: - using type = conditional_t, array::forcecast>, T>; -}; - -template -struct vectorize_helper { -private: - static constexpr size_t N = sizeof...(Args); - static constexpr size_t NVectorized = constexpr_sum(vectorize_arg::vectorize...); - static_assert(NVectorized >= 1, - "pybind11::vectorize(...) requires a function with at least one vectorizable argument"); - -public: - template - explicit vectorize_helper(T &&f) : f(std::forward(f)) { } - - object operator()(typename vectorize_arg::type... args) { - return run(args..., - make_index_sequence(), - select_indices::vectorize...>(), - make_index_sequence()); - } - -private: - remove_reference_t f; - - template using param_n_t = typename pack_element::call_type...>::type; - - // Runs a vectorized function given arguments tuple and three index sequences: - // - Index is the full set of 0 ... (N-1) argument indices; - // - VIndex is the subset of argument indices with vectorized parameters, letting us access - // vectorized arguments (anything not in this sequence is passed through) - // - BIndex is a incremental sequence (beginning at 0) of the same size as VIndex, so that - // we can store vectorized buffer_infos in an array (argument VIndex has its buffer at - // index BIndex in the array). - template object run( - typename vectorize_arg::type &...args, - index_sequence i_seq, index_sequence vi_seq, index_sequence bi_seq) { - - // Pointers to values the function was called with; the vectorized ones set here will start - // out as array_t pointers, but they will be changed them to T pointers before we make - // call the wrapped function. Non-vectorized pointers are left as-is. - std::array params{{ &args... }}; - - // The array of `buffer_info`s of vectorized arguments: - std::array buffers{{ reinterpret_cast(params[VIndex])->request()... }}; - - /* Determine dimensions parameters of output array */ - ssize_t nd = 0; - std::vector shape(0); - auto trivial = broadcast(buffers, nd, shape); - size_t ndim = (size_t) nd; - - size_t size = std::accumulate(shape.begin(), shape.end(), (size_t) 1, std::multiplies()); - - // If all arguments are 0-dimension arrays (i.e. single values) return a plain value (i.e. - // not wrapped in an array). - if (size == 1 && ndim == 0) { - PYBIND11_EXPAND_SIDE_EFFECTS(params[VIndex] = buffers[BIndex].ptr); - return cast(f(*reinterpret_cast *>(params[Index])...)); - } - - array_t result; - if (trivial == broadcast_trivial::f_trivial) result = array_t(shape); - else result = array_t(shape); - - if (size == 0) return result; - - /* Call the function */ - if (trivial == broadcast_trivial::non_trivial) - apply_broadcast(buffers, params, result, i_seq, vi_seq, bi_seq); - else - apply_trivial(buffers, params, result.mutable_data(), size, i_seq, vi_seq, bi_seq); - - return result; - } - - template - void apply_trivial(std::array &buffers, - std::array ¶ms, - Return *out, - size_t size, - index_sequence, index_sequence, index_sequence) { - - // Initialize an array of mutable byte references and sizes with references set to the - // appropriate pointer in `params`; as we iterate, we'll increment each pointer by its size - // (except for singletons, which get an increment of 0). - std::array, NVectorized> vecparams{{ - std::pair( - reinterpret_cast(params[VIndex] = buffers[BIndex].ptr), - buffers[BIndex].size == 1 ? 0 : sizeof(param_n_t) - )... - }}; - - for (size_t i = 0; i < size; ++i) { - out[i] = f(*reinterpret_cast *>(params[Index])...); - for (auto &x : vecparams) x.first += x.second; - } - } - - template - void apply_broadcast(std::array &buffers, - std::array ¶ms, - array_t &output_array, - index_sequence, index_sequence, index_sequence) { - - buffer_info output = output_array.request(); - multi_array_iterator input_iter(buffers, output.shape); - - for (array_iterator iter = array_begin(output), end = array_end(output); - iter != end; - ++iter, ++input_iter) { - PYBIND11_EXPAND_SIDE_EFFECTS(( - params[VIndex] = input_iter.template data() - )); - *iter = f(*reinterpret_cast *>(std::get(params))...); - } - } -}; - -template -vectorize_helper -vectorize_extractor(const Func &f, Return (*) (Args ...)) { - return detail::vectorize_helper(f); -} - -template struct handle_type_name> { - static PYBIND11_DESCR name() { - return _("numpy.ndarray[") + npy_format_descriptor::name() + _("]"); - } -}; - -NAMESPACE_END(detail) - -// Vanilla pointer vectorizer: -template -detail::vectorize_helper -vectorize(Return (*f) (Args ...)) { - return detail::vectorize_helper(f); -} - -// lambda vectorizer: -template ::operator())>::type> -auto vectorize(Func &&f) -> decltype( - detail::vectorize_extractor(std::forward(f), (FuncType *) nullptr)) { - return detail::vectorize_extractor(std::forward(f), (FuncType *) nullptr); -} - -// Vectorize a class method (non-const): -template ())), Return, Class *, Args...>> -Helper vectorize(Return (Class::*f)(Args...)) { - return Helper(std::mem_fn(f)); -} - -// Vectorize a class method (non-const): -template ())), Return, const Class *, Args...>> -Helper vectorize(Return (Class::*f)(Args...) const) { - return Helper(std::mem_fn(f)); -} - -NAMESPACE_END(pybind11) - -#if defined(_MSC_VER) -#pragma warning(pop) -#endif diff --git a/ptocr/postprocess/lanms/include/pybind11/operators.h b/ptocr/postprocess/lanms/include/pybind11/operators.h deleted file mode 100644 index 562987b..0000000 --- a/ptocr/postprocess/lanms/include/pybind11/operators.h +++ /dev/null @@ -1,167 +0,0 @@ -/* - pybind11/operator.h: Metatemplates for operator overloading - - Copyright (c) 2016 Wenzel Jakob - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#include "pybind11.h" - -#if defined(__clang__) && !defined(__INTEL_COMPILER) -# pragma clang diagnostic ignored "-Wunsequenced" // multiple unsequenced modifications to 'self' (when using def(py::self OP Type())) -#elif defined(_MSC_VER) -# pragma warning(push) -# pragma warning(disable: 4127) // warning C4127: Conditional expression is constant -#endif - -NAMESPACE_BEGIN(pybind11) -NAMESPACE_BEGIN(detail) - -/// Enumeration with all supported operator types -enum op_id : int { - op_add, op_sub, op_mul, op_div, op_mod, op_divmod, op_pow, op_lshift, - op_rshift, op_and, op_xor, op_or, op_neg, op_pos, op_abs, op_invert, - op_int, op_long, op_float, op_str, op_cmp, op_gt, op_ge, op_lt, op_le, - op_eq, op_ne, op_iadd, op_isub, op_imul, op_idiv, op_imod, op_ilshift, - op_irshift, op_iand, op_ixor, op_ior, op_complex, op_bool, op_nonzero, - op_repr, op_truediv, op_itruediv -}; - -enum op_type : int { - op_l, /* base type on left */ - op_r, /* base type on right */ - op_u /* unary operator */ -}; - -struct self_t { }; -static const self_t self = self_t(); - -/// Type for an unused type slot -struct undefined_t { }; - -/// Don't warn about an unused variable -inline self_t __self() { return self; } - -/// base template of operator implementations -template struct op_impl { }; - -/// Operator implementation generator -template struct op_ { - template void execute(Class &cl, const Extra&... extra) const { - using Base = typename Class::type; - using L_type = conditional_t::value, Base, L>; - using R_type = conditional_t::value, Base, R>; - using op = op_impl; - cl.def(op::name(), &op::execute, is_operator(), extra...); - #if PY_MAJOR_VERSION < 3 - if (id == op_truediv || id == op_itruediv) - cl.def(id == op_itruediv ? "__idiv__" : ot == op_l ? "__div__" : "__rdiv__", - &op::execute, is_operator(), extra...); - #endif - } - template void execute_cast(Class &cl, const Extra&... extra) const { - using Base = typename Class::type; - using L_type = conditional_t::value, Base, L>; - using R_type = conditional_t::value, Base, R>; - using op = op_impl; - cl.def(op::name(), &op::execute_cast, is_operator(), extra...); - #if PY_MAJOR_VERSION < 3 - if (id == op_truediv || id == op_itruediv) - cl.def(id == op_itruediv ? "__idiv__" : ot == op_l ? "__div__" : "__rdiv__", - &op::execute, is_operator(), extra...); - #endif - } -}; - -#define PYBIND11_BINARY_OPERATOR(id, rid, op, expr) \ -template struct op_impl { \ - static char const* name() { return "__" #id "__"; } \ - static auto execute(const L &l, const R &r) -> decltype(expr) { return (expr); } \ - static B execute_cast(const L &l, const R &r) { return B(expr); } \ -}; \ -template struct op_impl { \ - static char const* name() { return "__" #rid "__"; } \ - static auto execute(const R &r, const L &l) -> decltype(expr) { return (expr); } \ - static B execute_cast(const R &r, const L &l) { return B(expr); } \ -}; \ -inline op_ op(const self_t &, const self_t &) { \ - return op_(); \ -} \ -template op_ op(const self_t &, const T &) { \ - return op_(); \ -} \ -template op_ op(const T &, const self_t &) { \ - return op_(); \ -} - -#define PYBIND11_INPLACE_OPERATOR(id, op, expr) \ -template struct op_impl { \ - static char const* name() { return "__" #id "__"; } \ - static auto execute(L &l, const R &r) -> decltype(expr) { return expr; } \ - static B execute_cast(L &l, const R &r) { return B(expr); } \ -}; \ -template op_ op(const self_t &, const T &) { \ - return op_(); \ -} - -#define PYBIND11_UNARY_OPERATOR(id, op, expr) \ -template struct op_impl { \ - static char const* name() { return "__" #id "__"; } \ - static auto execute(const L &l) -> decltype(expr) { return expr; } \ - static B execute_cast(const L &l) { return B(expr); } \ -}; \ -inline op_ op(const self_t &) { \ - return op_(); \ -} - -PYBIND11_BINARY_OPERATOR(sub, rsub, operator-, l - r) -PYBIND11_BINARY_OPERATOR(add, radd, operator+, l + r) -PYBIND11_BINARY_OPERATOR(mul, rmul, operator*, l * r) -PYBIND11_BINARY_OPERATOR(truediv, rtruediv, operator/, l / r) -PYBIND11_BINARY_OPERATOR(mod, rmod, operator%, l % r) -PYBIND11_BINARY_OPERATOR(lshift, rlshift, operator<<, l << r) -PYBIND11_BINARY_OPERATOR(rshift, rrshift, operator>>, l >> r) -PYBIND11_BINARY_OPERATOR(and, rand, operator&, l & r) -PYBIND11_BINARY_OPERATOR(xor, rxor, operator^, l ^ r) -PYBIND11_BINARY_OPERATOR(eq, eq, operator==, l == r) -PYBIND11_BINARY_OPERATOR(ne, ne, operator!=, l != r) -PYBIND11_BINARY_OPERATOR(or, ror, operator|, l | r) -PYBIND11_BINARY_OPERATOR(gt, lt, operator>, l > r) -PYBIND11_BINARY_OPERATOR(ge, le, operator>=, l >= r) -PYBIND11_BINARY_OPERATOR(lt, gt, operator<, l < r) -PYBIND11_BINARY_OPERATOR(le, ge, operator<=, l <= r) -//PYBIND11_BINARY_OPERATOR(pow, rpow, pow, std::pow(l, r)) -PYBIND11_INPLACE_OPERATOR(iadd, operator+=, l += r) -PYBIND11_INPLACE_OPERATOR(isub, operator-=, l -= r) -PYBIND11_INPLACE_OPERATOR(imul, operator*=, l *= r) -PYBIND11_INPLACE_OPERATOR(itruediv, operator/=, l /= r) -PYBIND11_INPLACE_OPERATOR(imod, operator%=, l %= r) -PYBIND11_INPLACE_OPERATOR(ilshift, operator<<=, l <<= r) -PYBIND11_INPLACE_OPERATOR(irshift, operator>>=, l >>= r) -PYBIND11_INPLACE_OPERATOR(iand, operator&=, l &= r) -PYBIND11_INPLACE_OPERATOR(ixor, operator^=, l ^= r) -PYBIND11_INPLACE_OPERATOR(ior, operator|=, l |= r) -PYBIND11_UNARY_OPERATOR(neg, operator-, -l) -PYBIND11_UNARY_OPERATOR(pos, operator+, +l) -PYBIND11_UNARY_OPERATOR(abs, abs, std::abs(l)) -PYBIND11_UNARY_OPERATOR(invert, operator~, (~l)) -PYBIND11_UNARY_OPERATOR(bool, operator!, !!l) -PYBIND11_UNARY_OPERATOR(int, int_, (int) l) -PYBIND11_UNARY_OPERATOR(float, float_, (double) l) - -#undef PYBIND11_BINARY_OPERATOR -#undef PYBIND11_INPLACE_OPERATOR -#undef PYBIND11_UNARY_OPERATOR -NAMESPACE_END(detail) - -using detail::self; - -NAMESPACE_END(pybind11) - -#if defined(_MSC_VER) -# pragma warning(pop) -#endif diff --git a/ptocr/postprocess/lanms/include/pybind11/options.h b/ptocr/postprocess/lanms/include/pybind11/options.h deleted file mode 100644 index 3105551..0000000 --- a/ptocr/postprocess/lanms/include/pybind11/options.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - pybind11/options.h: global settings that are configurable at runtime. - - Copyright (c) 2016 Wenzel Jakob - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#include "common.h" - -NAMESPACE_BEGIN(pybind11) - -class options { -public: - - // Default RAII constructor, which leaves settings as they currently are. - options() : previous_state(global_state()) {} - - // Class is non-copyable. - options(const options&) = delete; - options& operator=(const options&) = delete; - - // Destructor, which restores settings that were in effect before. - ~options() { - global_state() = previous_state; - } - - // Setter methods (affect the global state): - - options& disable_user_defined_docstrings() & { global_state().show_user_defined_docstrings = false; return *this; } - - options& enable_user_defined_docstrings() & { global_state().show_user_defined_docstrings = true; return *this; } - - options& disable_function_signatures() & { global_state().show_function_signatures = false; return *this; } - - options& enable_function_signatures() & { global_state().show_function_signatures = true; return *this; } - - // Getter methods (return the global state): - - static bool show_user_defined_docstrings() { return global_state().show_user_defined_docstrings; } - - static bool show_function_signatures() { return global_state().show_function_signatures; } - - // This type is not meant to be allocated on the heap. - void* operator new(size_t) = delete; - -private: - - struct state { - bool show_user_defined_docstrings = true; //< Include user-supplied texts in docstrings. - bool show_function_signatures = true; //< Include auto-generated function signatures in docstrings. - }; - - static state &global_state() { - static state instance; - return instance; - } - - state previous_state; -}; - -NAMESPACE_END(pybind11) diff --git a/ptocr/postprocess/lanms/include/pybind11/pybind11.h b/ptocr/postprocess/lanms/include/pybind11/pybind11.h deleted file mode 100644 index d3f34ee..0000000 --- a/ptocr/postprocess/lanms/include/pybind11/pybind11.h +++ /dev/null @@ -1,1869 +0,0 @@ -/* - pybind11/pybind11.h: Main header file of the C++11 python - binding generator library - - Copyright (c) 2016 Wenzel Jakob - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#if defined(_MSC_VER) -# pragma warning(push) -# pragma warning(disable: 4100) // warning C4100: Unreferenced formal parameter -# pragma warning(disable: 4127) // warning C4127: Conditional expression is constant -# pragma warning(disable: 4512) // warning C4512: Assignment operator was implicitly defined as deleted -# pragma warning(disable: 4800) // warning C4800: 'int': forcing value to bool 'true' or 'false' (performance warning) -# pragma warning(disable: 4996) // warning C4996: The POSIX name for this item is deprecated. Instead, use the ISO C and C++ conformant name -# pragma warning(disable: 4702) // warning C4702: unreachable code -# pragma warning(disable: 4522) // warning C4522: multiple assignment operators specified -#elif defined(__INTEL_COMPILER) -# pragma warning(push) -# pragma warning(disable: 68) // integer conversion resulted in a change of sign -# pragma warning(disable: 186) // pointless comparison of unsigned integer with zero -# pragma warning(disable: 878) // incompatible exception specifications -# pragma warning(disable: 1334) // the "template" keyword used for syntactic disambiguation may only be used within a template -# pragma warning(disable: 1682) // implicit conversion of a 64-bit integral type to a smaller integral type (potential portability problem) -# pragma warning(disable: 1875) // offsetof applied to non-POD (Plain Old Data) types is nonstandard -# pragma warning(disable: 2196) // warning #2196: routine is both "inline" and "noinline" -#elif defined(__GNUG__) && !defined(__clang__) -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wunused-but-set-parameter" -# pragma GCC diagnostic ignored "-Wunused-but-set-variable" -# pragma GCC diagnostic ignored "-Wmissing-field-initializers" -# pragma GCC diagnostic ignored "-Wstrict-aliasing" -# pragma GCC diagnostic ignored "-Wattributes" -# if __GNUC__ >= 7 -# pragma GCC diagnostic ignored "-Wnoexcept-type" -# endif -#endif - -#include "attr.h" -#include "options.h" -#include "class_support.h" - -NAMESPACE_BEGIN(pybind11) - -/// Wraps an arbitrary C++ function/method/lambda function/.. into a callable Python object -class cpp_function : public function { -public: - cpp_function() { } - - /// Construct a cpp_function from a vanilla function pointer - template - cpp_function(Return (*f)(Args...), const Extra&... extra) { - initialize(f, f, extra...); - } - - /// Construct a cpp_function from a lambda function (possibly with internal state) - template , - std::is_function, std::is_pointer, std::is_member_pointer - >::value> - > - cpp_function(Func &&f, const Extra&... extra) { - using FuncType = typename detail::remove_class::operator())>::type; - initialize(std::forward(f), - (FuncType *) nullptr, extra...); - } - - /// Construct a cpp_function from a class method (non-const) - template - cpp_function(Return (Class::*f)(Arg...), const Extra&... extra) { - initialize([f](Class *c, Arg... args) -> Return { return (c->*f)(args...); }, - (Return (*) (Class *, Arg...)) nullptr, extra...); - } - - /// Construct a cpp_function from a class method (const) - template - cpp_function(Return (Class::*f)(Arg...) const, const Extra&... extra) { - initialize([f](const Class *c, Arg... args) -> Return { return (c->*f)(args...); }, - (Return (*)(const Class *, Arg ...)) nullptr, extra...); - } - - /// Return the function name - object name() const { return attr("__name__"); } - -protected: - /// Space optimization: don't inline this frequently instantiated fragment - PYBIND11_NOINLINE detail::function_record *make_function_record() { - return new detail::function_record(); - } - - /// Special internal constructor for functors, lambda functions, etc. - template - void initialize(Func &&f, Return (*)(Args...), const Extra&... extra) { - - struct capture { detail::remove_reference_t f; }; - - /* Store the function including any extra state it might have (e.g. a lambda capture object) */ - auto rec = make_function_record(); - - /* Store the capture object directly in the function record if there is enough space */ - if (sizeof(capture) <= sizeof(rec->data)) { - /* Without these pragmas, GCC warns that there might not be - enough space to use the placement new operator. However, the - 'if' statement above ensures that this is the case. */ -#if defined(__GNUG__) && !defined(__clang__) && __GNUC__ >= 6 -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wplacement-new" -#endif - new ((capture *) &rec->data) capture { std::forward(f) }; -#if defined(__GNUG__) && !defined(__clang__) && __GNUC__ >= 6 -# pragma GCC diagnostic pop -#endif - if (!std::is_trivially_destructible::value) - rec->free_data = [](detail::function_record *r) { ((capture *) &r->data)->~capture(); }; - } else { - rec->data[0] = new capture { std::forward(f) }; - rec->free_data = [](detail::function_record *r) { delete ((capture *) r->data[0]); }; - } - - /* Type casters for the function arguments and return value */ - using cast_in = detail::argument_loader; - using cast_out = detail::make_caster< - detail::conditional_t::value, detail::void_type, Return> - >; - - static_assert(detail::expected_num_args(sizeof...(Args), cast_in::has_args, cast_in::has_kwargs), - "The number of argument annotations does not match the number of function arguments"); - - /* Dispatch code which converts function arguments and performs the actual function call */ - rec->impl = [](detail::function_call &call) -> handle { - cast_in args_converter; - - /* Try to cast the function arguments into the C++ domain */ - if (!args_converter.load_args(call)) - return PYBIND11_TRY_NEXT_OVERLOAD; - - /* Invoke call policy pre-call hook */ - detail::process_attributes::precall(call); - - /* Get a pointer to the capture object */ - auto data = (sizeof(capture) <= sizeof(call.func.data) - ? &call.func.data : call.func.data[0]); - capture *cap = const_cast(reinterpret_cast(data)); - - /* Override policy for rvalues -- usually to enforce rvp::move on an rvalue */ - const auto policy = detail::return_value_policy_override::policy(call.func.policy); - - /* Function scope guard -- defaults to the compile-to-nothing `void_type` */ - using Guard = detail::extract_guard_t; - - /* Perform the function call */ - handle result = cast_out::cast( - std::move(args_converter).template call(cap->f), policy, call.parent); - - /* Invoke call policy post-call hook */ - detail::process_attributes::postcall(call, result); - - return result; - }; - - /* Process any user-provided function attributes */ - detail::process_attributes::init(extra..., rec); - - /* Generate a readable signature describing the function's arguments and return value types */ - using detail::descr; using detail::_; - PYBIND11_DESCR signature = _("(") + cast_in::arg_names() + _(") -> ") + cast_out::name(); - - /* Register the function with Python from generic (non-templated) code */ - initialize_generic(rec, signature.text(), signature.types(), sizeof...(Args)); - - if (cast_in::has_args) rec->has_args = true; - if (cast_in::has_kwargs) rec->has_kwargs = true; - - /* Stash some additional information used by an important optimization in 'functional.h' */ - using FunctionType = Return (*)(Args...); - constexpr bool is_function_ptr = - std::is_convertible::value && - sizeof(capture) == sizeof(void *); - if (is_function_ptr) { - rec->is_stateless = true; - rec->data[1] = const_cast(reinterpret_cast(&typeid(FunctionType))); - } - } - - /// Register a function call with Python (generic non-templated code goes here) - void initialize_generic(detail::function_record *rec, const char *text, - const std::type_info *const *types, size_t args) { - - /* Create copies of all referenced C-style strings */ - rec->name = strdup(rec->name ? rec->name : ""); - if (rec->doc) rec->doc = strdup(rec->doc); - for (auto &a: rec->args) { - if (a.name) - a.name = strdup(a.name); - if (a.descr) - a.descr = strdup(a.descr); - else if (a.value) - a.descr = strdup(a.value.attr("__repr__")().cast().c_str()); - } - - /* Generate a proper function signature */ - std::string signature; - size_t type_depth = 0, char_index = 0, type_index = 0, arg_index = 0; - while (true) { - char c = text[char_index++]; - if (c == '\0') - break; - - if (c == '{') { - // Write arg name for everything except *args, **kwargs and return type. - if (type_depth == 0 && text[char_index] != '*' && arg_index < args) { - if (!rec->args.empty() && rec->args[arg_index].name) { - signature += rec->args[arg_index].name; - } else if (arg_index == 0 && rec->is_method) { - signature += "self"; - } else { - signature += "arg" + std::to_string(arg_index - (rec->is_method ? 1 : 0)); - } - signature += ": "; - } - ++type_depth; - } else if (c == '}') { - --type_depth; - if (type_depth == 0) { - if (arg_index < rec->args.size() && rec->args[arg_index].descr) { - signature += "="; - signature += rec->args[arg_index].descr; - } - arg_index++; - } - } else if (c == '%') { - const std::type_info *t = types[type_index++]; - if (!t) - pybind11_fail("Internal error while parsing type signature (1)"); - if (auto tinfo = detail::get_type_info(*t)) { -#if defined(PYPY_VERSION) - signature += handle((PyObject *) tinfo->type) - .attr("__module__") - .cast() + "."; -#endif - signature += tinfo->type->tp_name; - } else { - std::string tname(t->name()); - detail::clean_type_id(tname); - signature += tname; - } - } else { - signature += c; - } - } - if (type_depth != 0 || types[type_index] != nullptr) - pybind11_fail("Internal error while parsing type signature (2)"); - - #if !defined(PYBIND11_CONSTEXPR_DESCR) - delete[] types; - delete[] text; - #endif - -#if PY_MAJOR_VERSION < 3 - if (strcmp(rec->name, "__next__") == 0) { - std::free(rec->name); - rec->name = strdup("next"); - } else if (strcmp(rec->name, "__bool__") == 0) { - std::free(rec->name); - rec->name = strdup("__nonzero__"); - } -#endif - rec->signature = strdup(signature.c_str()); - rec->args.shrink_to_fit(); - rec->is_constructor = !strcmp(rec->name, "__init__") || !strcmp(rec->name, "__setstate__"); - rec->nargs = (std::uint16_t) args; - - if (rec->sibling && PYBIND11_INSTANCE_METHOD_CHECK(rec->sibling.ptr())) - rec->sibling = PYBIND11_INSTANCE_METHOD_GET_FUNCTION(rec->sibling.ptr()); - - detail::function_record *chain = nullptr, *chain_start = rec; - if (rec->sibling) { - if (PyCFunction_Check(rec->sibling.ptr())) { - auto rec_capsule = reinterpret_borrow(PyCFunction_GET_SELF(rec->sibling.ptr())); - chain = (detail::function_record *) rec_capsule; - /* Never append a method to an overload chain of a parent class; - instead, hide the parent's overloads in this case */ - if (!chain->scope.is(rec->scope)) - chain = nullptr; - } - // Don't trigger for things like the default __init__, which are wrapper_descriptors that we are intentionally replacing - else if (!rec->sibling.is_none() && rec->name[0] != '_') - pybind11_fail("Cannot overload existing non-function object \"" + std::string(rec->name) + - "\" with a function of the same name"); - } - - if (!chain) { - /* No existing overload was found, create a new function object */ - rec->def = new PyMethodDef(); - std::memset(rec->def, 0, sizeof(PyMethodDef)); - rec->def->ml_name = rec->name; - rec->def->ml_meth = reinterpret_cast(*dispatcher); - rec->def->ml_flags = METH_VARARGS | METH_KEYWORDS; - - capsule rec_capsule(rec, [](void *ptr) { - destruct((detail::function_record *) ptr); - }); - - object scope_module; - if (rec->scope) { - if (hasattr(rec->scope, "__module__")) { - scope_module = rec->scope.attr("__module__"); - } else if (hasattr(rec->scope, "__name__")) { - scope_module = rec->scope.attr("__name__"); - } - } - - m_ptr = PyCFunction_NewEx(rec->def, rec_capsule.ptr(), scope_module.ptr()); - if (!m_ptr) - pybind11_fail("cpp_function::cpp_function(): Could not allocate function object"); - } else { - /* Append at the end of the overload chain */ - m_ptr = rec->sibling.ptr(); - inc_ref(); - chain_start = chain; - if (chain->is_method != rec->is_method) - pybind11_fail("overloading a method with both static and instance methods is not supported; " - #if defined(NDEBUG) - "compile in debug mode for more details" - #else - "error while attempting to bind " + std::string(rec->is_method ? "instance" : "static") + " method " + - std::string(pybind11::str(rec->scope.attr("__name__"))) + "." + std::string(rec->name) + signature - #endif - ); - while (chain->next) - chain = chain->next; - chain->next = rec; - } - - std::string signatures; - int index = 0; - /* Create a nice pydoc rec including all signatures and - docstrings of the functions in the overload chain */ - if (chain && options::show_function_signatures()) { - // First a generic signature - signatures += rec->name; - signatures += "(*args, **kwargs)\n"; - signatures += "Overloaded function.\n\n"; - } - // Then specific overload signatures - bool first_user_def = true; - for (auto it = chain_start; it != nullptr; it = it->next) { - if (options::show_function_signatures()) { - if (index > 0) signatures += "\n"; - if (chain) - signatures += std::to_string(++index) + ". "; - signatures += rec->name; - signatures += it->signature; - signatures += "\n"; - } - if (it->doc && strlen(it->doc) > 0 && options::show_user_defined_docstrings()) { - // If we're appending another docstring, and aren't printing function signatures, we - // need to append a newline first: - if (!options::show_function_signatures()) { - if (first_user_def) first_user_def = false; - else signatures += "\n"; - } - if (options::show_function_signatures()) signatures += "\n"; - signatures += it->doc; - if (options::show_function_signatures()) signatures += "\n"; - } - } - - /* Install docstring */ - PyCFunctionObject *func = (PyCFunctionObject *) m_ptr; - if (func->m_ml->ml_doc) - std::free(const_cast(func->m_ml->ml_doc)); - func->m_ml->ml_doc = strdup(signatures.c_str()); - - if (rec->is_method) { - m_ptr = PYBIND11_INSTANCE_METHOD_NEW(m_ptr, rec->scope.ptr()); - if (!m_ptr) - pybind11_fail("cpp_function::cpp_function(): Could not allocate instance method object"); - Py_DECREF(func); - } - } - - /// When a cpp_function is GCed, release any memory allocated by pybind11 - static void destruct(detail::function_record *rec) { - while (rec) { - detail::function_record *next = rec->next; - if (rec->free_data) - rec->free_data(rec); - std::free((char *) rec->name); - std::free((char *) rec->doc); - std::free((char *) rec->signature); - for (auto &arg: rec->args) { - std::free(const_cast(arg.name)); - std::free(const_cast(arg.descr)); - arg.value.dec_ref(); - } - if (rec->def) { - std::free(const_cast(rec->def->ml_doc)); - delete rec->def; - } - delete rec; - rec = next; - } - } - - /// Main dispatch logic for calls to functions bound using pybind11 - static PyObject *dispatcher(PyObject *self, PyObject *args_in, PyObject *kwargs_in) { - using namespace detail; - - /* Iterator over the list of potentially admissible overloads */ - function_record *overloads = (function_record *) PyCapsule_GetPointer(self, nullptr), - *it = overloads; - - /* Need to know how many arguments + keyword arguments there are to pick the right overload */ - const size_t n_args_in = (size_t) PyTuple_GET_SIZE(args_in); - - handle parent = n_args_in > 0 ? PyTuple_GET_ITEM(args_in, 0) : nullptr, - result = PYBIND11_TRY_NEXT_OVERLOAD; - - try { - // We do this in two passes: in the first pass, we load arguments with `convert=false`; - // in the second, we allow conversion (except for arguments with an explicit - // py::arg().noconvert()). This lets us prefer calls without conversion, with - // conversion as a fallback. - std::vector second_pass; - - // However, if there are no overloads, we can just skip the no-convert pass entirely - const bool overloaded = it != nullptr && it->next != nullptr; - - for (; it != nullptr; it = it->next) { - - /* For each overload: - 1. Copy all positional arguments we were given, also checking to make sure that - named positional arguments weren't *also* specified via kwarg. - 2. If we weren't given enough, try to make up the omitted ones by checking - whether they were provided by a kwarg matching the `py::arg("name")` name. If - so, use it (and remove it from kwargs; if not, see if the function binding - provided a default that we can use. - 3. Ensure that either all keyword arguments were "consumed", or that the function - takes a kwargs argument to accept unconsumed kwargs. - 4. Any positional arguments still left get put into a tuple (for args), and any - leftover kwargs get put into a dict. - 5. Pack everything into a vector; if we have py::args or py::kwargs, they are an - extra tuple or dict at the end of the positional arguments. - 6. Call the function call dispatcher (function_record::impl) - - If one of these fail, move on to the next overload and keep trying until we get a - result other than PYBIND11_TRY_NEXT_OVERLOAD. - */ - - function_record &func = *it; - size_t pos_args = func.nargs; // Number of positional arguments that we need - if (func.has_args) --pos_args; // (but don't count py::args - if (func.has_kwargs) --pos_args; // or py::kwargs) - - if (!func.has_args && n_args_in > pos_args) - continue; // Too many arguments for this overload - - if (n_args_in < pos_args && func.args.size() < pos_args) - continue; // Not enough arguments given, and not enough defaults to fill in the blanks - - function_call call(func, parent); - - size_t args_to_copy = std::min(pos_args, n_args_in); - size_t args_copied = 0; - - // 1. Copy any position arguments given. - bool bad_arg = false; - for (; args_copied < args_to_copy; ++args_copied) { - argument_record *arg_rec = args_copied < func.args.size() ? &func.args[args_copied] : nullptr; - if (kwargs_in && arg_rec && arg_rec->name && PyDict_GetItemString(kwargs_in, arg_rec->name)) { - bad_arg = true; - break; - } - - handle arg(PyTuple_GET_ITEM(args_in, args_copied)); - if (arg_rec && !arg_rec->none && arg.is_none()) { - bad_arg = true; - break; - } - call.args.push_back(arg); - call.args_convert.push_back(arg_rec ? arg_rec->convert : true); - } - if (bad_arg) - continue; // Maybe it was meant for another overload (issue #688) - - // We'll need to copy this if we steal some kwargs for defaults - dict kwargs = reinterpret_borrow(kwargs_in); - - // 2. Check kwargs and, failing that, defaults that may help complete the list - if (args_copied < pos_args) { - bool copied_kwargs = false; - - for (; args_copied < pos_args; ++args_copied) { - const auto &arg = func.args[args_copied]; - - handle value; - if (kwargs_in && arg.name) - value = PyDict_GetItemString(kwargs.ptr(), arg.name); - - if (value) { - // Consume a kwargs value - if (!copied_kwargs) { - kwargs = reinterpret_steal(PyDict_Copy(kwargs.ptr())); - copied_kwargs = true; - } - PyDict_DelItemString(kwargs.ptr(), arg.name); - } else if (arg.value) { - value = arg.value; - } - - if (value) { - call.args.push_back(value); - call.args_convert.push_back(arg.convert); - } - else - break; - } - - if (args_copied < pos_args) - continue; // Not enough arguments, defaults, or kwargs to fill the positional arguments - } - - // 3. Check everything was consumed (unless we have a kwargs arg) - if (kwargs && kwargs.size() > 0 && !func.has_kwargs) - continue; // Unconsumed kwargs, but no py::kwargs argument to accept them - - // 4a. If we have a py::args argument, create a new tuple with leftovers - tuple extra_args; - if (func.has_args) { - if (args_to_copy == 0) { - // We didn't copy out any position arguments from the args_in tuple, so we - // can reuse it directly without copying: - extra_args = reinterpret_borrow(args_in); - } else if (args_copied >= n_args_in) { - extra_args = tuple(0); - } else { - size_t args_size = n_args_in - args_copied; - extra_args = tuple(args_size); - for (size_t i = 0; i < args_size; ++i) { - handle item = PyTuple_GET_ITEM(args_in, args_copied + i); - extra_args[i] = item.inc_ref().ptr(); - } - } - call.args.push_back(extra_args); - call.args_convert.push_back(false); - } - - // 4b. If we have a py::kwargs, pass on any remaining kwargs - if (func.has_kwargs) { - if (!kwargs.ptr()) - kwargs = dict(); // If we didn't get one, send an empty one - call.args.push_back(kwargs); - call.args_convert.push_back(false); - } - - // 5. Put everything in a vector. Not technically step 5, we've been building it - // in `call.args` all along. - #if !defined(NDEBUG) - if (call.args.size() != func.nargs || call.args_convert.size() != func.nargs) - pybind11_fail("Internal error: function call dispatcher inserted wrong number of arguments!"); - #endif - - std::vector second_pass_convert; - if (overloaded) { - // We're in the first no-convert pass, so swap out the conversion flags for a - // set of all-false flags. If the call fails, we'll swap the flags back in for - // the conversion-allowed call below. - second_pass_convert.resize(func.nargs, false); - call.args_convert.swap(second_pass_convert); - } - - // 6. Call the function. - try { - loader_life_support guard{}; - result = func.impl(call); - } catch (reference_cast_error &) { - result = PYBIND11_TRY_NEXT_OVERLOAD; - } - - if (result.ptr() != PYBIND11_TRY_NEXT_OVERLOAD) - break; - - if (overloaded) { - // The (overloaded) call failed; if the call has at least one argument that - // permits conversion (i.e. it hasn't been explicitly specified `.noconvert()`) - // then add this call to the list of second pass overloads to try. - for (size_t i = func.is_method ? 1 : 0; i < pos_args; i++) { - if (second_pass_convert[i]) { - // Found one: swap the converting flags back in and store the call for - // the second pass. - call.args_convert.swap(second_pass_convert); - second_pass.push_back(std::move(call)); - break; - } - } - } - } - - if (overloaded && !second_pass.empty() && result.ptr() == PYBIND11_TRY_NEXT_OVERLOAD) { - // The no-conversion pass finished without success, try again with conversion allowed - for (auto &call : second_pass) { - try { - loader_life_support guard{}; - result = call.func.impl(call); - } catch (reference_cast_error &) { - result = PYBIND11_TRY_NEXT_OVERLOAD; - } - - if (result.ptr() != PYBIND11_TRY_NEXT_OVERLOAD) - break; - } - } - } catch (error_already_set &e) { - e.restore(); - return nullptr; - } catch (...) { - /* When an exception is caught, give each registered exception - translator a chance to translate it to a Python exception - in reverse order of registration. - - A translator may choose to do one of the following: - - - catch the exception and call PyErr_SetString or PyErr_SetObject - to set a standard (or custom) Python exception, or - - do nothing and let the exception fall through to the next translator, or - - delegate translation to the next translator by throwing a new type of exception. */ - - auto last_exception = std::current_exception(); - auto ®istered_exception_translators = get_internals().registered_exception_translators; - for (auto& translator : registered_exception_translators) { - try { - translator(last_exception); - } catch (...) { - last_exception = std::current_exception(); - continue; - } - return nullptr; - } - PyErr_SetString(PyExc_SystemError, "Exception escaped from default exception translator!"); - return nullptr; - } - - if (result.ptr() == PYBIND11_TRY_NEXT_OVERLOAD) { - if (overloads->is_operator) - return handle(Py_NotImplemented).inc_ref().ptr(); - - std::string msg = std::string(overloads->name) + "(): incompatible " + - std::string(overloads->is_constructor ? "constructor" : "function") + - " arguments. The following argument types are supported:\n"; - - int ctr = 0; - for (function_record *it2 = overloads; it2 != nullptr; it2 = it2->next) { - msg += " "+ std::to_string(++ctr) + ". "; - - bool wrote_sig = false; - if (overloads->is_constructor) { - // For a constructor, rewrite `(self: Object, arg0, ...) -> NoneType` as `Object(arg0, ...)` - std::string sig = it2->signature; - size_t start = sig.find('(') + 7; // skip "(self: " - if (start < sig.size()) { - // End at the , for the next argument - size_t end = sig.find(", "), next = end + 2; - size_t ret = sig.rfind(" -> "); - // Or the ), if there is no comma: - if (end >= sig.size()) next = end = sig.find(')'); - if (start < end && next < sig.size()) { - msg.append(sig, start, end - start); - msg += '('; - msg.append(sig, next, ret - next); - wrote_sig = true; - } - } - } - if (!wrote_sig) msg += it2->signature; - - msg += "\n"; - } - msg += "\nInvoked with: "; - auto args_ = reinterpret_borrow(args_in); - bool some_args = false; - for (size_t ti = overloads->is_constructor ? 1 : 0; ti < args_.size(); ++ti) { - if (!some_args) some_args = true; - else msg += ", "; - msg += pybind11::repr(args_[ti]); - } - if (kwargs_in) { - auto kwargs = reinterpret_borrow(kwargs_in); - if (kwargs.size() > 0) { - if (some_args) msg += "; "; - msg += "kwargs: "; - bool first = true; - for (auto kwarg : kwargs) { - if (first) first = false; - else msg += ", "; - msg += pybind11::str("{}={!r}").format(kwarg.first, kwarg.second); - } - } - } - - PyErr_SetString(PyExc_TypeError, msg.c_str()); - return nullptr; - } else if (!result) { - std::string msg = "Unable to convert function return value to a " - "Python type! The signature was\n\t"; - msg += it->signature; - PyErr_SetString(PyExc_TypeError, msg.c_str()); - return nullptr; - } else { - if (overloads->is_constructor) { - auto tinfo = get_type_info((PyTypeObject *) overloads->scope.ptr()); - tinfo->init_instance(reinterpret_cast(parent.ptr()), nullptr); - } - return result.ptr(); - } - } -}; - -/// Wrapper for Python extension modules -class module : public object { -public: - PYBIND11_OBJECT_DEFAULT(module, object, PyModule_Check) - - /// Create a new top-level Python module with the given name and docstring - explicit module(const char *name, const char *doc = nullptr) { - if (!options::show_user_defined_docstrings()) doc = nullptr; -#if PY_MAJOR_VERSION >= 3 - PyModuleDef *def = new PyModuleDef(); - std::memset(def, 0, sizeof(PyModuleDef)); - def->m_name = name; - def->m_doc = doc; - def->m_size = -1; - Py_INCREF(def); - m_ptr = PyModule_Create(def); -#else - m_ptr = Py_InitModule3(name, nullptr, doc); -#endif - if (m_ptr == nullptr) - pybind11_fail("Internal error in module::module()"); - inc_ref(); - } - - /** \rst - Create Python binding for a new function within the module scope. ``Func`` - can be a plain C++ function, a function pointer, or a lambda function. For - details on the ``Extra&& ... extra`` argument, see section :ref:`extras`. - \endrst */ - template - module &def(const char *name_, Func &&f, const Extra& ... extra) { - cpp_function func(std::forward(f), name(name_), scope(*this), - sibling(getattr(*this, name_, none())), extra...); - // NB: allow overwriting here because cpp_function sets up a chain with the intention of - // overwriting (and has already checked internally that it isn't overwriting non-functions). - add_object(name_, func, true /* overwrite */); - return *this; - } - - /** \rst - Create and return a new Python submodule with the given name and docstring. - This also works recursively, i.e. - - .. code-block:: cpp - - py::module m("example", "pybind11 example plugin"); - py::module m2 = m.def_submodule("sub", "A submodule of 'example'"); - py::module m3 = m2.def_submodule("subsub", "A submodule of 'example.sub'"); - \endrst */ - module def_submodule(const char *name, const char *doc = nullptr) { - std::string full_name = std::string(PyModule_GetName(m_ptr)) - + std::string(".") + std::string(name); - auto result = reinterpret_borrow(PyImport_AddModule(full_name.c_str())); - if (doc && options::show_user_defined_docstrings()) - result.attr("__doc__") = pybind11::str(doc); - attr(name) = result; - return result; - } - - /// Import and return a module or throws `error_already_set`. - static module import(const char *name) { - PyObject *obj = PyImport_ImportModule(name); - if (!obj) - throw error_already_set(); - return reinterpret_steal(obj); - } - - // Adds an object to the module using the given name. Throws if an object with the given name - // already exists. - // - // overwrite should almost always be false: attempting to overwrite objects that pybind11 has - // established will, in most cases, break things. - PYBIND11_NOINLINE void add_object(const char *name, handle obj, bool overwrite = false) { - if (!overwrite && hasattr(*this, name)) - pybind11_fail("Error during initialization: multiple incompatible definitions with name \"" + - std::string(name) + "\""); - - PyModule_AddObject(ptr(), name, obj.inc_ref().ptr() /* steals a reference */); - } -}; - -/// \ingroup python_builtins -/// Return a dictionary representing the global variables in the current execution frame, -/// or ``__main__.__dict__`` if there is no frame (usually when the interpreter is embedded). -inline dict globals() { - PyObject *p = PyEval_GetGlobals(); - return reinterpret_borrow(p ? p : module::import("__main__").attr("__dict__").ptr()); -} - -NAMESPACE_BEGIN(detail) -/// Generic support for creating new Python heap types -class generic_type : public object { - template friend class class_; -public: - PYBIND11_OBJECT_DEFAULT(generic_type, object, PyType_Check) -protected: - void initialize(const type_record &rec) { - if (rec.scope && hasattr(rec.scope, rec.name)) - pybind11_fail("generic_type: cannot initialize type \"" + std::string(rec.name) + - "\": an object with that name is already defined"); - - if (get_type_info(*rec.type)) - pybind11_fail("generic_type: type \"" + std::string(rec.name) + - "\" is already registered!"); - - m_ptr = make_new_python_type(rec); - - /* Register supplemental type information in C++ dict */ - auto *tinfo = new detail::type_info(); - tinfo->type = (PyTypeObject *) m_ptr; - tinfo->cpptype = rec.type; - tinfo->type_size = rec.type_size; - tinfo->operator_new = rec.operator_new; - tinfo->holder_size_in_ptrs = size_in_ptrs(rec.holder_size); - tinfo->init_instance = rec.init_instance; - tinfo->dealloc = rec.dealloc; - tinfo->simple_type = true; - tinfo->simple_ancestors = true; - - auto &internals = get_internals(); - auto tindex = std::type_index(*rec.type); - tinfo->direct_conversions = &internals.direct_conversions[tindex]; - tinfo->default_holder = rec.default_holder; - internals.registered_types_cpp[tindex] = tinfo; - internals.registered_types_py[(PyTypeObject *) m_ptr] = { tinfo }; - - if (rec.bases.size() > 1 || rec.multiple_inheritance) { - mark_parents_nonsimple(tinfo->type); - tinfo->simple_ancestors = false; - } - else if (rec.bases.size() == 1) { - auto parent_tinfo = get_type_info((PyTypeObject *) rec.bases[0].ptr()); - tinfo->simple_ancestors = parent_tinfo->simple_ancestors; - } - } - - /// Helper function which tags all parents of a type using mult. inheritance - void mark_parents_nonsimple(PyTypeObject *value) { - auto t = reinterpret_borrow(value->tp_bases); - for (handle h : t) { - auto tinfo2 = get_type_info((PyTypeObject *) h.ptr()); - if (tinfo2) - tinfo2->simple_type = false; - mark_parents_nonsimple((PyTypeObject *) h.ptr()); - } - } - - void install_buffer_funcs( - buffer_info *(*get_buffer)(PyObject *, void *), - void *get_buffer_data) { - PyHeapTypeObject *type = (PyHeapTypeObject*) m_ptr; - auto tinfo = detail::get_type_info(&type->ht_type); - - if (!type->ht_type.tp_as_buffer) - pybind11_fail( - "To be able to register buffer protocol support for the type '" + - std::string(tinfo->type->tp_name) + - "' the associated class<>(..) invocation must " - "include the pybind11::buffer_protocol() annotation!"); - - tinfo->get_buffer = get_buffer; - tinfo->get_buffer_data = get_buffer_data; - } - - void def_property_static_impl(const char *name, - handle fget, handle fset, - detail::function_record *rec_fget) { - const auto is_static = !(rec_fget->is_method && rec_fget->scope); - const auto has_doc = rec_fget->doc && pybind11::options::show_user_defined_docstrings(); - - auto property = handle((PyObject *) (is_static ? get_internals().static_property_type - : &PyProperty_Type)); - attr(name) = property(fget.ptr() ? fget : none(), - fset.ptr() ? fset : none(), - /*deleter*/none(), - pybind11::str(has_doc ? rec_fget->doc : "")); - } -}; - -/// Set the pointer to operator new if it exists. The cast is needed because it can be overloaded. -template (T::operator new))>> -void set_operator_new(type_record *r) { r->operator_new = &T::operator new; } - -template void set_operator_new(...) { } - -template struct has_operator_delete : std::false_type { }; -template struct has_operator_delete(T::operator delete))>> - : std::true_type { }; -template struct has_operator_delete_size : std::false_type { }; -template struct has_operator_delete_size(T::operator delete))>> - : std::true_type { }; -/// Call class-specific delete if it exists or global otherwise. Can also be an overload set. -template ::value, int> = 0> -void call_operator_delete(T *p, size_t) { T::operator delete(p); } -template ::value && has_operator_delete_size::value, int> = 0> -void call_operator_delete(T *p, size_t s) { T::operator delete(p, s); } - -inline void call_operator_delete(void *p, size_t) { ::operator delete(p); } - -NAMESPACE_END(detail) - -/// Given a pointer to a member function, cast it to its `Derived` version. -/// Forward everything else unchanged. -template -auto method_adaptor(F &&f) -> decltype(std::forward(f)) { return std::forward(f); } - -template -auto method_adaptor(Return (Class::*pmf)(Args...)) -> Return (Derived::*)(Args...) { return pmf; } - -template -auto method_adaptor(Return (Class::*pmf)(Args...) const) -> Return (Derived::*)(Args...) const { return pmf; } - -template -class class_ : public detail::generic_type { - template using is_holder = detail::is_holder_type; - template using is_subtype = detail::is_strict_base_of; - template using is_base = detail::is_strict_base_of; - // struct instead of using here to help MSVC: - template struct is_valid_class_option : - detail::any_of, is_subtype, is_base> {}; - -public: - using type = type_; - using type_alias = detail::exactly_one_t; - constexpr static bool has_alias = !std::is_void::value; - using holder_type = detail::exactly_one_t, options...>; - - static_assert(detail::all_of...>::value, - "Unknown/invalid class_ template parameters provided"); - - PYBIND11_OBJECT(class_, generic_type, PyType_Check) - - template - class_(handle scope, const char *name, const Extra &... extra) { - using namespace detail; - - // MI can only be specified via class_ template options, not constructor parameters - static_assert( - none_of...>::value || // no base class arguments, or: - ( constexpr_sum(is_pyobject::value...) == 1 && // Exactly one base - constexpr_sum(is_base::value...) == 0 && // no template option bases - none_of...>::value), // no multiple_inheritance attr - "Error: multiple inheritance bases must be specified via class_ template options"); - - type_record record; - record.scope = scope; - record.name = name; - record.type = &typeid(type); - record.type_size = sizeof(conditional_t); - record.holder_size = sizeof(holder_type); - record.init_instance = init_instance; - record.dealloc = dealloc; - record.default_holder = std::is_same>::value; - - set_operator_new(&record); - - /* Register base classes specified via template arguments to class_, if any */ - PYBIND11_EXPAND_SIDE_EFFECTS(add_base(record)); - - /* Process optional arguments, if any */ - process_attributes::init(extra..., &record); - - generic_type::initialize(record); - - if (has_alias) { - auto &instances = get_internals().registered_types_cpp; - instances[std::type_index(typeid(type_alias))] = instances[std::type_index(typeid(type))]; - } - } - - template ::value, int> = 0> - static void add_base(detail::type_record &rec) { - rec.add_base(typeid(Base), [](void *src) -> void * { - return static_cast(reinterpret_cast(src)); - }); - } - - template ::value, int> = 0> - static void add_base(detail::type_record &) { } - - template - class_ &def(const char *name_, Func&& f, const Extra&... extra) { - cpp_function cf(method_adaptor(std::forward(f)), name(name_), is_method(*this), - sibling(getattr(*this, name_, none())), extra...); - attr(cf.name()) = cf; - return *this; - } - - template class_ & - def_static(const char *name_, Func &&f, const Extra&... extra) { - static_assert(!std::is_member_function_pointer::value, - "def_static(...) called with a non-static member function pointer"); - cpp_function cf(std::forward(f), name(name_), scope(*this), - sibling(getattr(*this, name_, none())), extra...); - attr(cf.name()) = cf; - return *this; - } - - template - class_ &def(const detail::op_ &op, const Extra&... extra) { - op.execute(*this, extra...); - return *this; - } - - template - class_ & def_cast(const detail::op_ &op, const Extra&... extra) { - op.execute_cast(*this, extra...); - return *this; - } - - template - class_ &def(const detail::init &init, const Extra&... extra) { - init.execute(*this, extra...); - return *this; - } - - template - class_ &def(const detail::init_alias &init, const Extra&... extra) { - init.execute(*this, extra...); - return *this; - } - - template class_& def_buffer(Func &&func) { - struct capture { Func func; }; - capture *ptr = new capture { std::forward(func) }; - install_buffer_funcs([](PyObject *obj, void *ptr) -> buffer_info* { - detail::make_caster caster; - if (!caster.load(obj, false)) - return nullptr; - return new buffer_info(((capture *) ptr)->func(caster)); - }, ptr); - return *this; - } - - template - class_ &def_buffer(Return (Class::*func)(Args...)) { - return def_buffer([func] (type &obj) { return (obj.*func)(); }); - } - - template - class_ &def_buffer(Return (Class::*func)(Args...) const) { - return def_buffer([func] (const type &obj) { return (obj.*func)(); }); - } - - template - class_ &def_readwrite(const char *name, D C::*pm, const Extra&... extra) { - static_assert(std::is_base_of::value, "def_readwrite() requires a class member (or base class member)"); - cpp_function fget([pm](const type &c) -> const D &{ return c.*pm; }, is_method(*this)), - fset([pm](type &c, const D &value) { c.*pm = value; }, is_method(*this)); - def_property(name, fget, fset, return_value_policy::reference_internal, extra...); - return *this; - } - - template - class_ &def_readonly(const char *name, const D C::*pm, const Extra& ...extra) { - static_assert(std::is_base_of::value, "def_readonly() requires a class member (or base class member)"); - cpp_function fget([pm](const type &c) -> const D &{ return c.*pm; }, is_method(*this)); - def_property_readonly(name, fget, return_value_policy::reference_internal, extra...); - return *this; - } - - template - class_ &def_readwrite_static(const char *name, D *pm, const Extra& ...extra) { - cpp_function fget([pm](object) -> const D &{ return *pm; }, scope(*this)), - fset([pm](object, const D &value) { *pm = value; }, scope(*this)); - def_property_static(name, fget, fset, return_value_policy::reference, extra...); - return *this; - } - - template - class_ &def_readonly_static(const char *name, const D *pm, const Extra& ...extra) { - cpp_function fget([pm](object) -> const D &{ return *pm; }, scope(*this)); - def_property_readonly_static(name, fget, return_value_policy::reference, extra...); - return *this; - } - - /// Uses return_value_policy::reference_internal by default - template - class_ &def_property_readonly(const char *name, const Getter &fget, const Extra& ...extra) { - return def_property_readonly(name, cpp_function(method_adaptor(fget)), - return_value_policy::reference_internal, extra...); - } - - /// Uses cpp_function's return_value_policy by default - template - class_ &def_property_readonly(const char *name, const cpp_function &fget, const Extra& ...extra) { - return def_property(name, fget, cpp_function(), extra...); - } - - /// Uses return_value_policy::reference by default - template - class_ &def_property_readonly_static(const char *name, const Getter &fget, const Extra& ...extra) { - return def_property_readonly_static(name, cpp_function(fget), return_value_policy::reference, extra...); - } - - /// Uses cpp_function's return_value_policy by default - template - class_ &def_property_readonly_static(const char *name, const cpp_function &fget, const Extra& ...extra) { - return def_property_static(name, fget, cpp_function(), extra...); - } - - /// Uses return_value_policy::reference_internal by default - template - class_ &def_property(const char *name, const Getter &fget, const Setter &fset, const Extra& ...extra) { - return def_property(name, fget, cpp_function(method_adaptor(fset)), extra...); - } - template - class_ &def_property(const char *name, const Getter &fget, const cpp_function &fset, const Extra& ...extra) { - return def_property(name, cpp_function(method_adaptor(fget)), fset, - return_value_policy::reference_internal, extra...); - } - - /// Uses cpp_function's return_value_policy by default - template - class_ &def_property(const char *name, const cpp_function &fget, const cpp_function &fset, const Extra& ...extra) { - return def_property_static(name, fget, fset, is_method(*this), extra...); - } - - /// Uses return_value_policy::reference by default - template - class_ &def_property_static(const char *name, const Getter &fget, const cpp_function &fset, const Extra& ...extra) { - return def_property_static(name, cpp_function(fget), fset, return_value_policy::reference, extra...); - } - - /// Uses cpp_function's return_value_policy by default - template - class_ &def_property_static(const char *name, const cpp_function &fget, const cpp_function &fset, const Extra& ...extra) { - auto rec_fget = get_function_record(fget), rec_fset = get_function_record(fset); - char *doc_prev = rec_fget->doc; /* 'extra' field may include a property-specific documentation string */ - detail::process_attributes::init(extra..., rec_fget); - if (rec_fget->doc && rec_fget->doc != doc_prev) { - free(doc_prev); - rec_fget->doc = strdup(rec_fget->doc); - } - if (rec_fset) { - doc_prev = rec_fset->doc; - detail::process_attributes::init(extra..., rec_fset); - if (rec_fset->doc && rec_fset->doc != doc_prev) { - free(doc_prev); - rec_fset->doc = strdup(rec_fset->doc); - } - } - def_property_static_impl(name, fget, fset, rec_fget); - return *this; - } - -private: - /// Initialize holder object, variant 1: object derives from enable_shared_from_this - template - static void init_holder(detail::instance *inst, detail::value_and_holder &v_h, - const holder_type * /* unused */, const std::enable_shared_from_this * /* dummy */) { - try { - auto sh = std::dynamic_pointer_cast( - v_h.value_ptr()->shared_from_this()); - if (sh) { - new (&v_h.holder()) holder_type(std::move(sh)); - v_h.set_holder_constructed(); - } - } catch (const std::bad_weak_ptr &) {} - - if (!v_h.holder_constructed() && inst->owned) { - new (&v_h.holder()) holder_type(v_h.value_ptr()); - v_h.set_holder_constructed(); - } - } - - static void init_holder_from_existing(const detail::value_and_holder &v_h, - const holder_type *holder_ptr, std::true_type /*is_copy_constructible*/) { - new (&v_h.holder()) holder_type(*reinterpret_cast(holder_ptr)); - } - - static void init_holder_from_existing(const detail::value_and_holder &v_h, - const holder_type *holder_ptr, std::false_type /*is_copy_constructible*/) { - new (&v_h.holder()) holder_type(std::move(*const_cast(holder_ptr))); - } - - /// Initialize holder object, variant 2: try to construct from existing holder object, if possible - static void init_holder(detail::instance *inst, detail::value_and_holder &v_h, - const holder_type *holder_ptr, const void * /* dummy -- not enable_shared_from_this) */) { - if (holder_ptr) { - init_holder_from_existing(v_h, holder_ptr, std::is_copy_constructible()); - v_h.set_holder_constructed(); - } else if (inst->owned || detail::always_construct_holder::value) { - new (&v_h.holder()) holder_type(v_h.value_ptr()); - v_h.set_holder_constructed(); - } - } - - /// Performs instance initialization including constructing a holder and registering the known - /// instance. Should be called as soon as the `type` value_ptr is set for an instance. Takes an - /// optional pointer to an existing holder to use; if not specified and the instance is - /// `.owned`, a new holder will be constructed to manage the value pointer. - static void init_instance(detail::instance *inst, const void *holder_ptr) { - auto v_h = inst->get_value_and_holder(detail::get_type_info(typeid(type))); - if (!v_h.instance_registered()) { - register_instance(inst, v_h.value_ptr(), v_h.type); - v_h.set_instance_registered(); - } - init_holder(inst, v_h, (const holder_type *) holder_ptr, v_h.value_ptr()); - } - - /// Deallocates an instance; via holder, if constructed; otherwise via operator delete. - static void dealloc(const detail::value_and_holder &v_h) { - if (v_h.holder_constructed()) - v_h.holder().~holder_type(); - else - detail::call_operator_delete(v_h.value_ptr(), v_h.type->type_size); - } - - static detail::function_record *get_function_record(handle h) { - h = detail::get_function(h); - return h ? (detail::function_record *) reinterpret_borrow(PyCFunction_GET_SELF(h.ptr())) - : nullptr; - } -}; - -/// Binds C++ enumerations and enumeration classes to Python -template class enum_ : public class_ { -public: - using class_::def; - using class_::def_property_readonly_static; - using Scalar = typename std::underlying_type::type; - - template - enum_(const handle &scope, const char *name, const Extra&... extra) - : class_(scope, name, extra...), m_entries(), m_parent(scope) { - - constexpr bool is_arithmetic = detail::any_of...>::value; - - auto m_entries_ptr = m_entries.inc_ref().ptr(); - def("__repr__", [name, m_entries_ptr](Type value) -> pybind11::str { - for (const auto &kv : reinterpret_borrow(m_entries_ptr)) { - if (pybind11::cast(kv.second) == value) - return pybind11::str("{}.{}").format(name, kv.first); - } - return pybind11::str("{}.???").format(name); - }); - def_property_readonly_static("__members__", [m_entries_ptr](object /* self */) { - dict m; - for (const auto &kv : reinterpret_borrow(m_entries_ptr)) - m[kv.first] = kv.second; - return m; - }, return_value_policy::copy); - def("__init__", [](Type& value, Scalar i) { value = (Type)i; }); - def("__int__", [](Type value) { return (Scalar) value; }); - #if PY_MAJOR_VERSION < 3 - def("__long__", [](Type value) { return (Scalar) value; }); - #endif - def("__eq__", [](const Type &value, Type *value2) { return value2 && value == *value2; }); - def("__ne__", [](const Type &value, Type *value2) { return !value2 || value != *value2; }); - if (is_arithmetic) { - def("__lt__", [](const Type &value, Type *value2) { return value2 && value < *value2; }); - def("__gt__", [](const Type &value, Type *value2) { return value2 && value > *value2; }); - def("__le__", [](const Type &value, Type *value2) { return value2 && value <= *value2; }); - def("__ge__", [](const Type &value, Type *value2) { return value2 && value >= *value2; }); - } - if (std::is_convertible::value) { - // Don't provide comparison with the underlying type if the enum isn't convertible, - // i.e. if Type is a scoped enum, mirroring the C++ behaviour. (NB: we explicitly - // convert Type to Scalar below anyway because this needs to compile). - def("__eq__", [](const Type &value, Scalar value2) { return (Scalar) value == value2; }); - def("__ne__", [](const Type &value, Scalar value2) { return (Scalar) value != value2; }); - if (is_arithmetic) { - def("__lt__", [](const Type &value, Scalar value2) { return (Scalar) value < value2; }); - def("__gt__", [](const Type &value, Scalar value2) { return (Scalar) value > value2; }); - def("__le__", [](const Type &value, Scalar value2) { return (Scalar) value <= value2; }); - def("__ge__", [](const Type &value, Scalar value2) { return (Scalar) value >= value2; }); - def("__invert__", [](const Type &value) { return ~((Scalar) value); }); - def("__and__", [](const Type &value, Scalar value2) { return (Scalar) value & value2; }); - def("__or__", [](const Type &value, Scalar value2) { return (Scalar) value | value2; }); - def("__xor__", [](const Type &value, Scalar value2) { return (Scalar) value ^ value2; }); - def("__rand__", [](const Type &value, Scalar value2) { return (Scalar) value & value2; }); - def("__ror__", [](const Type &value, Scalar value2) { return (Scalar) value | value2; }); - def("__rxor__", [](const Type &value, Scalar value2) { return (Scalar) value ^ value2; }); - def("__and__", [](const Type &value, const Type &value2) { return (Scalar) value & (Scalar) value2; }); - def("__or__", [](const Type &value, const Type &value2) { return (Scalar) value | (Scalar) value2; }); - def("__xor__", [](const Type &value, const Type &value2) { return (Scalar) value ^ (Scalar) value2; }); - } - } - def("__hash__", [](const Type &value) { return (Scalar) value; }); - // Pickling and unpickling -- needed for use with the 'multiprocessing' module - def("__getstate__", [](const Type &value) { return pybind11::make_tuple((Scalar) value); }); - def("__setstate__", [](Type &p, tuple t) { new (&p) Type((Type) t[0].cast()); }); - } - - /// Export enumeration entries into the parent scope - enum_& export_values() { - for (const auto &kv : m_entries) - m_parent.attr(kv.first) = kv.second; - return *this; - } - - /// Add an enumeration entry - enum_& value(char const* name, Type value) { - auto v = pybind11::cast(value, return_value_policy::copy); - this->attr(name) = v; - m_entries[pybind11::str(name)] = v; - return *this; - } - -private: - dict m_entries; - handle m_parent; -}; - -NAMESPACE_BEGIN(detail) -template struct init { - template = 0> - static void execute(Class &cl, const Extra&... extra) { - using Base = typename Class::type; - /// Function which calls a specific C++ in-place constructor - cl.def("__init__", [](Base *self_, Args... args) { new (self_) Base(args...); }, extra...); - } - - template ::value, int> = 0> - static void execute(Class &cl, const Extra&... extra) { - using Base = typename Class::type; - using Alias = typename Class::type_alias; - handle cl_type = cl; - cl.def("__init__", [cl_type](handle self_, Args... args) { - if (self_.get_type().is(cl_type)) - new (self_.cast()) Base(args...); - else - new (self_.cast()) Alias(args...); - }, extra...); - } - - template ::value, int> = 0> - static void execute(Class &cl, const Extra&... extra) { - init_alias::execute(cl, extra...); - } -}; -template struct init_alias { - template ::value, int> = 0> - static void execute(Class &cl, const Extra&... extra) { - using Alias = typename Class::type_alias; - cl.def("__init__", [](Alias *self_, Args... args) { new (self_) Alias(args...); }, extra...); - } -}; - - -inline void keep_alive_impl(handle nurse, handle patient) { - if (!nurse || !patient) - pybind11_fail("Could not activate keep_alive!"); - - if (patient.is_none() || nurse.is_none()) - return; /* Nothing to keep alive or nothing to be kept alive by */ - - auto tinfo = all_type_info(Py_TYPE(nurse.ptr())); - if (!tinfo.empty()) { - /* It's a pybind-registered type, so we can store the patient in the - * internal list. */ - add_patient(nurse.ptr(), patient.ptr()); - } - else { - /* Fall back to clever approach based on weak references taken from - * Boost.Python. This is not used for pybind-registered types because - * the objects can be destroyed out-of-order in a GC pass. */ - cpp_function disable_lifesupport( - [patient](handle weakref) { patient.dec_ref(); weakref.dec_ref(); }); - - weakref wr(nurse, disable_lifesupport); - - patient.inc_ref(); /* reference patient and leak the weak reference */ - (void) wr.release(); - } -} - -PYBIND11_NOINLINE inline void keep_alive_impl(size_t Nurse, size_t Patient, function_call &call, handle ret) { - keep_alive_impl( - Nurse == 0 ? ret : Nurse <= call.args.size() ? call.args[Nurse - 1] : handle(), - Patient == 0 ? ret : Patient <= call.args.size() ? call.args[Patient - 1] : handle() - ); -} - -inline std::pair all_type_info_get_cache(PyTypeObject *type) { - auto res = get_internals().registered_types_py -#ifdef __cpp_lib_unordered_map_try_emplace - .try_emplace(type); -#else - .emplace(type, std::vector()); -#endif - if (res.second) { - // New cache entry created; set up a weak reference to automatically remove it if the type - // gets destroyed: - weakref((PyObject *) type, cpp_function([type](handle wr) { - get_internals().registered_types_py.erase(type); - wr.dec_ref(); - })).release(); - } - - return res; -} - -template -struct iterator_state { - Iterator it; - Sentinel end; - bool first_or_done; -}; - -NAMESPACE_END(detail) - -template detail::init init() { return detail::init(); } -template detail::init_alias init_alias() { return detail::init_alias(); } - -/// Makes a python iterator from a first and past-the-end C++ InputIterator. -template ()), - typename... Extra> -iterator make_iterator(Iterator first, Sentinel last, Extra &&... extra) { - typedef detail::iterator_state state; - - if (!detail::get_type_info(typeid(state), false)) { - class_(handle(), "iterator") - .def("__iter__", [](state &s) -> state& { return s; }) - .def("__next__", [](state &s) -> ValueType { - if (!s.first_or_done) - ++s.it; - else - s.first_or_done = false; - if (s.it == s.end) { - s.first_or_done = true; - throw stop_iteration(); - } - return *s.it; - }, std::forward(extra)..., Policy); - } - - return cast(state{first, last, true}); -} - -/// Makes an python iterator over the keys (`.first`) of a iterator over pairs from a -/// first and past-the-end InputIterator. -template ()).first), - typename... Extra> -iterator make_key_iterator(Iterator first, Sentinel last, Extra &&... extra) { - typedef detail::iterator_state state; - - if (!detail::get_type_info(typeid(state), false)) { - class_(handle(), "iterator") - .def("__iter__", [](state &s) -> state& { return s; }) - .def("__next__", [](state &s) -> KeyType { - if (!s.first_or_done) - ++s.it; - else - s.first_or_done = false; - if (s.it == s.end) { - s.first_or_done = true; - throw stop_iteration(); - } - return (*s.it).first; - }, std::forward(extra)..., Policy); - } - - return cast(state{first, last, true}); -} - -/// Makes an iterator over values of an stl container or other container supporting -/// `std::begin()`/`std::end()` -template iterator make_iterator(Type &value, Extra&&... extra) { - return make_iterator(std::begin(value), std::end(value), extra...); -} - -/// Makes an iterator over the keys (`.first`) of a stl map-like container supporting -/// `std::begin()`/`std::end()` -template iterator make_key_iterator(Type &value, Extra&&... extra) { - return make_key_iterator(std::begin(value), std::end(value), extra...); -} - -template void implicitly_convertible() { - auto implicit_caster = [](PyObject *obj, PyTypeObject *type) -> PyObject * { - if (!detail::make_caster().load(obj, false)) - return nullptr; - tuple args(1); - args[0] = obj; - PyObject *result = PyObject_Call((PyObject *) type, args.ptr(), nullptr); - if (result == nullptr) - PyErr_Clear(); - return result; - }; - - if (auto tinfo = detail::get_type_info(typeid(OutputType))) - tinfo->implicit_conversions.push_back(implicit_caster); - else - pybind11_fail("implicitly_convertible: Unable to find type " + type_id()); -} - -template -void register_exception_translator(ExceptionTranslator&& translator) { - detail::get_internals().registered_exception_translators.push_front( - std::forward(translator)); -} - -/** - * Wrapper to generate a new Python exception type. - * - * This should only be used with PyErr_SetString for now. - * It is not (yet) possible to use as a py::base. - * Template type argument is reserved for future use. - */ -template -class exception : public object { -public: - exception(handle scope, const char *name, PyObject *base = PyExc_Exception) { - std::string full_name = scope.attr("__name__").cast() + - std::string(".") + name; - m_ptr = PyErr_NewException(const_cast(full_name.c_str()), base, NULL); - if (hasattr(scope, name)) - pybind11_fail("Error during initialization: multiple incompatible " - "definitions with name \"" + std::string(name) + "\""); - scope.attr(name) = *this; - } - - // Sets the current python exception to this exception object with the given message - void operator()(const char *message) { - PyErr_SetString(m_ptr, message); - } -}; - -/** - * Registers a Python exception in `m` of the given `name` and installs an exception translator to - * translate the C++ exception to the created Python exception using the exceptions what() method. - * This is intended for simple exception translations; for more complex translation, register the - * exception object and translator directly. - */ -template -exception ®ister_exception(handle scope, - const char *name, - PyObject *base = PyExc_Exception) { - static exception ex(scope, name, base); - register_exception_translator([](std::exception_ptr p) { - if (!p) return; - try { - std::rethrow_exception(p); - } catch (const CppException &e) { - ex(e.what()); - } - }); - return ex; -} - -NAMESPACE_BEGIN(detail) -PYBIND11_NOINLINE inline void print(tuple args, dict kwargs) { - auto strings = tuple(args.size()); - for (size_t i = 0; i < args.size(); ++i) { - strings[i] = str(args[i]); - } - auto sep = kwargs.contains("sep") ? kwargs["sep"] : cast(" "); - auto line = sep.attr("join")(strings); - - object file; - if (kwargs.contains("file")) { - file = kwargs["file"].cast(); - } else { - try { - file = module::import("sys").attr("stdout"); - } catch (const error_already_set &) { - /* If print() is called from code that is executed as - part of garbage collection during interpreter shutdown, - importing 'sys' can fail. Give up rather than crashing the - interpreter in this case. */ - return; - } - } - - auto write = file.attr("write"); - write(line); - write(kwargs.contains("end") ? kwargs["end"] : cast("\n")); - - if (kwargs.contains("flush") && kwargs["flush"].cast()) - file.attr("flush")(); -} -NAMESPACE_END(detail) - -template -void print(Args &&...args) { - auto c = detail::collect_arguments(std::forward(args)...); - detail::print(c.args(), c.kwargs()); -} - -#if defined(WITH_THREAD) && !defined(PYPY_VERSION) - -/* The functions below essentially reproduce the PyGILState_* API using a RAII - * pattern, but there are a few important differences: - * - * 1. When acquiring the GIL from an non-main thread during the finalization - * phase, the GILState API blindly terminates the calling thread, which - * is often not what is wanted. This API does not do this. - * - * 2. The gil_scoped_release function can optionally cut the relationship - * of a PyThreadState and its associated thread, which allows moving it to - * another thread (this is a fairly rare/advanced use case). - * - * 3. The reference count of an acquired thread state can be controlled. This - * can be handy to prevent cases where callbacks issued from an external - * thread would otherwise constantly construct and destroy thread state data - * structures. - * - * See the Python bindings of NanoGUI (http://github.com/wjakob/nanogui) for an - * example which uses features 2 and 3 to migrate the Python thread of - * execution to another thread (to run the event loop on the original thread, - * in this case). - */ - -class gil_scoped_acquire { -public: - PYBIND11_NOINLINE gil_scoped_acquire() { - auto const &internals = detail::get_internals(); - tstate = (PyThreadState *) PyThread_get_key_value(internals.tstate); - - if (!tstate) { - tstate = PyThreadState_New(internals.istate); - #if !defined(NDEBUG) - if (!tstate) - pybind11_fail("scoped_acquire: could not create thread state!"); - #endif - tstate->gilstate_counter = 0; - #if PY_MAJOR_VERSION < 3 - PyThread_delete_key_value(internals.tstate); - #endif - PyThread_set_key_value(internals.tstate, tstate); - } else { - release = detail::get_thread_state_unchecked() != tstate; - } - - if (release) { - /* Work around an annoying assertion in PyThreadState_Swap */ - #if defined(Py_DEBUG) - PyInterpreterState *interp = tstate->interp; - tstate->interp = nullptr; - #endif - PyEval_AcquireThread(tstate); - #if defined(Py_DEBUG) - tstate->interp = interp; - #endif - } - - inc_ref(); - } - - void inc_ref() { - ++tstate->gilstate_counter; - } - - PYBIND11_NOINLINE void dec_ref() { - --tstate->gilstate_counter; - #if !defined(NDEBUG) - if (detail::get_thread_state_unchecked() != tstate) - pybind11_fail("scoped_acquire::dec_ref(): thread state must be current!"); - if (tstate->gilstate_counter < 0) - pybind11_fail("scoped_acquire::dec_ref(): reference count underflow!"); - #endif - if (tstate->gilstate_counter == 0) { - #if !defined(NDEBUG) - if (!release) - pybind11_fail("scoped_acquire::dec_ref(): internal error!"); - #endif - PyThreadState_Clear(tstate); - PyThreadState_DeleteCurrent(); - PyThread_delete_key_value(detail::get_internals().tstate); - release = false; - } - } - - PYBIND11_NOINLINE ~gil_scoped_acquire() { - dec_ref(); - if (release) - PyEval_SaveThread(); - } -private: - PyThreadState *tstate = nullptr; - bool release = true; -}; - -class gil_scoped_release { -public: - explicit gil_scoped_release(bool disassoc = false) : disassoc(disassoc) { - // `get_internals()` must be called here unconditionally in order to initialize - // `internals.tstate` for subsequent `gil_scoped_acquire` calls. Otherwise, an - // initialization race could occur as multiple threads try `gil_scoped_acquire`. - const auto &internals = detail::get_internals(); - tstate = PyEval_SaveThread(); - if (disassoc) { - auto key = internals.tstate; - #if PY_MAJOR_VERSION < 3 - PyThread_delete_key_value(key); - #else - PyThread_set_key_value(key, nullptr); - #endif - } - } - ~gil_scoped_release() { - if (!tstate) - return; - PyEval_RestoreThread(tstate); - if (disassoc) { - auto key = detail::get_internals().tstate; - #if PY_MAJOR_VERSION < 3 - PyThread_delete_key_value(key); - #endif - PyThread_set_key_value(key, tstate); - } - } -private: - PyThreadState *tstate; - bool disassoc; -}; -#elif defined(PYPY_VERSION) -class gil_scoped_acquire { - PyGILState_STATE state; -public: - gil_scoped_acquire() { state = PyGILState_Ensure(); } - ~gil_scoped_acquire() { PyGILState_Release(state); } -}; - -class gil_scoped_release { - PyThreadState *state; -public: - gil_scoped_release() { state = PyEval_SaveThread(); } - ~gil_scoped_release() { PyEval_RestoreThread(state); } -}; -#else -class gil_scoped_acquire { }; -class gil_scoped_release { }; -#endif - -error_already_set::~error_already_set() { - if (type) { - gil_scoped_acquire gil; - type.release().dec_ref(); - value.release().dec_ref(); - trace.release().dec_ref(); - } -} - -inline function get_type_overload(const void *this_ptr, const detail::type_info *this_type, const char *name) { - handle self = detail::get_object_handle(this_ptr, this_type); - if (!self) - return function(); - handle type = self.get_type(); - auto key = std::make_pair(type.ptr(), name); - - /* Cache functions that aren't overloaded in Python to avoid - many costly Python dictionary lookups below */ - auto &cache = detail::get_internals().inactive_overload_cache; - if (cache.find(key) != cache.end()) - return function(); - - function overload = getattr(self, name, function()); - if (overload.is_cpp_function()) { - cache.insert(key); - return function(); - } - - /* Don't call dispatch code if invoked from overridden function. - Unfortunately this doesn't work on PyPy. */ -#if !defined(PYPY_VERSION) - PyFrameObject *frame = PyThreadState_Get()->frame; - if (frame && (std::string) str(frame->f_code->co_name) == name && - frame->f_code->co_argcount > 0) { - PyFrame_FastToLocals(frame); - PyObject *self_caller = PyDict_GetItem( - frame->f_locals, PyTuple_GET_ITEM(frame->f_code->co_varnames, 0)); - if (self_caller == self.ptr()) - return function(); - } -#else - /* PyPy currently doesn't provide a detailed cpyext emulation of - frame objects, so we have to emulate this using Python. This - is going to be slow..*/ - dict d; d["self"] = self; d["name"] = pybind11::str(name); - PyObject *result = PyRun_String( - "import inspect\n" - "frame = inspect.currentframe()\n" - "if frame is not None:\n" - " frame = frame.f_back\n" - " if frame is not None and str(frame.f_code.co_name) == name and " - "frame.f_code.co_argcount > 0:\n" - " self_caller = frame.f_locals[frame.f_code.co_varnames[0]]\n" - " if self_caller == self:\n" - " self = None\n", - Py_file_input, d.ptr(), d.ptr()); - if (result == nullptr) - throw error_already_set(); - if (d["self"].is_none()) - return function(); - Py_DECREF(result); -#endif - - return overload; -} - -template function get_overload(const T *this_ptr, const char *name) { - auto tinfo = detail::get_type_info(typeid(T)); - return tinfo ? get_type_overload(this_ptr, tinfo, name) : function(); -} - -#define PYBIND11_OVERLOAD_INT(ret_type, cname, name, ...) { \ - pybind11::gil_scoped_acquire gil; \ - pybind11::function overload = pybind11::get_overload(static_cast(this), name); \ - if (overload) { \ - auto o = overload(__VA_ARGS__); \ - if (pybind11::detail::cast_is_temporary_value_reference::value) { \ - static pybind11::detail::overload_caster_t caster; \ - return pybind11::detail::cast_ref(std::move(o), caster); \ - } \ - else return pybind11::detail::cast_safe(std::move(o)); \ - } \ - } - -#define PYBIND11_OVERLOAD_NAME(ret_type, cname, name, fn, ...) \ - PYBIND11_OVERLOAD_INT(ret_type, cname, name, __VA_ARGS__) \ - return cname::fn(__VA_ARGS__) - -#define PYBIND11_OVERLOAD_PURE_NAME(ret_type, cname, name, fn, ...) \ - PYBIND11_OVERLOAD_INT(ret_type, cname, name, __VA_ARGS__) \ - pybind11::pybind11_fail("Tried to call pure virtual function \"" #cname "::" name "\""); - -#define PYBIND11_OVERLOAD(ret_type, cname, fn, ...) \ - PYBIND11_OVERLOAD_NAME(ret_type, cname, #fn, fn, __VA_ARGS__) - -#define PYBIND11_OVERLOAD_PURE(ret_type, cname, fn, ...) \ - PYBIND11_OVERLOAD_PURE_NAME(ret_type, cname, #fn, fn, __VA_ARGS__) - -NAMESPACE_END(pybind11) - -#if defined(_MSC_VER) -# pragma warning(pop) -#elif defined(__INTEL_COMPILER) -/* Leave ignored warnings on */ -#elif defined(__GNUG__) && !defined(__clang__) -# pragma GCC diagnostic pop -#endif diff --git a/ptocr/postprocess/lanms/include/pybind11/pytypes.h b/ptocr/postprocess/lanms/include/pybind11/pytypes.h deleted file mode 100644 index 095d40f..0000000 --- a/ptocr/postprocess/lanms/include/pybind11/pytypes.h +++ /dev/null @@ -1,1318 +0,0 @@ -/* - pybind11/typeid.h: Convenience wrapper classes for basic Python types - - Copyright (c) 2016 Wenzel Jakob - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#include "common.h" -#include "buffer_info.h" -#include -#include - -NAMESPACE_BEGIN(pybind11) - -/* A few forward declarations */ -class handle; class object; -class str; class iterator; -struct arg; struct arg_v; - -NAMESPACE_BEGIN(detail) -class args_proxy; -inline bool isinstance_generic(handle obj, const std::type_info &tp); - -// Accessor forward declarations -template class accessor; -namespace accessor_policies { - struct obj_attr; - struct str_attr; - struct generic_item; - struct sequence_item; - struct list_item; - struct tuple_item; -} -using obj_attr_accessor = accessor; -using str_attr_accessor = accessor; -using item_accessor = accessor; -using sequence_accessor = accessor; -using list_accessor = accessor; -using tuple_accessor = accessor; - -/// Tag and check to identify a class which implements the Python object API -class pyobject_tag { }; -template using is_pyobject = std::is_base_of>; - -/** \rst - A mixin class which adds common functions to `handle`, `object` and various accessors. - The only requirement for `Derived` is to implement ``PyObject *Derived::ptr() const``. -\endrst */ -template -class object_api : public pyobject_tag { - const Derived &derived() const { return static_cast(*this); } - -public: - /** \rst - Return an iterator equivalent to calling ``iter()`` in Python. The object - must be a collection which supports the iteration protocol. - \endrst */ - iterator begin() const; - /// Return a sentinel which ends iteration. - iterator end() const; - - /** \rst - Return an internal functor to invoke the object's sequence protocol. Casting - the returned ``detail::item_accessor`` instance to a `handle` or `object` - subclass causes a corresponding call to ``__getitem__``. Assigning a `handle` - or `object` subclass causes a call to ``__setitem__``. - \endrst */ - item_accessor operator[](handle key) const; - /// See above (the only difference is that they key is provided as a string literal) - item_accessor operator[](const char *key) const; - - /** \rst - Return an internal functor to access the object's attributes. Casting the - returned ``detail::obj_attr_accessor`` instance to a `handle` or `object` - subclass causes a corresponding call to ``getattr``. Assigning a `handle` - or `object` subclass causes a call to ``setattr``. - \endrst */ - obj_attr_accessor attr(handle key) const; - /// See above (the only difference is that they key is provided as a string literal) - str_attr_accessor attr(const char *key) const; - - /** \rst - Matches * unpacking in Python, e.g. to unpack arguments out of a ``tuple`` - or ``list`` for a function call. Applying another * to the result yields - ** unpacking, e.g. to unpack a dict as function keyword arguments. - See :ref:`calling_python_functions`. - \endrst */ - args_proxy operator*() const; - - /// Check if the given item is contained within this object, i.e. ``item in obj``. - template bool contains(T &&item) const; - - /** \rst - Assuming the Python object is a function or implements the ``__call__`` - protocol, ``operator()`` invokes the underlying function, passing an - arbitrary set of parameters. The result is returned as a `object` and - may need to be converted back into a Python object using `handle::cast()`. - - When some of the arguments cannot be converted to Python objects, the - function will throw a `cast_error` exception. When the Python function - call fails, a `error_already_set` exception is thrown. - \endrst */ - template - object operator()(Args &&...args) const; - template - PYBIND11_DEPRECATED("call(...) was deprecated in favor of operator()(...)") - object call(Args&&... args) const; - - /// Equivalent to ``obj is other`` in Python. - bool is(object_api const& other) const { return derived().ptr() == other.derived().ptr(); } - /// Equivalent to ``obj is None`` in Python. - bool is_none() const { return derived().ptr() == Py_None; } - PYBIND11_DEPRECATED("Use py::str(obj) instead") - pybind11::str str() const; - - /// Get or set the object's docstring, i.e. ``obj.__doc__``. - str_attr_accessor doc() const; - - /// Return the object's current reference count - int ref_count() const { return static_cast(Py_REFCNT(derived().ptr())); } - /// Return a handle to the Python type object underlying the instance - handle get_type() const; -}; - -NAMESPACE_END(detail) - -/** \rst - Holds a reference to a Python object (no reference counting) - - The `handle` class is a thin wrapper around an arbitrary Python object (i.e. a - ``PyObject *`` in Python's C API). It does not perform any automatic reference - counting and merely provides a basic C++ interface to various Python API functions. - - .. seealso:: - The `object` class inherits from `handle` and adds automatic reference - counting features. -\endrst */ -class handle : public detail::object_api { -public: - /// The default constructor creates a handle with a ``nullptr``-valued pointer - handle() = default; - /// Creates a ``handle`` from the given raw Python object pointer - handle(PyObject *ptr) : m_ptr(ptr) { } // Allow implicit conversion from PyObject* - - /// Return the underlying ``PyObject *`` pointer - PyObject *ptr() const { return m_ptr; } - PyObject *&ptr() { return m_ptr; } - - /** \rst - Manually increase the reference count of the Python object. Usually, it is - preferable to use the `object` class which derives from `handle` and calls - this function automatically. Returns a reference to itself. - \endrst */ - const handle& inc_ref() const & { Py_XINCREF(m_ptr); return *this; } - - /** \rst - Manually decrease the reference count of the Python object. Usually, it is - preferable to use the `object` class which derives from `handle` and calls - this function automatically. Returns a reference to itself. - \endrst */ - const handle& dec_ref() const & { Py_XDECREF(m_ptr); return *this; } - - /** \rst - Attempt to cast the Python object into the given C++ type. A `cast_error` - will be throw upon failure. - \endrst */ - template T cast() const; - /// Return ``true`` when the `handle` wraps a valid Python object - explicit operator bool() const { return m_ptr != nullptr; } - /** \rst - Deprecated: Check that the underlying pointers are the same. - Equivalent to ``obj1 is obj2`` in Python. - \endrst */ - PYBIND11_DEPRECATED("Use obj1.is(obj2) instead") - bool operator==(const handle &h) const { return m_ptr == h.m_ptr; } - PYBIND11_DEPRECATED("Use !obj1.is(obj2) instead") - bool operator!=(const handle &h) const { return m_ptr != h.m_ptr; } - PYBIND11_DEPRECATED("Use handle::operator bool() instead") - bool check() const { return m_ptr != nullptr; } -protected: - PyObject *m_ptr = nullptr; -}; - -/** \rst - Holds a reference to a Python object (with reference counting) - - Like `handle`, the `object` class is a thin wrapper around an arbitrary Python - object (i.e. a ``PyObject *`` in Python's C API). In contrast to `handle`, it - optionally increases the object's reference count upon construction, and it - *always* decreases the reference count when the `object` instance goes out of - scope and is destructed. When using `object` instances consistently, it is much - easier to get reference counting right at the first attempt. -\endrst */ -class object : public handle { -public: - object() = default; - PYBIND11_DEPRECATED("Use reinterpret_borrow() or reinterpret_steal()") - object(handle h, bool is_borrowed) : handle(h) { if (is_borrowed) inc_ref(); } - /// Copy constructor; always increases the reference count - object(const object &o) : handle(o) { inc_ref(); } - /// Move constructor; steals the object from ``other`` and preserves its reference count - object(object &&other) noexcept { m_ptr = other.m_ptr; other.m_ptr = nullptr; } - /// Destructor; automatically calls `handle::dec_ref()` - ~object() { dec_ref(); } - - /** \rst - Resets the internal pointer to ``nullptr`` without without decreasing the - object's reference count. The function returns a raw handle to the original - Python object. - \endrst */ - handle release() { - PyObject *tmp = m_ptr; - m_ptr = nullptr; - return handle(tmp); - } - - object& operator=(const object &other) { - other.inc_ref(); - dec_ref(); - m_ptr = other.m_ptr; - return *this; - } - - object& operator=(object &&other) noexcept { - if (this != &other) { - handle temp(m_ptr); - m_ptr = other.m_ptr; - other.m_ptr = nullptr; - temp.dec_ref(); - } - return *this; - } - - // Calling cast() on an object lvalue just copies (via handle::cast) - template T cast() const &; - // Calling on an object rvalue does a move, if needed and/or possible - template T cast() &&; - -protected: - // Tags for choosing constructors from raw PyObject * - struct borrowed_t { }; - struct stolen_t { }; - - template friend T reinterpret_borrow(handle); - template friend T reinterpret_steal(handle); - -public: - // Only accessible from derived classes and the reinterpret_* functions - object(handle h, borrowed_t) : handle(h) { inc_ref(); } - object(handle h, stolen_t) : handle(h) { } -}; - -/** \rst - Declare that a `handle` or ``PyObject *`` is a certain type and borrow the reference. - The target type ``T`` must be `object` or one of its derived classes. The function - doesn't do any conversions or checks. It's up to the user to make sure that the - target type is correct. - - .. code-block:: cpp - - PyObject *p = PyList_GetItem(obj, index); - py::object o = reinterpret_borrow(p); - // or - py::tuple t = reinterpret_borrow(p); // <-- `p` must be already be a `tuple` -\endrst */ -template T reinterpret_borrow(handle h) { return {h, object::borrowed_t{}}; } - -/** \rst - Like `reinterpret_borrow`, but steals the reference. - - .. code-block:: cpp - - PyObject *p = PyObject_Str(obj); - py::str s = reinterpret_steal(p); // <-- `p` must be already be a `str` -\endrst */ -template T reinterpret_steal(handle h) { return {h, object::stolen_t{}}; } - -NAMESPACE_BEGIN(detail) -inline std::string error_string(); -NAMESPACE_END(detail) - -/// Fetch and hold an error which was already set in Python. An instance of this is typically -/// thrown to propagate python-side errors back through C++ which can either be caught manually or -/// else falls back to the function dispatcher (which then raises the captured error back to -/// python). -class error_already_set : public std::runtime_error { -public: - /// Constructs a new exception from the current Python error indicator, if any. The current - /// Python error indicator will be cleared. - error_already_set() : std::runtime_error(detail::error_string()) { - PyErr_Fetch(&type.ptr(), &value.ptr(), &trace.ptr()); - } - - inline ~error_already_set(); - - /// Give the currently-held error back to Python, if any. If there is currently a Python error - /// already set it is cleared first. After this call, the current object no longer stores the - /// error variables (but the `.what()` string is still available). - void restore() { PyErr_Restore(type.release().ptr(), value.release().ptr(), trace.release().ptr()); } - - // Does nothing; provided for backwards compatibility. - PYBIND11_DEPRECATED("Use of error_already_set.clear() is deprecated") - void clear() {} - - /// Check if the currently trapped error type matches the given Python exception class (or a - /// subclass thereof). May also be passed a tuple to search for any exception class matches in - /// the given tuple. - bool matches(handle ex) const { return PyErr_GivenExceptionMatches(ex.ptr(), type.ptr()); } - -private: - object type, value, trace; -}; - -/** \defgroup python_builtins _ - Unless stated otherwise, the following C++ functions behave the same - as their Python counterparts. - */ - -/** \ingroup python_builtins - \rst - Return true if ``obj`` is an instance of ``T``. Type ``T`` must be a subclass of - `object` or a class which was exposed to Python as ``py::class_``. -\endrst */ -template ::value, int> = 0> -bool isinstance(handle obj) { return T::check_(obj); } - -template ::value, int> = 0> -bool isinstance(handle obj) { return detail::isinstance_generic(obj, typeid(T)); } - -template <> inline bool isinstance(handle obj) = delete; -template <> inline bool isinstance(handle obj) { return obj.ptr() != nullptr; } - -/// \ingroup python_builtins -/// Return true if ``obj`` is an instance of the ``type``. -inline bool isinstance(handle obj, handle type) { - const auto result = PyObject_IsInstance(obj.ptr(), type.ptr()); - if (result == -1) - throw error_already_set(); - return result != 0; -} - -/// \addtogroup python_builtins -/// @{ -inline bool hasattr(handle obj, handle name) { - return PyObject_HasAttr(obj.ptr(), name.ptr()) == 1; -} - -inline bool hasattr(handle obj, const char *name) { - return PyObject_HasAttrString(obj.ptr(), name) == 1; -} - -inline object getattr(handle obj, handle name) { - PyObject *result = PyObject_GetAttr(obj.ptr(), name.ptr()); - if (!result) { throw error_already_set(); } - return reinterpret_steal(result); -} - -inline object getattr(handle obj, const char *name) { - PyObject *result = PyObject_GetAttrString(obj.ptr(), name); - if (!result) { throw error_already_set(); } - return reinterpret_steal(result); -} - -inline object getattr(handle obj, handle name, handle default_) { - if (PyObject *result = PyObject_GetAttr(obj.ptr(), name.ptr())) { - return reinterpret_steal(result); - } else { - PyErr_Clear(); - return reinterpret_borrow(default_); - } -} - -inline object getattr(handle obj, const char *name, handle default_) { - if (PyObject *result = PyObject_GetAttrString(obj.ptr(), name)) { - return reinterpret_steal(result); - } else { - PyErr_Clear(); - return reinterpret_borrow(default_); - } -} - -inline void setattr(handle obj, handle name, handle value) { - if (PyObject_SetAttr(obj.ptr(), name.ptr(), value.ptr()) != 0) { throw error_already_set(); } -} - -inline void setattr(handle obj, const char *name, handle value) { - if (PyObject_SetAttrString(obj.ptr(), name, value.ptr()) != 0) { throw error_already_set(); } -} -/// @} python_builtins - -NAMESPACE_BEGIN(detail) -inline handle get_function(handle value) { - if (value) { -#if PY_MAJOR_VERSION >= 3 - if (PyInstanceMethod_Check(value.ptr())) - value = PyInstanceMethod_GET_FUNCTION(value.ptr()); - else -#endif - if (PyMethod_Check(value.ptr())) - value = PyMethod_GET_FUNCTION(value.ptr()); - } - return value; -} - -// Helper aliases/functions to support implicit casting of values given to python accessors/methods. -// When given a pyobject, this simply returns the pyobject as-is; for other C++ type, the value goes -// through pybind11::cast(obj) to convert it to an `object`. -template ::value, int> = 0> -auto object_or_cast(T &&o) -> decltype(std::forward(o)) { return std::forward(o); } -// The following casting version is implemented in cast.h: -template ::value, int> = 0> -object object_or_cast(T &&o); -// Match a PyObject*, which we want to convert directly to handle via its converting constructor -inline handle object_or_cast(PyObject *ptr) { return ptr; } - - -template -class accessor : public object_api> { - using key_type = typename Policy::key_type; - -public: - accessor(handle obj, key_type key) : obj(obj), key(std::move(key)) { } - accessor(const accessor &a) = default; - accessor(accessor &&a) = default; - - // accessor overload required to override default assignment operator (templates are not allowed - // to replace default compiler-generated assignments). - void operator=(const accessor &a) && { std::move(*this).operator=(handle(a)); } - void operator=(const accessor &a) & { operator=(handle(a)); } - - template void operator=(T &&value) && { - Policy::set(obj, key, object_or_cast(std::forward(value))); - } - template void operator=(T &&value) & { - get_cache() = reinterpret_borrow(object_or_cast(std::forward(value))); - } - - template - PYBIND11_DEPRECATED("Use of obj.attr(...) as bool is deprecated in favor of pybind11::hasattr(obj, ...)") - explicit operator enable_if_t::value || - std::is_same::value, bool>() const { - return hasattr(obj, key); - } - template - PYBIND11_DEPRECATED("Use of obj[key] as bool is deprecated in favor of obj.contains(key)") - explicit operator enable_if_t::value, bool>() const { - return obj.contains(key); - } - - operator object() const { return get_cache(); } - PyObject *ptr() const { return get_cache().ptr(); } - template T cast() const { return get_cache().template cast(); } - -private: - object &get_cache() const { - if (!cache) { cache = Policy::get(obj, key); } - return cache; - } - -private: - handle obj; - key_type key; - mutable object cache; -}; - -NAMESPACE_BEGIN(accessor_policies) -struct obj_attr { - using key_type = object; - static object get(handle obj, handle key) { return getattr(obj, key); } - static void set(handle obj, handle key, handle val) { setattr(obj, key, val); } -}; - -struct str_attr { - using key_type = const char *; - static object get(handle obj, const char *key) { return getattr(obj, key); } - static void set(handle obj, const char *key, handle val) { setattr(obj, key, val); } -}; - -struct generic_item { - using key_type = object; - - static object get(handle obj, handle key) { - PyObject *result = PyObject_GetItem(obj.ptr(), key.ptr()); - if (!result) { throw error_already_set(); } - return reinterpret_steal(result); - } - - static void set(handle obj, handle key, handle val) { - if (PyObject_SetItem(obj.ptr(), key.ptr(), val.ptr()) != 0) { throw error_already_set(); } - } -}; - -struct sequence_item { - using key_type = size_t; - - static object get(handle obj, size_t index) { - PyObject *result = PySequence_GetItem(obj.ptr(), static_cast(index)); - if (!result) { throw error_already_set(); } - return reinterpret_steal(result); - } - - static void set(handle obj, size_t index, handle val) { - // PySequence_SetItem does not steal a reference to 'val' - if (PySequence_SetItem(obj.ptr(), static_cast(index), val.ptr()) != 0) { - throw error_already_set(); - } - } -}; - -struct list_item { - using key_type = size_t; - - static object get(handle obj, size_t index) { - PyObject *result = PyList_GetItem(obj.ptr(), static_cast(index)); - if (!result) { throw error_already_set(); } - return reinterpret_borrow(result); - } - - static void set(handle obj, size_t index, handle val) { - // PyList_SetItem steals a reference to 'val' - if (PyList_SetItem(obj.ptr(), static_cast(index), val.inc_ref().ptr()) != 0) { - throw error_already_set(); - } - } -}; - -struct tuple_item { - using key_type = size_t; - - static object get(handle obj, size_t index) { - PyObject *result = PyTuple_GetItem(obj.ptr(), static_cast(index)); - if (!result) { throw error_already_set(); } - return reinterpret_borrow(result); - } - - static void set(handle obj, size_t index, handle val) { - // PyTuple_SetItem steals a reference to 'val' - if (PyTuple_SetItem(obj.ptr(), static_cast(index), val.inc_ref().ptr()) != 0) { - throw error_already_set(); - } - } -}; -NAMESPACE_END(accessor_policies) - -/// STL iterator template used for tuple, list, sequence and dict -template -class generic_iterator : public Policy { - using It = generic_iterator; - -public: - using difference_type = ssize_t; - using iterator_category = typename Policy::iterator_category; - using value_type = typename Policy::value_type; - using reference = typename Policy::reference; - using pointer = typename Policy::pointer; - - generic_iterator() = default; - generic_iterator(handle seq, ssize_t index) : Policy(seq, index) { } - - reference operator*() const { return Policy::dereference(); } - reference operator[](difference_type n) const { return *(*this + n); } - pointer operator->() const { return **this; } - - It &operator++() { Policy::increment(); return *this; } - It operator++(int) { auto copy = *this; Policy::increment(); return copy; } - It &operator--() { Policy::decrement(); return *this; } - It operator--(int) { auto copy = *this; Policy::decrement(); return copy; } - It &operator+=(difference_type n) { Policy::advance(n); return *this; } - It &operator-=(difference_type n) { Policy::advance(-n); return *this; } - - friend It operator+(const It &a, difference_type n) { auto copy = a; return copy += n; } - friend It operator+(difference_type n, const It &b) { return b + n; } - friend It operator-(const It &a, difference_type n) { auto copy = a; return copy -= n; } - friend difference_type operator-(const It &a, const It &b) { return a.distance_to(b); } - - friend bool operator==(const It &a, const It &b) { return a.equal(b); } - friend bool operator!=(const It &a, const It &b) { return !(a == b); } - friend bool operator< (const It &a, const It &b) { return b - a > 0; } - friend bool operator> (const It &a, const It &b) { return b < a; } - friend bool operator>=(const It &a, const It &b) { return !(a < b); } - friend bool operator<=(const It &a, const It &b) { return !(a > b); } -}; - -NAMESPACE_BEGIN(iterator_policies) -/// Quick proxy class needed to implement ``operator->`` for iterators which can't return pointers -template -struct arrow_proxy { - T value; - - arrow_proxy(T &&value) : value(std::move(value)) { } - T *operator->() const { return &value; } -}; - -/// Lightweight iterator policy using just a simple pointer: see ``PySequence_Fast_ITEMS`` -class sequence_fast_readonly { -protected: - using iterator_category = std::random_access_iterator_tag; - using value_type = handle; - using reference = const handle; - using pointer = arrow_proxy; - - sequence_fast_readonly(handle obj, ssize_t n) : ptr(PySequence_Fast_ITEMS(obj.ptr()) + n) { } - - reference dereference() const { return *ptr; } - void increment() { ++ptr; } - void decrement() { --ptr; } - void advance(ssize_t n) { ptr += n; } - bool equal(const sequence_fast_readonly &b) const { return ptr == b.ptr; } - ssize_t distance_to(const sequence_fast_readonly &b) const { return ptr - b.ptr; } - -private: - PyObject **ptr; -}; - -/// Full read and write access using the sequence protocol: see ``detail::sequence_accessor`` -class sequence_slow_readwrite { -protected: - using iterator_category = std::random_access_iterator_tag; - using value_type = object; - using reference = sequence_accessor; - using pointer = arrow_proxy; - - sequence_slow_readwrite(handle obj, ssize_t index) : obj(obj), index(index) { } - - reference dereference() const { return {obj, static_cast(index)}; } - void increment() { ++index; } - void decrement() { --index; } - void advance(ssize_t n) { index += n; } - bool equal(const sequence_slow_readwrite &b) const { return index == b.index; } - ssize_t distance_to(const sequence_slow_readwrite &b) const { return index - b.index; } - -private: - handle obj; - ssize_t index; -}; - -/// Python's dictionary protocol permits this to be a forward iterator -class dict_readonly { -protected: - using iterator_category = std::forward_iterator_tag; - using value_type = std::pair; - using reference = const value_type; - using pointer = arrow_proxy; - - dict_readonly() = default; - dict_readonly(handle obj, ssize_t pos) : obj(obj), pos(pos) { increment(); } - - reference dereference() const { return {key, value}; } - void increment() { if (!PyDict_Next(obj.ptr(), &pos, &key, &value)) { pos = -1; } } - bool equal(const dict_readonly &b) const { return pos == b.pos; } - -private: - handle obj; - PyObject *key, *value; - ssize_t pos = -1; -}; -NAMESPACE_END(iterator_policies) - -#if !defined(PYPY_VERSION) -using tuple_iterator = generic_iterator; -using list_iterator = generic_iterator; -#else -using tuple_iterator = generic_iterator; -using list_iterator = generic_iterator; -#endif - -using sequence_iterator = generic_iterator; -using dict_iterator = generic_iterator; - -inline bool PyIterable_Check(PyObject *obj) { - PyObject *iter = PyObject_GetIter(obj); - if (iter) { - Py_DECREF(iter); - return true; - } else { - PyErr_Clear(); - return false; - } -} - -inline bool PyNone_Check(PyObject *o) { return o == Py_None; } - -inline bool PyUnicode_Check_Permissive(PyObject *o) { return PyUnicode_Check(o) || PYBIND11_BYTES_CHECK(o); } - -class kwargs_proxy : public handle { -public: - explicit kwargs_proxy(handle h) : handle(h) { } -}; - -class args_proxy : public handle { -public: - explicit args_proxy(handle h) : handle(h) { } - kwargs_proxy operator*() const { return kwargs_proxy(*this); } -}; - -/// Python argument categories (using PEP 448 terms) -template using is_keyword = std::is_base_of; -template using is_s_unpacking = std::is_same; // * unpacking -template using is_ds_unpacking = std::is_same; // ** unpacking -template using is_positional = satisfies_none_of; -template using is_keyword_or_ds = satisfies_any_of; - -// Call argument collector forward declarations -template -class simple_collector; -template -class unpacking_collector; - -NAMESPACE_END(detail) - -// TODO: After the deprecated constructors are removed, this macro can be simplified by -// inheriting ctors: `using Parent::Parent`. It's not an option right now because -// the `using` statement triggers the parent deprecation warning even if the ctor -// isn't even used. -#define PYBIND11_OBJECT_COMMON(Name, Parent, CheckFun) \ - public: \ - PYBIND11_DEPRECATED("Use reinterpret_borrow<"#Name">() or reinterpret_steal<"#Name">()") \ - Name(handle h, bool is_borrowed) : Parent(is_borrowed ? Parent(h, borrowed_t{}) : Parent(h, stolen_t{})) { } \ - Name(handle h, borrowed_t) : Parent(h, borrowed_t{}) { } \ - Name(handle h, stolen_t) : Parent(h, stolen_t{}) { } \ - PYBIND11_DEPRECATED("Use py::isinstance(obj) instead") \ - bool check() const { return m_ptr != nullptr && (bool) CheckFun(m_ptr); } \ - static bool check_(handle h) { return h.ptr() != nullptr && CheckFun(h.ptr()); } - -#define PYBIND11_OBJECT_CVT(Name, Parent, CheckFun, ConvertFun) \ - PYBIND11_OBJECT_COMMON(Name, Parent, CheckFun) \ - /* This is deliberately not 'explicit' to allow implicit conversion from object: */ \ - Name(const object &o) : Parent(ConvertFun(o.ptr()), stolen_t{}) { if (!m_ptr) throw error_already_set(); } - -#define PYBIND11_OBJECT(Name, Parent, CheckFun) \ - PYBIND11_OBJECT_COMMON(Name, Parent, CheckFun) \ - /* This is deliberately not 'explicit' to allow implicit conversion from object: */ \ - Name(const object &o) : Parent(o) { } \ - Name(object &&o) : Parent(std::move(o)) { } - -#define PYBIND11_OBJECT_DEFAULT(Name, Parent, CheckFun) \ - PYBIND11_OBJECT(Name, Parent, CheckFun) \ - Name() : Parent() { } - -/// \addtogroup pytypes -/// @{ - -/** \rst - Wraps a Python iterator so that it can also be used as a C++ input iterator - - Caveat: copying an iterator does not (and cannot) clone the internal - state of the Python iterable. This also applies to the post-increment - operator. This iterator should only be used to retrieve the current - value using ``operator*()``. -\endrst */ -class iterator : public object { -public: - using iterator_category = std::input_iterator_tag; - using difference_type = ssize_t; - using value_type = handle; - using reference = const handle; - using pointer = const handle *; - - PYBIND11_OBJECT_DEFAULT(iterator, object, PyIter_Check) - - iterator& operator++() { - advance(); - return *this; - } - - iterator operator++(int) { - auto rv = *this; - advance(); - return rv; - } - - reference operator*() const { - if (m_ptr && !value.ptr()) { - auto& self = const_cast(*this); - self.advance(); - } - return value; - } - - pointer operator->() const { operator*(); return &value; } - - /** \rst - The value which marks the end of the iteration. ``it == iterator::sentinel()`` - is equivalent to catching ``StopIteration`` in Python. - - .. code-block:: cpp - - void foo(py::iterator it) { - while (it != py::iterator::sentinel()) { - // use `*it` - ++it; - } - } - \endrst */ - static iterator sentinel() { return {}; } - - friend bool operator==(const iterator &a, const iterator &b) { return a->ptr() == b->ptr(); } - friend bool operator!=(const iterator &a, const iterator &b) { return a->ptr() != b->ptr(); } - -private: - void advance() { - value = reinterpret_steal(PyIter_Next(m_ptr)); - if (PyErr_Occurred()) { throw error_already_set(); } - } - -private: - object value = {}; -}; - -class iterable : public object { -public: - PYBIND11_OBJECT_DEFAULT(iterable, object, detail::PyIterable_Check) -}; - -class bytes; - -class str : public object { -public: - PYBIND11_OBJECT_CVT(str, object, detail::PyUnicode_Check_Permissive, raw_str) - - str(const char *c, size_t n) - : object(PyUnicode_FromStringAndSize(c, (ssize_t) n), stolen_t{}) { - if (!m_ptr) pybind11_fail("Could not allocate string object!"); - } - - // 'explicit' is explicitly omitted from the following constructors to allow implicit conversion to py::str from C++ string-like objects - str(const char *c = "") - : object(PyUnicode_FromString(c), stolen_t{}) { - if (!m_ptr) pybind11_fail("Could not allocate string object!"); - } - - str(const std::string &s) : str(s.data(), s.size()) { } - - explicit str(const bytes &b); - - /** \rst - Return a string representation of the object. This is analogous to - the ``str()`` function in Python. - \endrst */ - explicit str(handle h) : object(raw_str(h.ptr()), stolen_t{}) { } - - operator std::string() const { - object temp = *this; - if (PyUnicode_Check(m_ptr)) { - temp = reinterpret_steal(PyUnicode_AsUTF8String(m_ptr)); - if (!temp) - pybind11_fail("Unable to extract string contents! (encoding issue)"); - } - char *buffer; - ssize_t length; - if (PYBIND11_BYTES_AS_STRING_AND_SIZE(temp.ptr(), &buffer, &length)) - pybind11_fail("Unable to extract string contents! (invalid type)"); - return std::string(buffer, (size_t) length); - } - - template - str format(Args &&...args) const { - return attr("format")(std::forward(args)...); - } - -private: - /// Return string representation -- always returns a new reference, even if already a str - static PyObject *raw_str(PyObject *op) { - PyObject *str_value = PyObject_Str(op); -#if PY_MAJOR_VERSION < 3 - if (!str_value) throw error_already_set(); - PyObject *unicode = PyUnicode_FromEncodedObject(str_value, "utf-8", nullptr); - Py_XDECREF(str_value); str_value = unicode; -#endif - return str_value; - } -}; -/// @} pytypes - -inline namespace literals { -/** \rst - String literal version of `str` - \endrst */ -inline str operator"" _s(const char *s, size_t size) { return {s, size}; } -} - -/// \addtogroup pytypes -/// @{ -class bytes : public object { -public: - PYBIND11_OBJECT(bytes, object, PYBIND11_BYTES_CHECK) - - // Allow implicit conversion: - bytes(const char *c = "") - : object(PYBIND11_BYTES_FROM_STRING(c), stolen_t{}) { - if (!m_ptr) pybind11_fail("Could not allocate bytes object!"); - } - - bytes(const char *c, size_t n) - : object(PYBIND11_BYTES_FROM_STRING_AND_SIZE(c, (ssize_t) n), stolen_t{}) { - if (!m_ptr) pybind11_fail("Could not allocate bytes object!"); - } - - // Allow implicit conversion: - bytes(const std::string &s) : bytes(s.data(), s.size()) { } - - explicit bytes(const pybind11::str &s); - - operator std::string() const { - char *buffer; - ssize_t length; - if (PYBIND11_BYTES_AS_STRING_AND_SIZE(m_ptr, &buffer, &length)) - pybind11_fail("Unable to extract bytes contents!"); - return std::string(buffer, (size_t) length); - } -}; - -inline bytes::bytes(const pybind11::str &s) { - object temp = s; - if (PyUnicode_Check(s.ptr())) { - temp = reinterpret_steal(PyUnicode_AsUTF8String(s.ptr())); - if (!temp) - pybind11_fail("Unable to extract string contents! (encoding issue)"); - } - char *buffer; - ssize_t length; - if (PYBIND11_BYTES_AS_STRING_AND_SIZE(temp.ptr(), &buffer, &length)) - pybind11_fail("Unable to extract string contents! (invalid type)"); - auto obj = reinterpret_steal(PYBIND11_BYTES_FROM_STRING_AND_SIZE(buffer, length)); - if (!obj) - pybind11_fail("Could not allocate bytes object!"); - m_ptr = obj.release().ptr(); -} - -inline str::str(const bytes& b) { - char *buffer; - ssize_t length; - if (PYBIND11_BYTES_AS_STRING_AND_SIZE(b.ptr(), &buffer, &length)) - pybind11_fail("Unable to extract bytes contents!"); - auto obj = reinterpret_steal(PyUnicode_FromStringAndSize(buffer, (ssize_t) length)); - if (!obj) - pybind11_fail("Could not allocate string object!"); - m_ptr = obj.release().ptr(); -} - -class none : public object { -public: - PYBIND11_OBJECT(none, object, detail::PyNone_Check) - none() : object(Py_None, borrowed_t{}) { } -}; - -class bool_ : public object { -public: - PYBIND11_OBJECT_CVT(bool_, object, PyBool_Check, raw_bool) - bool_() : object(Py_False, borrowed_t{}) { } - // Allow implicit conversion from and to `bool`: - bool_(bool value) : object(value ? Py_True : Py_False, borrowed_t{}) { } - operator bool() const { return m_ptr && PyLong_AsLong(m_ptr) != 0; } - -private: - /// Return the truth value of an object -- always returns a new reference - static PyObject *raw_bool(PyObject *op) { - const auto value = PyObject_IsTrue(op); - if (value == -1) return nullptr; - return handle(value ? Py_True : Py_False).inc_ref().ptr(); - } -}; - -NAMESPACE_BEGIN(detail) -// Converts a value to the given unsigned type. If an error occurs, you get back (Unsigned) -1; -// otherwise you get back the unsigned long or unsigned long long value cast to (Unsigned). -// (The distinction is critically important when casting a returned -1 error value to some other -// unsigned type: (A)-1 != (B)-1 when A and B are unsigned types of different sizes). -template -Unsigned as_unsigned(PyObject *o) { - if (sizeof(Unsigned) <= sizeof(unsigned long) -#if PY_VERSION_HEX < 0x03000000 - || PyInt_Check(o) -#endif - ) { - unsigned long v = PyLong_AsUnsignedLong(o); - return v == (unsigned long) -1 && PyErr_Occurred() ? (Unsigned) -1 : (Unsigned) v; - } - else { - unsigned long long v = PyLong_AsUnsignedLongLong(o); - return v == (unsigned long long) -1 && PyErr_Occurred() ? (Unsigned) -1 : (Unsigned) v; - } -} -NAMESPACE_END(detail) - -class int_ : public object { -public: - PYBIND11_OBJECT_CVT(int_, object, PYBIND11_LONG_CHECK, PyNumber_Long) - int_() : object(PyLong_FromLong(0), stolen_t{}) { } - // Allow implicit conversion from C++ integral types: - template ::value, int> = 0> - int_(T value) { - if (sizeof(T) <= sizeof(long)) { - if (std::is_signed::value) - m_ptr = PyLong_FromLong((long) value); - else - m_ptr = PyLong_FromUnsignedLong((unsigned long) value); - } else { - if (std::is_signed::value) - m_ptr = PyLong_FromLongLong((long long) value); - else - m_ptr = PyLong_FromUnsignedLongLong((unsigned long long) value); - } - if (!m_ptr) pybind11_fail("Could not allocate int object!"); - } - - template ::value, int> = 0> - operator T() const { - return std::is_unsigned::value - ? detail::as_unsigned(m_ptr) - : sizeof(T) <= sizeof(long) - ? (T) PyLong_AsLong(m_ptr) - : (T) PYBIND11_LONG_AS_LONGLONG(m_ptr); - } -}; - -class float_ : public object { -public: - PYBIND11_OBJECT_CVT(float_, object, PyFloat_Check, PyNumber_Float) - // Allow implicit conversion from float/double: - float_(float value) : object(PyFloat_FromDouble((double) value), stolen_t{}) { - if (!m_ptr) pybind11_fail("Could not allocate float object!"); - } - float_(double value = .0) : object(PyFloat_FromDouble((double) value), stolen_t{}) { - if (!m_ptr) pybind11_fail("Could not allocate float object!"); - } - operator float() const { return (float) PyFloat_AsDouble(m_ptr); } - operator double() const { return (double) PyFloat_AsDouble(m_ptr); } -}; - -class weakref : public object { -public: - PYBIND11_OBJECT_DEFAULT(weakref, object, PyWeakref_Check) - explicit weakref(handle obj, handle callback = {}) - : object(PyWeakref_NewRef(obj.ptr(), callback.ptr()), stolen_t{}) { - if (!m_ptr) pybind11_fail("Could not allocate weak reference!"); - } -}; - -class slice : public object { -public: - PYBIND11_OBJECT_DEFAULT(slice, object, PySlice_Check) - slice(ssize_t start_, ssize_t stop_, ssize_t step_) { - int_ start(start_), stop(stop_), step(step_); - m_ptr = PySlice_New(start.ptr(), stop.ptr(), step.ptr()); - if (!m_ptr) pybind11_fail("Could not allocate slice object!"); - } - bool compute(size_t length, size_t *start, size_t *stop, size_t *step, - size_t *slicelength) const { - return PySlice_GetIndicesEx((PYBIND11_SLICE_OBJECT *) m_ptr, - (ssize_t) length, (ssize_t *) start, - (ssize_t *) stop, (ssize_t *) step, - (ssize_t *) slicelength) == 0; - } -}; - -class capsule : public object { -public: - PYBIND11_OBJECT_DEFAULT(capsule, object, PyCapsule_CheckExact) - PYBIND11_DEPRECATED("Use reinterpret_borrow() or reinterpret_steal()") - capsule(PyObject *ptr, bool is_borrowed) : object(is_borrowed ? object(ptr, borrowed_t{}) : object(ptr, stolen_t{})) { } - - explicit capsule(const void *value, const char *name = nullptr, void (*destructor)(PyObject *) = nullptr) - : object(PyCapsule_New(const_cast(value), name, destructor), stolen_t{}) { - if (!m_ptr) - pybind11_fail("Could not allocate capsule object!"); - } - - PYBIND11_DEPRECATED("Please pass a destructor that takes a void pointer as input") - capsule(const void *value, void (*destruct)(PyObject *)) - : object(PyCapsule_New(const_cast(value), nullptr, destruct), stolen_t{}) { - if (!m_ptr) - pybind11_fail("Could not allocate capsule object!"); - } - - capsule(const void *value, void (*destructor)(void *)) { - m_ptr = PyCapsule_New(const_cast(value), nullptr, [](PyObject *o) { - auto destructor = reinterpret_cast(PyCapsule_GetContext(o)); - void *ptr = PyCapsule_GetPointer(o, nullptr); - destructor(ptr); - }); - - if (!m_ptr) - pybind11_fail("Could not allocate capsule object!"); - - if (PyCapsule_SetContext(m_ptr, (void *) destructor) != 0) - pybind11_fail("Could not set capsule context!"); - } - - capsule(void (*destructor)()) { - m_ptr = PyCapsule_New(reinterpret_cast(destructor), nullptr, [](PyObject *o) { - auto destructor = reinterpret_cast(PyCapsule_GetPointer(o, nullptr)); - destructor(); - }); - - if (!m_ptr) - pybind11_fail("Could not allocate capsule object!"); - } - - template operator T *() const { - auto name = this->name(); - T * result = static_cast(PyCapsule_GetPointer(m_ptr, name)); - if (!result) pybind11_fail("Unable to extract capsule contents!"); - return result; - } - - const char *name() const { return PyCapsule_GetName(m_ptr); } -}; - -class tuple : public object { -public: - PYBIND11_OBJECT_CVT(tuple, object, PyTuple_Check, PySequence_Tuple) - explicit tuple(size_t size = 0) : object(PyTuple_New((ssize_t) size), stolen_t{}) { - if (!m_ptr) pybind11_fail("Could not allocate tuple object!"); - } - size_t size() const { return (size_t) PyTuple_Size(m_ptr); } - detail::tuple_accessor operator[](size_t index) const { return {*this, index}; } - detail::tuple_iterator begin() const { return {*this, 0}; } - detail::tuple_iterator end() const { return {*this, PyTuple_GET_SIZE(m_ptr)}; } -}; - -class dict : public object { -public: - PYBIND11_OBJECT_CVT(dict, object, PyDict_Check, raw_dict) - dict() : object(PyDict_New(), stolen_t{}) { - if (!m_ptr) pybind11_fail("Could not allocate dict object!"); - } - template ...>::value>, - // MSVC workaround: it can't compile an out-of-line definition, so defer the collector - typename collector = detail::deferred_t, Args...>> - explicit dict(Args &&...args) : dict(collector(std::forward(args)...).kwargs()) { } - - size_t size() const { return (size_t) PyDict_Size(m_ptr); } - detail::dict_iterator begin() const { return {*this, 0}; } - detail::dict_iterator end() const { return {}; } - void clear() const { PyDict_Clear(ptr()); } - bool contains(handle key) const { return PyDict_Contains(ptr(), key.ptr()) == 1; } - bool contains(const char *key) const { return PyDict_Contains(ptr(), pybind11::str(key).ptr()) == 1; } - -private: - /// Call the `dict` Python type -- always returns a new reference - static PyObject *raw_dict(PyObject *op) { - if (PyDict_Check(op)) - return handle(op).inc_ref().ptr(); - return PyObject_CallFunctionObjArgs((PyObject *) &PyDict_Type, op, nullptr); - } -}; - -class sequence : public object { -public: - PYBIND11_OBJECT_DEFAULT(sequence, object, PySequence_Check) - size_t size() const { return (size_t) PySequence_Size(m_ptr); } - detail::sequence_accessor operator[](size_t index) const { return {*this, index}; } - detail::sequence_iterator begin() const { return {*this, 0}; } - detail::sequence_iterator end() const { return {*this, PySequence_Size(m_ptr)}; } -}; - -class list : public object { -public: - PYBIND11_OBJECT_CVT(list, object, PyList_Check, PySequence_List) - explicit list(size_t size = 0) : object(PyList_New((ssize_t) size), stolen_t{}) { - if (!m_ptr) pybind11_fail("Could not allocate list object!"); - } - size_t size() const { return (size_t) PyList_Size(m_ptr); } - detail::list_accessor operator[](size_t index) const { return {*this, index}; } - detail::list_iterator begin() const { return {*this, 0}; } - detail::list_iterator end() const { return {*this, PyList_GET_SIZE(m_ptr)}; } - template void append(T &&val) const { - PyList_Append(m_ptr, detail::object_or_cast(std::forward(val)).ptr()); - } -}; - -class args : public tuple { PYBIND11_OBJECT_DEFAULT(args, tuple, PyTuple_Check) }; -class kwargs : public dict { PYBIND11_OBJECT_DEFAULT(kwargs, dict, PyDict_Check) }; - -class set : public object { -public: - PYBIND11_OBJECT_CVT(set, object, PySet_Check, PySet_New) - set() : object(PySet_New(nullptr), stolen_t{}) { - if (!m_ptr) pybind11_fail("Could not allocate set object!"); - } - size_t size() const { return (size_t) PySet_Size(m_ptr); } - template bool add(T &&val) const { - return PySet_Add(m_ptr, detail::object_or_cast(std::forward(val)).ptr()) == 0; - } - void clear() const { PySet_Clear(m_ptr); } -}; - -class function : public object { -public: - PYBIND11_OBJECT_DEFAULT(function, object, PyCallable_Check) - handle cpp_function() const { - handle fun = detail::get_function(m_ptr); - if (fun && PyCFunction_Check(fun.ptr())) - return fun; - return handle(); - } - bool is_cpp_function() const { return (bool) cpp_function(); } -}; - -class buffer : public object { -public: - PYBIND11_OBJECT_DEFAULT(buffer, object, PyObject_CheckBuffer) - - buffer_info request(bool writable = false) { - int flags = PyBUF_STRIDES | PyBUF_FORMAT; - if (writable) flags |= PyBUF_WRITABLE; - Py_buffer *view = new Py_buffer(); - if (PyObject_GetBuffer(m_ptr, view, flags) != 0) { - delete view; - throw error_already_set(); - } - return buffer_info(view); - } -}; - -class memoryview : public object { -public: - explicit memoryview(const buffer_info& info) { - static Py_buffer buf { }; - // Py_buffer uses signed sizes, strides and shape!.. - static std::vector py_strides { }; - static std::vector py_shape { }; - buf.buf = info.ptr; - buf.itemsize = info.itemsize; - buf.format = const_cast(info.format.c_str()); - buf.ndim = (int) info.ndim; - buf.len = info.size; - py_strides.clear(); - py_shape.clear(); - for (size_t i = 0; i < (size_t) info.ndim; ++i) { - py_strides.push_back(info.strides[i]); - py_shape.push_back(info.shape[i]); - } - buf.strides = py_strides.data(); - buf.shape = py_shape.data(); - buf.suboffsets = nullptr; - buf.readonly = false; - buf.internal = nullptr; - - m_ptr = PyMemoryView_FromBuffer(&buf); - if (!m_ptr) - pybind11_fail("Unable to create memoryview from buffer descriptor"); - } - - PYBIND11_OBJECT_CVT(memoryview, object, PyMemoryView_Check, PyMemoryView_FromObject) -}; -/// @} pytypes - -/// \addtogroup python_builtins -/// @{ -inline size_t len(handle h) { - ssize_t result = PyObject_Length(h.ptr()); - if (result < 0) - pybind11_fail("Unable to compute length of object"); - return (size_t) result; -} - -inline str repr(handle h) { - PyObject *str_value = PyObject_Repr(h.ptr()); - if (!str_value) throw error_already_set(); -#if PY_MAJOR_VERSION < 3 - PyObject *unicode = PyUnicode_FromEncodedObject(str_value, "utf-8", nullptr); - Py_XDECREF(str_value); str_value = unicode; - if (!str_value) throw error_already_set(); -#endif - return reinterpret_steal(str_value); -} - -inline iterator iter(handle obj) { - PyObject *result = PyObject_GetIter(obj.ptr()); - if (!result) { throw error_already_set(); } - return reinterpret_steal(result); -} -/// @} python_builtins - -NAMESPACE_BEGIN(detail) -template iterator object_api::begin() const { return iter(derived()); } -template iterator object_api::end() const { return iterator::sentinel(); } -template item_accessor object_api::operator[](handle key) const { - return {derived(), reinterpret_borrow(key)}; -} -template item_accessor object_api::operator[](const char *key) const { - return {derived(), pybind11::str(key)}; -} -template obj_attr_accessor object_api::attr(handle key) const { - return {derived(), reinterpret_borrow(key)}; -} -template str_attr_accessor object_api::attr(const char *key) const { - return {derived(), key}; -} -template args_proxy object_api::operator*() const { - return args_proxy(derived().ptr()); -} -template template bool object_api::contains(T &&item) const { - return attr("__contains__")(std::forward(item)).template cast(); -} - -template -pybind11::str object_api::str() const { return pybind11::str(derived()); } - -template -str_attr_accessor object_api::doc() const { return attr("__doc__"); } - -template -handle object_api::get_type() const { return (PyObject *) Py_TYPE(derived().ptr()); } - -NAMESPACE_END(detail) -NAMESPACE_END(pybind11) diff --git a/ptocr/postprocess/lanms/include/pybind11/stl.h b/ptocr/postprocess/lanms/include/pybind11/stl.h deleted file mode 100644 index d07a81f..0000000 --- a/ptocr/postprocess/lanms/include/pybind11/stl.h +++ /dev/null @@ -1,367 +0,0 @@ -/* - pybind11/stl.h: Transparent conversion for STL data types - - Copyright (c) 2016 Wenzel Jakob - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#include "pybind11.h" -#include -#include -#include -#include -#include -#include -#include - -#if defined(_MSC_VER) -#pragma warning(push) -#pragma warning(disable: 4127) // warning C4127: Conditional expression is constant -#endif - -#ifdef __has_include -// std::optional (but including it in c++14 mode isn't allowed) -# if defined(PYBIND11_CPP17) && __has_include() -# include -# define PYBIND11_HAS_OPTIONAL 1 -# endif -// std::experimental::optional (but not allowed in c++11 mode) -# if defined(PYBIND11_CPP14) && __has_include() -# include -# define PYBIND11_HAS_EXP_OPTIONAL 1 -# endif -// std::variant -# if defined(PYBIND11_CPP17) && __has_include() -# include -# define PYBIND11_HAS_VARIANT 1 -# endif -#elif defined(_MSC_VER) && defined(PYBIND11_CPP17) -# include -# include -# define PYBIND11_HAS_OPTIONAL 1 -# define PYBIND11_HAS_VARIANT 1 -#endif - -NAMESPACE_BEGIN(pybind11) -NAMESPACE_BEGIN(detail) - -/// Extracts an const lvalue reference or rvalue reference for U based on the type of T (e.g. for -/// forwarding a container element). Typically used indirect via forwarded_type(), below. -template -using forwarded_type = conditional_t< - std::is_lvalue_reference::value, remove_reference_t &, remove_reference_t &&>; - -/// Forwards a value U as rvalue or lvalue according to whether T is rvalue or lvalue; typically -/// used for forwarding a container's elements. -template -forwarded_type forward_like(U &&u) { - return std::forward>(std::forward(u)); -} - -template struct set_caster { - using type = Type; - using key_conv = make_caster; - - bool load(handle src, bool convert) { - if (!isinstance(src)) - return false; - auto s = reinterpret_borrow(src); - value.clear(); - for (auto entry : s) { - key_conv conv; - if (!conv.load(entry, convert)) - return false; - value.insert(cast_op(std::move(conv))); - } - return true; - } - - template - static handle cast(T &&src, return_value_policy policy, handle parent) { - pybind11::set s; - for (auto &value: src) { - auto value_ = reinterpret_steal(key_conv::cast(forward_like(value), policy, parent)); - if (!value_ || !s.add(value_)) - return handle(); - } - return s.release(); - } - - PYBIND11_TYPE_CASTER(type, _("Set[") + key_conv::name() + _("]")); -}; - -template struct map_caster { - using key_conv = make_caster; - using value_conv = make_caster; - - bool load(handle src, bool convert) { - if (!isinstance(src)) - return false; - auto d = reinterpret_borrow(src); - value.clear(); - for (auto it : d) { - key_conv kconv; - value_conv vconv; - if (!kconv.load(it.first.ptr(), convert) || - !vconv.load(it.second.ptr(), convert)) - return false; - value.emplace(cast_op(std::move(kconv)), cast_op(std::move(vconv))); - } - return true; - } - - template - static handle cast(T &&src, return_value_policy policy, handle parent) { - dict d; - for (auto &kv: src) { - auto key = reinterpret_steal(key_conv::cast(forward_like(kv.first), policy, parent)); - auto value = reinterpret_steal(value_conv::cast(forward_like(kv.second), policy, parent)); - if (!key || !value) - return handle(); - d[key] = value; - } - return d.release(); - } - - PYBIND11_TYPE_CASTER(Type, _("Dict[") + key_conv::name() + _(", ") + value_conv::name() + _("]")); -}; - -template struct list_caster { - using value_conv = make_caster; - - bool load(handle src, bool convert) { - if (!isinstance(src)) - return false; - auto s = reinterpret_borrow(src); - value.clear(); - reserve_maybe(s, &value); - for (auto it : s) { - value_conv conv; - if (!conv.load(it, convert)) - return false; - value.push_back(cast_op(std::move(conv))); - } - return true; - } - -private: - template ().reserve(0)), void>::value, int> = 0> - void reserve_maybe(sequence s, Type *) { value.reserve(s.size()); } - void reserve_maybe(sequence, void *) { } - -public: - template - static handle cast(T &&src, return_value_policy policy, handle parent) { - list l(src.size()); - size_t index = 0; - for (auto &value: src) { - auto value_ = reinterpret_steal(value_conv::cast(forward_like(value), policy, parent)); - if (!value_) - return handle(); - PyList_SET_ITEM(l.ptr(), (ssize_t) index++, value_.release().ptr()); // steals a reference - } - return l.release(); - } - - PYBIND11_TYPE_CASTER(Type, _("List[") + value_conv::name() + _("]")); -}; - -template struct type_caster> - : list_caster, Type> { }; - -template struct type_caster> - : list_caster, Type> { }; - -template struct array_caster { - using value_conv = make_caster; - -private: - template - bool require_size(enable_if_t size) { - if (value.size() != size) - value.resize(size); - return true; - } - template - bool require_size(enable_if_t size) { - return size == Size; - } - -public: - bool load(handle src, bool convert) { - if (!isinstance(src)) - return false; - auto l = reinterpret_borrow(src); - if (!require_size(l.size())) - return false; - size_t ctr = 0; - for (auto it : l) { - value_conv conv; - if (!conv.load(it, convert)) - return false; - value[ctr++] = cast_op(std::move(conv)); - } - return true; - } - - template - static handle cast(T &&src, return_value_policy policy, handle parent) { - list l(src.size()); - size_t index = 0; - for (auto &value: src) { - auto value_ = reinterpret_steal(value_conv::cast(forward_like(value), policy, parent)); - if (!value_) - return handle(); - PyList_SET_ITEM(l.ptr(), (ssize_t) index++, value_.release().ptr()); // steals a reference - } - return l.release(); - } - - PYBIND11_TYPE_CASTER(ArrayType, _("List[") + value_conv::name() + _(_(""), _("[") + _() + _("]")) + _("]")); -}; - -template struct type_caster> - : array_caster, Type, false, Size> { }; - -template struct type_caster> - : array_caster, Type, true> { }; - -template struct type_caster> - : set_caster, Key> { }; - -template struct type_caster> - : set_caster, Key> { }; - -template struct type_caster> - : map_caster, Key, Value> { }; - -template struct type_caster> - : map_caster, Key, Value> { }; - -// This type caster is intended to be used for std::optional and std::experimental::optional -template struct optional_caster { - using value_conv = make_caster; - - template - static handle cast(T_ &&src, return_value_policy policy, handle parent) { - if (!src) - return none().inc_ref(); - return value_conv::cast(*std::forward(src), policy, parent); - } - - bool load(handle src, bool convert) { - if (!src) { - return false; - } else if (src.is_none()) { - return true; // default-constructed value is already empty - } - value_conv inner_caster; - if (!inner_caster.load(src, convert)) - return false; - - value.emplace(cast_op(std::move(inner_caster))); - return true; - } - - PYBIND11_TYPE_CASTER(T, _("Optional[") + value_conv::name() + _("]")); -}; - -#if PYBIND11_HAS_OPTIONAL -template struct type_caster> - : public optional_caster> {}; - -template<> struct type_caster - : public void_caster {}; -#endif - -#if PYBIND11_HAS_EXP_OPTIONAL -template struct type_caster> - : public optional_caster> {}; - -template<> struct type_caster - : public void_caster {}; -#endif - -/// Visit a variant and cast any found type to Python -struct variant_caster_visitor { - return_value_policy policy; - handle parent; - - template - handle operator()(T &&src) const { - return make_caster::cast(std::forward(src), policy, parent); - } -}; - -/// Helper class which abstracts away variant's `visit` function. `std::variant` and similar -/// `namespace::variant` types which provide a `namespace::visit()` function are handled here -/// automatically using argument-dependent lookup. Users can provide specializations for other -/// variant-like classes, e.g. `boost::variant` and `boost::apply_visitor`. -template class Variant> -struct visit_helper { - template - static auto call(Args &&...args) -> decltype(visit(std::forward(args)...)) { - return visit(std::forward(args)...); - } -}; - -/// Generic variant caster -template struct variant_caster; - -template class V, typename... Ts> -struct variant_caster> { - static_assert(sizeof...(Ts) > 0, "Variant must consist of at least one alternative."); - - template - bool load_alternative(handle src, bool convert, type_list) { - auto caster = make_caster(); - if (caster.load(src, convert)) { - value = cast_op(caster); - return true; - } - return load_alternative(src, convert, type_list{}); - } - - bool load_alternative(handle, bool, type_list<>) { return false; } - - bool load(handle src, bool convert) { - // Do a first pass without conversions to improve constructor resolution. - // E.g. `py::int_(1).cast>()` needs to fill the `int` - // slot of the variant. Without two-pass loading `double` would be filled - // because it appears first and a conversion is possible. - if (convert && load_alternative(src, false, type_list{})) - return true; - return load_alternative(src, convert, type_list{}); - } - - template - static handle cast(Variant &&src, return_value_policy policy, handle parent) { - return visit_helper::call(variant_caster_visitor{policy, parent}, - std::forward(src)); - } - - using Type = V; - PYBIND11_TYPE_CASTER(Type, _("Union[") + detail::concat(make_caster::name()...) + _("]")); -}; - -#if PYBIND11_HAS_VARIANT -template -struct type_caster> : variant_caster> { }; -#endif -NAMESPACE_END(detail) - -inline std::ostream &operator<<(std::ostream &os, const handle &obj) { - os << (std::string) str(obj); - return os; -} - -NAMESPACE_END(pybind11) - -#if defined(_MSC_VER) -#pragma warning(pop) -#endif diff --git a/ptocr/postprocess/lanms/include/pybind11/stl_bind.h b/ptocr/postprocess/lanms/include/pybind11/stl_bind.h deleted file mode 100644 index f16e9d2..0000000 --- a/ptocr/postprocess/lanms/include/pybind11/stl_bind.h +++ /dev/null @@ -1,585 +0,0 @@ -/* - pybind11/std_bind.h: Binding generators for STL data types - - Copyright (c) 2016 Sergey Lyskov and Wenzel Jakob - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#include "common.h" -#include "operators.h" - -#include -#include - -NAMESPACE_BEGIN(pybind11) -NAMESPACE_BEGIN(detail) - -/* SFINAE helper class used by 'is_comparable */ -template struct container_traits { - template static std::true_type test_comparable(decltype(std::declval() == std::declval())*); - template static std::false_type test_comparable(...); - template static std::true_type test_value(typename T2::value_type *); - template static std::false_type test_value(...); - template static std::true_type test_pair(typename T2::first_type *, typename T2::second_type *); - template static std::false_type test_pair(...); - - static constexpr const bool is_comparable = std::is_same(nullptr))>::value; - static constexpr const bool is_pair = std::is_same(nullptr, nullptr))>::value; - static constexpr const bool is_vector = std::is_same(nullptr))>::value; - static constexpr const bool is_element = !is_pair && !is_vector; -}; - -/* Default: is_comparable -> std::false_type */ -template -struct is_comparable : std::false_type { }; - -/* For non-map data structures, check whether operator== can be instantiated */ -template -struct is_comparable< - T, enable_if_t::is_element && - container_traits::is_comparable>> - : std::true_type { }; - -/* For a vector/map data structure, recursively check the value type (which is std::pair for maps) */ -template -struct is_comparable::is_vector>> { - static constexpr const bool value = - is_comparable::value; -}; - -/* For pairs, recursively check the two data types */ -template -struct is_comparable::is_pair>> { - static constexpr const bool value = - is_comparable::value && - is_comparable::value; -}; - -/* Fallback functions */ -template void vector_if_copy_constructible(const Args &...) { } -template void vector_if_equal_operator(const Args &...) { } -template void vector_if_insertion_operator(const Args &...) { } -template void vector_modifiers(const Args &...) { } - -template -void vector_if_copy_constructible(enable_if_t::value, Class_> &cl) { - cl.def(init(), "Copy constructor"); -} - -template -void vector_if_equal_operator(enable_if_t::value, Class_> &cl) { - using T = typename Vector::value_type; - - cl.def(self == self); - cl.def(self != self); - - cl.def("count", - [](const Vector &v, const T &x) { - return std::count(v.begin(), v.end(), x); - }, - arg("x"), - "Return the number of times ``x`` appears in the list" - ); - - cl.def("remove", [](Vector &v, const T &x) { - auto p = std::find(v.begin(), v.end(), x); - if (p != v.end()) - v.erase(p); - else - throw value_error(); - }, - arg("x"), - "Remove the first item from the list whose value is x. " - "It is an error if there is no such item." - ); - - cl.def("__contains__", - [](const Vector &v, const T &x) { - return std::find(v.begin(), v.end(), x) != v.end(); - }, - arg("x"), - "Return true the container contains ``x``" - ); -} - -// Vector modifiers -- requires a copyable vector_type: -// (Technically, some of these (pop and __delitem__) don't actually require copyability, but it seems -// silly to allow deletion but not insertion, so include them here too.) -template -void vector_modifiers(enable_if_t::value, Class_> &cl) { - using T = typename Vector::value_type; - using SizeType = typename Vector::size_type; - using DiffType = typename Vector::difference_type; - - cl.def("append", - [](Vector &v, const T &value) { v.push_back(value); }, - arg("x"), - "Add an item to the end of the list"); - - cl.def("__init__", [](Vector &v, iterable it) { - new (&v) Vector(); - try { - v.reserve(len(it)); - for (handle h : it) - v.push_back(h.cast()); - } catch (...) { - v.~Vector(); - throw; - } - }); - - cl.def("extend", - [](Vector &v, const Vector &src) { - v.insert(v.end(), src.begin(), src.end()); - }, - arg("L"), - "Extend the list by appending all the items in the given list" - ); - - cl.def("insert", - [](Vector &v, SizeType i, const T &x) { - if (i > v.size()) - throw index_error(); - v.insert(v.begin() + (DiffType) i, x); - }, - arg("i") , arg("x"), - "Insert an item at a given position." - ); - - cl.def("pop", - [](Vector &v) { - if (v.empty()) - throw index_error(); - T t = v.back(); - v.pop_back(); - return t; - }, - "Remove and return the last item" - ); - - cl.def("pop", - [](Vector &v, SizeType i) { - if (i >= v.size()) - throw index_error(); - T t = v[i]; - v.erase(v.begin() + (DiffType) i); - return t; - }, - arg("i"), - "Remove and return the item at index ``i``" - ); - - cl.def("__setitem__", - [](Vector &v, SizeType i, const T &t) { - if (i >= v.size()) - throw index_error(); - v[i] = t; - } - ); - - /// Slicing protocol - cl.def("__getitem__", - [](const Vector &v, slice slice) -> Vector * { - size_t start, stop, step, slicelength; - - if (!slice.compute(v.size(), &start, &stop, &step, &slicelength)) - throw error_already_set(); - - Vector *seq = new Vector(); - seq->reserve((size_t) slicelength); - - for (size_t i=0; ipush_back(v[start]); - start += step; - } - return seq; - }, - arg("s"), - "Retrieve list elements using a slice object" - ); - - cl.def("__setitem__", - [](Vector &v, slice slice, const Vector &value) { - size_t start, stop, step, slicelength; - if (!slice.compute(v.size(), &start, &stop, &step, &slicelength)) - throw error_already_set(); - - if (slicelength != value.size()) - throw std::runtime_error("Left and right hand size of slice assignment have different sizes!"); - - for (size_t i=0; i= v.size()) - throw index_error(); - v.erase(v.begin() + DiffType(i)); - }, - "Delete the list elements at index ``i``" - ); - - cl.def("__delitem__", - [](Vector &v, slice slice) { - size_t start, stop, step, slicelength; - - if (!slice.compute(v.size(), &start, &stop, &step, &slicelength)) - throw error_already_set(); - - if (step == 1 && false) { - v.erase(v.begin() + (DiffType) start, v.begin() + DiffType(start + slicelength)); - } else { - for (size_t i = 0; i < slicelength; ++i) { - v.erase(v.begin() + DiffType(start)); - start += step - 1; - } - } - }, - "Delete list elements using a slice object" - ); - -} - -// If the type has an operator[] that doesn't return a reference (most notably std::vector), -// we have to access by copying; otherwise we return by reference. -template using vector_needs_copy = negation< - std::is_same()[typename Vector::size_type()]), typename Vector::value_type &>>; - -// The usual case: access and iterate by reference -template -void vector_accessor(enable_if_t::value, Class_> &cl) { - using T = typename Vector::value_type; - using SizeType = typename Vector::size_type; - using ItType = typename Vector::iterator; - - cl.def("__getitem__", - [](Vector &v, SizeType i) -> T & { - if (i >= v.size()) - throw index_error(); - return v[i]; - }, - return_value_policy::reference_internal // ref + keepalive - ); - - cl.def("__iter__", - [](Vector &v) { - return make_iterator< - return_value_policy::reference_internal, ItType, ItType, T&>( - v.begin(), v.end()); - }, - keep_alive<0, 1>() /* Essential: keep list alive while iterator exists */ - ); -} - -// The case for special objects, like std::vector, that have to be returned-by-copy: -template -void vector_accessor(enable_if_t::value, Class_> &cl) { - using T = typename Vector::value_type; - using SizeType = typename Vector::size_type; - using ItType = typename Vector::iterator; - cl.def("__getitem__", - [](const Vector &v, SizeType i) -> T { - if (i >= v.size()) - throw index_error(); - return v[i]; - } - ); - - cl.def("__iter__", - [](Vector &v) { - return make_iterator< - return_value_policy::copy, ItType, ItType, T>( - v.begin(), v.end()); - }, - keep_alive<0, 1>() /* Essential: keep list alive while iterator exists */ - ); -} - -template auto vector_if_insertion_operator(Class_ &cl, std::string const &name) - -> decltype(std::declval() << std::declval(), void()) { - using size_type = typename Vector::size_type; - - cl.def("__repr__", - [name](Vector &v) { - std::ostringstream s; - s << name << '['; - for (size_type i=0; i < v.size(); ++i) { - s << v[i]; - if (i != v.size() - 1) - s << ", "; - } - s << ']'; - return s.str(); - }, - "Return the canonical string representation of this list." - ); -} - -// Provide the buffer interface for vectors if we have data() and we have a format for it -// GCC seems to have "void std::vector::data()" - doing SFINAE on the existence of data() is insufficient, we need to check it returns an appropriate pointer -template -struct vector_has_data_and_format : std::false_type {}; -template -struct vector_has_data_and_format::format(), std::declval().data()), typename Vector::value_type*>::value>> : std::true_type {}; - -// Add the buffer interface to a vector -template -enable_if_t...>::value> -vector_buffer(Class_& cl) { - using T = typename Vector::value_type; - - static_assert(vector_has_data_and_format::value, "There is not an appropriate format descriptor for this vector"); - - // numpy.h declares this for arbitrary types, but it may raise an exception and crash hard at runtime if PYBIND11_NUMPY_DTYPE hasn't been called, so check here - format_descriptor::format(); - - cl.def_buffer([](Vector& v) -> buffer_info { - return buffer_info(v.data(), static_cast(sizeof(T)), format_descriptor::format(), 1, {v.size()}, {sizeof(T)}); - }); - - cl.def("__init__", [](Vector& vec, buffer buf) { - auto info = buf.request(); - if (info.ndim != 1 || info.strides[0] % static_cast(sizeof(T))) - throw type_error("Only valid 1D buffers can be copied to a vector"); - if (!detail::compare_buffer_info::compare(info) || (ssize_t) sizeof(T) != info.itemsize) - throw type_error("Format mismatch (Python: " + info.format + " C++: " + format_descriptor::format() + ")"); - new (&vec) Vector(); - vec.reserve((size_t) info.shape[0]); - T *p = static_cast(info.ptr); - ssize_t step = info.strides[0] / static_cast(sizeof(T)); - T *end = p + info.shape[0] * step; - for (; p != end; p += step) - vec.push_back(*p); - }); - - return; -} - -template -enable_if_t...>::value> vector_buffer(Class_&) {} - -NAMESPACE_END(detail) - -// -// std::vector -// -template , typename... Args> -class_ bind_vector(module &m, std::string const &name, Args&&... args) { - using Class_ = class_; - - Class_ cl(m, name.c_str(), std::forward(args)...); - - // Declare the buffer interface if a buffer_protocol() is passed in - detail::vector_buffer(cl); - - cl.def(init<>()); - - // Register copy constructor (if possible) - detail::vector_if_copy_constructible(cl); - - // Register comparison-related operators and functions (if possible) - detail::vector_if_equal_operator(cl); - - // Register stream insertion operator (if possible) - detail::vector_if_insertion_operator(cl, name); - - // Modifiers require copyable vector value type - detail::vector_modifiers(cl); - - // Accessor and iterator; return by value if copyable, otherwise we return by ref + keep-alive - detail::vector_accessor(cl); - - cl.def("__bool__", - [](const Vector &v) -> bool { - return !v.empty(); - }, - "Check whether the list is nonempty" - ); - - cl.def("__len__", &Vector::size); - - - - -#if 0 - // C++ style functions deprecated, leaving it here as an example - cl.def(init()); - - cl.def("resize", - (void (Vector::*) (size_type count)) & Vector::resize, - "changes the number of elements stored"); - - cl.def("erase", - [](Vector &v, SizeType i) { - if (i >= v.size()) - throw index_error(); - v.erase(v.begin() + i); - }, "erases element at index ``i``"); - - cl.def("empty", &Vector::empty, "checks whether the container is empty"); - cl.def("size", &Vector::size, "returns the number of elements"); - cl.def("push_back", (void (Vector::*)(const T&)) &Vector::push_back, "adds an element to the end"); - cl.def("pop_back", &Vector::pop_back, "removes the last element"); - - cl.def("max_size", &Vector::max_size, "returns the maximum possible number of elements"); - cl.def("reserve", &Vector::reserve, "reserves storage"); - cl.def("capacity", &Vector::capacity, "returns the number of elements that can be held in currently allocated storage"); - cl.def("shrink_to_fit", &Vector::shrink_to_fit, "reduces memory usage by freeing unused memory"); - - cl.def("clear", &Vector::clear, "clears the contents"); - cl.def("swap", &Vector::swap, "swaps the contents"); - - cl.def("front", [](Vector &v) { - if (v.size()) return v.front(); - else throw index_error(); - }, "access the first element"); - - cl.def("back", [](Vector &v) { - if (v.size()) return v.back(); - else throw index_error(); - }, "access the last element "); - -#endif - - return cl; -} - - - -// -// std::map, std::unordered_map -// - -NAMESPACE_BEGIN(detail) - -/* Fallback functions */ -template void map_if_insertion_operator(const Args &...) { } -template void map_assignment(const Args &...) { } - -// Map assignment when copy-assignable: just copy the value -template -void map_assignment(enable_if_t::value, Class_> &cl) { - using KeyType = typename Map::key_type; - using MappedType = typename Map::mapped_type; - - cl.def("__setitem__", - [](Map &m, const KeyType &k, const MappedType &v) { - auto it = m.find(k); - if (it != m.end()) it->second = v; - else m.emplace(k, v); - } - ); -} - -// Not copy-assignable, but still copy-constructible: we can update the value by erasing and reinserting -template -void map_assignment(enable_if_t< - !std::is_copy_assignable::value && - is_copy_constructible::value, - Class_> &cl) { - using KeyType = typename Map::key_type; - using MappedType = typename Map::mapped_type; - - cl.def("__setitem__", - [](Map &m, const KeyType &k, const MappedType &v) { - // We can't use m[k] = v; because value type might not be default constructable - auto r = m.emplace(k, v); - if (!r.second) { - // value type is not copy assignable so the only way to insert it is to erase it first... - m.erase(r.first); - m.emplace(k, v); - } - } - ); -} - - -template auto map_if_insertion_operator(Class_ &cl, std::string const &name) --> decltype(std::declval() << std::declval() << std::declval(), void()) { - - cl.def("__repr__", - [name](Map &m) { - std::ostringstream s; - s << name << '{'; - bool f = false; - for (auto const &kv : m) { - if (f) - s << ", "; - s << kv.first << ": " << kv.second; - f = true; - } - s << '}'; - return s.str(); - }, - "Return the canonical string representation of this map." - ); -} - - -NAMESPACE_END(detail) - -template , typename... Args> -class_ bind_map(module &m, const std::string &name, Args&&... args) { - using KeyType = typename Map::key_type; - using MappedType = typename Map::mapped_type; - using Class_ = class_; - - Class_ cl(m, name.c_str(), std::forward(args)...); - - cl.def(init<>()); - - // Register stream insertion operator (if possible) - detail::map_if_insertion_operator(cl, name); - - cl.def("__bool__", - [](const Map &m) -> bool { return !m.empty(); }, - "Check whether the map is nonempty" - ); - - cl.def("__iter__", - [](Map &m) { return make_key_iterator(m.begin(), m.end()); }, - keep_alive<0, 1>() /* Essential: keep list alive while iterator exists */ - ); - - cl.def("items", - [](Map &m) { return make_iterator(m.begin(), m.end()); }, - keep_alive<0, 1>() /* Essential: keep list alive while iterator exists */ - ); - - cl.def("__getitem__", - [](Map &m, const KeyType &k) -> MappedType & { - auto it = m.find(k); - if (it == m.end()) - throw key_error(); - return it->second; - }, - return_value_policy::reference_internal // ref + keepalive - ); - - // Assignment provided only if the type is copyable - detail::map_assignment(cl); - - cl.def("__delitem__", - [](Map &m, const KeyType &k) { - auto it = m.find(k); - if (it == m.end()) - throw key_error(); - return m.erase(it); - } - ); - - cl.def("__len__", &Map::size); - - return cl; -} - -NAMESPACE_END(pybind11) diff --git a/ptocr/postprocess/lanms/include/pybind11/typeid.h b/ptocr/postprocess/lanms/include/pybind11/typeid.h deleted file mode 100644 index c903fb1..0000000 --- a/ptocr/postprocess/lanms/include/pybind11/typeid.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - pybind11/typeid.h: Compiler-independent access to type identifiers - - Copyright (c) 2016 Wenzel Jakob - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#include -#include - -#if defined(__GNUG__) -#include -#endif - -NAMESPACE_BEGIN(pybind11) -NAMESPACE_BEGIN(detail) -/// Erase all occurrences of a substring -inline void erase_all(std::string &string, const std::string &search) { - for (size_t pos = 0;;) { - pos = string.find(search, pos); - if (pos == std::string::npos) break; - string.erase(pos, search.length()); - } -} - -PYBIND11_NOINLINE inline void clean_type_id(std::string &name) { -#if defined(__GNUG__) - int status = 0; - std::unique_ptr res { - abi::__cxa_demangle(name.c_str(), nullptr, nullptr, &status), std::free }; - if (status == 0) - name = res.get(); -#else - detail::erase_all(name, "class "); - detail::erase_all(name, "struct "); - detail::erase_all(name, "enum "); -#endif - detail::erase_all(name, "pybind11::"); -} -NAMESPACE_END(detail) - -/// Return a string representation of a C++ type -template static std::string type_id() { - std::string name(typeid(T).name()); - detail::clean_type_id(name); - return name; -} - -NAMESPACE_END(pybind11) diff --git a/ptocr/postprocess/lanms/lanms.h b/ptocr/postprocess/lanms/lanms.h deleted file mode 100644 index 679666c..0000000 --- a/ptocr/postprocess/lanms/lanms.h +++ /dev/null @@ -1,234 +0,0 @@ -#pragma once - -#include "clipper/clipper.hpp" - -// locality-aware NMS -namespace lanms { - - namespace cl = ClipperLib; - - struct Polygon { - cl::Path poly; - float score; - }; - - float paths_area(const ClipperLib::Paths &ps) { - float area = 0; - for (auto &&p: ps) - area += cl::Area(p); - return area; - } - - float poly_iou(const Polygon &a, const Polygon &b) { - cl::Clipper clpr; - clpr.AddPath(a.poly, cl::ptSubject, true); - clpr.AddPath(b.poly, cl::ptClip, true); - - cl::Paths inter, uni; - clpr.Execute(cl::ctIntersection, inter, cl::pftEvenOdd); - clpr.Execute(cl::ctUnion, uni, cl::pftEvenOdd); - - auto inter_area = paths_area(inter), - uni_area = paths_area(uni); - return std::abs(inter_area) / std::max(std::abs(uni_area), 1.0f); - } - - bool should_merge(const Polygon &a, const Polygon &b, float iou_threshold) { - return poly_iou(a, b) > iou_threshold; - } - - /** - * Incrementally merge polygons - */ - class PolyMerger { - public: - PolyMerger(): score(0), nr_polys(0) { - memset(data, 0, sizeof(data)); - } - - /** - * Add a new polygon to be merged. - */ - void add(const Polygon &p_given) { - Polygon p; - if (nr_polys > 0) { - // vertices of two polygons to merge may not in the same order; - // we match their vertices by choosing the ordering that - // minimizes the total squared distance. - // see function normalize_poly for details. - p = normalize_poly(get(), p_given); - } else { - p = p_given; - } - assert(p.poly.size() == 4); - auto &poly = p.poly; - auto s = p.score; - data[0] += poly[0].X * s; - data[1] += poly[0].Y * s; - - data[2] += poly[1].X * s; - data[3] += poly[1].Y * s; - - data[4] += poly[2].X * s; - data[5] += poly[2].Y * s; - - data[6] += poly[3].X * s; - data[7] += poly[3].Y * s; - - score += p.score; - - nr_polys += 1; - } - - inline std::int64_t sqr(std::int64_t x) { return x * x; } - - Polygon normalize_poly( - const Polygon &ref, - const Polygon &p) { - - std::int64_t min_d = std::numeric_limits::max(); - size_t best_start = 0, best_order = 0; - - for (size_t start = 0; start < 4; start ++) { - size_t j = start; - std::int64_t d = ( - sqr(ref.poly[(j + 0) % 4].X - p.poly[(j + 0) % 4].X) - + sqr(ref.poly[(j + 0) % 4].Y - p.poly[(j + 0) % 4].Y) - + sqr(ref.poly[(j + 1) % 4].X - p.poly[(j + 1) % 4].X) - + sqr(ref.poly[(j + 1) % 4].Y - p.poly[(j + 1) % 4].Y) - + sqr(ref.poly[(j + 2) % 4].X - p.poly[(j + 2) % 4].X) - + sqr(ref.poly[(j + 2) % 4].Y - p.poly[(j + 2) % 4].Y) - + sqr(ref.poly[(j + 3) % 4].X - p.poly[(j + 3) % 4].X) - + sqr(ref.poly[(j + 3) % 4].Y - p.poly[(j + 3) % 4].Y) - ); - if (d < min_d) { - min_d = d; - best_start = start; - best_order = 0; - } - - d = ( - sqr(ref.poly[(j + 0) % 4].X - p.poly[(j + 3) % 4].X) - + sqr(ref.poly[(j + 0) % 4].Y - p.poly[(j + 3) % 4].Y) - + sqr(ref.poly[(j + 1) % 4].X - p.poly[(j + 2) % 4].X) - + sqr(ref.poly[(j + 1) % 4].Y - p.poly[(j + 2) % 4].Y) - + sqr(ref.poly[(j + 2) % 4].X - p.poly[(j + 1) % 4].X) - + sqr(ref.poly[(j + 2) % 4].Y - p.poly[(j + 1) % 4].Y) - + sqr(ref.poly[(j + 3) % 4].X - p.poly[(j + 0) % 4].X) - + sqr(ref.poly[(j + 3) % 4].Y - p.poly[(j + 0) % 4].Y) - ); - if (d < min_d) { - min_d = d; - best_start = start; - best_order = 1; - } - } - - Polygon r; - r.poly.resize(4); - auto j = best_start; - if (best_order == 0) { - for (size_t i = 0; i < 4; i ++) - r.poly[i] = p.poly[(j + i) % 4]; - } else { - for (size_t i = 0; i < 4; i ++) - r.poly[i] = p.poly[(j + 4 - i - 1) % 4]; - } - r.score = p.score; - return r; - } - - Polygon get() const { - Polygon p; - - auto &poly = p.poly; - poly.resize(4); - auto score_inv = 1.0f / std::max(1e-8f, score); - poly[0].X = data[0] * score_inv; - poly[0].Y = data[1] * score_inv; - poly[1].X = data[2] * score_inv; - poly[1].Y = data[3] * score_inv; - poly[2].X = data[4] * score_inv; - poly[2].Y = data[5] * score_inv; - poly[3].X = data[6] * score_inv; - poly[3].Y = data[7] * score_inv; - - assert(score > 0); - p.score = score; - - return p; - } - - private: - std::int64_t data[8]; - float score; - std::int32_t nr_polys; - }; - - - /** - * The standard NMS algorithm. - */ - std::vector standard_nms(std::vector &polys, float iou_threshold) { - size_t n = polys.size(); - if (n == 0) - return {}; - std::vector indices(n); - std::iota(std::begin(indices), std::end(indices), 0); - std::sort(std::begin(indices), std::end(indices), [&](size_t i, size_t j) { return polys[i].score > polys[j].score; }); - - std::vector keep; - while (indices.size()) { - size_t p = 0, cur = indices[0]; - keep.emplace_back(cur); - for (size_t i = 1; i < indices.size(); i ++) { - if (!should_merge(polys[cur], polys[indices[i]], iou_threshold)) { - indices[p ++] = indices[i]; - } - } - indices.resize(p); - } - - std::vector ret; - for (auto &&i: keep) { - ret.emplace_back(polys[i]); - } - return ret; - } - - std::vector - merge_quadrangle_n9(const float *data, size_t n, float iou_threshold) { - using cInt = cl::cInt; - - // first pass - std::vector polys; - for (size_t i = 0; i < n; i ++) { - auto p = data + i * 9; - Polygon poly{ - { - {cInt(p[0]), cInt(p[1])}, - {cInt(p[2]), cInt(p[3])}, - {cInt(p[4]), cInt(p[5])}, - {cInt(p[6]), cInt(p[7])}, - }, - p[8], - }; - - if (polys.size()) { - // merge with the last one - auto &bpoly = polys.back(); - if (should_merge(poly, bpoly, iou_threshold)) { - PolyMerger merger; - merger.add(bpoly); - merger.add(poly); - bpoly = merger.get(); - } else { - polys.emplace_back(poly); - } - } else { - polys.emplace_back(poly); - } - } - return standard_nms(polys, iou_threshold); - } -} diff --git a/ptocr/postprocess/locality_aware_nms.py b/ptocr/postprocess/locality_aware_nms.py deleted file mode 100644 index 1220ffa..0000000 --- a/ptocr/postprocess/locality_aware_nms.py +++ /dev/null @@ -1,199 +0,0 @@ -""" -Locality aware nms. -""" - -import numpy as np -from shapely.geometry import Polygon - - -def intersection(g, p): - """ - Intersection. - """ - g = Polygon(g[:8].reshape((4, 2))) - p = Polygon(p[:8].reshape((4, 2))) - g = g.buffer(0) - p = p.buffer(0) - if not g.is_valid or not p.is_valid: - return 0 - inter = Polygon(g).intersection(Polygon(p)).area - union = g.area + p.area - inter - if union == 0: - return 0 - else: - return inter / union - - -def intersection_iog(g, p): - """ - Intersection_iog. - """ - g = Polygon(g[:8].reshape((4, 2))) - p = Polygon(p[:8].reshape((4, 2))) - if not g.is_valid or not p.is_valid: - return 0 - inter = Polygon(g).intersection(Polygon(p)).area - #union = g.area + p.area - inter - union = p.area - if union == 0: - print("p_area is very small") - return 0 - else: - return inter / union - - -def weighted_merge(g, p): - """ - Weighted merge. - """ - g[:8] = (g[8] * g[:8] + p[8] * p[:8]) / (g[8] + p[8]) - g[8] = (g[8] + p[8]) - return g - - -def standard_nms(S, thres): - """ - Standard nms. - """ - order = np.argsort(S[:, 8])[::-1] - keep = [] - while order.size > 0: - i = order[0] - keep.append(i) - ovr = np.array([intersection(S[i], S[t]) for t in order[1:]]) - - inds = np.where(ovr <= thres)[0] - order = order[inds + 1] - - return S[keep] - - -def standard_nms_inds(S, thres): - """ - Standard nms, retun inds. - """ - order = np.argsort(S[:, 8])[::-1] - keep = [] - while order.size > 0: - i = order[0] - keep.append(i) - ovr = np.array([intersection(S[i], S[t]) for t in order[1:]]) - - inds = np.where(ovr <= thres)[0] - order = order[inds + 1] - - return keep - - -def nms(S, thres): - """ - nms. - """ - order = np.argsort(S[:, 8])[::-1] - keep = [] - while order.size > 0: - i = order[0] - keep.append(i) - ovr = np.array([intersection(S[i], S[t]) for t in order[1:]]) - - inds = np.where(ovr <= thres)[0] - order = order[inds + 1] - - return keep - - -def soft_nms(boxes_in, Nt_thres=0.3, threshold=0.8, sigma=0.5, method=2): - """ - soft_nms - :para boxes_in, N x 9 (coords + score) - :para threshould, eliminate cases min score(0.001) - :para Nt_thres, iou_threshi - :para sigma, gaussian weght - :method, linear or gaussian - """ - boxes = boxes_in.copy() - N = boxes.shape[0] - if N is None or N < 1: - return np.array([]) - pos, maxpos = 0, 0 - weight = 0.0 - inds = np.arange(N) - tbox, sbox = boxes[0].copy(), boxes[0].copy() - for i in range(N): - maxscore = boxes[i, 8] - maxpos = i - tbox = boxes[i].copy() - ti = inds[i] - pos = i + 1 - #get max box - while pos < N: - if maxscore < boxes[pos, 8]: - maxscore = boxes[pos, 8] - maxpos = pos - pos = pos + 1 - #add max box as a detection - boxes[i, :] = boxes[maxpos, :] - inds[i] = inds[maxpos] - #swap - boxes[maxpos, :] = tbox - inds[maxpos] = ti - tbox = boxes[i].copy() - pos = i + 1 - #NMS iteration - while pos < N: - sbox = boxes[pos].copy() - ts_iou_val = intersection(tbox, sbox) - if ts_iou_val > 0: - if method == 1: - if ts_iou_val > Nt_thres: - weight = 1 - ts_iou_val - else: - weight = 1 - elif method == 2: - weight = np.exp(-1.0 * ts_iou_val**2 / sigma) - else: - if ts_iou_val > Nt_thres: - weight = 0 - else: - weight = 1 - boxes[pos, 8] = weight * boxes[pos, 8] - #if box score falls below thresold, discard the box by - #swaping last box update N - if boxes[pos, 8] < threshold: - boxes[pos, :] = boxes[N - 1, :] - inds[pos] = inds[N - 1] - N = N - 1 - pos = pos - 1 - pos = pos + 1 - - return boxes[:N] - - -def nms_locality(polys, thres=0.3): - """ - locality aware nms of EAST - :param polys: a N*9 numpy array. first 8 coordinates, then prob - :return: boxes after nms - """ - S = [] - p = None - for g in polys: - if p is not None and intersection(g, p) > thres: - p = weighted_merge(g, p) - else: - if p is not None: - S.append(p) - p = g - if p is not None: - S.append(p) - - if len(S) == 0: - return np.array([]) - return standard_nms(np.array(S), thres) - - -if __name__ == '__main__': - # 343,350,448,135,474,143,369,359 - print( - Polygon(np.array([[343, 350], [448, 135], [474, 143], [369, 359]])) - .area) diff --git a/ptocr/postprocess/piexlmerge/Makefile b/ptocr/postprocess/piexlmerge/Makefile deleted file mode 100644 index 7603d58..0000000 --- a/ptocr/postprocess/piexlmerge/Makefile +++ /dev/null @@ -1,15 +0,0 @@ -CXXFLAGS = -I include -std=c++11 -O3 $(shell python3-config --cflags) - -DEPS = lanms.h $(shell find include -xtype f) -CXX_SOURCES = pixelmerge.cpp include/clipper/clipper.cpp -OPENCV = `pkg-config --cflags --libs opencv` - -LIB_SO = pixelmerge.so - -$(LIB_SO): $(CXX_SOURCES) $(DEPS) - $(CXX) -o $@ $(CXXFLAGS) $(LDFLAGS) $(CXX_SOURCES) --shared -fPIC $(OPENCV) - -clean: - rm -rf $(LIB_SO) - - diff --git a/ptocr/postprocess/piexlmerge/__init__.py b/ptocr/postprocess/piexlmerge/__init__.py deleted file mode 100644 index 0e3f802..0000000 --- a/ptocr/postprocess/piexlmerge/__init__.py +++ /dev/null @@ -1,88 +0,0 @@ - -import os -import cv2 -import torch -import time -import subprocess -import numpy as np - - - -BASE_DIR = os.path.dirname(os.path.realpath(__file__)) - -if subprocess.call(['make', '-C', BASE_DIR]) != 0: # return value - raise RuntimeError('Cannot compile pse: {}'.format(BASE_DIR)) - - -def pse(outputs,config): - - from .pixelmerge import pse_cpp,get_points,get_num - - score = torch.sigmoid(outputs[:, 0, :, :]) - outputs = (torch.sign(outputs - config['postprocess']['binary_th']) + 1) / 2 - - text = outputs[:, 0, :, :] - kernels = outputs[:, 0:config['base']['classes'], :, :] * text - - score = score.data.cpu().numpy()[0].astype(np.float32) - kernels = kernels.data.cpu().numpy()[0].astype(np.uint8) - - pred = pse_cpp(kernels,config['postprocess']['min_kernel_area'] / (config['postprocess']['scale'] * config['postprocess']['scale'])) - pred = np.array(pred).astype(np.uint8) - label_num = np.max(pred) + 1 - label_points = get_points(pred, score, label_num) - - label_values = [] - for label_idx in range(1, label_num): - label_values.append(label_idx) - -# label_values = [] -# label_sum = get_num(pred, label_num) -# for label_idx in range(1, label_num): -# if label_sum[label_idx] < config['postprocess']['min_kernel_area']: -# continue -# label_values.append(label_idx) - - - return pred,label_points,label_values - - -def pan(preds, config): - - from .pixelmerge import pan_cpp, get_points, get_num - - pred = (torch.sign(preds[0, 0:2, :, :]-config['postprocess']['bin_th']) + 1) / 2 - score = torch.sigmoid(preds[0]).cpu().numpy().astype(np.float32) - text = pred[0] # text - kernel = (pred[1] * text).cpu().numpy() # kernel - text = text.cpu().numpy() - -# score = torch.sigmoid(preds[0]).cpu().numpy().astype(np.float32) -# pred_t = torch.sigmoid(preds[0, 0:2, :, :]) -# text = pred_t[0]> 0.8 -# kernel = (pred_t[1]> 0.8)* text -# text = text.cpu().numpy() -# kernel = kernel.cpu().numpy() - - similarity_vectors = preds[0,2:].permute((1, 2, 0)).cpu().numpy().astype(np.float32) - - - label_num, label = cv2.connectedComponents(kernel.astype(np.uint8), connectivity=4) - label_values = [] - label_sum = get_num(label, label_num) - for label_idx in range(1, label_num): - if label_sum[label_idx] < config['postprocess']['min_kernel_area']: - continue - label_values.append(label_idx) - - pred = pan_cpp(text.astype(np.uint8), similarity_vectors, label, label_num, config['postprocess']['dis_thresh']) - pred = pred.reshape(text.shape) - - label_points = get_points(pred, score, label_num) - - return pred,label_points,label_values - - - - - diff --git a/ptocr/postprocess/piexlmerge/include/clipper/clipper.cpp b/ptocr/postprocess/piexlmerge/include/clipper/clipper.cpp deleted file mode 100644 index 4993ba9..0000000 --- a/ptocr/postprocess/piexlmerge/include/clipper/clipper.cpp +++ /dev/null @@ -1,4622 +0,0 @@ -/******************************************************************************* -* * -* Author : Angus Johnson * -* Version : 6.4.0 * -* Date : 2 July 2015 * -* Website : http://www.angusj.com * -* Copyright : Angus Johnson 2010-2015 * -* * -* License: * -* Use, modification & distribution is subject to Boost Software License Ver 1. * -* http://www.boost.org/LICENSE_1_0.txt * -* * -* Attributions: * -* The code in this library is an extension of Bala Vatti's clipping algorithm: * -* "A generic solution to polygon clipping" * -* Communications of the ACM, Vol 35, Issue 7 (July 1992) pp 56-63. * -* http://portal.acm.org/citation.cfm?id=129906 * -* * -* Computer graphics and geometric modeling: implementation and algorithms * -* By Max K. Agoston * -* Springer; 1 edition (January 4, 2005) * -* http://books.google.com/books?q=vatti+clipping+agoston * -* * -* See also: * -* "Polygon Offsetting by Computing Winding Numbers" * -* Paper no. DETC2005-85513 pp. 565-575 * -* ASME 2005 International Design Engineering Technical Conferences * -* and Computers and Information in Engineering Conference (IDETC/CIE2005) * -* September 24-28, 2005 , Long Beach, California, USA * -* http://www.me.berkeley.edu/~mcmains/pubs/DAC05OffsetPolygon.pdf * -* * -*******************************************************************************/ - -/******************************************************************************* -* * -* This is a translation of the Delphi Clipper library and the naming style * -* used has retained a Delphi flavour. * -* * -*******************************************************************************/ - -#include "clipper.hpp" -#include -#include -#include -#include -#include -#include -#include -#include - -namespace ClipperLib { - -static double const pi = 3.141592653589793238; -static double const two_pi = pi *2; -static double const def_arc_tolerance = 0.25; - -enum Direction { dRightToLeft, dLeftToRight }; - -static int const Unassigned = -1; //edge not currently 'owning' a solution -static int const Skip = -2; //edge that would otherwise close a path - -#define HORIZONTAL (-1.0E+40) -#define TOLERANCE (1.0e-20) -#define NEAR_ZERO(val) (((val) > -TOLERANCE) && ((val) < TOLERANCE)) - -struct TEdge { - IntPoint Bot; - IntPoint Curr; //current (updated for every new scanbeam) - IntPoint Top; - double Dx; - PolyType PolyTyp; - EdgeSide Side; //side only refers to current side of solution poly - int WindDelta; //1 or -1 depending on winding direction - int WindCnt; - int WindCnt2; //winding count of the opposite polytype - int OutIdx; - TEdge *Next; - TEdge *Prev; - TEdge *NextInLML; - TEdge *NextInAEL; - TEdge *PrevInAEL; - TEdge *NextInSEL; - TEdge *PrevInSEL; -}; - -struct IntersectNode { - TEdge *Edge1; - TEdge *Edge2; - IntPoint Pt; -}; - -struct LocalMinimum { - cInt Y; - TEdge *LeftBound; - TEdge *RightBound; -}; - -struct OutPt; - -//OutRec: contains a path in the clipping solution. Edges in the AEL will -//carry a pointer to an OutRec when they are part of the clipping solution. -struct OutRec { - int Idx; - bool IsHole; - bool IsOpen; - OutRec *FirstLeft; //see comments in clipper.pas - PolyNode *PolyNd; - OutPt *Pts; - OutPt *BottomPt; -}; - -struct OutPt { - int Idx; - IntPoint Pt; - OutPt *Next; - OutPt *Prev; -}; - -struct Join { - OutPt *OutPt1; - OutPt *OutPt2; - IntPoint OffPt; -}; - -struct LocMinSorter -{ - inline bool operator()(const LocalMinimum& locMin1, const LocalMinimum& locMin2) - { - return locMin2.Y < locMin1.Y; - } -}; - -//------------------------------------------------------------------------------ -//------------------------------------------------------------------------------ - -inline cInt Round(double val) -{ - if ((val < 0)) return static_cast(val - 0.5); - else return static_cast(val + 0.5); -} -//------------------------------------------------------------------------------ - -inline cInt Abs(cInt val) -{ - return val < 0 ? -val : val; -} - -//------------------------------------------------------------------------------ -// PolyTree methods ... -//------------------------------------------------------------------------------ - -void PolyTree::Clear() -{ - for (PolyNodes::size_type i = 0; i < AllNodes.size(); ++i) - delete AllNodes[i]; - AllNodes.resize(0); - Childs.resize(0); -} -//------------------------------------------------------------------------------ - -PolyNode* PolyTree::GetFirst() const -{ - if (!Childs.empty()) - return Childs[0]; - else - return 0; -} -//------------------------------------------------------------------------------ - -int PolyTree::Total() const -{ - int result = (int)AllNodes.size(); - //with negative offsets, ignore the hidden outer polygon ... - if (result > 0 && Childs[0] != AllNodes[0]) result--; - return result; -} - -//------------------------------------------------------------------------------ -// PolyNode methods ... -//------------------------------------------------------------------------------ - -PolyNode::PolyNode(): Childs(), Parent(0), Index(0), m_IsOpen(false) -{ -} -//------------------------------------------------------------------------------ - -int PolyNode::ChildCount() const -{ - return (int)Childs.size(); -} -//------------------------------------------------------------------------------ - -void PolyNode::AddChild(PolyNode& child) -{ - unsigned cnt = (unsigned)Childs.size(); - Childs.push_back(&child); - child.Parent = this; - child.Index = cnt; -} -//------------------------------------------------------------------------------ - -PolyNode* PolyNode::GetNext() const -{ - if (!Childs.empty()) - return Childs[0]; - else - return GetNextSiblingUp(); -} -//------------------------------------------------------------------------------ - -PolyNode* PolyNode::GetNextSiblingUp() const -{ - if (!Parent) //protects against PolyTree.GetNextSiblingUp() - return 0; - else if (Index == Parent->Childs.size() - 1) - return Parent->GetNextSiblingUp(); - else - return Parent->Childs[Index + 1]; -} -//------------------------------------------------------------------------------ - -bool PolyNode::IsHole() const -{ - bool result = true; - PolyNode* node = Parent; - while (node) - { - result = !result; - node = node->Parent; - } - return result; -} -//------------------------------------------------------------------------------ - -bool PolyNode::IsOpen() const -{ - return m_IsOpen; -} -//------------------------------------------------------------------------------ - -#ifndef use_int32 - -//------------------------------------------------------------------------------ -// Int128 class (enables safe math on signed 64bit integers) -// eg Int128 val1((long64)9223372036854775807); //ie 2^63 -1 -// Int128 val2((long64)9223372036854775807); -// Int128 val3 = val1 * val2; -// val3.AsString => "85070591730234615847396907784232501249" (8.5e+37) -//------------------------------------------------------------------------------ - -class Int128 -{ - public: - ulong64 lo; - long64 hi; - - Int128(long64 _lo = 0) - { - lo = (ulong64)_lo; - if (_lo < 0) hi = -1; else hi = 0; - } - - - Int128(const Int128 &val): lo(val.lo), hi(val.hi){} - - Int128(const long64& _hi, const ulong64& _lo): lo(_lo), hi(_hi){} - - Int128& operator = (const long64 &val) - { - lo = (ulong64)val; - if (val < 0) hi = -1; else hi = 0; - return *this; - } - - bool operator == (const Int128 &val) const - {return (hi == val.hi && lo == val.lo);} - - bool operator != (const Int128 &val) const - { return !(*this == val);} - - bool operator > (const Int128 &val) const - { - if (hi != val.hi) - return hi > val.hi; - else - return lo > val.lo; - } - - bool operator < (const Int128 &val) const - { - if (hi != val.hi) - return hi < val.hi; - else - return lo < val.lo; - } - - bool operator >= (const Int128 &val) const - { return !(*this < val);} - - bool operator <= (const Int128 &val) const - { return !(*this > val);} - - Int128& operator += (const Int128 &rhs) - { - hi += rhs.hi; - lo += rhs.lo; - if (lo < rhs.lo) hi++; - return *this; - } - - Int128 operator + (const Int128 &rhs) const - { - Int128 result(*this); - result+= rhs; - return result; - } - - Int128& operator -= (const Int128 &rhs) - { - *this += -rhs; - return *this; - } - - Int128 operator - (const Int128 &rhs) const - { - Int128 result(*this); - result -= rhs; - return result; - } - - Int128 operator-() const //unary negation - { - if (lo == 0) - return Int128(-hi, 0); - else - return Int128(~hi, ~lo + 1); - } - - operator double() const - { - const double shift64 = 18446744073709551616.0; //2^64 - if (hi < 0) - { - if (lo == 0) return (double)hi * shift64; - else return -(double)(~lo + ~hi * shift64); - } - else - return (double)(lo + hi * shift64); - } - -}; -//------------------------------------------------------------------------------ - -Int128 Int128Mul (long64 lhs, long64 rhs) -{ - bool negate = (lhs < 0) != (rhs < 0); - - if (lhs < 0) lhs = -lhs; - ulong64 int1Hi = ulong64(lhs) >> 32; - ulong64 int1Lo = ulong64(lhs & 0xFFFFFFFF); - - if (rhs < 0) rhs = -rhs; - ulong64 int2Hi = ulong64(rhs) >> 32; - ulong64 int2Lo = ulong64(rhs & 0xFFFFFFFF); - - //nb: see comments in clipper.pas - ulong64 a = int1Hi * int2Hi; - ulong64 b = int1Lo * int2Lo; - ulong64 c = int1Hi * int2Lo + int1Lo * int2Hi; - - Int128 tmp; - tmp.hi = long64(a + (c >> 32)); - tmp.lo = long64(c << 32); - tmp.lo += long64(b); - if (tmp.lo < b) tmp.hi++; - if (negate) tmp = -tmp; - return tmp; -}; -#endif - -//------------------------------------------------------------------------------ -// Miscellaneous global functions -//------------------------------------------------------------------------------ - -bool Orientation(const Path &poly) -{ - return Area(poly) >= 0; -} -//------------------------------------------------------------------------------ - -double Area(const Path &poly) -{ - int size = (int)poly.size(); - if (size < 3) return 0; - - double a = 0; - for (int i = 0, j = size -1; i < size; ++i) - { - a += ((double)poly[j].X + poly[i].X) * ((double)poly[j].Y - poly[i].Y); - j = i; - } - return -a * 0.5; -} -//------------------------------------------------------------------------------ - -double Area(const OutPt *op) -{ - const OutPt *startOp = op; - if (!op) return 0; - double a = 0; - do { - a += (double)(op->Prev->Pt.X + op->Pt.X) * (double)(op->Prev->Pt.Y - op->Pt.Y); - op = op->Next; - } while (op != startOp); - return a * 0.5; -} -//------------------------------------------------------------------------------ - -double Area(const OutRec &outRec) -{ - return Area(outRec.Pts); -} -//------------------------------------------------------------------------------ - -bool PointIsVertex(const IntPoint &Pt, OutPt *pp) -{ - OutPt *pp2 = pp; - do - { - if (pp2->Pt == Pt) return true; - pp2 = pp2->Next; - } - while (pp2 != pp); - return false; -} -//------------------------------------------------------------------------------ - -//See "The Point in Polygon Problem for Arbitrary Polygons" by Hormann & Agathos -//http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.88.5498&rep=rep1&type=pdf -int PointInPolygon(const IntPoint &pt, const Path &path) -{ - //returns 0 if false, +1 if true, -1 if pt ON polygon boundary - int result = 0; - size_t cnt = path.size(); - if (cnt < 3) return 0; - IntPoint ip = path[0]; - for(size_t i = 1; i <= cnt; ++i) - { - IntPoint ipNext = (i == cnt ? path[0] : path[i]); - if (ipNext.Y == pt.Y) - { - if ((ipNext.X == pt.X) || (ip.Y == pt.Y && - ((ipNext.X > pt.X) == (ip.X < pt.X)))) return -1; - } - if ((ip.Y < pt.Y) != (ipNext.Y < pt.Y)) - { - if (ip.X >= pt.X) - { - if (ipNext.X > pt.X) result = 1 - result; - else - { - double d = (double)(ip.X - pt.X) * (ipNext.Y - pt.Y) - - (double)(ipNext.X - pt.X) * (ip.Y - pt.Y); - if (!d) return -1; - if ((d > 0) == (ipNext.Y > ip.Y)) result = 1 - result; - } - } else - { - if (ipNext.X > pt.X) - { - double d = (double)(ip.X - pt.X) * (ipNext.Y - pt.Y) - - (double)(ipNext.X - pt.X) * (ip.Y - pt.Y); - if (!d) return -1; - if ((d > 0) == (ipNext.Y > ip.Y)) result = 1 - result; - } - } - } - ip = ipNext; - } - return result; -} -//------------------------------------------------------------------------------ - -int PointInPolygon (const IntPoint &pt, OutPt *op) -{ - //returns 0 if false, +1 if true, -1 if pt ON polygon boundary - int result = 0; - OutPt* startOp = op; - for(;;) - { - if (op->Next->Pt.Y == pt.Y) - { - if ((op->Next->Pt.X == pt.X) || (op->Pt.Y == pt.Y && - ((op->Next->Pt.X > pt.X) == (op->Pt.X < pt.X)))) return -1; - } - if ((op->Pt.Y < pt.Y) != (op->Next->Pt.Y < pt.Y)) - { - if (op->Pt.X >= pt.X) - { - if (op->Next->Pt.X > pt.X) result = 1 - result; - else - { - double d = (double)(op->Pt.X - pt.X) * (op->Next->Pt.Y - pt.Y) - - (double)(op->Next->Pt.X - pt.X) * (op->Pt.Y - pt.Y); - if (!d) return -1; - if ((d > 0) == (op->Next->Pt.Y > op->Pt.Y)) result = 1 - result; - } - } else - { - if (op->Next->Pt.X > pt.X) - { - double d = (double)(op->Pt.X - pt.X) * (op->Next->Pt.Y - pt.Y) - - (double)(op->Next->Pt.X - pt.X) * (op->Pt.Y - pt.Y); - if (!d) return -1; - if ((d > 0) == (op->Next->Pt.Y > op->Pt.Y)) result = 1 - result; - } - } - } - op = op->Next; - if (startOp == op) break; - } - return result; -} -//------------------------------------------------------------------------------ - -bool Poly2ContainsPoly1(OutPt *OutPt1, OutPt *OutPt2) -{ - OutPt* op = OutPt1; - do - { - //nb: PointInPolygon returns 0 if false, +1 if true, -1 if pt on polygon - int res = PointInPolygon(op->Pt, OutPt2); - if (res >= 0) return res > 0; - op = op->Next; - } - while (op != OutPt1); - return true; -} -//---------------------------------------------------------------------- - -bool SlopesEqual(const TEdge &e1, const TEdge &e2, bool UseFullInt64Range) -{ -#ifndef use_int32 - if (UseFullInt64Range) - return Int128Mul(e1.Top.Y - e1.Bot.Y, e2.Top.X - e2.Bot.X) == - Int128Mul(e1.Top.X - e1.Bot.X, e2.Top.Y - e2.Bot.Y); - else -#endif - return (e1.Top.Y - e1.Bot.Y) * (e2.Top.X - e2.Bot.X) == - (e1.Top.X - e1.Bot.X) * (e2.Top.Y - e2.Bot.Y); -} -//------------------------------------------------------------------------------ - -bool SlopesEqual(const IntPoint pt1, const IntPoint pt2, - const IntPoint pt3, bool UseFullInt64Range) -{ -#ifndef use_int32 - if (UseFullInt64Range) - return Int128Mul(pt1.Y-pt2.Y, pt2.X-pt3.X) == Int128Mul(pt1.X-pt2.X, pt2.Y-pt3.Y); - else -#endif - return (pt1.Y-pt2.Y)*(pt2.X-pt3.X) == (pt1.X-pt2.X)*(pt2.Y-pt3.Y); -} -//------------------------------------------------------------------------------ - -bool SlopesEqual(const IntPoint pt1, const IntPoint pt2, - const IntPoint pt3, const IntPoint pt4, bool UseFullInt64Range) -{ -#ifndef use_int32 - if (UseFullInt64Range) - return Int128Mul(pt1.Y-pt2.Y, pt3.X-pt4.X) == Int128Mul(pt1.X-pt2.X, pt3.Y-pt4.Y); - else -#endif - return (pt1.Y-pt2.Y)*(pt3.X-pt4.X) == (pt1.X-pt2.X)*(pt3.Y-pt4.Y); -} -//------------------------------------------------------------------------------ - -inline bool IsHorizontal(TEdge &e) -{ - return e.Dx == HORIZONTAL; -} -//------------------------------------------------------------------------------ - -inline double GetDx(const IntPoint pt1, const IntPoint pt2) -{ - return (pt1.Y == pt2.Y) ? - HORIZONTAL : (double)(pt2.X - pt1.X) / (pt2.Y - pt1.Y); -} -//--------------------------------------------------------------------------- - -inline void SetDx(TEdge &e) -{ - cInt dy = (e.Top.Y - e.Bot.Y); - if (dy == 0) e.Dx = HORIZONTAL; - else e.Dx = (double)(e.Top.X - e.Bot.X) / dy; -} -//--------------------------------------------------------------------------- - -inline void SwapSides(TEdge &Edge1, TEdge &Edge2) -{ - EdgeSide Side = Edge1.Side; - Edge1.Side = Edge2.Side; - Edge2.Side = Side; -} -//------------------------------------------------------------------------------ - -inline void SwapPolyIndexes(TEdge &Edge1, TEdge &Edge2) -{ - int OutIdx = Edge1.OutIdx; - Edge1.OutIdx = Edge2.OutIdx; - Edge2.OutIdx = OutIdx; -} -//------------------------------------------------------------------------------ - -inline cInt TopX(TEdge &edge, const cInt currentY) -{ - return ( currentY == edge.Top.Y ) ? - edge.Top.X : edge.Bot.X + Round(edge.Dx *(currentY - edge.Bot.Y)); -} -//------------------------------------------------------------------------------ - -void IntersectPoint(TEdge &Edge1, TEdge &Edge2, IntPoint &ip) -{ -#ifdef use_xyz - ip.Z = 0; -#endif - - double b1, b2; - if (Edge1.Dx == Edge2.Dx) - { - ip.Y = Edge1.Curr.Y; - ip.X = TopX(Edge1, ip.Y); - return; - } - else if (Edge1.Dx == 0) - { - ip.X = Edge1.Bot.X; - if (IsHorizontal(Edge2)) - ip.Y = Edge2.Bot.Y; - else - { - b2 = Edge2.Bot.Y - (Edge2.Bot.X / Edge2.Dx); - ip.Y = Round(ip.X / Edge2.Dx + b2); - } - } - else if (Edge2.Dx == 0) - { - ip.X = Edge2.Bot.X; - if (IsHorizontal(Edge1)) - ip.Y = Edge1.Bot.Y; - else - { - b1 = Edge1.Bot.Y - (Edge1.Bot.X / Edge1.Dx); - ip.Y = Round(ip.X / Edge1.Dx + b1); - } - } - else - { - b1 = Edge1.Bot.X - Edge1.Bot.Y * Edge1.Dx; - b2 = Edge2.Bot.X - Edge2.Bot.Y * Edge2.Dx; - double q = (b2-b1) / (Edge1.Dx - Edge2.Dx); - ip.Y = Round(q); - if (std::fabs(Edge1.Dx) < std::fabs(Edge2.Dx)) - ip.X = Round(Edge1.Dx * q + b1); - else - ip.X = Round(Edge2.Dx * q + b2); - } - - if (ip.Y < Edge1.Top.Y || ip.Y < Edge2.Top.Y) - { - if (Edge1.Top.Y > Edge2.Top.Y) - ip.Y = Edge1.Top.Y; - else - ip.Y = Edge2.Top.Y; - if (std::fabs(Edge1.Dx) < std::fabs(Edge2.Dx)) - ip.X = TopX(Edge1, ip.Y); - else - ip.X = TopX(Edge2, ip.Y); - } - //finally, don't allow 'ip' to be BELOW curr.Y (ie bottom of scanbeam) ... - if (ip.Y > Edge1.Curr.Y) - { - ip.Y = Edge1.Curr.Y; - //use the more vertical edge to derive X ... - if (std::fabs(Edge1.Dx) > std::fabs(Edge2.Dx)) - ip.X = TopX(Edge2, ip.Y); else - ip.X = TopX(Edge1, ip.Y); - } -} -//------------------------------------------------------------------------------ - -void ReversePolyPtLinks(OutPt *pp) -{ - if (!pp) return; - OutPt *pp1, *pp2; - pp1 = pp; - do { - pp2 = pp1->Next; - pp1->Next = pp1->Prev; - pp1->Prev = pp2; - pp1 = pp2; - } while( pp1 != pp ); -} -//------------------------------------------------------------------------------ - -void DisposeOutPts(OutPt*& pp) -{ - if (pp == 0) return; - pp->Prev->Next = 0; - while( pp ) - { - OutPt *tmpPp = pp; - pp = pp->Next; - delete tmpPp; - } -} -//------------------------------------------------------------------------------ - -inline void InitEdge(TEdge* e, TEdge* eNext, TEdge* ePrev, const IntPoint& Pt) -{ - std::memset(e, 0, sizeof(TEdge)); - e->Next = eNext; - e->Prev = ePrev; - e->Curr = Pt; - e->OutIdx = Unassigned; -} -//------------------------------------------------------------------------------ - -void InitEdge2(TEdge& e, PolyType Pt) -{ - if (e.Curr.Y >= e.Next->Curr.Y) - { - e.Bot = e.Curr; - e.Top = e.Next->Curr; - } else - { - e.Top = e.Curr; - e.Bot = e.Next->Curr; - } - SetDx(e); - e.PolyTyp = Pt; -} -//------------------------------------------------------------------------------ - -TEdge* RemoveEdge(TEdge* e) -{ - //removes e from double_linked_list (but without removing from memory) - e->Prev->Next = e->Next; - e->Next->Prev = e->Prev; - TEdge* result = e->Next; - e->Prev = 0; //flag as removed (see ClipperBase.Clear) - return result; -} -//------------------------------------------------------------------------------ - -inline void ReverseHorizontal(TEdge &e) -{ - //swap horizontal edges' Top and Bottom x's so they follow the natural - //progression of the bounds - ie so their xbots will align with the - //adjoining lower edge. [Helpful in the ProcessHorizontal() method.] - std::swap(e.Top.X, e.Bot.X); -#ifdef use_xyz - std::swap(e.Top.Z, e.Bot.Z); -#endif -} -//------------------------------------------------------------------------------ - -void SwapPoints(IntPoint &pt1, IntPoint &pt2) -{ - IntPoint tmp = pt1; - pt1 = pt2; - pt2 = tmp; -} -//------------------------------------------------------------------------------ - -bool GetOverlapSegment(IntPoint pt1a, IntPoint pt1b, IntPoint pt2a, - IntPoint pt2b, IntPoint &pt1, IntPoint &pt2) -{ - //precondition: segments are Collinear. - if (Abs(pt1a.X - pt1b.X) > Abs(pt1a.Y - pt1b.Y)) - { - if (pt1a.X > pt1b.X) SwapPoints(pt1a, pt1b); - if (pt2a.X > pt2b.X) SwapPoints(pt2a, pt2b); - if (pt1a.X > pt2a.X) pt1 = pt1a; else pt1 = pt2a; - if (pt1b.X < pt2b.X) pt2 = pt1b; else pt2 = pt2b; - return pt1.X < pt2.X; - } else - { - if (pt1a.Y < pt1b.Y) SwapPoints(pt1a, pt1b); - if (pt2a.Y < pt2b.Y) SwapPoints(pt2a, pt2b); - if (pt1a.Y < pt2a.Y) pt1 = pt1a; else pt1 = pt2a; - if (pt1b.Y > pt2b.Y) pt2 = pt1b; else pt2 = pt2b; - return pt1.Y > pt2.Y; - } -} -//------------------------------------------------------------------------------ - -bool FirstIsBottomPt(const OutPt* btmPt1, const OutPt* btmPt2) -{ - OutPt *p = btmPt1->Prev; - while ((p->Pt == btmPt1->Pt) && (p != btmPt1)) p = p->Prev; - double dx1p = std::fabs(GetDx(btmPt1->Pt, p->Pt)); - p = btmPt1->Next; - while ((p->Pt == btmPt1->Pt) && (p != btmPt1)) p = p->Next; - double dx1n = std::fabs(GetDx(btmPt1->Pt, p->Pt)); - - p = btmPt2->Prev; - while ((p->Pt == btmPt2->Pt) && (p != btmPt2)) p = p->Prev; - double dx2p = std::fabs(GetDx(btmPt2->Pt, p->Pt)); - p = btmPt2->Next; - while ((p->Pt == btmPt2->Pt) && (p != btmPt2)) p = p->Next; - double dx2n = std::fabs(GetDx(btmPt2->Pt, p->Pt)); - - if (std::max(dx1p, dx1n) == std::max(dx2p, dx2n) && - std::min(dx1p, dx1n) == std::min(dx2p, dx2n)) - return Area(btmPt1) > 0; //if otherwise identical use orientation - else - return (dx1p >= dx2p && dx1p >= dx2n) || (dx1n >= dx2p && dx1n >= dx2n); -} -//------------------------------------------------------------------------------ - -OutPt* GetBottomPt(OutPt *pp) -{ - OutPt* dups = 0; - OutPt* p = pp->Next; - while (p != pp) - { - if (p->Pt.Y > pp->Pt.Y) - { - pp = p; - dups = 0; - } - else if (p->Pt.Y == pp->Pt.Y && p->Pt.X <= pp->Pt.X) - { - if (p->Pt.X < pp->Pt.X) - { - dups = 0; - pp = p; - } else - { - if (p->Next != pp && p->Prev != pp) dups = p; - } - } - p = p->Next; - } - if (dups) - { - //there appears to be at least 2 vertices at BottomPt so ... - while (dups != p) - { - if (!FirstIsBottomPt(p, dups)) pp = dups; - dups = dups->Next; - while (dups->Pt != pp->Pt) dups = dups->Next; - } - } - return pp; -} -//------------------------------------------------------------------------------ - -bool Pt2IsBetweenPt1AndPt3(const IntPoint pt1, - const IntPoint pt2, const IntPoint pt3) -{ - if ((pt1 == pt3) || (pt1 == pt2) || (pt3 == pt2)) - return false; - else if (pt1.X != pt3.X) - return (pt2.X > pt1.X) == (pt2.X < pt3.X); - else - return (pt2.Y > pt1.Y) == (pt2.Y < pt3.Y); -} -//------------------------------------------------------------------------------ - -bool HorzSegmentsOverlap(cInt seg1a, cInt seg1b, cInt seg2a, cInt seg2b) -{ - if (seg1a > seg1b) std::swap(seg1a, seg1b); - if (seg2a > seg2b) std::swap(seg2a, seg2b); - return (seg1a < seg2b) && (seg2a < seg1b); -} - -//------------------------------------------------------------------------------ -// ClipperBase class methods ... -//------------------------------------------------------------------------------ - -ClipperBase::ClipperBase() //constructor -{ - m_CurrentLM = m_MinimaList.begin(); //begin() == end() here - m_UseFullRange = false; -} -//------------------------------------------------------------------------------ - -ClipperBase::~ClipperBase() //destructor -{ - Clear(); -} -//------------------------------------------------------------------------------ - -void RangeTest(const IntPoint& Pt, bool& useFullRange) -{ - if (useFullRange) - { - if (Pt.X > hiRange || Pt.Y > hiRange || -Pt.X > hiRange || -Pt.Y > hiRange) - throw clipperException("Coordinate outside allowed range"); - } - else if (Pt.X > loRange|| Pt.Y > loRange || -Pt.X > loRange || -Pt.Y > loRange) - { - useFullRange = true; - RangeTest(Pt, useFullRange); - } -} -//------------------------------------------------------------------------------ - -TEdge* FindNextLocMin(TEdge* E) -{ - for (;;) - { - while (E->Bot != E->Prev->Bot || E->Curr == E->Top) E = E->Next; - if (!IsHorizontal(*E) && !IsHorizontal(*E->Prev)) break; - while (IsHorizontal(*E->Prev)) E = E->Prev; - TEdge* E2 = E; - while (IsHorizontal(*E)) E = E->Next; - if (E->Top.Y == E->Prev->Bot.Y) continue; //ie just an intermediate horz. - if (E2->Prev->Bot.X < E->Bot.X) E = E2; - break; - } - return E; -} -//------------------------------------------------------------------------------ - -TEdge* ClipperBase::ProcessBound(TEdge* E, bool NextIsForward) -{ - TEdge *Result = E; - TEdge *Horz = 0; - - if (E->OutIdx == Skip) - { - //if edges still remain in the current bound beyond the skip edge then - //create another LocMin and call ProcessBound once more - if (NextIsForward) - { - while (E->Top.Y == E->Next->Bot.Y) E = E->Next; - //don't include top horizontals when parsing a bound a second time, - //they will be contained in the opposite bound ... - while (E != Result && IsHorizontal(*E)) E = E->Prev; - } - else - { - while (E->Top.Y == E->Prev->Bot.Y) E = E->Prev; - while (E != Result && IsHorizontal(*E)) E = E->Next; - } - - if (E == Result) - { - if (NextIsForward) Result = E->Next; - else Result = E->Prev; - } - else - { - //there are more edges in the bound beyond result starting with E - if (NextIsForward) - E = Result->Next; - else - E = Result->Prev; - MinimaList::value_type locMin; - locMin.Y = E->Bot.Y; - locMin.LeftBound = 0; - locMin.RightBound = E; - E->WindDelta = 0; - Result = ProcessBound(E, NextIsForward); - m_MinimaList.push_back(locMin); - } - return Result; - } - - TEdge *EStart; - - if (IsHorizontal(*E)) - { - //We need to be careful with open paths because this may not be a - //true local minima (ie E may be following a skip edge). - //Also, consecutive horz. edges may start heading left before going right. - if (NextIsForward) - EStart = E->Prev; - else - EStart = E->Next; - if (IsHorizontal(*EStart)) //ie an adjoining horizontal skip edge - { - if (EStart->Bot.X != E->Bot.X && EStart->Top.X != E->Bot.X) - ReverseHorizontal(*E); - } - else if (EStart->Bot.X != E->Bot.X) - ReverseHorizontal(*E); - } - - EStart = E; - if (NextIsForward) - { - while (Result->Top.Y == Result->Next->Bot.Y && Result->Next->OutIdx != Skip) - Result = Result->Next; - if (IsHorizontal(*Result) && Result->Next->OutIdx != Skip) - { - //nb: at the top of a bound, horizontals are added to the bound - //only when the preceding edge attaches to the horizontal's left vertex - //unless a Skip edge is encountered when that becomes the top divide - Horz = Result; - while (IsHorizontal(*Horz->Prev)) Horz = Horz->Prev; - if (Horz->Prev->Top.X > Result->Next->Top.X) Result = Horz->Prev; - } - while (E != Result) - { - E->NextInLML = E->Next; - if (IsHorizontal(*E) && E != EStart && - E->Bot.X != E->Prev->Top.X) ReverseHorizontal(*E); - E = E->Next; - } - if (IsHorizontal(*E) && E != EStart && E->Bot.X != E->Prev->Top.X) - ReverseHorizontal(*E); - Result = Result->Next; //move to the edge just beyond current bound - } else - { - while (Result->Top.Y == Result->Prev->Bot.Y && Result->Prev->OutIdx != Skip) - Result = Result->Prev; - if (IsHorizontal(*Result) && Result->Prev->OutIdx != Skip) - { - Horz = Result; - while (IsHorizontal(*Horz->Next)) Horz = Horz->Next; - if (Horz->Next->Top.X == Result->Prev->Top.X || - Horz->Next->Top.X > Result->Prev->Top.X) Result = Horz->Next; - } - - while (E != Result) - { - E->NextInLML = E->Prev; - if (IsHorizontal(*E) && E != EStart && E->Bot.X != E->Next->Top.X) - ReverseHorizontal(*E); - E = E->Prev; - } - if (IsHorizontal(*E) && E != EStart && E->Bot.X != E->Next->Top.X) - ReverseHorizontal(*E); - Result = Result->Prev; //move to the edge just beyond current bound - } - - return Result; -} -//------------------------------------------------------------------------------ - -bool ClipperBase::AddPath(const Path &pg, PolyType PolyTyp, bool Closed) -{ -#ifdef use_lines - if (!Closed && PolyTyp == ptClip) - throw clipperException("AddPath: Open paths must be subject."); -#else - if (!Closed) - throw clipperException("AddPath: Open paths have been disabled."); -#endif - - int highI = (int)pg.size() -1; - if (Closed) while (highI > 0 && (pg[highI] == pg[0])) --highI; - while (highI > 0 && (pg[highI] == pg[highI -1])) --highI; - if ((Closed && highI < 2) || (!Closed && highI < 1)) return false; - - //create a new edge array ... - TEdge *edges = new TEdge [highI +1]; - - bool IsFlat = true; - //1. Basic (first) edge initialization ... - try - { - edges[1].Curr = pg[1]; - RangeTest(pg[0], m_UseFullRange); - RangeTest(pg[highI], m_UseFullRange); - InitEdge(&edges[0], &edges[1], &edges[highI], pg[0]); - InitEdge(&edges[highI], &edges[0], &edges[highI-1], pg[highI]); - for (int i = highI - 1; i >= 1; --i) - { - RangeTest(pg[i], m_UseFullRange); - InitEdge(&edges[i], &edges[i+1], &edges[i-1], pg[i]); - } - } - catch(...) - { - delete [] edges; - throw; //range test fails - } - TEdge *eStart = &edges[0]; - - //2. Remove duplicate vertices, and (when closed) collinear edges ... - TEdge *E = eStart, *eLoopStop = eStart; - for (;;) - { - //nb: allows matching start and end points when not Closed ... - if (E->Curr == E->Next->Curr && (Closed || E->Next != eStart)) - { - if (E == E->Next) break; - if (E == eStart) eStart = E->Next; - E = RemoveEdge(E); - eLoopStop = E; - continue; - } - if (E->Prev == E->Next) - break; //only two vertices - else if (Closed && - SlopesEqual(E->Prev->Curr, E->Curr, E->Next->Curr, m_UseFullRange) && - (!m_PreserveCollinear || - !Pt2IsBetweenPt1AndPt3(E->Prev->Curr, E->Curr, E->Next->Curr))) - { - //Collinear edges are allowed for open paths but in closed paths - //the default is to merge adjacent collinear edges into a single edge. - //However, if the PreserveCollinear property is enabled, only overlapping - //collinear edges (ie spikes) will be removed from closed paths. - if (E == eStart) eStart = E->Next; - E = RemoveEdge(E); - E = E->Prev; - eLoopStop = E; - continue; - } - E = E->Next; - if ((E == eLoopStop) || (!Closed && E->Next == eStart)) break; - } - - if ((!Closed && (E == E->Next)) || (Closed && (E->Prev == E->Next))) - { - delete [] edges; - return false; - } - - if (!Closed) - { - m_HasOpenPaths = true; - eStart->Prev->OutIdx = Skip; - } - - //3. Do second stage of edge initialization ... - E = eStart; - do - { - InitEdge2(*E, PolyTyp); - E = E->Next; - if (IsFlat && E->Curr.Y != eStart->Curr.Y) IsFlat = false; - } - while (E != eStart); - - //4. Finally, add edge bounds to LocalMinima list ... - - //Totally flat paths must be handled differently when adding them - //to LocalMinima list to avoid endless loops etc ... - if (IsFlat) - { - if (Closed) - { - delete [] edges; - return false; - } - E->Prev->OutIdx = Skip; - MinimaList::value_type locMin; - locMin.Y = E->Bot.Y; - locMin.LeftBound = 0; - locMin.RightBound = E; - locMin.RightBound->Side = esRight; - locMin.RightBound->WindDelta = 0; - for (;;) - { - if (E->Bot.X != E->Prev->Top.X) ReverseHorizontal(*E); - if (E->Next->OutIdx == Skip) break; - E->NextInLML = E->Next; - E = E->Next; - } - m_MinimaList.push_back(locMin); - m_edges.push_back(edges); - return true; - } - - m_edges.push_back(edges); - bool leftBoundIsForward; - TEdge* EMin = 0; - - //workaround to avoid an endless loop in the while loop below when - //open paths have matching start and end points ... - if (E->Prev->Bot == E->Prev->Top) E = E->Next; - - for (;;) - { - E = FindNextLocMin(E); - if (E == EMin) break; - else if (!EMin) EMin = E; - - //E and E.Prev now share a local minima (left aligned if horizontal). - //Compare their slopes to find which starts which bound ... - MinimaList::value_type locMin; - locMin.Y = E->Bot.Y; - if (E->Dx < E->Prev->Dx) - { - locMin.LeftBound = E->Prev; - locMin.RightBound = E; - leftBoundIsForward = false; //Q.nextInLML = Q.prev - } else - { - locMin.LeftBound = E; - locMin.RightBound = E->Prev; - leftBoundIsForward = true; //Q.nextInLML = Q.next - } - - if (!Closed) locMin.LeftBound->WindDelta = 0; - else if (locMin.LeftBound->Next == locMin.RightBound) - locMin.LeftBound->WindDelta = -1; - else locMin.LeftBound->WindDelta = 1; - locMin.RightBound->WindDelta = -locMin.LeftBound->WindDelta; - - E = ProcessBound(locMin.LeftBound, leftBoundIsForward); - if (E->OutIdx == Skip) E = ProcessBound(E, leftBoundIsForward); - - TEdge* E2 = ProcessBound(locMin.RightBound, !leftBoundIsForward); - if (E2->OutIdx == Skip) E2 = ProcessBound(E2, !leftBoundIsForward); - - if (locMin.LeftBound->OutIdx == Skip) - locMin.LeftBound = 0; - else if (locMin.RightBound->OutIdx == Skip) - locMin.RightBound = 0; - m_MinimaList.push_back(locMin); - if (!leftBoundIsForward) E = E2; - } - return true; -} -//------------------------------------------------------------------------------ - -bool ClipperBase::AddPaths(const Paths &ppg, PolyType PolyTyp, bool Closed) -{ - bool result = false; - for (Paths::size_type i = 0; i < ppg.size(); ++i) - if (AddPath(ppg[i], PolyTyp, Closed)) result = true; - return result; -} -//------------------------------------------------------------------------------ - -void ClipperBase::Clear() -{ - DisposeLocalMinimaList(); - for (EdgeList::size_type i = 0; i < m_edges.size(); ++i) - { - TEdge* edges = m_edges[i]; - delete [] edges; - } - m_edges.clear(); - m_UseFullRange = false; - m_HasOpenPaths = false; -} -//------------------------------------------------------------------------------ - -void ClipperBase::Reset() -{ - m_CurrentLM = m_MinimaList.begin(); - if (m_CurrentLM == m_MinimaList.end()) return; //ie nothing to process - std::sort(m_MinimaList.begin(), m_MinimaList.end(), LocMinSorter()); - - m_Scanbeam = ScanbeamList(); //clears/resets priority_queue - //reset all edges ... - for (MinimaList::iterator lm = m_MinimaList.begin(); lm != m_MinimaList.end(); ++lm) - { - InsertScanbeam(lm->Y); - TEdge* e = lm->LeftBound; - if (e) - { - e->Curr = e->Bot; - e->Side = esLeft; - e->OutIdx = Unassigned; - } - - e = lm->RightBound; - if (e) - { - e->Curr = e->Bot; - e->Side = esRight; - e->OutIdx = Unassigned; - } - } - m_ActiveEdges = 0; - m_CurrentLM = m_MinimaList.begin(); -} -//------------------------------------------------------------------------------ - -void ClipperBase::DisposeLocalMinimaList() -{ - m_MinimaList.clear(); - m_CurrentLM = m_MinimaList.begin(); -} -//------------------------------------------------------------------------------ - -bool ClipperBase::PopLocalMinima(cInt Y, const LocalMinimum *&locMin) -{ - if (m_CurrentLM == m_MinimaList.end() || (*m_CurrentLM).Y != Y) return false; - locMin = &(*m_CurrentLM); - ++m_CurrentLM; - return true; -} -//------------------------------------------------------------------------------ - -IntRect ClipperBase::GetBounds() -{ - IntRect result; - MinimaList::iterator lm = m_MinimaList.begin(); - if (lm == m_MinimaList.end()) - { - result.left = result.top = result.right = result.bottom = 0; - return result; - } - result.left = lm->LeftBound->Bot.X; - result.top = lm->LeftBound->Bot.Y; - result.right = lm->LeftBound->Bot.X; - result.bottom = lm->LeftBound->Bot.Y; - while (lm != m_MinimaList.end()) - { - //todo - needs fixing for open paths - result.bottom = std::max(result.bottom, lm->LeftBound->Bot.Y); - TEdge* e = lm->LeftBound; - for (;;) { - TEdge* bottomE = e; - while (e->NextInLML) - { - if (e->Bot.X < result.left) result.left = e->Bot.X; - if (e->Bot.X > result.right) result.right = e->Bot.X; - e = e->NextInLML; - } - result.left = std::min(result.left, e->Bot.X); - result.right = std::max(result.right, e->Bot.X); - result.left = std::min(result.left, e->Top.X); - result.right = std::max(result.right, e->Top.X); - result.top = std::min(result.top, e->Top.Y); - if (bottomE == lm->LeftBound) e = lm->RightBound; - else break; - } - ++lm; - } - return result; -} -//------------------------------------------------------------------------------ - -void ClipperBase::InsertScanbeam(const cInt Y) -{ - m_Scanbeam.push(Y); -} -//------------------------------------------------------------------------------ - -bool ClipperBase::PopScanbeam(cInt &Y) -{ - if (m_Scanbeam.empty()) return false; - Y = m_Scanbeam.top(); - m_Scanbeam.pop(); - while (!m_Scanbeam.empty() && Y == m_Scanbeam.top()) { m_Scanbeam.pop(); } // Pop duplicates. - return true; -} -//------------------------------------------------------------------------------ - -void ClipperBase::DisposeAllOutRecs(){ - for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) - DisposeOutRec(i); - m_PolyOuts.clear(); -} -//------------------------------------------------------------------------------ - -void ClipperBase::DisposeOutRec(PolyOutList::size_type index) -{ - OutRec *outRec = m_PolyOuts[index]; - if (outRec->Pts) DisposeOutPts(outRec->Pts); - delete outRec; - m_PolyOuts[index] = 0; -} -//------------------------------------------------------------------------------ - -void ClipperBase::DeleteFromAEL(TEdge *e) -{ - TEdge* AelPrev = e->PrevInAEL; - TEdge* AelNext = e->NextInAEL; - if (!AelPrev && !AelNext && (e != m_ActiveEdges)) return; //already deleted - if (AelPrev) AelPrev->NextInAEL = AelNext; - else m_ActiveEdges = AelNext; - if (AelNext) AelNext->PrevInAEL = AelPrev; - e->NextInAEL = 0; - e->PrevInAEL = 0; -} -//------------------------------------------------------------------------------ - -OutRec* ClipperBase::CreateOutRec() -{ - OutRec* result = new OutRec; - result->IsHole = false; - result->IsOpen = false; - result->FirstLeft = 0; - result->Pts = 0; - result->BottomPt = 0; - result->PolyNd = 0; - m_PolyOuts.push_back(result); - result->Idx = (int)m_PolyOuts.size() - 1; - return result; -} -//------------------------------------------------------------------------------ - -void ClipperBase::SwapPositionsInAEL(TEdge *Edge1, TEdge *Edge2) -{ - //check that one or other edge hasn't already been removed from AEL ... - if (Edge1->NextInAEL == Edge1->PrevInAEL || - Edge2->NextInAEL == Edge2->PrevInAEL) return; - - if (Edge1->NextInAEL == Edge2) - { - TEdge* Next = Edge2->NextInAEL; - if (Next) Next->PrevInAEL = Edge1; - TEdge* Prev = Edge1->PrevInAEL; - if (Prev) Prev->NextInAEL = Edge2; - Edge2->PrevInAEL = Prev; - Edge2->NextInAEL = Edge1; - Edge1->PrevInAEL = Edge2; - Edge1->NextInAEL = Next; - } - else if (Edge2->NextInAEL == Edge1) - { - TEdge* Next = Edge1->NextInAEL; - if (Next) Next->PrevInAEL = Edge2; - TEdge* Prev = Edge2->PrevInAEL; - if (Prev) Prev->NextInAEL = Edge1; - Edge1->PrevInAEL = Prev; - Edge1->NextInAEL = Edge2; - Edge2->PrevInAEL = Edge1; - Edge2->NextInAEL = Next; - } - else - { - TEdge* Next = Edge1->NextInAEL; - TEdge* Prev = Edge1->PrevInAEL; - Edge1->NextInAEL = Edge2->NextInAEL; - if (Edge1->NextInAEL) Edge1->NextInAEL->PrevInAEL = Edge1; - Edge1->PrevInAEL = Edge2->PrevInAEL; - if (Edge1->PrevInAEL) Edge1->PrevInAEL->NextInAEL = Edge1; - Edge2->NextInAEL = Next; - if (Edge2->NextInAEL) Edge2->NextInAEL->PrevInAEL = Edge2; - Edge2->PrevInAEL = Prev; - if (Edge2->PrevInAEL) Edge2->PrevInAEL->NextInAEL = Edge2; - } - - if (!Edge1->PrevInAEL) m_ActiveEdges = Edge1; - else if (!Edge2->PrevInAEL) m_ActiveEdges = Edge2; -} -//------------------------------------------------------------------------------ - -void ClipperBase::UpdateEdgeIntoAEL(TEdge *&e) -{ - if (!e->NextInLML) - throw clipperException("UpdateEdgeIntoAEL: invalid call"); - - e->NextInLML->OutIdx = e->OutIdx; - TEdge* AelPrev = e->PrevInAEL; - TEdge* AelNext = e->NextInAEL; - if (AelPrev) AelPrev->NextInAEL = e->NextInLML; - else m_ActiveEdges = e->NextInLML; - if (AelNext) AelNext->PrevInAEL = e->NextInLML; - e->NextInLML->Side = e->Side; - e->NextInLML->WindDelta = e->WindDelta; - e->NextInLML->WindCnt = e->WindCnt; - e->NextInLML->WindCnt2 = e->WindCnt2; - e = e->NextInLML; - e->Curr = e->Bot; - e->PrevInAEL = AelPrev; - e->NextInAEL = AelNext; - if (!IsHorizontal(*e)) InsertScanbeam(e->Top.Y); -} -//------------------------------------------------------------------------------ - -bool ClipperBase::LocalMinimaPending() -{ - return (m_CurrentLM != m_MinimaList.end()); -} - -//------------------------------------------------------------------------------ -// TClipper methods ... -//------------------------------------------------------------------------------ - -Clipper::Clipper(int initOptions) : ClipperBase() //constructor -{ - m_ExecuteLocked = false; - m_UseFullRange = false; - m_ReverseOutput = ((initOptions & ioReverseSolution) != 0); - m_StrictSimple = ((initOptions & ioStrictlySimple) != 0); - m_PreserveCollinear = ((initOptions & ioPreserveCollinear) != 0); - m_HasOpenPaths = false; -#ifdef use_xyz - m_ZFill = 0; -#endif -} -//------------------------------------------------------------------------------ - -#ifdef use_xyz -void Clipper::ZFillFunction(ZFillCallback zFillFunc) -{ - m_ZFill = zFillFunc; -} -//------------------------------------------------------------------------------ -#endif - -bool Clipper::Execute(ClipType clipType, Paths &solution, PolyFillType fillType) -{ - return Execute(clipType, solution, fillType, fillType); -} -//------------------------------------------------------------------------------ - -bool Clipper::Execute(ClipType clipType, PolyTree &polytree, PolyFillType fillType) -{ - return Execute(clipType, polytree, fillType, fillType); -} -//------------------------------------------------------------------------------ - -bool Clipper::Execute(ClipType clipType, Paths &solution, - PolyFillType subjFillType, PolyFillType clipFillType) -{ - if( m_ExecuteLocked ) return false; - if (m_HasOpenPaths) - throw clipperException("Error: PolyTree struct is needed for open path clipping."); - m_ExecuteLocked = true; - solution.resize(0); - m_SubjFillType = subjFillType; - m_ClipFillType = clipFillType; - m_ClipType = clipType; - m_UsingPolyTree = false; - bool succeeded = ExecuteInternal(); - if (succeeded) BuildResult(solution); - DisposeAllOutRecs(); - m_ExecuteLocked = false; - return succeeded; -} -//------------------------------------------------------------------------------ - -bool Clipper::Execute(ClipType clipType, PolyTree& polytree, - PolyFillType subjFillType, PolyFillType clipFillType) -{ - if( m_ExecuteLocked ) return false; - m_ExecuteLocked = true; - m_SubjFillType = subjFillType; - m_ClipFillType = clipFillType; - m_ClipType = clipType; - m_UsingPolyTree = true; - bool succeeded = ExecuteInternal(); - if (succeeded) BuildResult2(polytree); - DisposeAllOutRecs(); - m_ExecuteLocked = false; - return succeeded; -} -//------------------------------------------------------------------------------ - -void Clipper::FixHoleLinkage(OutRec &outrec) -{ - //skip OutRecs that (a) contain outermost polygons or - //(b) already have the correct owner/child linkage ... - if (!outrec.FirstLeft || - (outrec.IsHole != outrec.FirstLeft->IsHole && - outrec.FirstLeft->Pts)) return; - - OutRec* orfl = outrec.FirstLeft; - while (orfl && ((orfl->IsHole == outrec.IsHole) || !orfl->Pts)) - orfl = orfl->FirstLeft; - outrec.FirstLeft = orfl; -} -//------------------------------------------------------------------------------ - -bool Clipper::ExecuteInternal() -{ - bool succeeded = true; - try { - Reset(); - m_Maxima = MaximaList(); - m_SortedEdges = 0; - - succeeded = true; - cInt botY, topY; - if (!PopScanbeam(botY)) return false; - InsertLocalMinimaIntoAEL(botY); - while (PopScanbeam(topY) || LocalMinimaPending()) - { - ProcessHorizontals(); - ClearGhostJoins(); - if (!ProcessIntersections(topY)) - { - succeeded = false; - break; - } - ProcessEdgesAtTopOfScanbeam(topY); - botY = topY; - InsertLocalMinimaIntoAEL(botY); - } - } - catch(...) - { - succeeded = false; - } - - if (succeeded) - { - //fix orientations ... - for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) - { - OutRec *outRec = m_PolyOuts[i]; - if (!outRec->Pts || outRec->IsOpen) continue; - if ((outRec->IsHole ^ m_ReverseOutput) == (Area(*outRec) > 0)) - ReversePolyPtLinks(outRec->Pts); - } - - if (!m_Joins.empty()) JoinCommonEdges(); - - //unfortunately FixupOutPolygon() must be done after JoinCommonEdges() - for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) - { - OutRec *outRec = m_PolyOuts[i]; - if (!outRec->Pts) continue; - if (outRec->IsOpen) - FixupOutPolyline(*outRec); - else - FixupOutPolygon(*outRec); - } - - if (m_StrictSimple) DoSimplePolygons(); - } - - ClearJoins(); - ClearGhostJoins(); - return succeeded; -} -//------------------------------------------------------------------------------ - -void Clipper::SetWindingCount(TEdge &edge) -{ - TEdge *e = edge.PrevInAEL; - //find the edge of the same polytype that immediately preceeds 'edge' in AEL - while (e && ((e->PolyTyp != edge.PolyTyp) || (e->WindDelta == 0))) e = e->PrevInAEL; - if (!e) - { - if (edge.WindDelta == 0) - { - PolyFillType pft = (edge.PolyTyp == ptSubject ? m_SubjFillType : m_ClipFillType); - edge.WindCnt = (pft == pftNegative ? -1 : 1); - } - else - edge.WindCnt = edge.WindDelta; - edge.WindCnt2 = 0; - e = m_ActiveEdges; //ie get ready to calc WindCnt2 - } - else if (edge.WindDelta == 0 && m_ClipType != ctUnion) - { - edge.WindCnt = 1; - edge.WindCnt2 = e->WindCnt2; - e = e->NextInAEL; //ie get ready to calc WindCnt2 - } - else if (IsEvenOddFillType(edge)) - { - //EvenOdd filling ... - if (edge.WindDelta == 0) - { - //are we inside a subj polygon ... - bool Inside = true; - TEdge *e2 = e->PrevInAEL; - while (e2) - { - if (e2->PolyTyp == e->PolyTyp && e2->WindDelta != 0) - Inside = !Inside; - e2 = e2->PrevInAEL; - } - edge.WindCnt = (Inside ? 0 : 1); - } - else - { - edge.WindCnt = edge.WindDelta; - } - edge.WindCnt2 = e->WindCnt2; - e = e->NextInAEL; //ie get ready to calc WindCnt2 - } - else - { - //nonZero, Positive or Negative filling ... - if (e->WindCnt * e->WindDelta < 0) - { - //prev edge is 'decreasing' WindCount (WC) toward zero - //so we're outside the previous polygon ... - if (Abs(e->WindCnt) > 1) - { - //outside prev poly but still inside another. - //when reversing direction of prev poly use the same WC - if (e->WindDelta * edge.WindDelta < 0) edge.WindCnt = e->WindCnt; - //otherwise continue to 'decrease' WC ... - else edge.WindCnt = e->WindCnt + edge.WindDelta; - } - else - //now outside all polys of same polytype so set own WC ... - edge.WindCnt = (edge.WindDelta == 0 ? 1 : edge.WindDelta); - } else - { - //prev edge is 'increasing' WindCount (WC) away from zero - //so we're inside the previous polygon ... - if (edge.WindDelta == 0) - edge.WindCnt = (e->WindCnt < 0 ? e->WindCnt - 1 : e->WindCnt + 1); - //if wind direction is reversing prev then use same WC - else if (e->WindDelta * edge.WindDelta < 0) edge.WindCnt = e->WindCnt; - //otherwise add to WC ... - else edge.WindCnt = e->WindCnt + edge.WindDelta; - } - edge.WindCnt2 = e->WindCnt2; - e = e->NextInAEL; //ie get ready to calc WindCnt2 - } - - //update WindCnt2 ... - if (IsEvenOddAltFillType(edge)) - { - //EvenOdd filling ... - while (e != &edge) - { - if (e->WindDelta != 0) - edge.WindCnt2 = (edge.WindCnt2 == 0 ? 1 : 0); - e = e->NextInAEL; - } - } else - { - //nonZero, Positive or Negative filling ... - while ( e != &edge ) - { - edge.WindCnt2 += e->WindDelta; - e = e->NextInAEL; - } - } -} -//------------------------------------------------------------------------------ - -bool Clipper::IsEvenOddFillType(const TEdge& edge) const -{ - if (edge.PolyTyp == ptSubject) - return m_SubjFillType == pftEvenOdd; else - return m_ClipFillType == pftEvenOdd; -} -//------------------------------------------------------------------------------ - -bool Clipper::IsEvenOddAltFillType(const TEdge& edge) const -{ - if (edge.PolyTyp == ptSubject) - return m_ClipFillType == pftEvenOdd; else - return m_SubjFillType == pftEvenOdd; -} -//------------------------------------------------------------------------------ - -bool Clipper::IsContributing(const TEdge& edge) const -{ - PolyFillType pft, pft2; - if (edge.PolyTyp == ptSubject) - { - pft = m_SubjFillType; - pft2 = m_ClipFillType; - } else - { - pft = m_ClipFillType; - pft2 = m_SubjFillType; - } - - switch(pft) - { - case pftEvenOdd: - //return false if a subj line has been flagged as inside a subj polygon - if (edge.WindDelta == 0 && edge.WindCnt != 1) return false; - break; - case pftNonZero: - if (Abs(edge.WindCnt) != 1) return false; - break; - case pftPositive: - if (edge.WindCnt != 1) return false; - break; - default: //pftNegative - if (edge.WindCnt != -1) return false; - } - - switch(m_ClipType) - { - case ctIntersection: - switch(pft2) - { - case pftEvenOdd: - case pftNonZero: - return (edge.WindCnt2 != 0); - case pftPositive: - return (edge.WindCnt2 > 0); - default: - return (edge.WindCnt2 < 0); - } - break; - case ctUnion: - switch(pft2) - { - case pftEvenOdd: - case pftNonZero: - return (edge.WindCnt2 == 0); - case pftPositive: - return (edge.WindCnt2 <= 0); - default: - return (edge.WindCnt2 >= 0); - } - break; - case ctDifference: - if (edge.PolyTyp == ptSubject) - switch(pft2) - { - case pftEvenOdd: - case pftNonZero: - return (edge.WindCnt2 == 0); - case pftPositive: - return (edge.WindCnt2 <= 0); - default: - return (edge.WindCnt2 >= 0); - } - else - switch(pft2) - { - case pftEvenOdd: - case pftNonZero: - return (edge.WindCnt2 != 0); - case pftPositive: - return (edge.WindCnt2 > 0); - default: - return (edge.WindCnt2 < 0); - } - break; - case ctXor: - if (edge.WindDelta == 0) //XOr always contributing unless open - switch(pft2) - { - case pftEvenOdd: - case pftNonZero: - return (edge.WindCnt2 == 0); - case pftPositive: - return (edge.WindCnt2 <= 0); - default: - return (edge.WindCnt2 >= 0); - } - else - return true; - break; - default: - return true; - } -} -//------------------------------------------------------------------------------ - -OutPt* Clipper::AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &Pt) -{ - OutPt* result; - TEdge *e, *prevE; - if (IsHorizontal(*e2) || ( e1->Dx > e2->Dx )) - { - result = AddOutPt(e1, Pt); - e2->OutIdx = e1->OutIdx; - e1->Side = esLeft; - e2->Side = esRight; - e = e1; - if (e->PrevInAEL == e2) - prevE = e2->PrevInAEL; - else - prevE = e->PrevInAEL; - } else - { - result = AddOutPt(e2, Pt); - e1->OutIdx = e2->OutIdx; - e1->Side = esRight; - e2->Side = esLeft; - e = e2; - if (e->PrevInAEL == e1) - prevE = e1->PrevInAEL; - else - prevE = e->PrevInAEL; - } - - if (prevE && prevE->OutIdx >= 0) - { - cInt xPrev = TopX(*prevE, Pt.Y); - cInt xE = TopX(*e, Pt.Y); - if (xPrev == xE && (e->WindDelta != 0) && (prevE->WindDelta != 0) && - SlopesEqual(IntPoint(xPrev, Pt.Y), prevE->Top, IntPoint(xE, Pt.Y), e->Top, m_UseFullRange)) - { - OutPt* outPt = AddOutPt(prevE, Pt); - AddJoin(result, outPt, e->Top); - } - } - return result; -} -//------------------------------------------------------------------------------ - -void Clipper::AddLocalMaxPoly(TEdge *e1, TEdge *e2, const IntPoint &Pt) -{ - AddOutPt( e1, Pt ); - if (e2->WindDelta == 0) AddOutPt(e2, Pt); - if( e1->OutIdx == e2->OutIdx ) - { - e1->OutIdx = Unassigned; - e2->OutIdx = Unassigned; - } - else if (e1->OutIdx < e2->OutIdx) - AppendPolygon(e1, e2); - else - AppendPolygon(e2, e1); -} -//------------------------------------------------------------------------------ - -void Clipper::AddEdgeToSEL(TEdge *edge) -{ - //SEL pointers in PEdge are reused to build a list of horizontal edges. - //However, we don't need to worry about order with horizontal edge processing. - if( !m_SortedEdges ) - { - m_SortedEdges = edge; - edge->PrevInSEL = 0; - edge->NextInSEL = 0; - } - else - { - edge->NextInSEL = m_SortedEdges; - edge->PrevInSEL = 0; - m_SortedEdges->PrevInSEL = edge; - m_SortedEdges = edge; - } -} -//------------------------------------------------------------------------------ - -bool Clipper::PopEdgeFromSEL(TEdge *&edge) -{ - if (!m_SortedEdges) return false; - edge = m_SortedEdges; - DeleteFromSEL(m_SortedEdges); - return true; -} -//------------------------------------------------------------------------------ - -void Clipper::CopyAELToSEL() -{ - TEdge* e = m_ActiveEdges; - m_SortedEdges = e; - while ( e ) - { - e->PrevInSEL = e->PrevInAEL; - e->NextInSEL = e->NextInAEL; - e = e->NextInAEL; - } -} -//------------------------------------------------------------------------------ - -void Clipper::AddJoin(OutPt *op1, OutPt *op2, const IntPoint OffPt) -{ - Join* j = new Join; - j->OutPt1 = op1; - j->OutPt2 = op2; - j->OffPt = OffPt; - m_Joins.push_back(j); -} -//------------------------------------------------------------------------------ - -void Clipper::ClearJoins() -{ - for (JoinList::size_type i = 0; i < m_Joins.size(); i++) - delete m_Joins[i]; - m_Joins.resize(0); -} -//------------------------------------------------------------------------------ - -void Clipper::ClearGhostJoins() -{ - for (JoinList::size_type i = 0; i < m_GhostJoins.size(); i++) - delete m_GhostJoins[i]; - m_GhostJoins.resize(0); -} -//------------------------------------------------------------------------------ - -void Clipper::AddGhostJoin(OutPt *op, const IntPoint OffPt) -{ - Join* j = new Join; - j->OutPt1 = op; - j->OutPt2 = 0; - j->OffPt = OffPt; - m_GhostJoins.push_back(j); -} -//------------------------------------------------------------------------------ - -void Clipper::InsertLocalMinimaIntoAEL(const cInt botY) -{ - const LocalMinimum *lm; - while (PopLocalMinima(botY, lm)) - { - TEdge* lb = lm->LeftBound; - TEdge* rb = lm->RightBound; - - OutPt *Op1 = 0; - if (!lb) - { - //nb: don't insert LB into either AEL or SEL - InsertEdgeIntoAEL(rb, 0); - SetWindingCount(*rb); - if (IsContributing(*rb)) - Op1 = AddOutPt(rb, rb->Bot); - } - else if (!rb) - { - InsertEdgeIntoAEL(lb, 0); - SetWindingCount(*lb); - if (IsContributing(*lb)) - Op1 = AddOutPt(lb, lb->Bot); - InsertScanbeam(lb->Top.Y); - } - else - { - InsertEdgeIntoAEL(lb, 0); - InsertEdgeIntoAEL(rb, lb); - SetWindingCount( *lb ); - rb->WindCnt = lb->WindCnt; - rb->WindCnt2 = lb->WindCnt2; - if (IsContributing(*lb)) - Op1 = AddLocalMinPoly(lb, rb, lb->Bot); - InsertScanbeam(lb->Top.Y); - } - - if (rb) - { - if (IsHorizontal(*rb)) - { - AddEdgeToSEL(rb); - if (rb->NextInLML) - InsertScanbeam(rb->NextInLML->Top.Y); - } - else InsertScanbeam( rb->Top.Y ); - } - - if (!lb || !rb) continue; - - //if any output polygons share an edge, they'll need joining later ... - if (Op1 && IsHorizontal(*rb) && - m_GhostJoins.size() > 0 && (rb->WindDelta != 0)) - { - for (JoinList::size_type i = 0; i < m_GhostJoins.size(); ++i) - { - Join* jr = m_GhostJoins[i]; - //if the horizontal Rb and a 'ghost' horizontal overlap, then convert - //the 'ghost' join to a real join ready for later ... - if (HorzSegmentsOverlap(jr->OutPt1->Pt.X, jr->OffPt.X, rb->Bot.X, rb->Top.X)) - AddJoin(jr->OutPt1, Op1, jr->OffPt); - } - } - - if (lb->OutIdx >= 0 && lb->PrevInAEL && - lb->PrevInAEL->Curr.X == lb->Bot.X && - lb->PrevInAEL->OutIdx >= 0 && - SlopesEqual(lb->PrevInAEL->Bot, lb->PrevInAEL->Top, lb->Curr, lb->Top, m_UseFullRange) && - (lb->WindDelta != 0) && (lb->PrevInAEL->WindDelta != 0)) - { - OutPt *Op2 = AddOutPt(lb->PrevInAEL, lb->Bot); - AddJoin(Op1, Op2, lb->Top); - } - - if(lb->NextInAEL != rb) - { - - if (rb->OutIdx >= 0 && rb->PrevInAEL->OutIdx >= 0 && - SlopesEqual(rb->PrevInAEL->Curr, rb->PrevInAEL->Top, rb->Curr, rb->Top, m_UseFullRange) && - (rb->WindDelta != 0) && (rb->PrevInAEL->WindDelta != 0)) - { - OutPt *Op2 = AddOutPt(rb->PrevInAEL, rb->Bot); - AddJoin(Op1, Op2, rb->Top); - } - - TEdge* e = lb->NextInAEL; - if (e) - { - while( e != rb ) - { - //nb: For calculating winding counts etc, IntersectEdges() assumes - //that param1 will be to the Right of param2 ABOVE the intersection ... - IntersectEdges(rb , e , lb->Curr); //order important here - e = e->NextInAEL; - } - } - } - - } -} -//------------------------------------------------------------------------------ - -void Clipper::DeleteFromSEL(TEdge *e) -{ - TEdge* SelPrev = e->PrevInSEL; - TEdge* SelNext = e->NextInSEL; - if( !SelPrev && !SelNext && (e != m_SortedEdges) ) return; //already deleted - if( SelPrev ) SelPrev->NextInSEL = SelNext; - else m_SortedEdges = SelNext; - if( SelNext ) SelNext->PrevInSEL = SelPrev; - e->NextInSEL = 0; - e->PrevInSEL = 0; -} -//------------------------------------------------------------------------------ - -#ifdef use_xyz -void Clipper::SetZ(IntPoint& pt, TEdge& e1, TEdge& e2) -{ - if (pt.Z != 0 || !m_ZFill) return; - else if (pt == e1.Bot) pt.Z = e1.Bot.Z; - else if (pt == e1.Top) pt.Z = e1.Top.Z; - else if (pt == e2.Bot) pt.Z = e2.Bot.Z; - else if (pt == e2.Top) pt.Z = e2.Top.Z; - else (*m_ZFill)(e1.Bot, e1.Top, e2.Bot, e2.Top, pt); -} -//------------------------------------------------------------------------------ -#endif - -void Clipper::IntersectEdges(TEdge *e1, TEdge *e2, IntPoint &Pt) -{ - bool e1Contributing = ( e1->OutIdx >= 0 ); - bool e2Contributing = ( e2->OutIdx >= 0 ); - -#ifdef use_xyz - SetZ(Pt, *e1, *e2); -#endif - -#ifdef use_lines - //if either edge is on an OPEN path ... - if (e1->WindDelta == 0 || e2->WindDelta == 0) - { - //ignore subject-subject open path intersections UNLESS they - //are both open paths, AND they are both 'contributing maximas' ... - if (e1->WindDelta == 0 && e2->WindDelta == 0) return; - - //if intersecting a subj line with a subj poly ... - else if (e1->PolyTyp == e2->PolyTyp && - e1->WindDelta != e2->WindDelta && m_ClipType == ctUnion) - { - if (e1->WindDelta == 0) - { - if (e2Contributing) - { - AddOutPt(e1, Pt); - if (e1Contributing) e1->OutIdx = Unassigned; - } - } - else - { - if (e1Contributing) - { - AddOutPt(e2, Pt); - if (e2Contributing) e2->OutIdx = Unassigned; - } - } - } - else if (e1->PolyTyp != e2->PolyTyp) - { - //toggle subj open path OutIdx on/off when Abs(clip.WndCnt) == 1 ... - if ((e1->WindDelta == 0) && abs(e2->WindCnt) == 1 && - (m_ClipType != ctUnion || e2->WindCnt2 == 0)) - { - AddOutPt(e1, Pt); - if (e1Contributing) e1->OutIdx = Unassigned; - } - else if ((e2->WindDelta == 0) && (abs(e1->WindCnt) == 1) && - (m_ClipType != ctUnion || e1->WindCnt2 == 0)) - { - AddOutPt(e2, Pt); - if (e2Contributing) e2->OutIdx = Unassigned; - } - } - return; - } -#endif - - //update winding counts... - //assumes that e1 will be to the Right of e2 ABOVE the intersection - if ( e1->PolyTyp == e2->PolyTyp ) - { - if ( IsEvenOddFillType( *e1) ) - { - int oldE1WindCnt = e1->WindCnt; - e1->WindCnt = e2->WindCnt; - e2->WindCnt = oldE1WindCnt; - } else - { - if (e1->WindCnt + e2->WindDelta == 0 ) e1->WindCnt = -e1->WindCnt; - else e1->WindCnt += e2->WindDelta; - if ( e2->WindCnt - e1->WindDelta == 0 ) e2->WindCnt = -e2->WindCnt; - else e2->WindCnt -= e1->WindDelta; - } - } else - { - if (!IsEvenOddFillType(*e2)) e1->WindCnt2 += e2->WindDelta; - else e1->WindCnt2 = ( e1->WindCnt2 == 0 ) ? 1 : 0; - if (!IsEvenOddFillType(*e1)) e2->WindCnt2 -= e1->WindDelta; - else e2->WindCnt2 = ( e2->WindCnt2 == 0 ) ? 1 : 0; - } - - PolyFillType e1FillType, e2FillType, e1FillType2, e2FillType2; - if (e1->PolyTyp == ptSubject) - { - e1FillType = m_SubjFillType; - e1FillType2 = m_ClipFillType; - } else - { - e1FillType = m_ClipFillType; - e1FillType2 = m_SubjFillType; - } - if (e2->PolyTyp == ptSubject) - { - e2FillType = m_SubjFillType; - e2FillType2 = m_ClipFillType; - } else - { - e2FillType = m_ClipFillType; - e2FillType2 = m_SubjFillType; - } - - cInt e1Wc, e2Wc; - switch (e1FillType) - { - case pftPositive: e1Wc = e1->WindCnt; break; - case pftNegative: e1Wc = -e1->WindCnt; break; - default: e1Wc = Abs(e1->WindCnt); - } - switch(e2FillType) - { - case pftPositive: e2Wc = e2->WindCnt; break; - case pftNegative: e2Wc = -e2->WindCnt; break; - default: e2Wc = Abs(e2->WindCnt); - } - - if ( e1Contributing && e2Contributing ) - { - if ((e1Wc != 0 && e1Wc != 1) || (e2Wc != 0 && e2Wc != 1) || - (e1->PolyTyp != e2->PolyTyp && m_ClipType != ctXor) ) - { - AddLocalMaxPoly(e1, e2, Pt); - } - else - { - AddOutPt(e1, Pt); - AddOutPt(e2, Pt); - SwapSides( *e1 , *e2 ); - SwapPolyIndexes( *e1 , *e2 ); - } - } - else if ( e1Contributing ) - { - if (e2Wc == 0 || e2Wc == 1) - { - AddOutPt(e1, Pt); - SwapSides(*e1, *e2); - SwapPolyIndexes(*e1, *e2); - } - } - else if ( e2Contributing ) - { - if (e1Wc == 0 || e1Wc == 1) - { - AddOutPt(e2, Pt); - SwapSides(*e1, *e2); - SwapPolyIndexes(*e1, *e2); - } - } - else if ( (e1Wc == 0 || e1Wc == 1) && (e2Wc == 0 || e2Wc == 1)) - { - //neither edge is currently contributing ... - - cInt e1Wc2, e2Wc2; - switch (e1FillType2) - { - case pftPositive: e1Wc2 = e1->WindCnt2; break; - case pftNegative : e1Wc2 = -e1->WindCnt2; break; - default: e1Wc2 = Abs(e1->WindCnt2); - } - switch (e2FillType2) - { - case pftPositive: e2Wc2 = e2->WindCnt2; break; - case pftNegative: e2Wc2 = -e2->WindCnt2; break; - default: e2Wc2 = Abs(e2->WindCnt2); - } - - if (e1->PolyTyp != e2->PolyTyp) - { - AddLocalMinPoly(e1, e2, Pt); - } - else if (e1Wc == 1 && e2Wc == 1) - switch( m_ClipType ) { - case ctIntersection: - if (e1Wc2 > 0 && e2Wc2 > 0) - AddLocalMinPoly(e1, e2, Pt); - break; - case ctUnion: - if ( e1Wc2 <= 0 && e2Wc2 <= 0 ) - AddLocalMinPoly(e1, e2, Pt); - break; - case ctDifference: - if (((e1->PolyTyp == ptClip) && (e1Wc2 > 0) && (e2Wc2 > 0)) || - ((e1->PolyTyp == ptSubject) && (e1Wc2 <= 0) && (e2Wc2 <= 0))) - AddLocalMinPoly(e1, e2, Pt); - break; - case ctXor: - AddLocalMinPoly(e1, e2, Pt); - } - else - SwapSides( *e1, *e2 ); - } -} -//------------------------------------------------------------------------------ - -void Clipper::SetHoleState(TEdge *e, OutRec *outrec) -{ - TEdge *e2 = e->PrevInAEL; - TEdge *eTmp = 0; - while (e2) - { - if (e2->OutIdx >= 0 && e2->WindDelta != 0) - { - if (!eTmp) eTmp = e2; - else if (eTmp->OutIdx == e2->OutIdx) eTmp = 0; - } - e2 = e2->PrevInAEL; - } - if (!eTmp) - { - outrec->FirstLeft = 0; - outrec->IsHole = false; - } - else - { - outrec->FirstLeft = m_PolyOuts[eTmp->OutIdx]; - outrec->IsHole = !outrec->FirstLeft->IsHole; - } -} -//------------------------------------------------------------------------------ - -OutRec* GetLowermostRec(OutRec *outRec1, OutRec *outRec2) -{ - //work out which polygon fragment has the correct hole state ... - if (!outRec1->BottomPt) - outRec1->BottomPt = GetBottomPt(outRec1->Pts); - if (!outRec2->BottomPt) - outRec2->BottomPt = GetBottomPt(outRec2->Pts); - OutPt *OutPt1 = outRec1->BottomPt; - OutPt *OutPt2 = outRec2->BottomPt; - if (OutPt1->Pt.Y > OutPt2->Pt.Y) return outRec1; - else if (OutPt1->Pt.Y < OutPt2->Pt.Y) return outRec2; - else if (OutPt1->Pt.X < OutPt2->Pt.X) return outRec1; - else if (OutPt1->Pt.X > OutPt2->Pt.X) return outRec2; - else if (OutPt1->Next == OutPt1) return outRec2; - else if (OutPt2->Next == OutPt2) return outRec1; - else if (FirstIsBottomPt(OutPt1, OutPt2)) return outRec1; - else return outRec2; -} -//------------------------------------------------------------------------------ - -bool OutRec1RightOfOutRec2(OutRec* outRec1, OutRec* outRec2) -{ - do - { - outRec1 = outRec1->FirstLeft; - if (outRec1 == outRec2) return true; - } while (outRec1); - return false; -} -//------------------------------------------------------------------------------ - -OutRec* Clipper::GetOutRec(int Idx) -{ - OutRec* outrec = m_PolyOuts[Idx]; - while (outrec != m_PolyOuts[outrec->Idx]) - outrec = m_PolyOuts[outrec->Idx]; - return outrec; -} -//------------------------------------------------------------------------------ - -void Clipper::AppendPolygon(TEdge *e1, TEdge *e2) -{ - //get the start and ends of both output polygons ... - OutRec *outRec1 = m_PolyOuts[e1->OutIdx]; - OutRec *outRec2 = m_PolyOuts[e2->OutIdx]; - - OutRec *holeStateRec; - if (OutRec1RightOfOutRec2(outRec1, outRec2)) - holeStateRec = outRec2; - else if (OutRec1RightOfOutRec2(outRec2, outRec1)) - holeStateRec = outRec1; - else - holeStateRec = GetLowermostRec(outRec1, outRec2); - - //get the start and ends of both output polygons and - //join e2 poly onto e1 poly and delete pointers to e2 ... - - OutPt* p1_lft = outRec1->Pts; - OutPt* p1_rt = p1_lft->Prev; - OutPt* p2_lft = outRec2->Pts; - OutPt* p2_rt = p2_lft->Prev; - - //join e2 poly onto e1 poly and delete pointers to e2 ... - if( e1->Side == esLeft ) - { - if( e2->Side == esLeft ) - { - //z y x a b c - ReversePolyPtLinks(p2_lft); - p2_lft->Next = p1_lft; - p1_lft->Prev = p2_lft; - p1_rt->Next = p2_rt; - p2_rt->Prev = p1_rt; - outRec1->Pts = p2_rt; - } else - { - //x y z a b c - p2_rt->Next = p1_lft; - p1_lft->Prev = p2_rt; - p2_lft->Prev = p1_rt; - p1_rt->Next = p2_lft; - outRec1->Pts = p2_lft; - } - } else - { - if( e2->Side == esRight ) - { - //a b c z y x - ReversePolyPtLinks(p2_lft); - p1_rt->Next = p2_rt; - p2_rt->Prev = p1_rt; - p2_lft->Next = p1_lft; - p1_lft->Prev = p2_lft; - } else - { - //a b c x y z - p1_rt->Next = p2_lft; - p2_lft->Prev = p1_rt; - p1_lft->Prev = p2_rt; - p2_rt->Next = p1_lft; - } - } - - outRec1->BottomPt = 0; - if (holeStateRec == outRec2) - { - if (outRec2->FirstLeft != outRec1) - outRec1->FirstLeft = outRec2->FirstLeft; - outRec1->IsHole = outRec2->IsHole; - } - outRec2->Pts = 0; - outRec2->BottomPt = 0; - outRec2->FirstLeft = outRec1; - - int OKIdx = e1->OutIdx; - int ObsoleteIdx = e2->OutIdx; - - e1->OutIdx = Unassigned; //nb: safe because we only get here via AddLocalMaxPoly - e2->OutIdx = Unassigned; - - TEdge* e = m_ActiveEdges; - while( e ) - { - if( e->OutIdx == ObsoleteIdx ) - { - e->OutIdx = OKIdx; - e->Side = e1->Side; - break; - } - e = e->NextInAEL; - } - - outRec2->Idx = outRec1->Idx; -} -//------------------------------------------------------------------------------ - -OutPt* Clipper::AddOutPt(TEdge *e, const IntPoint &pt) -{ - if( e->OutIdx < 0 ) - { - OutRec *outRec = CreateOutRec(); - outRec->IsOpen = (e->WindDelta == 0); - OutPt* newOp = new OutPt; - outRec->Pts = newOp; - newOp->Idx = outRec->Idx; - newOp->Pt = pt; - newOp->Next = newOp; - newOp->Prev = newOp; - if (!outRec->IsOpen) - SetHoleState(e, outRec); - e->OutIdx = outRec->Idx; - return newOp; - } else - { - OutRec *outRec = m_PolyOuts[e->OutIdx]; - //OutRec.Pts is the 'Left-most' point & OutRec.Pts.Prev is the 'Right-most' - OutPt* op = outRec->Pts; - - bool ToFront = (e->Side == esLeft); - if (ToFront && (pt == op->Pt)) return op; - else if (!ToFront && (pt == op->Prev->Pt)) return op->Prev; - - OutPt* newOp = new OutPt; - newOp->Idx = outRec->Idx; - newOp->Pt = pt; - newOp->Next = op; - newOp->Prev = op->Prev; - newOp->Prev->Next = newOp; - op->Prev = newOp; - if (ToFront) outRec->Pts = newOp; - return newOp; - } -} -//------------------------------------------------------------------------------ - -OutPt* Clipper::GetLastOutPt(TEdge *e) -{ - OutRec *outRec = m_PolyOuts[e->OutIdx]; - if (e->Side == esLeft) - return outRec->Pts; - else - return outRec->Pts->Prev; -} -//------------------------------------------------------------------------------ - -void Clipper::ProcessHorizontals() -{ - TEdge* horzEdge; - while (PopEdgeFromSEL(horzEdge)) - ProcessHorizontal(horzEdge); -} -//------------------------------------------------------------------------------ - -inline bool IsMinima(TEdge *e) -{ - return e && (e->Prev->NextInLML != e) && (e->Next->NextInLML != e); -} -//------------------------------------------------------------------------------ - -inline bool IsMaxima(TEdge *e, const cInt Y) -{ - return e && e->Top.Y == Y && !e->NextInLML; -} -//------------------------------------------------------------------------------ - -inline bool IsIntermediate(TEdge *e, const cInt Y) -{ - return e->Top.Y == Y && e->NextInLML; -} -//------------------------------------------------------------------------------ - -TEdge *GetMaximaPair(TEdge *e) -{ - if ((e->Next->Top == e->Top) && !e->Next->NextInLML) - return e->Next; - else if ((e->Prev->Top == e->Top) && !e->Prev->NextInLML) - return e->Prev; - else return 0; -} -//------------------------------------------------------------------------------ - -TEdge *GetMaximaPairEx(TEdge *e) -{ - //as GetMaximaPair() but returns 0 if MaxPair isn't in AEL (unless it's horizontal) - TEdge* result = GetMaximaPair(e); - if (result && (result->OutIdx == Skip || - (result->NextInAEL == result->PrevInAEL && !IsHorizontal(*result)))) return 0; - return result; -} -//------------------------------------------------------------------------------ - -void Clipper::SwapPositionsInSEL(TEdge *Edge1, TEdge *Edge2) -{ - if( !( Edge1->NextInSEL ) && !( Edge1->PrevInSEL ) ) return; - if( !( Edge2->NextInSEL ) && !( Edge2->PrevInSEL ) ) return; - - if( Edge1->NextInSEL == Edge2 ) - { - TEdge* Next = Edge2->NextInSEL; - if( Next ) Next->PrevInSEL = Edge1; - TEdge* Prev = Edge1->PrevInSEL; - if( Prev ) Prev->NextInSEL = Edge2; - Edge2->PrevInSEL = Prev; - Edge2->NextInSEL = Edge1; - Edge1->PrevInSEL = Edge2; - Edge1->NextInSEL = Next; - } - else if( Edge2->NextInSEL == Edge1 ) - { - TEdge* Next = Edge1->NextInSEL; - if( Next ) Next->PrevInSEL = Edge2; - TEdge* Prev = Edge2->PrevInSEL; - if( Prev ) Prev->NextInSEL = Edge1; - Edge1->PrevInSEL = Prev; - Edge1->NextInSEL = Edge2; - Edge2->PrevInSEL = Edge1; - Edge2->NextInSEL = Next; - } - else - { - TEdge* Next = Edge1->NextInSEL; - TEdge* Prev = Edge1->PrevInSEL; - Edge1->NextInSEL = Edge2->NextInSEL; - if( Edge1->NextInSEL ) Edge1->NextInSEL->PrevInSEL = Edge1; - Edge1->PrevInSEL = Edge2->PrevInSEL; - if( Edge1->PrevInSEL ) Edge1->PrevInSEL->NextInSEL = Edge1; - Edge2->NextInSEL = Next; - if( Edge2->NextInSEL ) Edge2->NextInSEL->PrevInSEL = Edge2; - Edge2->PrevInSEL = Prev; - if( Edge2->PrevInSEL ) Edge2->PrevInSEL->NextInSEL = Edge2; - } - - if( !Edge1->PrevInSEL ) m_SortedEdges = Edge1; - else if( !Edge2->PrevInSEL ) m_SortedEdges = Edge2; -} -//------------------------------------------------------------------------------ - -TEdge* GetNextInAEL(TEdge *e, Direction dir) -{ - return dir == dLeftToRight ? e->NextInAEL : e->PrevInAEL; -} -//------------------------------------------------------------------------------ - -void GetHorzDirection(TEdge& HorzEdge, Direction& Dir, cInt& Left, cInt& Right) -{ - if (HorzEdge.Bot.X < HorzEdge.Top.X) - { - Left = HorzEdge.Bot.X; - Right = HorzEdge.Top.X; - Dir = dLeftToRight; - } else - { - Left = HorzEdge.Top.X; - Right = HorzEdge.Bot.X; - Dir = dRightToLeft; - } -} -//------------------------------------------------------------------------ - -/******************************************************************************* -* Notes: Horizontal edges (HEs) at scanline intersections (ie at the Top or * -* Bottom of a scanbeam) are processed as if layered. The order in which HEs * -* are processed doesn't matter. HEs intersect with other HE Bot.Xs only [#] * -* (or they could intersect with Top.Xs only, ie EITHER Bot.Xs OR Top.Xs), * -* and with other non-horizontal edges [*]. Once these intersections are * -* processed, intermediate HEs then 'promote' the Edge above (NextInLML) into * -* the AEL. These 'promoted' edges may in turn intersect [%] with other HEs. * -*******************************************************************************/ - -void Clipper::ProcessHorizontal(TEdge *horzEdge) -{ - Direction dir; - cInt horzLeft, horzRight; - bool IsOpen = (horzEdge->WindDelta == 0); - - GetHorzDirection(*horzEdge, dir, horzLeft, horzRight); - - TEdge* eLastHorz = horzEdge, *eMaxPair = 0; - while (eLastHorz->NextInLML && IsHorizontal(*eLastHorz->NextInLML)) - eLastHorz = eLastHorz->NextInLML; - if (!eLastHorz->NextInLML) - eMaxPair = GetMaximaPair(eLastHorz); - - MaximaList::const_iterator maxIt; - MaximaList::const_reverse_iterator maxRit; - if (m_Maxima.size() > 0) - { - //get the first maxima in range (X) ... - if (dir == dLeftToRight) - { - maxIt = m_Maxima.begin(); - while (maxIt != m_Maxima.end() && *maxIt <= horzEdge->Bot.X) maxIt++; - if (maxIt != m_Maxima.end() && *maxIt >= eLastHorz->Top.X) - maxIt = m_Maxima.end(); - } - else - { - maxRit = m_Maxima.rbegin(); - while (maxRit != m_Maxima.rend() && *maxRit > horzEdge->Bot.X) maxRit++; - if (maxRit != m_Maxima.rend() && *maxRit <= eLastHorz->Top.X) - maxRit = m_Maxima.rend(); - } - } - - OutPt* op1 = 0; - - for (;;) //loop through consec. horizontal edges - { - - bool IsLastHorz = (horzEdge == eLastHorz); - TEdge* e = GetNextInAEL(horzEdge, dir); - while(e) - { - - //this code block inserts extra coords into horizontal edges (in output - //polygons) whereever maxima touch these horizontal edges. This helps - //'simplifying' polygons (ie if the Simplify property is set). - if (m_Maxima.size() > 0) - { - if (dir == dLeftToRight) - { - while (maxIt != m_Maxima.end() && *maxIt < e->Curr.X) - { - if (horzEdge->OutIdx >= 0 && !IsOpen) - AddOutPt(horzEdge, IntPoint(*maxIt, horzEdge->Bot.Y)); - maxIt++; - } - } - else - { - while (maxRit != m_Maxima.rend() && *maxRit > e->Curr.X) - { - if (horzEdge->OutIdx >= 0 && !IsOpen) - AddOutPt(horzEdge, IntPoint(*maxRit, horzEdge->Bot.Y)); - maxRit++; - } - } - }; - - if ((dir == dLeftToRight && e->Curr.X > horzRight) || - (dir == dRightToLeft && e->Curr.X < horzLeft)) break; - - //Also break if we've got to the end of an intermediate horizontal edge ... - //nb: Smaller Dx's are to the right of larger Dx's ABOVE the horizontal. - if (e->Curr.X == horzEdge->Top.X && horzEdge->NextInLML && - e->Dx < horzEdge->NextInLML->Dx) break; - - if (horzEdge->OutIdx >= 0 && !IsOpen) //note: may be done multiple times - { - op1 = AddOutPt(horzEdge, e->Curr); - TEdge* eNextHorz = m_SortedEdges; - while (eNextHorz) - { - if (eNextHorz->OutIdx >= 0 && - HorzSegmentsOverlap(horzEdge->Bot.X, - horzEdge->Top.X, eNextHorz->Bot.X, eNextHorz->Top.X)) - { - OutPt* op2 = GetLastOutPt(eNextHorz); - AddJoin(op2, op1, eNextHorz->Top); - } - eNextHorz = eNextHorz->NextInSEL; - } - AddGhostJoin(op1, horzEdge->Bot); - } - - //OK, so far we're still in range of the horizontal Edge but make sure - //we're at the last of consec. horizontals when matching with eMaxPair - if(e == eMaxPair && IsLastHorz) - { - if (horzEdge->OutIdx >= 0) - AddLocalMaxPoly(horzEdge, eMaxPair, horzEdge->Top); - DeleteFromAEL(horzEdge); - DeleteFromAEL(eMaxPair); - return; - } - - if(dir == dLeftToRight) - { - IntPoint Pt = IntPoint(e->Curr.X, horzEdge->Curr.Y); - IntersectEdges(horzEdge, e, Pt); - } - else - { - IntPoint Pt = IntPoint(e->Curr.X, horzEdge->Curr.Y); - IntersectEdges( e, horzEdge, Pt); - } - TEdge* eNext = GetNextInAEL(e, dir); - SwapPositionsInAEL( horzEdge, e ); - e = eNext; - } //end while(e) - - //Break out of loop if HorzEdge.NextInLML is not also horizontal ... - if (!horzEdge->NextInLML || !IsHorizontal(*horzEdge->NextInLML)) break; - - UpdateEdgeIntoAEL(horzEdge); - if (horzEdge->OutIdx >= 0) AddOutPt(horzEdge, horzEdge->Bot); - GetHorzDirection(*horzEdge, dir, horzLeft, horzRight); - - } //end for (;;) - - if (horzEdge->OutIdx >= 0 && !op1) - { - op1 = GetLastOutPt(horzEdge); - TEdge* eNextHorz = m_SortedEdges; - while (eNextHorz) - { - if (eNextHorz->OutIdx >= 0 && - HorzSegmentsOverlap(horzEdge->Bot.X, - horzEdge->Top.X, eNextHorz->Bot.X, eNextHorz->Top.X)) - { - OutPt* op2 = GetLastOutPt(eNextHorz); - AddJoin(op2, op1, eNextHorz->Top); - } - eNextHorz = eNextHorz->NextInSEL; - } - AddGhostJoin(op1, horzEdge->Top); - } - - if (horzEdge->NextInLML) - { - if(horzEdge->OutIdx >= 0) - { - op1 = AddOutPt( horzEdge, horzEdge->Top); - UpdateEdgeIntoAEL(horzEdge); - if (horzEdge->WindDelta == 0) return; - //nb: HorzEdge is no longer horizontal here - TEdge* ePrev = horzEdge->PrevInAEL; - TEdge* eNext = horzEdge->NextInAEL; - if (ePrev && ePrev->Curr.X == horzEdge->Bot.X && - ePrev->Curr.Y == horzEdge->Bot.Y && ePrev->WindDelta != 0 && - (ePrev->OutIdx >= 0 && ePrev->Curr.Y > ePrev->Top.Y && - SlopesEqual(*horzEdge, *ePrev, m_UseFullRange))) - { - OutPt* op2 = AddOutPt(ePrev, horzEdge->Bot); - AddJoin(op1, op2, horzEdge->Top); - } - else if (eNext && eNext->Curr.X == horzEdge->Bot.X && - eNext->Curr.Y == horzEdge->Bot.Y && eNext->WindDelta != 0 && - eNext->OutIdx >= 0 && eNext->Curr.Y > eNext->Top.Y && - SlopesEqual(*horzEdge, *eNext, m_UseFullRange)) - { - OutPt* op2 = AddOutPt(eNext, horzEdge->Bot); - AddJoin(op1, op2, horzEdge->Top); - } - } - else - UpdateEdgeIntoAEL(horzEdge); - } - else - { - if (horzEdge->OutIdx >= 0) AddOutPt(horzEdge, horzEdge->Top); - DeleteFromAEL(horzEdge); - } -} -//------------------------------------------------------------------------------ - -bool Clipper::ProcessIntersections(const cInt topY) -{ - if( !m_ActiveEdges ) return true; - try { - BuildIntersectList(topY); - size_t IlSize = m_IntersectList.size(); - if (IlSize == 0) return true; - if (IlSize == 1 || FixupIntersectionOrder()) ProcessIntersectList(); - else return false; - } - catch(...) - { - m_SortedEdges = 0; - DisposeIntersectNodes(); - throw clipperException("ProcessIntersections error"); - } - m_SortedEdges = 0; - return true; -} -//------------------------------------------------------------------------------ - -void Clipper::DisposeIntersectNodes() -{ - for (size_t i = 0; i < m_IntersectList.size(); ++i ) - delete m_IntersectList[i]; - m_IntersectList.clear(); -} -//------------------------------------------------------------------------------ - -void Clipper::BuildIntersectList(const cInt topY) -{ - if ( !m_ActiveEdges ) return; - - //prepare for sorting ... - TEdge* e = m_ActiveEdges; - m_SortedEdges = e; - while( e ) - { - e->PrevInSEL = e->PrevInAEL; - e->NextInSEL = e->NextInAEL; - e->Curr.X = TopX( *e, topY ); - e = e->NextInAEL; - } - - //bubblesort ... - bool isModified; - do - { - isModified = false; - e = m_SortedEdges; - while( e->NextInSEL ) - { - TEdge *eNext = e->NextInSEL; - IntPoint Pt; - if(e->Curr.X > eNext->Curr.X) - { - IntersectPoint(*e, *eNext, Pt); - if (Pt.Y < topY) Pt = IntPoint(TopX(*e, topY), topY); - IntersectNode * newNode = new IntersectNode; - newNode->Edge1 = e; - newNode->Edge2 = eNext; - newNode->Pt = Pt; - m_IntersectList.push_back(newNode); - - SwapPositionsInSEL(e, eNext); - isModified = true; - } - else - e = eNext; - } - if( e->PrevInSEL ) e->PrevInSEL->NextInSEL = 0; - else break; - } - while ( isModified ); - m_SortedEdges = 0; //important -} -//------------------------------------------------------------------------------ - - -void Clipper::ProcessIntersectList() -{ - for (size_t i = 0; i < m_IntersectList.size(); ++i) - { - IntersectNode* iNode = m_IntersectList[i]; - { - IntersectEdges( iNode->Edge1, iNode->Edge2, iNode->Pt); - SwapPositionsInAEL( iNode->Edge1 , iNode->Edge2 ); - } - delete iNode; - } - m_IntersectList.clear(); -} -//------------------------------------------------------------------------------ - -bool IntersectListSort(IntersectNode* node1, IntersectNode* node2) -{ - return node2->Pt.Y < node1->Pt.Y; -} -//------------------------------------------------------------------------------ - -inline bool EdgesAdjacent(const IntersectNode &inode) -{ - return (inode.Edge1->NextInSEL == inode.Edge2) || - (inode.Edge1->PrevInSEL == inode.Edge2); -} -//------------------------------------------------------------------------------ - -bool Clipper::FixupIntersectionOrder() -{ - //pre-condition: intersections are sorted Bottom-most first. - //Now it's crucial that intersections are made only between adjacent edges, - //so to ensure this the order of intersections may need adjusting ... - CopyAELToSEL(); - std::sort(m_IntersectList.begin(), m_IntersectList.end(), IntersectListSort); - size_t cnt = m_IntersectList.size(); - for (size_t i = 0; i < cnt; ++i) - { - if (!EdgesAdjacent(*m_IntersectList[i])) - { - size_t j = i + 1; - while (j < cnt && !EdgesAdjacent(*m_IntersectList[j])) j++; - if (j == cnt) return false; - std::swap(m_IntersectList[i], m_IntersectList[j]); - } - SwapPositionsInSEL(m_IntersectList[i]->Edge1, m_IntersectList[i]->Edge2); - } - return true; -} -//------------------------------------------------------------------------------ - -void Clipper::DoMaxima(TEdge *e) -{ - TEdge* eMaxPair = GetMaximaPairEx(e); - if (!eMaxPair) - { - if (e->OutIdx >= 0) - AddOutPt(e, e->Top); - DeleteFromAEL(e); - return; - } - - TEdge* eNext = e->NextInAEL; - while(eNext && eNext != eMaxPair) - { - IntersectEdges(e, eNext, e->Top); - SwapPositionsInAEL(e, eNext); - eNext = e->NextInAEL; - } - - if(e->OutIdx == Unassigned && eMaxPair->OutIdx == Unassigned) - { - DeleteFromAEL(e); - DeleteFromAEL(eMaxPair); - } - else if( e->OutIdx >= 0 && eMaxPair->OutIdx >= 0 ) - { - if (e->OutIdx >= 0) AddLocalMaxPoly(e, eMaxPair, e->Top); - DeleteFromAEL(e); - DeleteFromAEL(eMaxPair); - } -#ifdef use_lines - else if (e->WindDelta == 0) - { - if (e->OutIdx >= 0) - { - AddOutPt(e, e->Top); - e->OutIdx = Unassigned; - } - DeleteFromAEL(e); - - if (eMaxPair->OutIdx >= 0) - { - AddOutPt(eMaxPair, e->Top); - eMaxPair->OutIdx = Unassigned; - } - DeleteFromAEL(eMaxPair); - } -#endif - else throw clipperException("DoMaxima error"); -} -//------------------------------------------------------------------------------ - -void Clipper::ProcessEdgesAtTopOfScanbeam(const cInt topY) -{ - TEdge* e = m_ActiveEdges; - while( e ) - { - //1. process maxima, treating them as if they're 'bent' horizontal edges, - // but exclude maxima with horizontal edges. nb: e can't be a horizontal. - bool IsMaximaEdge = IsMaxima(e, topY); - - if(IsMaximaEdge) - { - TEdge* eMaxPair = GetMaximaPairEx(e); - IsMaximaEdge = (!eMaxPair || !IsHorizontal(*eMaxPair)); - } - - if(IsMaximaEdge) - { - if (m_StrictSimple) m_Maxima.push_back(e->Top.X); - TEdge* ePrev = e->PrevInAEL; - DoMaxima(e); - if( !ePrev ) e = m_ActiveEdges; - else e = ePrev->NextInAEL; - } - else - { - //2. promote horizontal edges, otherwise update Curr.X and Curr.Y ... - if (IsIntermediate(e, topY) && IsHorizontal(*e->NextInLML)) - { - UpdateEdgeIntoAEL(e); - if (e->OutIdx >= 0) - AddOutPt(e, e->Bot); - AddEdgeToSEL(e); - } - else - { - e->Curr.X = TopX( *e, topY ); - e->Curr.Y = topY; - } - - //When StrictlySimple and 'e' is being touched by another edge, then - //make sure both edges have a vertex here ... - if (m_StrictSimple) - { - TEdge* ePrev = e->PrevInAEL; - if ((e->OutIdx >= 0) && (e->WindDelta != 0) && ePrev && (ePrev->OutIdx >= 0) && - (ePrev->Curr.X == e->Curr.X) && (ePrev->WindDelta != 0)) - { - IntPoint pt = e->Curr; -#ifdef use_xyz - SetZ(pt, *ePrev, *e); -#endif - OutPt* op = AddOutPt(ePrev, pt); - OutPt* op2 = AddOutPt(e, pt); - AddJoin(op, op2, pt); //StrictlySimple (type-3) join - } - } - - e = e->NextInAEL; - } - } - - //3. Process horizontals at the Top of the scanbeam ... - m_Maxima.sort(); - ProcessHorizontals(); - m_Maxima.clear(); - - //4. Promote intermediate vertices ... - e = m_ActiveEdges; - while(e) - { - if(IsIntermediate(e, topY)) - { - OutPt* op = 0; - if( e->OutIdx >= 0 ) - op = AddOutPt(e, e->Top); - UpdateEdgeIntoAEL(e); - - //if output polygons share an edge, they'll need joining later ... - TEdge* ePrev = e->PrevInAEL; - TEdge* eNext = e->NextInAEL; - if (ePrev && ePrev->Curr.X == e->Bot.X && - ePrev->Curr.Y == e->Bot.Y && op && - ePrev->OutIdx >= 0 && ePrev->Curr.Y > ePrev->Top.Y && - SlopesEqual(e->Curr, e->Top, ePrev->Curr, ePrev->Top, m_UseFullRange) && - (e->WindDelta != 0) && (ePrev->WindDelta != 0)) - { - OutPt* op2 = AddOutPt(ePrev, e->Bot); - AddJoin(op, op2, e->Top); - } - else if (eNext && eNext->Curr.X == e->Bot.X && - eNext->Curr.Y == e->Bot.Y && op && - eNext->OutIdx >= 0 && eNext->Curr.Y > eNext->Top.Y && - SlopesEqual(e->Curr, e->Top, eNext->Curr, eNext->Top, m_UseFullRange) && - (e->WindDelta != 0) && (eNext->WindDelta != 0)) - { - OutPt* op2 = AddOutPt(eNext, e->Bot); - AddJoin(op, op2, e->Top); - } - } - e = e->NextInAEL; - } -} -//------------------------------------------------------------------------------ - -void Clipper::FixupOutPolyline(OutRec &outrec) -{ - OutPt *pp = outrec.Pts; - OutPt *lastPP = pp->Prev; - while (pp != lastPP) - { - pp = pp->Next; - if (pp->Pt == pp->Prev->Pt) - { - if (pp == lastPP) lastPP = pp->Prev; - OutPt *tmpPP = pp->Prev; - tmpPP->Next = pp->Next; - pp->Next->Prev = tmpPP; - delete pp; - pp = tmpPP; - } - } - - if (pp == pp->Prev) - { - DisposeOutPts(pp); - outrec.Pts = 0; - return; - } -} -//------------------------------------------------------------------------------ - -void Clipper::FixupOutPolygon(OutRec &outrec) -{ - //FixupOutPolygon() - removes duplicate points and simplifies consecutive - //parallel edges by removing the middle vertex. - OutPt *lastOK = 0; - outrec.BottomPt = 0; - OutPt *pp = outrec.Pts; - bool preserveCol = m_PreserveCollinear || m_StrictSimple; - - for (;;) - { - if (pp->Prev == pp || pp->Prev == pp->Next) - { - DisposeOutPts(pp); - outrec.Pts = 0; - return; - } - - //test for duplicate points and collinear edges ... - if ((pp->Pt == pp->Next->Pt) || (pp->Pt == pp->Prev->Pt) || - (SlopesEqual(pp->Prev->Pt, pp->Pt, pp->Next->Pt, m_UseFullRange) && - (!preserveCol || !Pt2IsBetweenPt1AndPt3(pp->Prev->Pt, pp->Pt, pp->Next->Pt)))) - { - lastOK = 0; - OutPt *tmp = pp; - pp->Prev->Next = pp->Next; - pp->Next->Prev = pp->Prev; - pp = pp->Prev; - delete tmp; - } - else if (pp == lastOK) break; - else - { - if (!lastOK) lastOK = pp; - pp = pp->Next; - } - } - outrec.Pts = pp; -} -//------------------------------------------------------------------------------ - -int PointCount(OutPt *Pts) -{ - if (!Pts) return 0; - int result = 0; - OutPt* p = Pts; - do - { - result++; - p = p->Next; - } - while (p != Pts); - return result; -} -//------------------------------------------------------------------------------ - -void Clipper::BuildResult(Paths &polys) -{ - polys.reserve(m_PolyOuts.size()); - for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) - { - if (!m_PolyOuts[i]->Pts) continue; - Path pg; - OutPt* p = m_PolyOuts[i]->Pts->Prev; - int cnt = PointCount(p); - if (cnt < 2) continue; - pg.reserve(cnt); - for (int i = 0; i < cnt; ++i) - { - pg.push_back(p->Pt); - p = p->Prev; - } - polys.push_back(pg); - } -} -//------------------------------------------------------------------------------ - -void Clipper::BuildResult2(PolyTree& polytree) -{ - polytree.Clear(); - polytree.AllNodes.reserve(m_PolyOuts.size()); - //add each output polygon/contour to polytree ... - for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); i++) - { - OutRec* outRec = m_PolyOuts[i]; - int cnt = PointCount(outRec->Pts); - if ((outRec->IsOpen && cnt < 2) || (!outRec->IsOpen && cnt < 3)) continue; - FixHoleLinkage(*outRec); - PolyNode* pn = new PolyNode(); - //nb: polytree takes ownership of all the PolyNodes - polytree.AllNodes.push_back(pn); - outRec->PolyNd = pn; - pn->Parent = 0; - pn->Index = 0; - pn->Contour.reserve(cnt); - OutPt *op = outRec->Pts->Prev; - for (int j = 0; j < cnt; j++) - { - pn->Contour.push_back(op->Pt); - op = op->Prev; - } - } - - //fixup PolyNode links etc ... - polytree.Childs.reserve(m_PolyOuts.size()); - for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); i++) - { - OutRec* outRec = m_PolyOuts[i]; - if (!outRec->PolyNd) continue; - if (outRec->IsOpen) - { - outRec->PolyNd->m_IsOpen = true; - polytree.AddChild(*outRec->PolyNd); - } - else if (outRec->FirstLeft && outRec->FirstLeft->PolyNd) - outRec->FirstLeft->PolyNd->AddChild(*outRec->PolyNd); - else - polytree.AddChild(*outRec->PolyNd); - } -} -//------------------------------------------------------------------------------ - -void SwapIntersectNodes(IntersectNode &int1, IntersectNode &int2) -{ - //just swap the contents (because fIntersectNodes is a single-linked-list) - IntersectNode inode = int1; //gets a copy of Int1 - int1.Edge1 = int2.Edge1; - int1.Edge2 = int2.Edge2; - int1.Pt = int2.Pt; - int2.Edge1 = inode.Edge1; - int2.Edge2 = inode.Edge2; - int2.Pt = inode.Pt; -} -//------------------------------------------------------------------------------ - -inline bool E2InsertsBeforeE1(TEdge &e1, TEdge &e2) -{ - if (e2.Curr.X == e1.Curr.X) - { - if (e2.Top.Y > e1.Top.Y) - return e2.Top.X < TopX(e1, e2.Top.Y); - else return e1.Top.X > TopX(e2, e1.Top.Y); - } - else return e2.Curr.X < e1.Curr.X; -} -//------------------------------------------------------------------------------ - -bool GetOverlap(const cInt a1, const cInt a2, const cInt b1, const cInt b2, - cInt& Left, cInt& Right) -{ - if (a1 < a2) - { - if (b1 < b2) {Left = std::max(a1,b1); Right = std::min(a2,b2);} - else {Left = std::max(a1,b2); Right = std::min(a2,b1);} - } - else - { - if (b1 < b2) {Left = std::max(a2,b1); Right = std::min(a1,b2);} - else {Left = std::max(a2,b2); Right = std::min(a1,b1);} - } - return Left < Right; -} -//------------------------------------------------------------------------------ - -inline void UpdateOutPtIdxs(OutRec& outrec) -{ - OutPt* op = outrec.Pts; - do - { - op->Idx = outrec.Idx; - op = op->Prev; - } - while(op != outrec.Pts); -} -//------------------------------------------------------------------------------ - -void Clipper::InsertEdgeIntoAEL(TEdge *edge, TEdge* startEdge) -{ - if(!m_ActiveEdges) - { - edge->PrevInAEL = 0; - edge->NextInAEL = 0; - m_ActiveEdges = edge; - } - else if(!startEdge && E2InsertsBeforeE1(*m_ActiveEdges, *edge)) - { - edge->PrevInAEL = 0; - edge->NextInAEL = m_ActiveEdges; - m_ActiveEdges->PrevInAEL = edge; - m_ActiveEdges = edge; - } - else - { - if(!startEdge) startEdge = m_ActiveEdges; - while(startEdge->NextInAEL && - !E2InsertsBeforeE1(*startEdge->NextInAEL , *edge)) - startEdge = startEdge->NextInAEL; - edge->NextInAEL = startEdge->NextInAEL; - if(startEdge->NextInAEL) startEdge->NextInAEL->PrevInAEL = edge; - edge->PrevInAEL = startEdge; - startEdge->NextInAEL = edge; - } -} -//---------------------------------------------------------------------- - -OutPt* DupOutPt(OutPt* outPt, bool InsertAfter) -{ - OutPt* result = new OutPt; - result->Pt = outPt->Pt; - result->Idx = outPt->Idx; - if (InsertAfter) - { - result->Next = outPt->Next; - result->Prev = outPt; - outPt->Next->Prev = result; - outPt->Next = result; - } - else - { - result->Prev = outPt->Prev; - result->Next = outPt; - outPt->Prev->Next = result; - outPt->Prev = result; - } - return result; -} -//------------------------------------------------------------------------------ - -bool JoinHorz(OutPt* op1, OutPt* op1b, OutPt* op2, OutPt* op2b, - const IntPoint Pt, bool DiscardLeft) -{ - Direction Dir1 = (op1->Pt.X > op1b->Pt.X ? dRightToLeft : dLeftToRight); - Direction Dir2 = (op2->Pt.X > op2b->Pt.X ? dRightToLeft : dLeftToRight); - if (Dir1 == Dir2) return false; - - //When DiscardLeft, we want Op1b to be on the Left of Op1, otherwise we - //want Op1b to be on the Right. (And likewise with Op2 and Op2b.) - //So, to facilitate this while inserting Op1b and Op2b ... - //when DiscardLeft, make sure we're AT or RIGHT of Pt before adding Op1b, - //otherwise make sure we're AT or LEFT of Pt. (Likewise with Op2b.) - if (Dir1 == dLeftToRight) - { - while (op1->Next->Pt.X <= Pt.X && - op1->Next->Pt.X >= op1->Pt.X && op1->Next->Pt.Y == Pt.Y) - op1 = op1->Next; - if (DiscardLeft && (op1->Pt.X != Pt.X)) op1 = op1->Next; - op1b = DupOutPt(op1, !DiscardLeft); - if (op1b->Pt != Pt) - { - op1 = op1b; - op1->Pt = Pt; - op1b = DupOutPt(op1, !DiscardLeft); - } - } - else - { - while (op1->Next->Pt.X >= Pt.X && - op1->Next->Pt.X <= op1->Pt.X && op1->Next->Pt.Y == Pt.Y) - op1 = op1->Next; - if (!DiscardLeft && (op1->Pt.X != Pt.X)) op1 = op1->Next; - op1b = DupOutPt(op1, DiscardLeft); - if (op1b->Pt != Pt) - { - op1 = op1b; - op1->Pt = Pt; - op1b = DupOutPt(op1, DiscardLeft); - } - } - - if (Dir2 == dLeftToRight) - { - while (op2->Next->Pt.X <= Pt.X && - op2->Next->Pt.X >= op2->Pt.X && op2->Next->Pt.Y == Pt.Y) - op2 = op2->Next; - if (DiscardLeft && (op2->Pt.X != Pt.X)) op2 = op2->Next; - op2b = DupOutPt(op2, !DiscardLeft); - if (op2b->Pt != Pt) - { - op2 = op2b; - op2->Pt = Pt; - op2b = DupOutPt(op2, !DiscardLeft); - }; - } else - { - while (op2->Next->Pt.X >= Pt.X && - op2->Next->Pt.X <= op2->Pt.X && op2->Next->Pt.Y == Pt.Y) - op2 = op2->Next; - if (!DiscardLeft && (op2->Pt.X != Pt.X)) op2 = op2->Next; - op2b = DupOutPt(op2, DiscardLeft); - if (op2b->Pt != Pt) - { - op2 = op2b; - op2->Pt = Pt; - op2b = DupOutPt(op2, DiscardLeft); - }; - }; - - if ((Dir1 == dLeftToRight) == DiscardLeft) - { - op1->Prev = op2; - op2->Next = op1; - op1b->Next = op2b; - op2b->Prev = op1b; - } - else - { - op1->Next = op2; - op2->Prev = op1; - op1b->Prev = op2b; - op2b->Next = op1b; - } - return true; -} -//------------------------------------------------------------------------------ - -bool Clipper::JoinPoints(Join *j, OutRec* outRec1, OutRec* outRec2) -{ - OutPt *op1 = j->OutPt1, *op1b; - OutPt *op2 = j->OutPt2, *op2b; - - //There are 3 kinds of joins for output polygons ... - //1. Horizontal joins where Join.OutPt1 & Join.OutPt2 are vertices anywhere - //along (horizontal) collinear edges (& Join.OffPt is on the same horizontal). - //2. Non-horizontal joins where Join.OutPt1 & Join.OutPt2 are at the same - //location at the Bottom of the overlapping segment (& Join.OffPt is above). - //3. StrictSimple joins where edges touch but are not collinear and where - //Join.OutPt1, Join.OutPt2 & Join.OffPt all share the same point. - bool isHorizontal = (j->OutPt1->Pt.Y == j->OffPt.Y); - - if (isHorizontal && (j->OffPt == j->OutPt1->Pt) && - (j->OffPt == j->OutPt2->Pt)) - { - //Strictly Simple join ... - if (outRec1 != outRec2) return false; - op1b = j->OutPt1->Next; - while (op1b != op1 && (op1b->Pt == j->OffPt)) - op1b = op1b->Next; - bool reverse1 = (op1b->Pt.Y > j->OffPt.Y); - op2b = j->OutPt2->Next; - while (op2b != op2 && (op2b->Pt == j->OffPt)) - op2b = op2b->Next; - bool reverse2 = (op2b->Pt.Y > j->OffPt.Y); - if (reverse1 == reverse2) return false; - if (reverse1) - { - op1b = DupOutPt(op1, false); - op2b = DupOutPt(op2, true); - op1->Prev = op2; - op2->Next = op1; - op1b->Next = op2b; - op2b->Prev = op1b; - j->OutPt1 = op1; - j->OutPt2 = op1b; - return true; - } else - { - op1b = DupOutPt(op1, true); - op2b = DupOutPt(op2, false); - op1->Next = op2; - op2->Prev = op1; - op1b->Prev = op2b; - op2b->Next = op1b; - j->OutPt1 = op1; - j->OutPt2 = op1b; - return true; - } - } - else if (isHorizontal) - { - //treat horizontal joins differently to non-horizontal joins since with - //them we're not yet sure where the overlapping is. OutPt1.Pt & OutPt2.Pt - //may be anywhere along the horizontal edge. - op1b = op1; - while (op1->Prev->Pt.Y == op1->Pt.Y && op1->Prev != op1b && op1->Prev != op2) - op1 = op1->Prev; - while (op1b->Next->Pt.Y == op1b->Pt.Y && op1b->Next != op1 && op1b->Next != op2) - op1b = op1b->Next; - if (op1b->Next == op1 || op1b->Next == op2) return false; //a flat 'polygon' - - op2b = op2; - while (op2->Prev->Pt.Y == op2->Pt.Y && op2->Prev != op2b && op2->Prev != op1b) - op2 = op2->Prev; - while (op2b->Next->Pt.Y == op2b->Pt.Y && op2b->Next != op2 && op2b->Next != op1) - op2b = op2b->Next; - if (op2b->Next == op2 || op2b->Next == op1) return false; //a flat 'polygon' - - cInt Left, Right; - //Op1 --> Op1b & Op2 --> Op2b are the extremites of the horizontal edges - if (!GetOverlap(op1->Pt.X, op1b->Pt.X, op2->Pt.X, op2b->Pt.X, Left, Right)) - return false; - - //DiscardLeftSide: when overlapping edges are joined, a spike will created - //which needs to be cleaned up. However, we don't want Op1 or Op2 caught up - //on the discard Side as either may still be needed for other joins ... - IntPoint Pt; - bool DiscardLeftSide; - if (op1->Pt.X >= Left && op1->Pt.X <= Right) - { - Pt = op1->Pt; DiscardLeftSide = (op1->Pt.X > op1b->Pt.X); - } - else if (op2->Pt.X >= Left&& op2->Pt.X <= Right) - { - Pt = op2->Pt; DiscardLeftSide = (op2->Pt.X > op2b->Pt.X); - } - else if (op1b->Pt.X >= Left && op1b->Pt.X <= Right) - { - Pt = op1b->Pt; DiscardLeftSide = op1b->Pt.X > op1->Pt.X; - } - else - { - Pt = op2b->Pt; DiscardLeftSide = (op2b->Pt.X > op2->Pt.X); - } - j->OutPt1 = op1; j->OutPt2 = op2; - return JoinHorz(op1, op1b, op2, op2b, Pt, DiscardLeftSide); - } else - { - //nb: For non-horizontal joins ... - // 1. Jr.OutPt1.Pt.Y == Jr.OutPt2.Pt.Y - // 2. Jr.OutPt1.Pt > Jr.OffPt.Y - - //make sure the polygons are correctly oriented ... - op1b = op1->Next; - while ((op1b->Pt == op1->Pt) && (op1b != op1)) op1b = op1b->Next; - bool Reverse1 = ((op1b->Pt.Y > op1->Pt.Y) || - !SlopesEqual(op1->Pt, op1b->Pt, j->OffPt, m_UseFullRange)); - if (Reverse1) - { - op1b = op1->Prev; - while ((op1b->Pt == op1->Pt) && (op1b != op1)) op1b = op1b->Prev; - if ((op1b->Pt.Y > op1->Pt.Y) || - !SlopesEqual(op1->Pt, op1b->Pt, j->OffPt, m_UseFullRange)) return false; - }; - op2b = op2->Next; - while ((op2b->Pt == op2->Pt) && (op2b != op2))op2b = op2b->Next; - bool Reverse2 = ((op2b->Pt.Y > op2->Pt.Y) || - !SlopesEqual(op2->Pt, op2b->Pt, j->OffPt, m_UseFullRange)); - if (Reverse2) - { - op2b = op2->Prev; - while ((op2b->Pt == op2->Pt) && (op2b != op2)) op2b = op2b->Prev; - if ((op2b->Pt.Y > op2->Pt.Y) || - !SlopesEqual(op2->Pt, op2b->Pt, j->OffPt, m_UseFullRange)) return false; - } - - if ((op1b == op1) || (op2b == op2) || (op1b == op2b) || - ((outRec1 == outRec2) && (Reverse1 == Reverse2))) return false; - - if (Reverse1) - { - op1b = DupOutPt(op1, false); - op2b = DupOutPt(op2, true); - op1->Prev = op2; - op2->Next = op1; - op1b->Next = op2b; - op2b->Prev = op1b; - j->OutPt1 = op1; - j->OutPt2 = op1b; - return true; - } else - { - op1b = DupOutPt(op1, true); - op2b = DupOutPt(op2, false); - op1->Next = op2; - op2->Prev = op1; - op1b->Prev = op2b; - op2b->Next = op1b; - j->OutPt1 = op1; - j->OutPt2 = op1b; - return true; - } - } -} -//---------------------------------------------------------------------- - -static OutRec* ParseFirstLeft(OutRec* FirstLeft) -{ - while (FirstLeft && !FirstLeft->Pts) - FirstLeft = FirstLeft->FirstLeft; - return FirstLeft; -} -//------------------------------------------------------------------------------ - -void Clipper::FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec) -{ - //tests if NewOutRec contains the polygon before reassigning FirstLeft - for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) - { - OutRec* outRec = m_PolyOuts[i]; - OutRec* firstLeft = ParseFirstLeft(outRec->FirstLeft); - if (outRec->Pts && firstLeft == OldOutRec) - { - if (Poly2ContainsPoly1(outRec->Pts, NewOutRec->Pts)) - outRec->FirstLeft = NewOutRec; - } - } -} -//---------------------------------------------------------------------- - -void Clipper::FixupFirstLefts2(OutRec* InnerOutRec, OutRec* OuterOutRec) -{ - //A polygon has split into two such that one is now the inner of the other. - //It's possible that these polygons now wrap around other polygons, so check - //every polygon that's also contained by OuterOutRec's FirstLeft container - //(including 0) to see if they've become inner to the new inner polygon ... - OutRec* orfl = OuterOutRec->FirstLeft; - for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) - { - OutRec* outRec = m_PolyOuts[i]; - - if (!outRec->Pts || outRec == OuterOutRec || outRec == InnerOutRec) - continue; - OutRec* firstLeft = ParseFirstLeft(outRec->FirstLeft); - if (firstLeft != orfl && firstLeft != InnerOutRec && firstLeft != OuterOutRec) - continue; - if (Poly2ContainsPoly1(outRec->Pts, InnerOutRec->Pts)) - outRec->FirstLeft = InnerOutRec; - else if (Poly2ContainsPoly1(outRec->Pts, OuterOutRec->Pts)) - outRec->FirstLeft = OuterOutRec; - else if (outRec->FirstLeft == InnerOutRec || outRec->FirstLeft == OuterOutRec) - outRec->FirstLeft = orfl; - } -} -//---------------------------------------------------------------------- -void Clipper::FixupFirstLefts3(OutRec* OldOutRec, OutRec* NewOutRec) -{ - //reassigns FirstLeft WITHOUT testing if NewOutRec contains the polygon - for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) - { - OutRec* outRec = m_PolyOuts[i]; - OutRec* firstLeft = ParseFirstLeft(outRec->FirstLeft); - if (outRec->Pts && outRec->FirstLeft == OldOutRec) - outRec->FirstLeft = NewOutRec; - } -} -//---------------------------------------------------------------------- - -void Clipper::JoinCommonEdges() -{ - for (JoinList::size_type i = 0; i < m_Joins.size(); i++) - { - Join* join = m_Joins[i]; - - OutRec *outRec1 = GetOutRec(join->OutPt1->Idx); - OutRec *outRec2 = GetOutRec(join->OutPt2->Idx); - - if (!outRec1->Pts || !outRec2->Pts) continue; - if (outRec1->IsOpen || outRec2->IsOpen) continue; - - //get the polygon fragment with the correct hole state (FirstLeft) - //before calling JoinPoints() ... - OutRec *holeStateRec; - if (outRec1 == outRec2) holeStateRec = outRec1; - else if (OutRec1RightOfOutRec2(outRec1, outRec2)) holeStateRec = outRec2; - else if (OutRec1RightOfOutRec2(outRec2, outRec1)) holeStateRec = outRec1; - else holeStateRec = GetLowermostRec(outRec1, outRec2); - - if (!JoinPoints(join, outRec1, outRec2)) continue; - - if (outRec1 == outRec2) - { - //instead of joining two polygons, we've just created a new one by - //splitting one polygon into two. - outRec1->Pts = join->OutPt1; - outRec1->BottomPt = 0; - outRec2 = CreateOutRec(); - outRec2->Pts = join->OutPt2; - - //update all OutRec2.Pts Idx's ... - UpdateOutPtIdxs(*outRec2); - - if (Poly2ContainsPoly1(outRec2->Pts, outRec1->Pts)) - { - //outRec1 contains outRec2 ... - outRec2->IsHole = !outRec1->IsHole; - outRec2->FirstLeft = outRec1; - - if (m_UsingPolyTree) FixupFirstLefts2(outRec2, outRec1); - - if ((outRec2->IsHole ^ m_ReverseOutput) == (Area(*outRec2) > 0)) - ReversePolyPtLinks(outRec2->Pts); - - } else if (Poly2ContainsPoly1(outRec1->Pts, outRec2->Pts)) - { - //outRec2 contains outRec1 ... - outRec2->IsHole = outRec1->IsHole; - outRec1->IsHole = !outRec2->IsHole; - outRec2->FirstLeft = outRec1->FirstLeft; - outRec1->FirstLeft = outRec2; - - if (m_UsingPolyTree) FixupFirstLefts2(outRec1, outRec2); - - if ((outRec1->IsHole ^ m_ReverseOutput) == (Area(*outRec1) > 0)) - ReversePolyPtLinks(outRec1->Pts); - } - else - { - //the 2 polygons are completely separate ... - outRec2->IsHole = outRec1->IsHole; - outRec2->FirstLeft = outRec1->FirstLeft; - - //fixup FirstLeft pointers that may need reassigning to OutRec2 - if (m_UsingPolyTree) FixupFirstLefts1(outRec1, outRec2); - } - - } else - { - //joined 2 polygons together ... - - outRec2->Pts = 0; - outRec2->BottomPt = 0; - outRec2->Idx = outRec1->Idx; - - outRec1->IsHole = holeStateRec->IsHole; - if (holeStateRec == outRec2) - outRec1->FirstLeft = outRec2->FirstLeft; - outRec2->FirstLeft = outRec1; - - if (m_UsingPolyTree) FixupFirstLefts3(outRec2, outRec1); - } - } -} - -//------------------------------------------------------------------------------ -// ClipperOffset support functions ... -//------------------------------------------------------------------------------ - -DoublePoint GetUnitNormal(const IntPoint &pt1, const IntPoint &pt2) -{ - if(pt2.X == pt1.X && pt2.Y == pt1.Y) - return DoublePoint(0, 0); - - double Dx = (double)(pt2.X - pt1.X); - double dy = (double)(pt2.Y - pt1.Y); - double f = 1 *1.0/ std::sqrt( Dx*Dx + dy*dy ); - Dx *= f; - dy *= f; - return DoublePoint(dy, -Dx); -} - -//------------------------------------------------------------------------------ -// ClipperOffset class -//------------------------------------------------------------------------------ - -ClipperOffset::ClipperOffset(double miterLimit, double arcTolerance) -{ - this->MiterLimit = miterLimit; - this->ArcTolerance = arcTolerance; - m_lowest.X = -1; -} -//------------------------------------------------------------------------------ - -ClipperOffset::~ClipperOffset() -{ - Clear(); -} -//------------------------------------------------------------------------------ - -void ClipperOffset::Clear() -{ - for (int i = 0; i < m_polyNodes.ChildCount(); ++i) - delete m_polyNodes.Childs[i]; - m_polyNodes.Childs.clear(); - m_lowest.X = -1; -} -//------------------------------------------------------------------------------ - -void ClipperOffset::AddPath(const Path& path, JoinType joinType, EndType endType) -{ - int highI = (int)path.size() - 1; - if (highI < 0) return; - PolyNode* newNode = new PolyNode(); - newNode->m_jointype = joinType; - newNode->m_endtype = endType; - - //strip duplicate points from path and also get index to the lowest point ... - if (endType == etClosedLine || endType == etClosedPolygon) - while (highI > 0 && path[0] == path[highI]) highI--; - newNode->Contour.reserve(highI + 1); - newNode->Contour.push_back(path[0]); - int j = 0, k = 0; - for (int i = 1; i <= highI; i++) - if (newNode->Contour[j] != path[i]) - { - j++; - newNode->Contour.push_back(path[i]); - if (path[i].Y > newNode->Contour[k].Y || - (path[i].Y == newNode->Contour[k].Y && - path[i].X < newNode->Contour[k].X)) k = j; - } - if (endType == etClosedPolygon && j < 2) - { - delete newNode; - return; - } - m_polyNodes.AddChild(*newNode); - - //if this path's lowest pt is lower than all the others then update m_lowest - if (endType != etClosedPolygon) return; - if (m_lowest.X < 0) - m_lowest = IntPoint(m_polyNodes.ChildCount() - 1, k); - else - { - IntPoint ip = m_polyNodes.Childs[(int)m_lowest.X]->Contour[(int)m_lowest.Y]; - if (newNode->Contour[k].Y > ip.Y || - (newNode->Contour[k].Y == ip.Y && - newNode->Contour[k].X < ip.X)) - m_lowest = IntPoint(m_polyNodes.ChildCount() - 1, k); - } -} -//------------------------------------------------------------------------------ - -void ClipperOffset::AddPaths(const Paths& paths, JoinType joinType, EndType endType) -{ - for (Paths::size_type i = 0; i < paths.size(); ++i) - AddPath(paths[i], joinType, endType); -} -//------------------------------------------------------------------------------ - -void ClipperOffset::FixOrientations() -{ - //fixup orientations of all closed paths if the orientation of the - //closed path with the lowermost vertex is wrong ... - if (m_lowest.X >= 0 && - !Orientation(m_polyNodes.Childs[(int)m_lowest.X]->Contour)) - { - for (int i = 0; i < m_polyNodes.ChildCount(); ++i) - { - PolyNode& node = *m_polyNodes.Childs[i]; - if (node.m_endtype == etClosedPolygon || - (node.m_endtype == etClosedLine && Orientation(node.Contour))) - ReversePath(node.Contour); - } - } else - { - for (int i = 0; i < m_polyNodes.ChildCount(); ++i) - { - PolyNode& node = *m_polyNodes.Childs[i]; - if (node.m_endtype == etClosedLine && !Orientation(node.Contour)) - ReversePath(node.Contour); - } - } -} -//------------------------------------------------------------------------------ - -void ClipperOffset::Execute(Paths& solution, double delta) -{ - solution.clear(); - FixOrientations(); - DoOffset(delta); - - //now clean up 'corners' ... - Clipper clpr; - clpr.AddPaths(m_destPolys, ptSubject, true); - if (delta > 0) - { - clpr.Execute(ctUnion, solution, pftPositive, pftPositive); - } - else - { - IntRect r = clpr.GetBounds(); - Path outer(4); - outer[0] = IntPoint(r.left - 10, r.bottom + 10); - outer[1] = IntPoint(r.right + 10, r.bottom + 10); - outer[2] = IntPoint(r.right + 10, r.top - 10); - outer[3] = IntPoint(r.left - 10, r.top - 10); - - clpr.AddPath(outer, ptSubject, true); - clpr.ReverseSolution(true); - clpr.Execute(ctUnion, solution, pftNegative, pftNegative); - if (solution.size() > 0) solution.erase(solution.begin()); - } -} -//------------------------------------------------------------------------------ - -void ClipperOffset::Execute(PolyTree& solution, double delta) -{ - solution.Clear(); - FixOrientations(); - DoOffset(delta); - - //now clean up 'corners' ... - Clipper clpr; - clpr.AddPaths(m_destPolys, ptSubject, true); - if (delta > 0) - { - clpr.Execute(ctUnion, solution, pftPositive, pftPositive); - } - else - { - IntRect r = clpr.GetBounds(); - Path outer(4); - outer[0] = IntPoint(r.left - 10, r.bottom + 10); - outer[1] = IntPoint(r.right + 10, r.bottom + 10); - outer[2] = IntPoint(r.right + 10, r.top - 10); - outer[3] = IntPoint(r.left - 10, r.top - 10); - - clpr.AddPath(outer, ptSubject, true); - clpr.ReverseSolution(true); - clpr.Execute(ctUnion, solution, pftNegative, pftNegative); - //remove the outer PolyNode rectangle ... - if (solution.ChildCount() == 1 && solution.Childs[0]->ChildCount() > 0) - { - PolyNode* outerNode = solution.Childs[0]; - solution.Childs.reserve(outerNode->ChildCount()); - solution.Childs[0] = outerNode->Childs[0]; - solution.Childs[0]->Parent = outerNode->Parent; - for (int i = 1; i < outerNode->ChildCount(); ++i) - solution.AddChild(*outerNode->Childs[i]); - } - else - solution.Clear(); - } -} -//------------------------------------------------------------------------------ - -void ClipperOffset::DoOffset(double delta) -{ - m_destPolys.clear(); - m_delta = delta; - - //if Zero offset, just copy any CLOSED polygons to m_p and return ... - if (NEAR_ZERO(delta)) - { - m_destPolys.reserve(m_polyNodes.ChildCount()); - for (int i = 0; i < m_polyNodes.ChildCount(); i++) - { - PolyNode& node = *m_polyNodes.Childs[i]; - if (node.m_endtype == etClosedPolygon) - m_destPolys.push_back(node.Contour); - } - return; - } - - //see offset_triginometry3.svg in the documentation folder ... - if (MiterLimit > 2) m_miterLim = 2/(MiterLimit * MiterLimit); - else m_miterLim = 0.5; - - double y; - if (ArcTolerance <= 0.0) y = def_arc_tolerance; - else if (ArcTolerance > std::fabs(delta) * def_arc_tolerance) - y = std::fabs(delta) * def_arc_tolerance; - else y = ArcTolerance; - //see offset_triginometry2.svg in the documentation folder ... - double steps = pi / std::acos(1 - y / std::fabs(delta)); - if (steps > std::fabs(delta) * pi) - steps = std::fabs(delta) * pi; //ie excessive precision check - m_sin = std::sin(two_pi / steps); - m_cos = std::cos(two_pi / steps); - m_StepsPerRad = steps / two_pi; - if (delta < 0.0) m_sin = -m_sin; - - m_destPolys.reserve(m_polyNodes.ChildCount() * 2); - for (int i = 0; i < m_polyNodes.ChildCount(); i++) - { - PolyNode& node = *m_polyNodes.Childs[i]; - m_srcPoly = node.Contour; - - int len = (int)m_srcPoly.size(); - if (len == 0 || (delta <= 0 && (len < 3 || node.m_endtype != etClosedPolygon))) - continue; - - m_destPoly.clear(); - if (len == 1) - { - if (node.m_jointype == jtRound) - { - double X = 1.0, Y = 0.0; - for (cInt j = 1; j <= steps; j++) - { - m_destPoly.push_back(IntPoint( - Round(m_srcPoly[0].X + X * delta), - Round(m_srcPoly[0].Y + Y * delta))); - double X2 = X; - X = X * m_cos - m_sin * Y; - Y = X2 * m_sin + Y * m_cos; - } - } - else - { - double X = -1.0, Y = -1.0; - for (int j = 0; j < 4; ++j) - { - m_destPoly.push_back(IntPoint( - Round(m_srcPoly[0].X + X * delta), - Round(m_srcPoly[0].Y + Y * delta))); - if (X < 0) X = 1; - else if (Y < 0) Y = 1; - else X = -1; - } - } - m_destPolys.push_back(m_destPoly); - continue; - } - //build m_normals ... - m_normals.clear(); - m_normals.reserve(len); - for (int j = 0; j < len - 1; ++j) - m_normals.push_back(GetUnitNormal(m_srcPoly[j], m_srcPoly[j + 1])); - if (node.m_endtype == etClosedLine || node.m_endtype == etClosedPolygon) - m_normals.push_back(GetUnitNormal(m_srcPoly[len - 1], m_srcPoly[0])); - else - m_normals.push_back(DoublePoint(m_normals[len - 2])); - - if (node.m_endtype == etClosedPolygon) - { - int k = len - 1; - for (int j = 0; j < len; ++j) - OffsetPoint(j, k, node.m_jointype); - m_destPolys.push_back(m_destPoly); - } - else if (node.m_endtype == etClosedLine) - { - int k = len - 1; - for (int j = 0; j < len; ++j) - OffsetPoint(j, k, node.m_jointype); - m_destPolys.push_back(m_destPoly); - m_destPoly.clear(); - //re-build m_normals ... - DoublePoint n = m_normals[len -1]; - for (int j = len - 1; j > 0; j--) - m_normals[j] = DoublePoint(-m_normals[j - 1].X, -m_normals[j - 1].Y); - m_normals[0] = DoublePoint(-n.X, -n.Y); - k = 0; - for (int j = len - 1; j >= 0; j--) - OffsetPoint(j, k, node.m_jointype); - m_destPolys.push_back(m_destPoly); - } - else - { - int k = 0; - for (int j = 1; j < len - 1; ++j) - OffsetPoint(j, k, node.m_jointype); - - IntPoint pt1; - if (node.m_endtype == etOpenButt) - { - int j = len - 1; - pt1 = IntPoint((cInt)Round(m_srcPoly[j].X + m_normals[j].X * - delta), (cInt)Round(m_srcPoly[j].Y + m_normals[j].Y * delta)); - m_destPoly.push_back(pt1); - pt1 = IntPoint((cInt)Round(m_srcPoly[j].X - m_normals[j].X * - delta), (cInt)Round(m_srcPoly[j].Y - m_normals[j].Y * delta)); - m_destPoly.push_back(pt1); - } - else - { - int j = len - 1; - k = len - 2; - m_sinA = 0; - m_normals[j] = DoublePoint(-m_normals[j].X, -m_normals[j].Y); - if (node.m_endtype == etOpenSquare) - DoSquare(j, k); - else - DoRound(j, k); - } - - //re-build m_normals ... - for (int j = len - 1; j > 0; j--) - m_normals[j] = DoublePoint(-m_normals[j - 1].X, -m_normals[j - 1].Y); - m_normals[0] = DoublePoint(-m_normals[1].X, -m_normals[1].Y); - - k = len - 1; - for (int j = k - 1; j > 0; --j) OffsetPoint(j, k, node.m_jointype); - - if (node.m_endtype == etOpenButt) - { - pt1 = IntPoint((cInt)Round(m_srcPoly[0].X - m_normals[0].X * delta), - (cInt)Round(m_srcPoly[0].Y - m_normals[0].Y * delta)); - m_destPoly.push_back(pt1); - pt1 = IntPoint((cInt)Round(m_srcPoly[0].X + m_normals[0].X * delta), - (cInt)Round(m_srcPoly[0].Y + m_normals[0].Y * delta)); - m_destPoly.push_back(pt1); - } - else - { - k = 1; - m_sinA = 0; - if (node.m_endtype == etOpenSquare) - DoSquare(0, 1); - else - DoRound(0, 1); - } - m_destPolys.push_back(m_destPoly); - } - } -} -//------------------------------------------------------------------------------ - -void ClipperOffset::OffsetPoint(int j, int& k, JoinType jointype) -{ - //cross product ... - m_sinA = (m_normals[k].X * m_normals[j].Y - m_normals[j].X * m_normals[k].Y); - if (std::fabs(m_sinA * m_delta) < 1.0) - { - //dot product ... - double cosA = (m_normals[k].X * m_normals[j].X + m_normals[j].Y * m_normals[k].Y ); - if (cosA > 0) // angle => 0 degrees - { - m_destPoly.push_back(IntPoint(Round(m_srcPoly[j].X + m_normals[k].X * m_delta), - Round(m_srcPoly[j].Y + m_normals[k].Y * m_delta))); - return; - } - //else angle => 180 degrees - } - else if (m_sinA > 1.0) m_sinA = 1.0; - else if (m_sinA < -1.0) m_sinA = -1.0; - - if (m_sinA * m_delta < 0) - { - m_destPoly.push_back(IntPoint(Round(m_srcPoly[j].X + m_normals[k].X * m_delta), - Round(m_srcPoly[j].Y + m_normals[k].Y * m_delta))); - m_destPoly.push_back(m_srcPoly[j]); - m_destPoly.push_back(IntPoint(Round(m_srcPoly[j].X + m_normals[j].X * m_delta), - Round(m_srcPoly[j].Y + m_normals[j].Y * m_delta))); - } - else - switch (jointype) - { - case jtMiter: - { - double r = 1 + (m_normals[j].X * m_normals[k].X + - m_normals[j].Y * m_normals[k].Y); - if (r >= m_miterLim) DoMiter(j, k, r); else DoSquare(j, k); - break; - } - case jtSquare: DoSquare(j, k); break; - case jtRound: DoRound(j, k); break; - } - k = j; -} -//------------------------------------------------------------------------------ - -void ClipperOffset::DoSquare(int j, int k) -{ - double dx = std::tan(std::atan2(m_sinA, - m_normals[k].X * m_normals[j].X + m_normals[k].Y * m_normals[j].Y) / 4); - m_destPoly.push_back(IntPoint( - Round(m_srcPoly[j].X + m_delta * (m_normals[k].X - m_normals[k].Y * dx)), - Round(m_srcPoly[j].Y + m_delta * (m_normals[k].Y + m_normals[k].X * dx)))); - m_destPoly.push_back(IntPoint( - Round(m_srcPoly[j].X + m_delta * (m_normals[j].X + m_normals[j].Y * dx)), - Round(m_srcPoly[j].Y + m_delta * (m_normals[j].Y - m_normals[j].X * dx)))); -} -//------------------------------------------------------------------------------ - -void ClipperOffset::DoMiter(int j, int k, double r) -{ - double q = m_delta / r; - m_destPoly.push_back(IntPoint(Round(m_srcPoly[j].X + (m_normals[k].X + m_normals[j].X) * q), - Round(m_srcPoly[j].Y + (m_normals[k].Y + m_normals[j].Y) * q))); -} -//------------------------------------------------------------------------------ - -void ClipperOffset::DoRound(int j, int k) -{ - double a = std::atan2(m_sinA, - m_normals[k].X * m_normals[j].X + m_normals[k].Y * m_normals[j].Y); - int steps = std::max((int)Round(m_StepsPerRad * std::fabs(a)), 1); - - double X = m_normals[k].X, Y = m_normals[k].Y, X2; - for (int i = 0; i < steps; ++i) - { - m_destPoly.push_back(IntPoint( - Round(m_srcPoly[j].X + X * m_delta), - Round(m_srcPoly[j].Y + Y * m_delta))); - X2 = X; - X = X * m_cos - m_sin * Y; - Y = X2 * m_sin + Y * m_cos; - } - m_destPoly.push_back(IntPoint( - Round(m_srcPoly[j].X + m_normals[j].X * m_delta), - Round(m_srcPoly[j].Y + m_normals[j].Y * m_delta))); -} - -//------------------------------------------------------------------------------ -// Miscellaneous public functions -//------------------------------------------------------------------------------ - -void Clipper::DoSimplePolygons() -{ - PolyOutList::size_type i = 0; - while (i < m_PolyOuts.size()) - { - OutRec* outrec = m_PolyOuts[i++]; - OutPt* op = outrec->Pts; - if (!op || outrec->IsOpen) continue; - do //for each Pt in Polygon until duplicate found do ... - { - OutPt* op2 = op->Next; - while (op2 != outrec->Pts) - { - if ((op->Pt == op2->Pt) && op2->Next != op && op2->Prev != op) - { - //split the polygon into two ... - OutPt* op3 = op->Prev; - OutPt* op4 = op2->Prev; - op->Prev = op4; - op4->Next = op; - op2->Prev = op3; - op3->Next = op2; - - outrec->Pts = op; - OutRec* outrec2 = CreateOutRec(); - outrec2->Pts = op2; - UpdateOutPtIdxs(*outrec2); - if (Poly2ContainsPoly1(outrec2->Pts, outrec->Pts)) - { - //OutRec2 is contained by OutRec1 ... - outrec2->IsHole = !outrec->IsHole; - outrec2->FirstLeft = outrec; - if (m_UsingPolyTree) FixupFirstLefts2(outrec2, outrec); - } - else - if (Poly2ContainsPoly1(outrec->Pts, outrec2->Pts)) - { - //OutRec1 is contained by OutRec2 ... - outrec2->IsHole = outrec->IsHole; - outrec->IsHole = !outrec2->IsHole; - outrec2->FirstLeft = outrec->FirstLeft; - outrec->FirstLeft = outrec2; - if (m_UsingPolyTree) FixupFirstLefts2(outrec, outrec2); - } - else - { - //the 2 polygons are separate ... - outrec2->IsHole = outrec->IsHole; - outrec2->FirstLeft = outrec->FirstLeft; - if (m_UsingPolyTree) FixupFirstLefts1(outrec, outrec2); - } - op2 = op; //ie get ready for the Next iteration - } - op2 = op2->Next; - } - op = op->Next; - } - while (op != outrec->Pts); - } -} -//------------------------------------------------------------------------------ - -void ReversePath(Path& p) -{ - std::reverse(p.begin(), p.end()); -} -//------------------------------------------------------------------------------ - -void ReversePaths(Paths& p) -{ - for (Paths::size_type i = 0; i < p.size(); ++i) - ReversePath(p[i]); -} -//------------------------------------------------------------------------------ - -void SimplifyPolygon(const Path &in_poly, Paths &out_polys, PolyFillType fillType) -{ - Clipper c; - c.StrictlySimple(true); - c.AddPath(in_poly, ptSubject, true); - c.Execute(ctUnion, out_polys, fillType, fillType); -} -//------------------------------------------------------------------------------ - -void SimplifyPolygons(const Paths &in_polys, Paths &out_polys, PolyFillType fillType) -{ - Clipper c; - c.StrictlySimple(true); - c.AddPaths(in_polys, ptSubject, true); - c.Execute(ctUnion, out_polys, fillType, fillType); -} -//------------------------------------------------------------------------------ - -void SimplifyPolygons(Paths &polys, PolyFillType fillType) -{ - SimplifyPolygons(polys, polys, fillType); -} -//------------------------------------------------------------------------------ - -inline double DistanceSqrd(const IntPoint& pt1, const IntPoint& pt2) -{ - double Dx = ((double)pt1.X - pt2.X); - double dy = ((double)pt1.Y - pt2.Y); - return (Dx*Dx + dy*dy); -} -//------------------------------------------------------------------------------ - -double DistanceFromLineSqrd( - const IntPoint& pt, const IntPoint& ln1, const IntPoint& ln2) -{ - //The equation of a line in general form (Ax + By + C = 0) - //given 2 points (x,y) & (x,y) is ... - //(y - y)x + (x - x)y + (y - y)x - (x - x)y = 0 - //A = (y - y); B = (x - x); C = (y - y)x - (x - x)y - //perpendicular distance of point (x,y) = (Ax + By + C)/Sqrt(A + B) - //see http://en.wikipedia.org/wiki/Perpendicular_distance - double A = double(ln1.Y - ln2.Y); - double B = double(ln2.X - ln1.X); - double C = A * ln1.X + B * ln1.Y; - C = A * pt.X + B * pt.Y - C; - return (C * C) / (A * A + B * B); -} -//--------------------------------------------------------------------------- - -bool SlopesNearCollinear(const IntPoint& pt1, - const IntPoint& pt2, const IntPoint& pt3, double distSqrd) -{ - //this function is more accurate when the point that's geometrically - //between the other 2 points is the one that's tested for distance. - //ie makes it more likely to pick up 'spikes' ... - if (Abs(pt1.X - pt2.X) > Abs(pt1.Y - pt2.Y)) - { - if ((pt1.X > pt2.X) == (pt1.X < pt3.X)) - return DistanceFromLineSqrd(pt1, pt2, pt3) < distSqrd; - else if ((pt2.X > pt1.X) == (pt2.X < pt3.X)) - return DistanceFromLineSqrd(pt2, pt1, pt3) < distSqrd; - else - return DistanceFromLineSqrd(pt3, pt1, pt2) < distSqrd; - } - else - { - if ((pt1.Y > pt2.Y) == (pt1.Y < pt3.Y)) - return DistanceFromLineSqrd(pt1, pt2, pt3) < distSqrd; - else if ((pt2.Y > pt1.Y) == (pt2.Y < pt3.Y)) - return DistanceFromLineSqrd(pt2, pt1, pt3) < distSqrd; - else - return DistanceFromLineSqrd(pt3, pt1, pt2) < distSqrd; - } -} -//------------------------------------------------------------------------------ - -bool PointsAreClose(IntPoint pt1, IntPoint pt2, double distSqrd) -{ - double Dx = (double)pt1.X - pt2.X; - double dy = (double)pt1.Y - pt2.Y; - return ((Dx * Dx) + (dy * dy) <= distSqrd); -} -//------------------------------------------------------------------------------ - -OutPt* ExcludeOp(OutPt* op) -{ - OutPt* result = op->Prev; - result->Next = op->Next; - op->Next->Prev = result; - result->Idx = 0; - return result; -} -//------------------------------------------------------------------------------ - -void CleanPolygon(const Path& in_poly, Path& out_poly, double distance) -{ - //distance = proximity in units/pixels below which vertices - //will be stripped. Default ~= sqrt(2). - - size_t size = in_poly.size(); - - if (size == 0) - { - out_poly.clear(); - return; - } - - OutPt* outPts = new OutPt[size]; - for (size_t i = 0; i < size; ++i) - { - outPts[i].Pt = in_poly[i]; - outPts[i].Next = &outPts[(i + 1) % size]; - outPts[i].Next->Prev = &outPts[i]; - outPts[i].Idx = 0; - } - - double distSqrd = distance * distance; - OutPt* op = &outPts[0]; - while (op->Idx == 0 && op->Next != op->Prev) - { - if (PointsAreClose(op->Pt, op->Prev->Pt, distSqrd)) - { - op = ExcludeOp(op); - size--; - } - else if (PointsAreClose(op->Prev->Pt, op->Next->Pt, distSqrd)) - { - ExcludeOp(op->Next); - op = ExcludeOp(op); - size -= 2; - } - else if (SlopesNearCollinear(op->Prev->Pt, op->Pt, op->Next->Pt, distSqrd)) - { - op = ExcludeOp(op); - size--; - } - else - { - op->Idx = 1; - op = op->Next; - } - } - - if (size < 3) size = 0; - out_poly.resize(size); - for (size_t i = 0; i < size; ++i) - { - out_poly[i] = op->Pt; - op = op->Next; - } - delete [] outPts; -} -//------------------------------------------------------------------------------ - -void CleanPolygon(Path& poly, double distance) -{ - CleanPolygon(poly, poly, distance); -} -//------------------------------------------------------------------------------ - -void CleanPolygons(const Paths& in_polys, Paths& out_polys, double distance) -{ - out_polys.resize(in_polys.size()); - for (Paths::size_type i = 0; i < in_polys.size(); ++i) - CleanPolygon(in_polys[i], out_polys[i], distance); -} -//------------------------------------------------------------------------------ - -void CleanPolygons(Paths& polys, double distance) -{ - CleanPolygons(polys, polys, distance); -} -//------------------------------------------------------------------------------ - -void Minkowski(const Path& poly, const Path& path, - Paths& solution, bool isSum, bool isClosed) -{ - int delta = (isClosed ? 1 : 0); - size_t polyCnt = poly.size(); - size_t pathCnt = path.size(); - Paths pp; - pp.reserve(pathCnt); - if (isSum) - for (size_t i = 0; i < pathCnt; ++i) - { - Path p; - p.reserve(polyCnt); - for (size_t j = 0; j < poly.size(); ++j) - p.push_back(IntPoint(path[i].X + poly[j].X, path[i].Y + poly[j].Y)); - pp.push_back(p); - } - else - for (size_t i = 0; i < pathCnt; ++i) - { - Path p; - p.reserve(polyCnt); - for (size_t j = 0; j < poly.size(); ++j) - p.push_back(IntPoint(path[i].X - poly[j].X, path[i].Y - poly[j].Y)); - pp.push_back(p); - } - - solution.clear(); - solution.reserve((pathCnt + delta) * (polyCnt + 1)); - for (size_t i = 0; i < pathCnt - 1 + delta; ++i) - for (size_t j = 0; j < polyCnt; ++j) - { - Path quad; - quad.reserve(4); - quad.push_back(pp[i % pathCnt][j % polyCnt]); - quad.push_back(pp[(i + 1) % pathCnt][j % polyCnt]); - quad.push_back(pp[(i + 1) % pathCnt][(j + 1) % polyCnt]); - quad.push_back(pp[i % pathCnt][(j + 1) % polyCnt]); - if (!Orientation(quad)) ReversePath(quad); - solution.push_back(quad); - } -} -//------------------------------------------------------------------------------ - -void MinkowskiSum(const Path& pattern, const Path& path, Paths& solution, bool pathIsClosed) -{ - Minkowski(pattern, path, solution, true, pathIsClosed); - Clipper c; - c.AddPaths(solution, ptSubject, true); - c.Execute(ctUnion, solution, pftNonZero, pftNonZero); -} -//------------------------------------------------------------------------------ - -void TranslatePath(const Path& input, Path& output, const IntPoint delta) -{ - //precondition: input != output - output.resize(input.size()); - for (size_t i = 0; i < input.size(); ++i) - output[i] = IntPoint(input[i].X + delta.X, input[i].Y + delta.Y); -} -//------------------------------------------------------------------------------ - -void MinkowskiSum(const Path& pattern, const Paths& paths, Paths& solution, bool pathIsClosed) -{ - Clipper c; - for (size_t i = 0; i < paths.size(); ++i) - { - Paths tmp; - Minkowski(pattern, paths[i], tmp, true, pathIsClosed); - c.AddPaths(tmp, ptSubject, true); - if (pathIsClosed) - { - Path tmp2; - TranslatePath(paths[i], tmp2, pattern[0]); - c.AddPath(tmp2, ptClip, true); - } - } - c.Execute(ctUnion, solution, pftNonZero, pftNonZero); -} -//------------------------------------------------------------------------------ - -void MinkowskiDiff(const Path& poly1, const Path& poly2, Paths& solution) -{ - Minkowski(poly1, poly2, solution, false, true); - Clipper c; - c.AddPaths(solution, ptSubject, true); - c.Execute(ctUnion, solution, pftNonZero, pftNonZero); -} -//------------------------------------------------------------------------------ - -enum NodeType {ntAny, ntOpen, ntClosed}; - -void AddPolyNodeToPaths(const PolyNode& polynode, NodeType nodetype, Paths& paths) -{ - bool match = true; - if (nodetype == ntClosed) match = !polynode.IsOpen(); - else if (nodetype == ntOpen) return; - - if (!polynode.Contour.empty() && match) - paths.push_back(polynode.Contour); - for (int i = 0; i < polynode.ChildCount(); ++i) - AddPolyNodeToPaths(*polynode.Childs[i], nodetype, paths); -} -//------------------------------------------------------------------------------ - -void PolyTreeToPaths(const PolyTree& polytree, Paths& paths) -{ - paths.resize(0); - paths.reserve(polytree.Total()); - AddPolyNodeToPaths(polytree, ntAny, paths); -} -//------------------------------------------------------------------------------ - -void ClosedPathsFromPolyTree(const PolyTree& polytree, Paths& paths) -{ - paths.resize(0); - paths.reserve(polytree.Total()); - AddPolyNodeToPaths(polytree, ntClosed, paths); -} -//------------------------------------------------------------------------------ - -void OpenPathsFromPolyTree(PolyTree& polytree, Paths& paths) -{ - paths.resize(0); - paths.reserve(polytree.Total()); - //Open paths are top level only, so ... - for (int i = 0; i < polytree.ChildCount(); ++i) - if (polytree.Childs[i]->IsOpen()) - paths.push_back(polytree.Childs[i]->Contour); -} -//------------------------------------------------------------------------------ - -std::ostream& operator <<(std::ostream &s, const IntPoint &p) -{ - s << "(" << p.X << "," << p.Y << ")"; - return s; -} -//------------------------------------------------------------------------------ - -std::ostream& operator <<(std::ostream &s, const Path &p) -{ - if (p.empty()) return s; - Path::size_type last = p.size() -1; - for (Path::size_type i = 0; i < last; i++) - s << "(" << p[i].X << "," << p[i].Y << "), "; - s << "(" << p[last].X << "," << p[last].Y << ")\n"; - return s; -} -//------------------------------------------------------------------------------ - -std::ostream& operator <<(std::ostream &s, const Paths &p) -{ - for (Paths::size_type i = 0; i < p.size(); i++) - s << p[i]; - s << "\n"; - return s; -} -//------------------------------------------------------------------------------ - -} //ClipperLib namespace diff --git a/ptocr/postprocess/piexlmerge/include/clipper/clipper.hpp b/ptocr/postprocess/piexlmerge/include/clipper/clipper.hpp deleted file mode 100644 index 1c38371..0000000 --- a/ptocr/postprocess/piexlmerge/include/clipper/clipper.hpp +++ /dev/null @@ -1,404 +0,0 @@ -/******************************************************************************* -* * -* Author : Angus Johnson * -* Version : 6.4.0 * -* Date : 2 July 2015 * -* Website : http://www.angusj.com * -* Copyright : Angus Johnson 2010-2015 * -* * -* License: * -* Use, modification & distribution is subject to Boost Software License Ver 1. * -* http://www.boost.org/LICENSE_1_0.txt * -* * -* Attributions: * -* The code in this library is an extension of Bala Vatti's clipping algorithm: * -* "A generic solution to polygon clipping" * -* Communications of the ACM, Vol 35, Issue 7 (July 1992) pp 56-63. * -* http://portal.acm.org/citation.cfm?id=129906 * -* * -* Computer graphics and geometric modeling: implementation and algorithms * -* By Max K. Agoston * -* Springer; 1 edition (January 4, 2005) * -* http://books.google.com/books?q=vatti+clipping+agoston * -* * -* See also: * -* "Polygon Offsetting by Computing Winding Numbers" * -* Paper no. DETC2005-85513 pp. 565-575 * -* ASME 2005 International Design Engineering Technical Conferences * -* and Computers and Information in Engineering Conference (IDETC/CIE2005) * -* September 24-28, 2005 , Long Beach, California, USA * -* http://www.me.berkeley.edu/~mcmains/pubs/DAC05OffsetPolygon.pdf * -* * -*******************************************************************************/ - -#ifndef clipper_hpp -#define clipper_hpp - -#define CLIPPER_VERSION "6.2.6" - -//use_int32: When enabled 32bit ints are used instead of 64bit ints. This -//improve performance but coordinate values are limited to the range +/- 46340 -//#define use_int32 - -//use_xyz: adds a Z member to IntPoint. Adds a minor cost to perfomance. -//#define use_xyz - -//use_lines: Enables line clipping. Adds a very minor cost to performance. -#define use_lines - -//use_deprecated: Enables temporary support for the obsolete functions -//#define use_deprecated - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace ClipperLib { - -enum ClipType { ctIntersection, ctUnion, ctDifference, ctXor }; -enum PolyType { ptSubject, ptClip }; -//By far the most widely used winding rules for polygon filling are -//EvenOdd & NonZero (GDI, GDI+, XLib, OpenGL, Cairo, AGG, Quartz, SVG, Gr32) -//Others rules include Positive, Negative and ABS_GTR_EQ_TWO (only in OpenGL) -//see http://glprogramming.com/red/chapter11.html -enum PolyFillType { pftEvenOdd, pftNonZero, pftPositive, pftNegative }; - -#ifdef use_int32 - typedef int cInt; - static cInt const loRange = 0x7FFF; - static cInt const hiRange = 0x7FFF; -#else - typedef signed long long cInt; - static cInt const loRange = 0x3FFFFFFF; - static cInt const hiRange = 0x3FFFFFFFFFFFFFFFLL; - typedef signed long long long64; //used by Int128 class - typedef unsigned long long ulong64; - -#endif - -struct IntPoint { - cInt X; - cInt Y; -#ifdef use_xyz - cInt Z; - IntPoint(cInt x = 0, cInt y = 0, cInt z = 0): X(x), Y(y), Z(z) {}; -#else - IntPoint(cInt x = 0, cInt y = 0): X(x), Y(y) {}; -#endif - - friend inline bool operator== (const IntPoint& a, const IntPoint& b) - { - return a.X == b.X && a.Y == b.Y; - } - friend inline bool operator!= (const IntPoint& a, const IntPoint& b) - { - return a.X != b.X || a.Y != b.Y; - } -}; -//------------------------------------------------------------------------------ - -typedef std::vector< IntPoint > Path; -typedef std::vector< Path > Paths; - -inline Path& operator <<(Path& poly, const IntPoint& p) {poly.push_back(p); return poly;} -inline Paths& operator <<(Paths& polys, const Path& p) {polys.push_back(p); return polys;} - -std::ostream& operator <<(std::ostream &s, const IntPoint &p); -std::ostream& operator <<(std::ostream &s, const Path &p); -std::ostream& operator <<(std::ostream &s, const Paths &p); - -struct DoublePoint -{ - double X; - double Y; - DoublePoint(double x = 0, double y = 0) : X(x), Y(y) {} - DoublePoint(IntPoint ip) : X((double)ip.X), Y((double)ip.Y) {} -}; -//------------------------------------------------------------------------------ - -#ifdef use_xyz -typedef void (*ZFillCallback)(IntPoint& e1bot, IntPoint& e1top, IntPoint& e2bot, IntPoint& e2top, IntPoint& pt); -#endif - -enum InitOptions {ioReverseSolution = 1, ioStrictlySimple = 2, ioPreserveCollinear = 4}; -enum JoinType {jtSquare, jtRound, jtMiter}; -enum EndType {etClosedPolygon, etClosedLine, etOpenButt, etOpenSquare, etOpenRound}; - -class PolyNode; -typedef std::vector< PolyNode* > PolyNodes; - -class PolyNode -{ -public: - PolyNode(); - virtual ~PolyNode(){}; - Path Contour; - PolyNodes Childs; - PolyNode* Parent; - PolyNode* GetNext() const; - bool IsHole() const; - bool IsOpen() const; - int ChildCount() const; -private: - unsigned Index; //node index in Parent.Childs - bool m_IsOpen; - JoinType m_jointype; - EndType m_endtype; - PolyNode* GetNextSiblingUp() const; - void AddChild(PolyNode& child); - friend class Clipper; //to access Index - friend class ClipperOffset; -}; - -class PolyTree: public PolyNode -{ -public: - ~PolyTree(){Clear();}; - PolyNode* GetFirst() const; - void Clear(); - int Total() const; -private: - PolyNodes AllNodes; - friend class Clipper; //to access AllNodes -}; - -bool Orientation(const Path &poly); -double Area(const Path &poly); -int PointInPolygon(const IntPoint &pt, const Path &path); - -void SimplifyPolygon(const Path &in_poly, Paths &out_polys, PolyFillType fillType = pftEvenOdd); -void SimplifyPolygons(const Paths &in_polys, Paths &out_polys, PolyFillType fillType = pftEvenOdd); -void SimplifyPolygons(Paths &polys, PolyFillType fillType = pftEvenOdd); - -void CleanPolygon(const Path& in_poly, Path& out_poly, double distance = 1.415); -void CleanPolygon(Path& poly, double distance = 1.415); -void CleanPolygons(const Paths& in_polys, Paths& out_polys, double distance = 1.415); -void CleanPolygons(Paths& polys, double distance = 1.415); - -void MinkowskiSum(const Path& pattern, const Path& path, Paths& solution, bool pathIsClosed); -void MinkowskiSum(const Path& pattern, const Paths& paths, Paths& solution, bool pathIsClosed); -void MinkowskiDiff(const Path& poly1, const Path& poly2, Paths& solution); - -void PolyTreeToPaths(const PolyTree& polytree, Paths& paths); -void ClosedPathsFromPolyTree(const PolyTree& polytree, Paths& paths); -void OpenPathsFromPolyTree(PolyTree& polytree, Paths& paths); - -void ReversePath(Path& p); -void ReversePaths(Paths& p); - -struct IntRect { cInt left; cInt top; cInt right; cInt bottom; }; - -//enums that are used internally ... -enum EdgeSide { esLeft = 1, esRight = 2}; - -//forward declarations (for stuff used internally) ... -struct TEdge; -struct IntersectNode; -struct LocalMinimum; -struct OutPt; -struct OutRec; -struct Join; - -typedef std::vector < OutRec* > PolyOutList; -typedef std::vector < TEdge* > EdgeList; -typedef std::vector < Join* > JoinList; -typedef std::vector < IntersectNode* > IntersectList; - -//------------------------------------------------------------------------------ - -//ClipperBase is the ancestor to the Clipper class. It should not be -//instantiated directly. This class simply abstracts the conversion of sets of -//polygon coordinates into edge objects that are stored in a LocalMinima list. -class ClipperBase -{ -public: - ClipperBase(); - virtual ~ClipperBase(); - virtual bool AddPath(const Path &pg, PolyType PolyTyp, bool Closed); - bool AddPaths(const Paths &ppg, PolyType PolyTyp, bool Closed); - virtual void Clear(); - IntRect GetBounds(); - bool PreserveCollinear() {return m_PreserveCollinear;}; - void PreserveCollinear(bool value) {m_PreserveCollinear = value;}; -protected: - void DisposeLocalMinimaList(); - TEdge* AddBoundsToLML(TEdge *e, bool IsClosed); - virtual void Reset(); - TEdge* ProcessBound(TEdge* E, bool IsClockwise); - void InsertScanbeam(const cInt Y); - bool PopScanbeam(cInt &Y); - bool LocalMinimaPending(); - bool PopLocalMinima(cInt Y, const LocalMinimum *&locMin); - OutRec* CreateOutRec(); - void DisposeAllOutRecs(); - void DisposeOutRec(PolyOutList::size_type index); - void SwapPositionsInAEL(TEdge *edge1, TEdge *edge2); - void DeleteFromAEL(TEdge *e); - void UpdateEdgeIntoAEL(TEdge *&e); - - typedef std::vector MinimaList; - MinimaList::iterator m_CurrentLM; - MinimaList m_MinimaList; - - bool m_UseFullRange; - EdgeList m_edges; - bool m_PreserveCollinear; - bool m_HasOpenPaths; - PolyOutList m_PolyOuts; - TEdge *m_ActiveEdges; - - typedef std::priority_queue ScanbeamList; - ScanbeamList m_Scanbeam; -}; -//------------------------------------------------------------------------------ - -class Clipper : public virtual ClipperBase -{ -public: - Clipper(int initOptions = 0); - bool Execute(ClipType clipType, - Paths &solution, - PolyFillType fillType = pftEvenOdd); - bool Execute(ClipType clipType, - Paths &solution, - PolyFillType subjFillType, - PolyFillType clipFillType); - bool Execute(ClipType clipType, - PolyTree &polytree, - PolyFillType fillType = pftEvenOdd); - bool Execute(ClipType clipType, - PolyTree &polytree, - PolyFillType subjFillType, - PolyFillType clipFillType); - bool ReverseSolution() { return m_ReverseOutput; }; - void ReverseSolution(bool value) {m_ReverseOutput = value;}; - bool StrictlySimple() {return m_StrictSimple;}; - void StrictlySimple(bool value) {m_StrictSimple = value;}; - //set the callback function for z value filling on intersections (otherwise Z is 0) -#ifdef use_xyz - void ZFillFunction(ZFillCallback zFillFunc); -#endif -protected: - virtual bool ExecuteInternal(); -private: - JoinList m_Joins; - JoinList m_GhostJoins; - IntersectList m_IntersectList; - ClipType m_ClipType; - typedef std::list MaximaList; - MaximaList m_Maxima; - TEdge *m_SortedEdges; - bool m_ExecuteLocked; - PolyFillType m_ClipFillType; - PolyFillType m_SubjFillType; - bool m_ReverseOutput; - bool m_UsingPolyTree; - bool m_StrictSimple; -#ifdef use_xyz - ZFillCallback m_ZFill; //custom callback -#endif - void SetWindingCount(TEdge& edge); - bool IsEvenOddFillType(const TEdge& edge) const; - bool IsEvenOddAltFillType(const TEdge& edge) const; - void InsertLocalMinimaIntoAEL(const cInt botY); - void InsertEdgeIntoAEL(TEdge *edge, TEdge* startEdge); - void AddEdgeToSEL(TEdge *edge); - bool PopEdgeFromSEL(TEdge *&edge); - void CopyAELToSEL(); - void DeleteFromSEL(TEdge *e); - void SwapPositionsInSEL(TEdge *edge1, TEdge *edge2); - bool IsContributing(const TEdge& edge) const; - bool IsTopHorz(const cInt XPos); - void DoMaxima(TEdge *e); - void ProcessHorizontals(); - void ProcessHorizontal(TEdge *horzEdge); - void AddLocalMaxPoly(TEdge *e1, TEdge *e2, const IntPoint &pt); - OutPt* AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &pt); - OutRec* GetOutRec(int idx); - void AppendPolygon(TEdge *e1, TEdge *e2); - void IntersectEdges(TEdge *e1, TEdge *e2, IntPoint &pt); - OutPt* AddOutPt(TEdge *e, const IntPoint &pt); - OutPt* GetLastOutPt(TEdge *e); - bool ProcessIntersections(const cInt topY); - void BuildIntersectList(const cInt topY); - void ProcessIntersectList(); - void ProcessEdgesAtTopOfScanbeam(const cInt topY); - void BuildResult(Paths& polys); - void BuildResult2(PolyTree& polytree); - void SetHoleState(TEdge *e, OutRec *outrec); - void DisposeIntersectNodes(); - bool FixupIntersectionOrder(); - void FixupOutPolygon(OutRec &outrec); - void FixupOutPolyline(OutRec &outrec); - bool IsHole(TEdge *e); - bool FindOwnerFromSplitRecs(OutRec &outRec, OutRec *&currOrfl); - void FixHoleLinkage(OutRec &outrec); - void AddJoin(OutPt *op1, OutPt *op2, const IntPoint offPt); - void ClearJoins(); - void ClearGhostJoins(); - void AddGhostJoin(OutPt *op, const IntPoint offPt); - bool JoinPoints(Join *j, OutRec* outRec1, OutRec* outRec2); - void JoinCommonEdges(); - void DoSimplePolygons(); - void FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec); - void FixupFirstLefts2(OutRec* InnerOutRec, OutRec* OuterOutRec); - void FixupFirstLefts3(OutRec* OldOutRec, OutRec* NewOutRec); -#ifdef use_xyz - void SetZ(IntPoint& pt, TEdge& e1, TEdge& e2); -#endif -}; -//------------------------------------------------------------------------------ - -class ClipperOffset -{ -public: - ClipperOffset(double miterLimit = 2.0, double roundPrecision = 0.25); - ~ClipperOffset(); - void AddPath(const Path& path, JoinType joinType, EndType endType); - void AddPaths(const Paths& paths, JoinType joinType, EndType endType); - void Execute(Paths& solution, double delta); - void Execute(PolyTree& solution, double delta); - void Clear(); - double MiterLimit; - double ArcTolerance; -private: - Paths m_destPolys; - Path m_srcPoly; - Path m_destPoly; - std::vector m_normals; - double m_delta, m_sinA, m_sin, m_cos; - double m_miterLim, m_StepsPerRad; - IntPoint m_lowest; - PolyNode m_polyNodes; - - void FixOrientations(); - void DoOffset(double delta); - void OffsetPoint(int j, int& k, JoinType jointype); - void DoSquare(int j, int k); - void DoMiter(int j, int k, double r); - void DoRound(int j, int k); -}; -//------------------------------------------------------------------------------ - -class clipperException : public std::exception -{ - public: - clipperException(const char* description): m_descr(description) {} - virtual ~clipperException() throw() {} - virtual const char* what() const throw() {return m_descr.c_str();} - private: - std::string m_descr; -}; -//------------------------------------------------------------------------------ - -} //ClipperLib namespace - -#endif //clipper_hpp - - diff --git a/ptocr/postprocess/piexlmerge/include/pybind11/attr.h b/ptocr/postprocess/piexlmerge/include/pybind11/attr.h deleted file mode 100644 index 8732cfe..0000000 --- a/ptocr/postprocess/piexlmerge/include/pybind11/attr.h +++ /dev/null @@ -1,492 +0,0 @@ -/* - pybind11/attr.h: Infrastructure for processing custom - type and function attributes - - Copyright (c) 2016 Wenzel Jakob - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#include "cast.h" - -NAMESPACE_BEGIN(PYBIND11_NAMESPACE) - -/// \addtogroup annotations -/// @{ - -/// Annotation for methods -struct is_method { handle class_; is_method(const handle &c) : class_(c) { } }; - -/// Annotation for operators -struct is_operator { }; - -/// Annotation for parent scope -struct scope { handle value; scope(const handle &s) : value(s) { } }; - -/// Annotation for documentation -struct doc { const char *value; doc(const char *value) : value(value) { } }; - -/// Annotation for function names -struct name { const char *value; name(const char *value) : value(value) { } }; - -/// Annotation indicating that a function is an overload associated with a given "sibling" -struct sibling { handle value; sibling(const handle &value) : value(value.ptr()) { } }; - -/// Annotation indicating that a class derives from another given type -template struct base { - PYBIND11_DEPRECATED("base() was deprecated in favor of specifying 'T' as a template argument to class_") - base() { } -}; - -/// Keep patient alive while nurse lives -template struct keep_alive { }; - -/// Annotation indicating that a class is involved in a multiple inheritance relationship -struct multiple_inheritance { }; - -/// Annotation which enables dynamic attributes, i.e. adds `__dict__` to a class -struct dynamic_attr { }; - -/// Annotation which enables the buffer protocol for a type -struct buffer_protocol { }; - -/// Annotation which requests that a special metaclass is created for a type -struct metaclass { - handle value; - - PYBIND11_DEPRECATED("py::metaclass() is no longer required. It's turned on by default now.") - metaclass() {} - - /// Override pybind11's default metaclass - explicit metaclass(handle value) : value(value) { } -}; - -/// Annotation that marks a class as local to the module: -struct module_local { const bool value; constexpr module_local(bool v = true) : value(v) { } }; - -/// Annotation to mark enums as an arithmetic type -struct arithmetic { }; - -/** \rst - A call policy which places one or more guard variables (``Ts...``) around the function call. - - For example, this definition: - - .. code-block:: cpp - - m.def("foo", foo, py::call_guard()); - - is equivalent to the following pseudocode: - - .. code-block:: cpp - - m.def("foo", [](args...) { - T scope_guard; - return foo(args...); // forwarded arguments - }); - \endrst */ -template struct call_guard; - -template <> struct call_guard<> { using type = detail::void_type; }; - -template -struct call_guard { - static_assert(std::is_default_constructible::value, - "The guard type must be default constructible"); - - using type = T; -}; - -template -struct call_guard { - struct type { - T guard{}; // Compose multiple guard types with left-to-right default-constructor order - typename call_guard::type next{}; - }; -}; - -/// @} annotations - -NAMESPACE_BEGIN(detail) -/* Forward declarations */ -enum op_id : int; -enum op_type : int; -struct undefined_t; -template struct op_; -inline void keep_alive_impl(size_t Nurse, size_t Patient, function_call &call, handle ret); - -/// Internal data structure which holds metadata about a keyword argument -struct argument_record { - const char *name; ///< Argument name - const char *descr; ///< Human-readable version of the argument value - handle value; ///< Associated Python object - bool convert : 1; ///< True if the argument is allowed to convert when loading - bool none : 1; ///< True if None is allowed when loading - - argument_record(const char *name, const char *descr, handle value, bool convert, bool none) - : name(name), descr(descr), value(value), convert(convert), none(none) { } -}; - -/// Internal data structure which holds metadata about a bound function (signature, overloads, etc.) -struct function_record { - function_record() - : is_constructor(false), is_new_style_constructor(false), is_stateless(false), - is_operator(false), has_args(false), has_kwargs(false), is_method(false) { } - - /// Function name - char *name = nullptr; /* why no C++ strings? They generate heavier code.. */ - - // User-specified documentation string - char *doc = nullptr; - - /// Human-readable version of the function signature - char *signature = nullptr; - - /// List of registered keyword arguments - std::vector args; - - /// Pointer to lambda function which converts arguments and performs the actual call - handle (*impl) (function_call &) = nullptr; - - /// Storage for the wrapped function pointer and captured data, if any - void *data[3] = { }; - - /// Pointer to custom destructor for 'data' (if needed) - void (*free_data) (function_record *ptr) = nullptr; - - /// Return value policy associated with this function - return_value_policy policy = return_value_policy::automatic; - - /// True if name == '__init__' - bool is_constructor : 1; - - /// True if this is a new-style `__init__` defined in `detail/init.h` - bool is_new_style_constructor : 1; - - /// True if this is a stateless function pointer - bool is_stateless : 1; - - /// True if this is an operator (__add__), etc. - bool is_operator : 1; - - /// True if the function has a '*args' argument - bool has_args : 1; - - /// True if the function has a '**kwargs' argument - bool has_kwargs : 1; - - /// True if this is a method - bool is_method : 1; - - /// Number of arguments (including py::args and/or py::kwargs, if present) - std::uint16_t nargs; - - /// Python method object - PyMethodDef *def = nullptr; - - /// Python handle to the parent scope (a class or a module) - handle scope; - - /// Python handle to the sibling function representing an overload chain - handle sibling; - - /// Pointer to next overload - function_record *next = nullptr; -}; - -/// Special data structure which (temporarily) holds metadata about a bound class -struct type_record { - PYBIND11_NOINLINE type_record() - : multiple_inheritance(false), dynamic_attr(false), buffer_protocol(false), module_local(false) { } - - /// Handle to the parent scope - handle scope; - - /// Name of the class - const char *name = nullptr; - - // Pointer to RTTI type_info data structure - const std::type_info *type = nullptr; - - /// How large is the underlying C++ type? - size_t type_size = 0; - - /// What is the alignment of the underlying C++ type? - size_t type_align = 0; - - /// How large is the type's holder? - size_t holder_size = 0; - - /// The global operator new can be overridden with a class-specific variant - void *(*operator_new)(size_t) = nullptr; - - /// Function pointer to class_<..>::init_instance - void (*init_instance)(instance *, const void *) = nullptr; - - /// Function pointer to class_<..>::dealloc - void (*dealloc)(detail::value_and_holder &) = nullptr; - - /// List of base classes of the newly created type - list bases; - - /// Optional docstring - const char *doc = nullptr; - - /// Custom metaclass (optional) - handle metaclass; - - /// Multiple inheritance marker - bool multiple_inheritance : 1; - - /// Does the class manage a __dict__? - bool dynamic_attr : 1; - - /// Does the class implement the buffer protocol? - bool buffer_protocol : 1; - - /// Is the default (unique_ptr) holder type used? - bool default_holder : 1; - - /// Is the class definition local to the module shared object? - bool module_local : 1; - - PYBIND11_NOINLINE void add_base(const std::type_info &base, void *(*caster)(void *)) { - auto base_info = detail::get_type_info(base, false); - if (!base_info) { - std::string tname(base.name()); - detail::clean_type_id(tname); - pybind11_fail("generic_type: type \"" + std::string(name) + - "\" referenced unknown base type \"" + tname + "\""); - } - - if (default_holder != base_info->default_holder) { - std::string tname(base.name()); - detail::clean_type_id(tname); - pybind11_fail("generic_type: type \"" + std::string(name) + "\" " + - (default_holder ? "does not have" : "has") + - " a non-default holder type while its base \"" + tname + "\" " + - (base_info->default_holder ? "does not" : "does")); - } - - bases.append((PyObject *) base_info->type); - - if (base_info->type->tp_dictoffset != 0) - dynamic_attr = true; - - if (caster) - base_info->implicit_casts.emplace_back(type, caster); - } -}; - -inline function_call::function_call(const function_record &f, handle p) : - func(f), parent(p) { - args.reserve(f.nargs); - args_convert.reserve(f.nargs); -} - -/// Tag for a new-style `__init__` defined in `detail/init.h` -struct is_new_style_constructor { }; - -/** - * Partial template specializations to process custom attributes provided to - * cpp_function_ and class_. These are either used to initialize the respective - * fields in the type_record and function_record data structures or executed at - * runtime to deal with custom call policies (e.g. keep_alive). - */ -template struct process_attribute; - -template struct process_attribute_default { - /// Default implementation: do nothing - static void init(const T &, function_record *) { } - static void init(const T &, type_record *) { } - static void precall(function_call &) { } - static void postcall(function_call &, handle) { } -}; - -/// Process an attribute specifying the function's name -template <> struct process_attribute : process_attribute_default { - static void init(const name &n, function_record *r) { r->name = const_cast(n.value); } -}; - -/// Process an attribute specifying the function's docstring -template <> struct process_attribute : process_attribute_default { - static void init(const doc &n, function_record *r) { r->doc = const_cast(n.value); } -}; - -/// Process an attribute specifying the function's docstring (provided as a C-style string) -template <> struct process_attribute : process_attribute_default { - static void init(const char *d, function_record *r) { r->doc = const_cast(d); } - static void init(const char *d, type_record *r) { r->doc = const_cast(d); } -}; -template <> struct process_attribute : process_attribute { }; - -/// Process an attribute indicating the function's return value policy -template <> struct process_attribute : process_attribute_default { - static void init(const return_value_policy &p, function_record *r) { r->policy = p; } -}; - -/// Process an attribute which indicates that this is an overloaded function associated with a given sibling -template <> struct process_attribute : process_attribute_default { - static void init(const sibling &s, function_record *r) { r->sibling = s.value; } -}; - -/// Process an attribute which indicates that this function is a method -template <> struct process_attribute : process_attribute_default { - static void init(const is_method &s, function_record *r) { r->is_method = true; r->scope = s.class_; } -}; - -/// Process an attribute which indicates the parent scope of a method -template <> struct process_attribute : process_attribute_default { - static void init(const scope &s, function_record *r) { r->scope = s.value; } -}; - -/// Process an attribute which indicates that this function is an operator -template <> struct process_attribute : process_attribute_default { - static void init(const is_operator &, function_record *r) { r->is_operator = true; } -}; - -template <> struct process_attribute : process_attribute_default { - static void init(const is_new_style_constructor &, function_record *r) { r->is_new_style_constructor = true; } -}; - -/// Process a keyword argument attribute (*without* a default value) -template <> struct process_attribute : process_attribute_default { - static void init(const arg &a, function_record *r) { - if (r->is_method && r->args.empty()) - r->args.emplace_back("self", nullptr, handle(), true /*convert*/, false /*none not allowed*/); - r->args.emplace_back(a.name, nullptr, handle(), !a.flag_noconvert, a.flag_none); - } -}; - -/// Process a keyword argument attribute (*with* a default value) -template <> struct process_attribute : process_attribute_default { - static void init(const arg_v &a, function_record *r) { - if (r->is_method && r->args.empty()) - r->args.emplace_back("self", nullptr /*descr*/, handle() /*parent*/, true /*convert*/, false /*none not allowed*/); - - if (!a.value) { -#if !defined(NDEBUG) - std::string descr("'"); - if (a.name) descr += std::string(a.name) + ": "; - descr += a.type + "'"; - if (r->is_method) { - if (r->name) - descr += " in method '" + (std::string) str(r->scope) + "." + (std::string) r->name + "'"; - else - descr += " in method of '" + (std::string) str(r->scope) + "'"; - } else if (r->name) { - descr += " in function '" + (std::string) r->name + "'"; - } - pybind11_fail("arg(): could not convert default argument " - + descr + " into a Python object (type not registered yet?)"); -#else - pybind11_fail("arg(): could not convert default argument " - "into a Python object (type not registered yet?). " - "Compile in debug mode for more information."); -#endif - } - r->args.emplace_back(a.name, a.descr, a.value.inc_ref(), !a.flag_noconvert, a.flag_none); - } -}; - -/// Process a parent class attribute. Single inheritance only (class_ itself already guarantees that) -template -struct process_attribute::value>> : process_attribute_default { - static void init(const handle &h, type_record *r) { r->bases.append(h); } -}; - -/// Process a parent class attribute (deprecated, does not support multiple inheritance) -template -struct process_attribute> : process_attribute_default> { - static void init(const base &, type_record *r) { r->add_base(typeid(T), nullptr); } -}; - -/// Process a multiple inheritance attribute -template <> -struct process_attribute : process_attribute_default { - static void init(const multiple_inheritance &, type_record *r) { r->multiple_inheritance = true; } -}; - -template <> -struct process_attribute : process_attribute_default { - static void init(const dynamic_attr &, type_record *r) { r->dynamic_attr = true; } -}; - -template <> -struct process_attribute : process_attribute_default { - static void init(const buffer_protocol &, type_record *r) { r->buffer_protocol = true; } -}; - -template <> -struct process_attribute : process_attribute_default { - static void init(const metaclass &m, type_record *r) { r->metaclass = m.value; } -}; - -template <> -struct process_attribute : process_attribute_default { - static void init(const module_local &l, type_record *r) { r->module_local = l.value; } -}; - -/// Process an 'arithmetic' attribute for enums (does nothing here) -template <> -struct process_attribute : process_attribute_default {}; - -template -struct process_attribute> : process_attribute_default> { }; - -/** - * Process a keep_alive call policy -- invokes keep_alive_impl during the - * pre-call handler if both Nurse, Patient != 0 and use the post-call handler - * otherwise - */ -template struct process_attribute> : public process_attribute_default> { - template = 0> - static void precall(function_call &call) { keep_alive_impl(Nurse, Patient, call, handle()); } - template = 0> - static void postcall(function_call &, handle) { } - template = 0> - static void precall(function_call &) { } - template = 0> - static void postcall(function_call &call, handle ret) { keep_alive_impl(Nurse, Patient, call, ret); } -}; - -/// Recursively iterate over variadic template arguments -template struct process_attributes { - static void init(const Args&... args, function_record *r) { - int unused[] = { 0, (process_attribute::type>::init(args, r), 0) ... }; - ignore_unused(unused); - } - static void init(const Args&... args, type_record *r) { - int unused[] = { 0, (process_attribute::type>::init(args, r), 0) ... }; - ignore_unused(unused); - } - static void precall(function_call &call) { - int unused[] = { 0, (process_attribute::type>::precall(call), 0) ... }; - ignore_unused(unused); - } - static void postcall(function_call &call, handle fn_ret) { - int unused[] = { 0, (process_attribute::type>::postcall(call, fn_ret), 0) ... }; - ignore_unused(unused); - } -}; - -template -using is_call_guard = is_instantiation; - -/// Extract the ``type`` from the first `call_guard` in `Extras...` (or `void_type` if none found) -template -using extract_guard_t = typename exactly_one_t, Extra...>::type; - -/// Check the number of named arguments at compile time -template ::value...), - size_t self = constexpr_sum(std::is_same::value...)> -constexpr bool expected_num_args(size_t nargs, bool has_args, bool has_kwargs) { - return named == 0 || (self + named + has_args + has_kwargs) == nargs; -} - -NAMESPACE_END(detail) -NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/ptocr/postprocess/piexlmerge/include/pybind11/buffer_info.h b/ptocr/postprocess/piexlmerge/include/pybind11/buffer_info.h deleted file mode 100644 index 9f072fa..0000000 --- a/ptocr/postprocess/piexlmerge/include/pybind11/buffer_info.h +++ /dev/null @@ -1,108 +0,0 @@ -/* - pybind11/buffer_info.h: Python buffer object interface - - Copyright (c) 2016 Wenzel Jakob - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#include "detail/common.h" - -NAMESPACE_BEGIN(PYBIND11_NAMESPACE) - -/// Information record describing a Python buffer object -struct buffer_info { - void *ptr = nullptr; // Pointer to the underlying storage - ssize_t itemsize = 0; // Size of individual items in bytes - ssize_t size = 0; // Total number of entries - std::string format; // For homogeneous buffers, this should be set to format_descriptor::format() - ssize_t ndim = 0; // Number of dimensions - std::vector shape; // Shape of the tensor (1 entry per dimension) - std::vector strides; // Number of entries between adjacent entries (for each per dimension) - - buffer_info() { } - - buffer_info(void *ptr, ssize_t itemsize, const std::string &format, ssize_t ndim, - detail::any_container shape_in, detail::any_container strides_in) - : ptr(ptr), itemsize(itemsize), size(1), format(format), ndim(ndim), - shape(std::move(shape_in)), strides(std::move(strides_in)) { - if (ndim != (ssize_t) shape.size() || ndim != (ssize_t) strides.size()) - pybind11_fail("buffer_info: ndim doesn't match shape and/or strides length"); - for (size_t i = 0; i < (size_t) ndim; ++i) - size *= shape[i]; - } - - template - buffer_info(T *ptr, detail::any_container shape_in, detail::any_container strides_in) - : buffer_info(private_ctr_tag(), ptr, sizeof(T), format_descriptor::format(), static_cast(shape_in->size()), std::move(shape_in), std::move(strides_in)) { } - - buffer_info(void *ptr, ssize_t itemsize, const std::string &format, ssize_t size) - : buffer_info(ptr, itemsize, format, 1, {size}, {itemsize}) { } - - template - buffer_info(T *ptr, ssize_t size) - : buffer_info(ptr, sizeof(T), format_descriptor::format(), size) { } - - explicit buffer_info(Py_buffer *view, bool ownview = true) - : buffer_info(view->buf, view->itemsize, view->format, view->ndim, - {view->shape, view->shape + view->ndim}, {view->strides, view->strides + view->ndim}) { - this->view = view; - this->ownview = ownview; - } - - buffer_info(const buffer_info &) = delete; - buffer_info& operator=(const buffer_info &) = delete; - - buffer_info(buffer_info &&other) { - (*this) = std::move(other); - } - - buffer_info& operator=(buffer_info &&rhs) { - ptr = rhs.ptr; - itemsize = rhs.itemsize; - size = rhs.size; - format = std::move(rhs.format); - ndim = rhs.ndim; - shape = std::move(rhs.shape); - strides = std::move(rhs.strides); - std::swap(view, rhs.view); - std::swap(ownview, rhs.ownview); - return *this; - } - - ~buffer_info() { - if (view && ownview) { PyBuffer_Release(view); delete view; } - } - -private: - struct private_ctr_tag { }; - - buffer_info(private_ctr_tag, void *ptr, ssize_t itemsize, const std::string &format, ssize_t ndim, - detail::any_container &&shape_in, detail::any_container &&strides_in) - : buffer_info(ptr, itemsize, format, ndim, std::move(shape_in), std::move(strides_in)) { } - - Py_buffer *view = nullptr; - bool ownview = false; -}; - -NAMESPACE_BEGIN(detail) - -template struct compare_buffer_info { - static bool compare(const buffer_info& b) { - return b.format == format_descriptor::format() && b.itemsize == (ssize_t) sizeof(T); - } -}; - -template struct compare_buffer_info::value>> { - static bool compare(const buffer_info& b) { - return (size_t) b.itemsize == sizeof(T) && (b.format == format_descriptor::value || - ((sizeof(T) == sizeof(long)) && b.format == (std::is_unsigned::value ? "L" : "l")) || - ((sizeof(T) == sizeof(size_t)) && b.format == (std::is_unsigned::value ? "N" : "n"))); - } -}; - -NAMESPACE_END(detail) -NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/ptocr/postprocess/piexlmerge/include/pybind11/cast.h b/ptocr/postprocess/piexlmerge/include/pybind11/cast.h deleted file mode 100644 index 80abb2b..0000000 --- a/ptocr/postprocess/piexlmerge/include/pybind11/cast.h +++ /dev/null @@ -1,2128 +0,0 @@ -/* - pybind11/cast.h: Partial template specializations to cast between - C++ and Python types - - Copyright (c) 2016 Wenzel Jakob - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#include "pytypes.h" -#include "detail/typeid.h" -#include "detail/descr.h" -#include "detail/internals.h" -#include -#include -#include -#include - -#if defined(PYBIND11_CPP17) -# if defined(__has_include) -# if __has_include() -# define PYBIND11_HAS_STRING_VIEW -# endif -# elif defined(_MSC_VER) -# define PYBIND11_HAS_STRING_VIEW -# endif -#endif -#ifdef PYBIND11_HAS_STRING_VIEW -#include -#endif - -NAMESPACE_BEGIN(PYBIND11_NAMESPACE) -NAMESPACE_BEGIN(detail) - -/// A life support system for temporary objects created by `type_caster::load()`. -/// Adding a patient will keep it alive up until the enclosing function returns. -class loader_life_support { -public: - /// A new patient frame is created when a function is entered - loader_life_support() { - get_internals().loader_patient_stack.push_back(nullptr); - } - - /// ... and destroyed after it returns - ~loader_life_support() { - auto &stack = get_internals().loader_patient_stack; - if (stack.empty()) - pybind11_fail("loader_life_support: internal error"); - - auto ptr = stack.back(); - stack.pop_back(); - Py_CLEAR(ptr); - - // A heuristic to reduce the stack's capacity (e.g. after long recursive calls) - if (stack.capacity() > 16 && stack.size() != 0 && stack.capacity() / stack.size() > 2) - stack.shrink_to_fit(); - } - - /// This can only be used inside a pybind11-bound function, either by `argument_loader` - /// at argument preparation time or by `py::cast()` at execution time. - PYBIND11_NOINLINE static void add_patient(handle h) { - auto &stack = get_internals().loader_patient_stack; - if (stack.empty()) - throw cast_error("When called outside a bound function, py::cast() cannot " - "do Python -> C++ conversions which require the creation " - "of temporary values"); - - auto &list_ptr = stack.back(); - if (list_ptr == nullptr) { - list_ptr = PyList_New(1); - if (!list_ptr) - pybind11_fail("loader_life_support: error allocating list"); - PyList_SET_ITEM(list_ptr, 0, h.inc_ref().ptr()); - } else { - auto result = PyList_Append(list_ptr, h.ptr()); - if (result == -1) - pybind11_fail("loader_life_support: error adding patient"); - } - } -}; - -// Gets the cache entry for the given type, creating it if necessary. The return value is the pair -// returned by emplace, i.e. an iterator for the entry and a bool set to `true` if the entry was -// just created. -inline std::pair all_type_info_get_cache(PyTypeObject *type); - -// Populates a just-created cache entry. -PYBIND11_NOINLINE inline void all_type_info_populate(PyTypeObject *t, std::vector &bases) { - std::vector check; - for (handle parent : reinterpret_borrow(t->tp_bases)) - check.push_back((PyTypeObject *) parent.ptr()); - - auto const &type_dict = get_internals().registered_types_py; - for (size_t i = 0; i < check.size(); i++) { - auto type = check[i]; - // Ignore Python2 old-style class super type: - if (!PyType_Check((PyObject *) type)) continue; - - // Check `type` in the current set of registered python types: - auto it = type_dict.find(type); - if (it != type_dict.end()) { - // We found a cache entry for it, so it's either pybind-registered or has pre-computed - // pybind bases, but we have to make sure we haven't already seen the type(s) before: we - // want to follow Python/virtual C++ rules that there should only be one instance of a - // common base. - for (auto *tinfo : it->second) { - // NB: Could use a second set here, rather than doing a linear search, but since - // having a large number of immediate pybind11-registered types seems fairly - // unlikely, that probably isn't worthwhile. - bool found = false; - for (auto *known : bases) { - if (known == tinfo) { found = true; break; } - } - if (!found) bases.push_back(tinfo); - } - } - else if (type->tp_bases) { - // It's some python type, so keep follow its bases classes to look for one or more - // registered types - if (i + 1 == check.size()) { - // When we're at the end, we can pop off the current element to avoid growing - // `check` when adding just one base (which is typical--i.e. when there is no - // multiple inheritance) - check.pop_back(); - i--; - } - for (handle parent : reinterpret_borrow(type->tp_bases)) - check.push_back((PyTypeObject *) parent.ptr()); - } - } -} - -/** - * Extracts vector of type_info pointers of pybind-registered roots of the given Python type. Will - * be just 1 pybind type for the Python type of a pybind-registered class, or for any Python-side - * derived class that uses single inheritance. Will contain as many types as required for a Python - * class that uses multiple inheritance to inherit (directly or indirectly) from multiple - * pybind-registered classes. Will be empty if neither the type nor any base classes are - * pybind-registered. - * - * The value is cached for the lifetime of the Python type. - */ -inline const std::vector &all_type_info(PyTypeObject *type) { - auto ins = all_type_info_get_cache(type); - if (ins.second) - // New cache entry: populate it - all_type_info_populate(type, ins.first->second); - - return ins.first->second; -} - -/** - * Gets a single pybind11 type info for a python type. Returns nullptr if neither the type nor any - * ancestors are pybind11-registered. Throws an exception if there are multiple bases--use - * `all_type_info` instead if you want to support multiple bases. - */ -PYBIND11_NOINLINE inline detail::type_info* get_type_info(PyTypeObject *type) { - auto &bases = all_type_info(type); - if (bases.size() == 0) - return nullptr; - if (bases.size() > 1) - pybind11_fail("pybind11::detail::get_type_info: type has multiple pybind11-registered bases"); - return bases.front(); -} - -inline detail::type_info *get_local_type_info(const std::type_index &tp) { - auto &locals = registered_local_types_cpp(); - auto it = locals.find(tp); - if (it != locals.end()) - return it->second; - return nullptr; -} - -inline detail::type_info *get_global_type_info(const std::type_index &tp) { - auto &types = get_internals().registered_types_cpp; - auto it = types.find(tp); - if (it != types.end()) - return it->second; - return nullptr; -} - -/// Return the type info for a given C++ type; on lookup failure can either throw or return nullptr. -PYBIND11_NOINLINE inline detail::type_info *get_type_info(const std::type_index &tp, - bool throw_if_missing = false) { - if (auto ltype = get_local_type_info(tp)) - return ltype; - if (auto gtype = get_global_type_info(tp)) - return gtype; - - if (throw_if_missing) { - std::string tname = tp.name(); - detail::clean_type_id(tname); - pybind11_fail("pybind11::detail::get_type_info: unable to find type info for \"" + tname + "\""); - } - return nullptr; -} - -PYBIND11_NOINLINE inline handle get_type_handle(const std::type_info &tp, bool throw_if_missing) { - detail::type_info *type_info = get_type_info(tp, throw_if_missing); - return handle(type_info ? ((PyObject *) type_info->type) : nullptr); -} - -struct value_and_holder { - instance *inst; - size_t index; - const detail::type_info *type; - void **vh; - - // Main constructor for a found value/holder: - value_and_holder(instance *i, const detail::type_info *type, size_t vpos, size_t index) : - inst{i}, index{index}, type{type}, - vh{inst->simple_layout ? inst->simple_value_holder : &inst->nonsimple.values_and_holders[vpos]} - {} - - // Default constructor (used to signal a value-and-holder not found by get_value_and_holder()) - value_and_holder() : inst{nullptr} {} - - // Used for past-the-end iterator - value_and_holder(size_t index) : index{index} {} - - template V *&value_ptr() const { - return reinterpret_cast(vh[0]); - } - // True if this `value_and_holder` has a non-null value pointer - explicit operator bool() const { return value_ptr(); } - - template H &holder() const { - return reinterpret_cast(vh[1]); - } - bool holder_constructed() const { - return inst->simple_layout - ? inst->simple_holder_constructed - : inst->nonsimple.status[index] & instance::status_holder_constructed; - } - void set_holder_constructed(bool v = true) { - if (inst->simple_layout) - inst->simple_holder_constructed = v; - else if (v) - inst->nonsimple.status[index] |= instance::status_holder_constructed; - else - inst->nonsimple.status[index] &= (uint8_t) ~instance::status_holder_constructed; - } - bool instance_registered() const { - return inst->simple_layout - ? inst->simple_instance_registered - : inst->nonsimple.status[index] & instance::status_instance_registered; - } - void set_instance_registered(bool v = true) { - if (inst->simple_layout) - inst->simple_instance_registered = v; - else if (v) - inst->nonsimple.status[index] |= instance::status_instance_registered; - else - inst->nonsimple.status[index] &= (uint8_t) ~instance::status_instance_registered; - } -}; - -// Container for accessing and iterating over an instance's values/holders -struct values_and_holders { -private: - instance *inst; - using type_vec = std::vector; - const type_vec &tinfo; - -public: - values_and_holders(instance *inst) : inst{inst}, tinfo(all_type_info(Py_TYPE(inst))) {} - - struct iterator { - private: - instance *inst; - const type_vec *types; - value_and_holder curr; - friend struct values_and_holders; - iterator(instance *inst, const type_vec *tinfo) - : inst{inst}, types{tinfo}, - curr(inst /* instance */, - types->empty() ? nullptr : (*types)[0] /* type info */, - 0, /* vpos: (non-simple types only): the first vptr comes first */ - 0 /* index */) - {} - // Past-the-end iterator: - iterator(size_t end) : curr(end) {} - public: - bool operator==(const iterator &other) { return curr.index == other.curr.index; } - bool operator!=(const iterator &other) { return curr.index != other.curr.index; } - iterator &operator++() { - if (!inst->simple_layout) - curr.vh += 1 + (*types)[curr.index]->holder_size_in_ptrs; - ++curr.index; - curr.type = curr.index < types->size() ? (*types)[curr.index] : nullptr; - return *this; - } - value_and_holder &operator*() { return curr; } - value_and_holder *operator->() { return &curr; } - }; - - iterator begin() { return iterator(inst, &tinfo); } - iterator end() { return iterator(tinfo.size()); } - - iterator find(const type_info *find_type) { - auto it = begin(), endit = end(); - while (it != endit && it->type != find_type) ++it; - return it; - } - - size_t size() { return tinfo.size(); } -}; - -/** - * Extracts C++ value and holder pointer references from an instance (which may contain multiple - * values/holders for python-side multiple inheritance) that match the given type. Throws an error - * if the given type (or ValueType, if omitted) is not a pybind11 base of the given instance. If - * `find_type` is omitted (or explicitly specified as nullptr) the first value/holder are returned, - * regardless of type (and the resulting .type will be nullptr). - * - * The returned object should be short-lived: in particular, it must not outlive the called-upon - * instance. - */ -PYBIND11_NOINLINE inline value_and_holder instance::get_value_and_holder(const type_info *find_type /*= nullptr default in common.h*/, bool throw_if_missing /*= true in common.h*/) { - // Optimize common case: - if (!find_type || Py_TYPE(this) == find_type->type) - return value_and_holder(this, find_type, 0, 0); - - detail::values_and_holders vhs(this); - auto it = vhs.find(find_type); - if (it != vhs.end()) - return *it; - - if (!throw_if_missing) - return value_and_holder(); - -#if defined(NDEBUG) - pybind11_fail("pybind11::detail::instance::get_value_and_holder: " - "type is not a pybind11 base of the given instance " - "(compile in debug mode for type details)"); -#else - pybind11_fail("pybind11::detail::instance::get_value_and_holder: `" + - std::string(find_type->type->tp_name) + "' is not a pybind11 base of the given `" + - std::string(Py_TYPE(this)->tp_name) + "' instance"); -#endif -} - -PYBIND11_NOINLINE inline void instance::allocate_layout() { - auto &tinfo = all_type_info(Py_TYPE(this)); - - const size_t n_types = tinfo.size(); - - if (n_types == 0) - pybind11_fail("instance allocation failed: new instance has no pybind11-registered base types"); - - simple_layout = - n_types == 1 && tinfo.front()->holder_size_in_ptrs <= instance_simple_holder_in_ptrs(); - - // Simple path: no python-side multiple inheritance, and a small-enough holder - if (simple_layout) { - simple_value_holder[0] = nullptr; - simple_holder_constructed = false; - simple_instance_registered = false; - } - else { // multiple base types or a too-large holder - // Allocate space to hold: [v1*][h1][v2*][h2]...[bb...] where [vN*] is a value pointer, - // [hN] is the (uninitialized) holder instance for value N, and [bb...] is a set of bool - // values that tracks whether each associated holder has been initialized. Each [block] is - // padded, if necessary, to an integer multiple of sizeof(void *). - size_t space = 0; - for (auto t : tinfo) { - space += 1; // value pointer - space += t->holder_size_in_ptrs; // holder instance - } - size_t flags_at = space; - space += size_in_ptrs(n_types); // status bytes (holder_constructed and instance_registered) - - // Allocate space for flags, values, and holders, and initialize it to 0 (flags and values, - // in particular, need to be 0). Use Python's memory allocation functions: in Python 3.6 - // they default to using pymalloc, which is designed to be efficient for small allocations - // like the one we're doing here; in earlier versions (and for larger allocations) they are - // just wrappers around malloc. -#if PY_VERSION_HEX >= 0x03050000 - nonsimple.values_and_holders = (void **) PyMem_Calloc(space, sizeof(void *)); - if (!nonsimple.values_and_holders) throw std::bad_alloc(); -#else - nonsimple.values_and_holders = (void **) PyMem_New(void *, space); - if (!nonsimple.values_and_holders) throw std::bad_alloc(); - std::memset(nonsimple.values_and_holders, 0, space * sizeof(void *)); -#endif - nonsimple.status = reinterpret_cast(&nonsimple.values_and_holders[flags_at]); - } - owned = true; -} - -PYBIND11_NOINLINE inline void instance::deallocate_layout() { - if (!simple_layout) - PyMem_Free(nonsimple.values_and_holders); -} - -PYBIND11_NOINLINE inline bool isinstance_generic(handle obj, const std::type_info &tp) { - handle type = detail::get_type_handle(tp, false); - if (!type) - return false; - return isinstance(obj, type); -} - -PYBIND11_NOINLINE inline std::string error_string() { - if (!PyErr_Occurred()) { - PyErr_SetString(PyExc_RuntimeError, "Unknown internal error occurred"); - return "Unknown internal error occurred"; - } - - error_scope scope; // Preserve error state - - std::string errorString; - if (scope.type) { - errorString += handle(scope.type).attr("__name__").cast(); - errorString += ": "; - } - if (scope.value) - errorString += (std::string) str(scope.value); - - PyErr_NormalizeException(&scope.type, &scope.value, &scope.trace); - -#if PY_MAJOR_VERSION >= 3 - if (scope.trace != nullptr) - PyException_SetTraceback(scope.value, scope.trace); -#endif - -#if !defined(PYPY_VERSION) - if (scope.trace) { - PyTracebackObject *trace = (PyTracebackObject *) scope.trace; - - /* Get the deepest trace possible */ - while (trace->tb_next) - trace = trace->tb_next; - - PyFrameObject *frame = trace->tb_frame; - errorString += "\n\nAt:\n"; - while (frame) { - int lineno = PyFrame_GetLineNumber(frame); - errorString += - " " + handle(frame->f_code->co_filename).cast() + - "(" + std::to_string(lineno) + "): " + - handle(frame->f_code->co_name).cast() + "\n"; - frame = frame->f_back; - } - } -#endif - - return errorString; -} - -PYBIND11_NOINLINE inline handle get_object_handle(const void *ptr, const detail::type_info *type ) { - auto &instances = get_internals().registered_instances; - auto range = instances.equal_range(ptr); - for (auto it = range.first; it != range.second; ++it) { - for (auto vh : values_and_holders(it->second)) { - if (vh.type == type) - return handle((PyObject *) it->second); - } - } - return handle(); -} - -inline PyThreadState *get_thread_state_unchecked() { -#if defined(PYPY_VERSION) - return PyThreadState_GET(); -#elif PY_VERSION_HEX < 0x03000000 - return _PyThreadState_Current; -#elif PY_VERSION_HEX < 0x03050000 - return (PyThreadState*) _Py_atomic_load_relaxed(&_PyThreadState_Current); -#elif PY_VERSION_HEX < 0x03050200 - return (PyThreadState*) _PyThreadState_Current.value; -#else - return _PyThreadState_UncheckedGet(); -#endif -} - -// Forward declarations -inline void keep_alive_impl(handle nurse, handle patient); -inline PyObject *make_new_instance(PyTypeObject *type); - -class type_caster_generic { -public: - PYBIND11_NOINLINE type_caster_generic(const std::type_info &type_info) - : typeinfo(get_type_info(type_info)), cpptype(&type_info) { } - - type_caster_generic(const type_info *typeinfo) - : typeinfo(typeinfo), cpptype(typeinfo ? typeinfo->cpptype : nullptr) { } - - bool load(handle src, bool convert) { - return load_impl(src, convert); - } - - PYBIND11_NOINLINE static handle cast(const void *_src, return_value_policy policy, handle parent, - const detail::type_info *tinfo, - void *(*copy_constructor)(const void *), - void *(*move_constructor)(const void *), - const void *existing_holder = nullptr) { - if (!tinfo) // no type info: error will be set already - return handle(); - - void *src = const_cast(_src); - if (src == nullptr) - return none().release(); - - auto it_instances = get_internals().registered_instances.equal_range(src); - for (auto it_i = it_instances.first; it_i != it_instances.second; ++it_i) { - for (auto instance_type : detail::all_type_info(Py_TYPE(it_i->second))) { - if (instance_type && same_type(*instance_type->cpptype, *tinfo->cpptype)) - return handle((PyObject *) it_i->second).inc_ref(); - } - } - - auto inst = reinterpret_steal(make_new_instance(tinfo->type)); - auto wrapper = reinterpret_cast(inst.ptr()); - wrapper->owned = false; - void *&valueptr = values_and_holders(wrapper).begin()->value_ptr(); - - switch (policy) { - case return_value_policy::automatic: - case return_value_policy::take_ownership: - valueptr = src; - wrapper->owned = true; - break; - - case return_value_policy::automatic_reference: - case return_value_policy::reference: - valueptr = src; - wrapper->owned = false; - break; - - case return_value_policy::copy: - if (copy_constructor) - valueptr = copy_constructor(src); - else - throw cast_error("return_value_policy = copy, but the " - "object is non-copyable!"); - wrapper->owned = true; - break; - - case return_value_policy::move: - if (move_constructor) - valueptr = move_constructor(src); - else if (copy_constructor) - valueptr = copy_constructor(src); - else - throw cast_error("return_value_policy = move, but the " - "object is neither movable nor copyable!"); - wrapper->owned = true; - break; - - case return_value_policy::reference_internal: - valueptr = src; - wrapper->owned = false; - keep_alive_impl(inst, parent); - break; - - default: - throw cast_error("unhandled return_value_policy: should not happen!"); - } - - tinfo->init_instance(wrapper, existing_holder); - - return inst.release(); - } - - // Base methods for generic caster; there are overridden in copyable_holder_caster - void load_value(value_and_holder &&v_h) { - auto *&vptr = v_h.value_ptr(); - // Lazy allocation for unallocated values: - if (vptr == nullptr) { - auto *type = v_h.type ? v_h.type : typeinfo; - if (type->operator_new) { - vptr = type->operator_new(type->type_size); - } else { - #if defined(PYBIND11_CPP17) - if (type->type_align > __STDCPP_DEFAULT_NEW_ALIGNMENT__) - vptr = ::operator new(type->type_size, - (std::align_val_t) type->type_align); - else - #endif - vptr = ::operator new(type->type_size); - } - } - value = vptr; - } - bool try_implicit_casts(handle src, bool convert) { - for (auto &cast : typeinfo->implicit_casts) { - type_caster_generic sub_caster(*cast.first); - if (sub_caster.load(src, convert)) { - value = cast.second(sub_caster.value); - return true; - } - } - return false; - } - bool try_direct_conversions(handle src) { - for (auto &converter : *typeinfo->direct_conversions) { - if (converter(src.ptr(), value)) - return true; - } - return false; - } - void check_holder_compat() {} - - PYBIND11_NOINLINE static void *local_load(PyObject *src, const type_info *ti) { - auto caster = type_caster_generic(ti); - if (caster.load(src, false)) - return caster.value; - return nullptr; - } - - /// Try to load with foreign typeinfo, if available. Used when there is no - /// native typeinfo, or when the native one wasn't able to produce a value. - PYBIND11_NOINLINE bool try_load_foreign_module_local(handle src) { - constexpr auto *local_key = PYBIND11_MODULE_LOCAL_ID; - const auto pytype = src.get_type(); - if (!hasattr(pytype, local_key)) - return false; - - type_info *foreign_typeinfo = reinterpret_borrow(getattr(pytype, local_key)); - // Only consider this foreign loader if actually foreign and is a loader of the correct cpp type - if (foreign_typeinfo->module_local_load == &local_load - || (cpptype && !same_type(*cpptype, *foreign_typeinfo->cpptype))) - return false; - - if (auto result = foreign_typeinfo->module_local_load(src.ptr(), foreign_typeinfo)) { - value = result; - return true; - } - return false; - } - - // Implementation of `load`; this takes the type of `this` so that it can dispatch the relevant - // bits of code between here and copyable_holder_caster where the two classes need different - // logic (without having to resort to virtual inheritance). - template - PYBIND11_NOINLINE bool load_impl(handle src, bool convert) { - if (!src) return false; - if (!typeinfo) return try_load_foreign_module_local(src); - if (src.is_none()) { - // Defer accepting None to other overloads (if we aren't in convert mode): - if (!convert) return false; - value = nullptr; - return true; - } - - auto &this_ = static_cast(*this); - this_.check_holder_compat(); - - PyTypeObject *srctype = Py_TYPE(src.ptr()); - - // Case 1: If src is an exact type match for the target type then we can reinterpret_cast - // the instance's value pointer to the target type: - if (srctype == typeinfo->type) { - this_.load_value(reinterpret_cast(src.ptr())->get_value_and_holder()); - return true; - } - // Case 2: We have a derived class - else if (PyType_IsSubtype(srctype, typeinfo->type)) { - auto &bases = all_type_info(srctype); - bool no_cpp_mi = typeinfo->simple_type; - - // Case 2a: the python type is a Python-inherited derived class that inherits from just - // one simple (no MI) pybind11 class, or is an exact match, so the C++ instance is of - // the right type and we can use reinterpret_cast. - // (This is essentially the same as case 2b, but because not using multiple inheritance - // is extremely common, we handle it specially to avoid the loop iterator and type - // pointer lookup overhead) - if (bases.size() == 1 && (no_cpp_mi || bases.front()->type == typeinfo->type)) { - this_.load_value(reinterpret_cast(src.ptr())->get_value_and_holder()); - return true; - } - // Case 2b: the python type inherits from multiple C++ bases. Check the bases to see if - // we can find an exact match (or, for a simple C++ type, an inherited match); if so, we - // can safely reinterpret_cast to the relevant pointer. - else if (bases.size() > 1) { - for (auto base : bases) { - if (no_cpp_mi ? PyType_IsSubtype(base->type, typeinfo->type) : base->type == typeinfo->type) { - this_.load_value(reinterpret_cast(src.ptr())->get_value_and_holder(base)); - return true; - } - } - } - - // Case 2c: C++ multiple inheritance is involved and we couldn't find an exact type match - // in the registered bases, above, so try implicit casting (needed for proper C++ casting - // when MI is involved). - if (this_.try_implicit_casts(src, convert)) - return true; - } - - // Perform an implicit conversion - if (convert) { - for (auto &converter : typeinfo->implicit_conversions) { - auto temp = reinterpret_steal(converter(src.ptr(), typeinfo->type)); - if (load_impl(temp, false)) { - loader_life_support::add_patient(temp); - return true; - } - } - if (this_.try_direct_conversions(src)) - return true; - } - - // Failed to match local typeinfo. Try again with global. - if (typeinfo->module_local) { - if (auto gtype = get_global_type_info(*typeinfo->cpptype)) { - typeinfo = gtype; - return load(src, false); - } - } - - // Global typeinfo has precedence over foreign module_local - return try_load_foreign_module_local(src); - } - - - // Called to do type lookup and wrap the pointer and type in a pair when a dynamic_cast - // isn't needed or can't be used. If the type is unknown, sets the error and returns a pair - // with .second = nullptr. (p.first = nullptr is not an error: it becomes None). - PYBIND11_NOINLINE static std::pair src_and_type( - const void *src, const std::type_info &cast_type, const std::type_info *rtti_type = nullptr) { - if (auto *tpi = get_type_info(cast_type)) - return {src, const_cast(tpi)}; - - // Not found, set error: - std::string tname = rtti_type ? rtti_type->name() : cast_type.name(); - detail::clean_type_id(tname); - std::string msg = "Unregistered type : " + tname; - PyErr_SetString(PyExc_TypeError, msg.c_str()); - return {nullptr, nullptr}; - } - - const type_info *typeinfo = nullptr; - const std::type_info *cpptype = nullptr; - void *value = nullptr; -}; - -/** - * Determine suitable casting operator for pointer-or-lvalue-casting type casters. The type caster - * needs to provide `operator T*()` and `operator T&()` operators. - * - * If the type supports moving the value away via an `operator T&&() &&` method, it should use - * `movable_cast_op_type` instead. - */ -template -using cast_op_type = - conditional_t>::value, - typename std::add_pointer>::type, - typename std::add_lvalue_reference>::type>; - -/** - * Determine suitable casting operator for a type caster with a movable value. Such a type caster - * needs to provide `operator T*()`, `operator T&()`, and `operator T&&() &&`. The latter will be - * called in appropriate contexts where the value can be moved rather than copied. - * - * These operator are automatically provided when using the PYBIND11_TYPE_CASTER macro. - */ -template -using movable_cast_op_type = - conditional_t::type>::value, - typename std::add_pointer>::type, - conditional_t::value, - typename std::add_rvalue_reference>::type, - typename std::add_lvalue_reference>::type>>; - -// std::is_copy_constructible isn't quite enough: it lets std::vector (and similar) through when -// T is non-copyable, but code containing such a copy constructor fails to actually compile. -template struct is_copy_constructible : std::is_copy_constructible {}; - -// Specialization for types that appear to be copy constructible but also look like stl containers -// (we specifically check for: has `value_type` and `reference` with `reference = value_type&`): if -// so, copy constructability depends on whether the value_type is copy constructible. -template struct is_copy_constructible, - std::is_same - >::value>> : is_copy_constructible {}; - -#if !defined(PYBIND11_CPP17) -// Likewise for std::pair before C++17 (which mandates that the copy constructor not exist when the -// two types aren't themselves copy constructible). -template struct is_copy_constructible> - : all_of, is_copy_constructible> {}; -#endif - -NAMESPACE_END(detail) - -// polymorphic_type_hook::get(src, tinfo) determines whether the object pointed -// to by `src` actually is an instance of some class derived from `itype`. -// If so, it sets `tinfo` to point to the std::type_info representing that derived -// type, and returns a pointer to the start of the most-derived object of that type -// (in which `src` is a subobject; this will be the same address as `src` in most -// single inheritance cases). If not, or if `src` is nullptr, it simply returns `src` -// and leaves `tinfo` at its default value of nullptr. -// -// The default polymorphic_type_hook just returns src. A specialization for polymorphic -// types determines the runtime type of the passed object and adjusts the this-pointer -// appropriately via dynamic_cast. This is what enables a C++ Animal* to appear -// to Python as a Dog (if Dog inherits from Animal, Animal is polymorphic, Dog is -// registered with pybind11, and this Animal is in fact a Dog). -// -// You may specialize polymorphic_type_hook yourself for types that want to appear -// polymorphic to Python but do not use C++ RTTI. (This is a not uncommon pattern -// in performance-sensitive applications, used most notably in LLVM.) -template -struct polymorphic_type_hook -{ - static const void *get(const itype *src, const std::type_info*&) { return src; } -}; -template -struct polymorphic_type_hook::value>> -{ - static const void *get(const itype *src, const std::type_info*& type) { - type = src ? &typeid(*src) : nullptr; - return dynamic_cast(src); - } -}; - -NAMESPACE_BEGIN(detail) - -/// Generic type caster for objects stored on the heap -template class type_caster_base : public type_caster_generic { - using itype = intrinsic_t; - -public: - static constexpr auto name = _(); - - type_caster_base() : type_caster_base(typeid(type)) { } - explicit type_caster_base(const std::type_info &info) : type_caster_generic(info) { } - - static handle cast(const itype &src, return_value_policy policy, handle parent) { - if (policy == return_value_policy::automatic || policy == return_value_policy::automatic_reference) - policy = return_value_policy::copy; - return cast(&src, policy, parent); - } - - static handle cast(itype &&src, return_value_policy, handle parent) { - return cast(&src, return_value_policy::move, parent); - } - - // Returns a (pointer, type_info) pair taking care of necessary type lookup for a - // polymorphic type (using RTTI by default, but can be overridden by specializing - // polymorphic_type_hook). If the instance isn't derived, returns the base version. - static std::pair src_and_type(const itype *src) { - auto &cast_type = typeid(itype); - const std::type_info *instance_type = nullptr; - const void *vsrc = polymorphic_type_hook::get(src, instance_type); - if (instance_type && !same_type(cast_type, *instance_type)) { - // This is a base pointer to a derived type. If the derived type is registered - // with pybind11, we want to make the full derived object available. - // In the typical case where itype is polymorphic, we get the correct - // derived pointer (which may be != base pointer) by a dynamic_cast to - // most derived type. If itype is not polymorphic, we won't get here - // except via a user-provided specialization of polymorphic_type_hook, - // and the user has promised that no this-pointer adjustment is - // required in that case, so it's OK to use static_cast. - if (const auto *tpi = get_type_info(*instance_type)) - return {vsrc, tpi}; - } - // Otherwise we have either a nullptr, an `itype` pointer, or an unknown derived pointer, so - // don't do a cast - return type_caster_generic::src_and_type(src, cast_type, instance_type); - } - - static handle cast(const itype *src, return_value_policy policy, handle parent) { - auto st = src_and_type(src); - return type_caster_generic::cast( - st.first, policy, parent, st.second, - make_copy_constructor(src), make_move_constructor(src)); - } - - static handle cast_holder(const itype *src, const void *holder) { - auto st = src_and_type(src); - return type_caster_generic::cast( - st.first, return_value_policy::take_ownership, {}, st.second, - nullptr, nullptr, holder); - } - - template using cast_op_type = detail::cast_op_type; - - operator itype*() { return (type *) value; } - operator itype&() { if (!value) throw reference_cast_error(); return *((itype *) value); } - -protected: - using Constructor = void *(*)(const void *); - - /* Only enabled when the types are {copy,move}-constructible *and* when the type - does not have a private operator new implementation. */ - template ::value>> - static auto make_copy_constructor(const T *x) -> decltype(new T(*x), Constructor{}) { - return [](const void *arg) -> void * { - return new T(*reinterpret_cast(arg)); - }; - } - - template ::value>> - static auto make_move_constructor(const T *x) -> decltype(new T(std::move(*const_cast(x))), Constructor{}) { - return [](const void *arg) -> void * { - return new T(std::move(*const_cast(reinterpret_cast(arg)))); - }; - } - - static Constructor make_copy_constructor(...) { return nullptr; } - static Constructor make_move_constructor(...) { return nullptr; } -}; - -template class type_caster : public type_caster_base { }; -template using make_caster = type_caster>; - -// Shortcut for calling a caster's `cast_op_type` cast operator for casting a type_caster to a T -template typename make_caster::template cast_op_type cast_op(make_caster &caster) { - return caster.operator typename make_caster::template cast_op_type(); -} -template typename make_caster::template cast_op_type::type> -cast_op(make_caster &&caster) { - return std::move(caster).operator - typename make_caster::template cast_op_type::type>(); -} - -template class type_caster> { -private: - using caster_t = make_caster; - caster_t subcaster; - using subcaster_cast_op_type = typename caster_t::template cast_op_type; - static_assert(std::is_same::type &, subcaster_cast_op_type>::value, - "std::reference_wrapper caster requires T to have a caster with an `T &` operator"); -public: - bool load(handle src, bool convert) { return subcaster.load(src, convert); } - static constexpr auto name = caster_t::name; - static handle cast(const std::reference_wrapper &src, return_value_policy policy, handle parent) { - // It is definitely wrong to take ownership of this pointer, so mask that rvp - if (policy == return_value_policy::take_ownership || policy == return_value_policy::automatic) - policy = return_value_policy::automatic_reference; - return caster_t::cast(&src.get(), policy, parent); - } - template using cast_op_type = std::reference_wrapper; - operator std::reference_wrapper() { return subcaster.operator subcaster_cast_op_type&(); } -}; - -#define PYBIND11_TYPE_CASTER(type, py_name) \ - protected: \ - type value; \ - public: \ - static constexpr auto name = py_name; \ - template >::value, int> = 0> \ - static handle cast(T_ *src, return_value_policy policy, handle parent) { \ - if (!src) return none().release(); \ - if (policy == return_value_policy::take_ownership) { \ - auto h = cast(std::move(*src), policy, parent); delete src; return h; \ - } else { \ - return cast(*src, policy, parent); \ - } \ - } \ - operator type*() { return &value; } \ - operator type&() { return value; } \ - operator type&&() && { return std::move(value); } \ - template using cast_op_type = pybind11::detail::movable_cast_op_type - - -template using is_std_char_type = any_of< - std::is_same, /* std::string */ - std::is_same, /* std::u16string */ - std::is_same, /* std::u32string */ - std::is_same /* std::wstring */ ->; - -template -struct type_caster::value && !is_std_char_type::value>> { - using _py_type_0 = conditional_t; - using _py_type_1 = conditional_t::value, _py_type_0, typename std::make_unsigned<_py_type_0>::type>; - using py_type = conditional_t::value, double, _py_type_1>; -public: - - bool load(handle src, bool convert) { - py_type py_value; - - if (!src) - return false; - - if (std::is_floating_point::value) { - if (convert || PyFloat_Check(src.ptr())) - py_value = (py_type) PyFloat_AsDouble(src.ptr()); - else - return false; - } else if (PyFloat_Check(src.ptr())) { - return false; - } else if (std::is_unsigned::value) { - py_value = as_unsigned(src.ptr()); - } else { // signed integer: - py_value = sizeof(T) <= sizeof(long) - ? (py_type) PyLong_AsLong(src.ptr()) - : (py_type) PYBIND11_LONG_AS_LONGLONG(src.ptr()); - } - - bool py_err = py_value == (py_type) -1 && PyErr_Occurred(); - if (py_err || (std::is_integral::value && sizeof(py_type) != sizeof(T) && - (py_value < (py_type) std::numeric_limits::min() || - py_value > (py_type) std::numeric_limits::max()))) { - bool type_error = py_err && PyErr_ExceptionMatches( -#if PY_VERSION_HEX < 0x03000000 && !defined(PYPY_VERSION) - PyExc_SystemError -#else - PyExc_TypeError -#endif - ); - PyErr_Clear(); - if (type_error && convert && PyNumber_Check(src.ptr())) { - auto tmp = reinterpret_steal(std::is_floating_point::value - ? PyNumber_Float(src.ptr()) - : PyNumber_Long(src.ptr())); - PyErr_Clear(); - return load(tmp, false); - } - return false; - } - - value = (T) py_value; - return true; - } - - template - static typename std::enable_if::value, handle>::type - cast(U src, return_value_policy /* policy */, handle /* parent */) { - return PyFloat_FromDouble((double) src); - } - - template - static typename std::enable_if::value && std::is_signed::value && (sizeof(U) <= sizeof(long)), handle>::type - cast(U src, return_value_policy /* policy */, handle /* parent */) { - return PYBIND11_LONG_FROM_SIGNED((long) src); - } - - template - static typename std::enable_if::value && std::is_unsigned::value && (sizeof(U) <= sizeof(unsigned long)), handle>::type - cast(U src, return_value_policy /* policy */, handle /* parent */) { - return PYBIND11_LONG_FROM_UNSIGNED((unsigned long) src); - } - - template - static typename std::enable_if::value && std::is_signed::value && (sizeof(U) > sizeof(long)), handle>::type - cast(U src, return_value_policy /* policy */, handle /* parent */) { - return PyLong_FromLongLong((long long) src); - } - - template - static typename std::enable_if::value && std::is_unsigned::value && (sizeof(U) > sizeof(unsigned long)), handle>::type - cast(U src, return_value_policy /* policy */, handle /* parent */) { - return PyLong_FromUnsignedLongLong((unsigned long long) src); - } - - PYBIND11_TYPE_CASTER(T, _::value>("int", "float")); -}; - -template struct void_caster { -public: - bool load(handle src, bool) { - if (src && src.is_none()) - return true; - return false; - } - static handle cast(T, return_value_policy /* policy */, handle /* parent */) { - return none().inc_ref(); - } - PYBIND11_TYPE_CASTER(T, _("None")); -}; - -template <> class type_caster : public void_caster {}; - -template <> class type_caster : public type_caster { -public: - using type_caster::cast; - - bool load(handle h, bool) { - if (!h) { - return false; - } else if (h.is_none()) { - value = nullptr; - return true; - } - - /* Check if this is a capsule */ - if (isinstance(h)) { - value = reinterpret_borrow(h); - return true; - } - - /* Check if this is a C++ type */ - auto &bases = all_type_info((PyTypeObject *) h.get_type().ptr()); - if (bases.size() == 1) { // Only allowing loading from a single-value type - value = values_and_holders(reinterpret_cast(h.ptr())).begin()->value_ptr(); - return true; - } - - /* Fail */ - return false; - } - - static handle cast(const void *ptr, return_value_policy /* policy */, handle /* parent */) { - if (ptr) - return capsule(ptr).release(); - else - return none().inc_ref(); - } - - template using cast_op_type = void*&; - operator void *&() { return value; } - static constexpr auto name = _("capsule"); -private: - void *value = nullptr; -}; - -template <> class type_caster : public void_caster { }; - -template <> class type_caster { -public: - bool load(handle src, bool convert) { - if (!src) return false; - else if (src.ptr() == Py_True) { value = true; return true; } - else if (src.ptr() == Py_False) { value = false; return true; } - else if (convert || !strcmp("numpy.bool_", Py_TYPE(src.ptr())->tp_name)) { - // (allow non-implicit conversion for numpy booleans) - - Py_ssize_t res = -1; - if (src.is_none()) { - res = 0; // None is implicitly converted to False - } - #if defined(PYPY_VERSION) - // On PyPy, check that "__bool__" (or "__nonzero__" on Python 2.7) attr exists - else if (hasattr(src, PYBIND11_BOOL_ATTR)) { - res = PyObject_IsTrue(src.ptr()); - } - #else - // Alternate approach for CPython: this does the same as the above, but optimized - // using the CPython API so as to avoid an unneeded attribute lookup. - else if (auto tp_as_number = src.ptr()->ob_type->tp_as_number) { - if (PYBIND11_NB_BOOL(tp_as_number)) { - res = (*PYBIND11_NB_BOOL(tp_as_number))(src.ptr()); - } - } - #endif - if (res == 0 || res == 1) { - value = (bool) res; - return true; - } - } - return false; - } - static handle cast(bool src, return_value_policy /* policy */, handle /* parent */) { - return handle(src ? Py_True : Py_False).inc_ref(); - } - PYBIND11_TYPE_CASTER(bool, _("bool")); -}; - -// Helper class for UTF-{8,16,32} C++ stl strings: -template struct string_caster { - using CharT = typename StringType::value_type; - - // Simplify life by being able to assume standard char sizes (the standard only guarantees - // minimums, but Python requires exact sizes) - static_assert(!std::is_same::value || sizeof(CharT) == 1, "Unsupported char size != 1"); - static_assert(!std::is_same::value || sizeof(CharT) == 2, "Unsupported char16_t size != 2"); - static_assert(!std::is_same::value || sizeof(CharT) == 4, "Unsupported char32_t size != 4"); - // wchar_t can be either 16 bits (Windows) or 32 (everywhere else) - static_assert(!std::is_same::value || sizeof(CharT) == 2 || sizeof(CharT) == 4, - "Unsupported wchar_t size != 2/4"); - static constexpr size_t UTF_N = 8 * sizeof(CharT); - - bool load(handle src, bool) { -#if PY_MAJOR_VERSION < 3 - object temp; -#endif - handle load_src = src; - if (!src) { - return false; - } else if (!PyUnicode_Check(load_src.ptr())) { -#if PY_MAJOR_VERSION >= 3 - return load_bytes(load_src); -#else - if (sizeof(CharT) == 1) { - return load_bytes(load_src); - } - - // The below is a guaranteed failure in Python 3 when PyUnicode_Check returns false - if (!PYBIND11_BYTES_CHECK(load_src.ptr())) - return false; - - temp = reinterpret_steal(PyUnicode_FromObject(load_src.ptr())); - if (!temp) { PyErr_Clear(); return false; } - load_src = temp; -#endif - } - - object utfNbytes = reinterpret_steal(PyUnicode_AsEncodedString( - load_src.ptr(), UTF_N == 8 ? "utf-8" : UTF_N == 16 ? "utf-16" : "utf-32", nullptr)); - if (!utfNbytes) { PyErr_Clear(); return false; } - - const CharT *buffer = reinterpret_cast(PYBIND11_BYTES_AS_STRING(utfNbytes.ptr())); - size_t length = (size_t) PYBIND11_BYTES_SIZE(utfNbytes.ptr()) / sizeof(CharT); - if (UTF_N > 8) { buffer++; length--; } // Skip BOM for UTF-16/32 - value = StringType(buffer, length); - - // If we're loading a string_view we need to keep the encoded Python object alive: - if (IsView) - loader_life_support::add_patient(utfNbytes); - - return true; - } - - static handle cast(const StringType &src, return_value_policy /* policy */, handle /* parent */) { - const char *buffer = reinterpret_cast(src.data()); - ssize_t nbytes = ssize_t(src.size() * sizeof(CharT)); - handle s = decode_utfN(buffer, nbytes); - if (!s) throw error_already_set(); - return s; - } - - PYBIND11_TYPE_CASTER(StringType, _(PYBIND11_STRING_NAME)); - -private: - static handle decode_utfN(const char *buffer, ssize_t nbytes) { -#if !defined(PYPY_VERSION) - return - UTF_N == 8 ? PyUnicode_DecodeUTF8(buffer, nbytes, nullptr) : - UTF_N == 16 ? PyUnicode_DecodeUTF16(buffer, nbytes, nullptr, nullptr) : - PyUnicode_DecodeUTF32(buffer, nbytes, nullptr, nullptr); -#else - // PyPy seems to have multiple problems related to PyUnicode_UTF*: the UTF8 version - // sometimes segfaults for unknown reasons, while the UTF16 and 32 versions require a - // non-const char * arguments, which is also a nuisance, so bypass the whole thing by just - // passing the encoding as a string value, which works properly: - return PyUnicode_Decode(buffer, nbytes, UTF_N == 8 ? "utf-8" : UTF_N == 16 ? "utf-16" : "utf-32", nullptr); -#endif - } - - // When loading into a std::string or char*, accept a bytes object as-is (i.e. - // without any encoding/decoding attempt). For other C++ char sizes this is a no-op. - // which supports loading a unicode from a str, doesn't take this path. - template - bool load_bytes(enable_if_t src) { - if (PYBIND11_BYTES_CHECK(src.ptr())) { - // We were passed a Python 3 raw bytes; accept it into a std::string or char* - // without any encoding attempt. - const char *bytes = PYBIND11_BYTES_AS_STRING(src.ptr()); - if (bytes) { - value = StringType(bytes, (size_t) PYBIND11_BYTES_SIZE(src.ptr())); - return true; - } - } - - return false; - } - - template - bool load_bytes(enable_if_t) { return false; } -}; - -template -struct type_caster, enable_if_t::value>> - : string_caster> {}; - -#ifdef PYBIND11_HAS_STRING_VIEW -template -struct type_caster, enable_if_t::value>> - : string_caster, true> {}; -#endif - -// Type caster for C-style strings. We basically use a std::string type caster, but also add the -// ability to use None as a nullptr char* (which the string caster doesn't allow). -template struct type_caster::value>> { - using StringType = std::basic_string; - using StringCaster = type_caster; - StringCaster str_caster; - bool none = false; - CharT one_char = 0; -public: - bool load(handle src, bool convert) { - if (!src) return false; - if (src.is_none()) { - // Defer accepting None to other overloads (if we aren't in convert mode): - if (!convert) return false; - none = true; - return true; - } - return str_caster.load(src, convert); - } - - static handle cast(const CharT *src, return_value_policy policy, handle parent) { - if (src == nullptr) return pybind11::none().inc_ref(); - return StringCaster::cast(StringType(src), policy, parent); - } - - static handle cast(CharT src, return_value_policy policy, handle parent) { - if (std::is_same::value) { - handle s = PyUnicode_DecodeLatin1((const char *) &src, 1, nullptr); - if (!s) throw error_already_set(); - return s; - } - return StringCaster::cast(StringType(1, src), policy, parent); - } - - operator CharT*() { return none ? nullptr : const_cast(static_cast(str_caster).c_str()); } - operator CharT&() { - if (none) - throw value_error("Cannot convert None to a character"); - - auto &value = static_cast(str_caster); - size_t str_len = value.size(); - if (str_len == 0) - throw value_error("Cannot convert empty string to a character"); - - // If we're in UTF-8 mode, we have two possible failures: one for a unicode character that - // is too high, and one for multiple unicode characters (caught later), so we need to figure - // out how long the first encoded character is in bytes to distinguish between these two - // errors. We also allow want to allow unicode characters U+0080 through U+00FF, as those - // can fit into a single char value. - if (StringCaster::UTF_N == 8 && str_len > 1 && str_len <= 4) { - unsigned char v0 = static_cast(value[0]); - size_t char0_bytes = !(v0 & 0x80) ? 1 : // low bits only: 0-127 - (v0 & 0xE0) == 0xC0 ? 2 : // 0b110xxxxx - start of 2-byte sequence - (v0 & 0xF0) == 0xE0 ? 3 : // 0b1110xxxx - start of 3-byte sequence - 4; // 0b11110xxx - start of 4-byte sequence - - if (char0_bytes == str_len) { - // If we have a 128-255 value, we can decode it into a single char: - if (char0_bytes == 2 && (v0 & 0xFC) == 0xC0) { // 0x110000xx 0x10xxxxxx - one_char = static_cast(((v0 & 3) << 6) + (static_cast(value[1]) & 0x3F)); - return one_char; - } - // Otherwise we have a single character, but it's > U+00FF - throw value_error("Character code point not in range(0x100)"); - } - } - - // UTF-16 is much easier: we can only have a surrogate pair for values above U+FFFF, thus a - // surrogate pair with total length 2 instantly indicates a range error (but not a "your - // string was too long" error). - else if (StringCaster::UTF_N == 16 && str_len == 2) { - one_char = static_cast(value[0]); - if (one_char >= 0xD800 && one_char < 0xE000) - throw value_error("Character code point not in range(0x10000)"); - } - - if (str_len != 1) - throw value_error("Expected a character, but multi-character string found"); - - one_char = value[0]; - return one_char; - } - - static constexpr auto name = _(PYBIND11_STRING_NAME); - template using cast_op_type = pybind11::detail::cast_op_type<_T>; -}; - -// Base implementation for std::tuple and std::pair -template class Tuple, typename... Ts> class tuple_caster { - using type = Tuple; - static constexpr auto size = sizeof...(Ts); - using indices = make_index_sequence; -public: - - bool load(handle src, bool convert) { - if (!isinstance(src)) - return false; - const auto seq = reinterpret_borrow(src); - if (seq.size() != size) - return false; - return load_impl(seq, convert, indices{}); - } - - template - static handle cast(T &&src, return_value_policy policy, handle parent) { - return cast_impl(std::forward(src), policy, parent, indices{}); - } - - static constexpr auto name = _("Tuple[") + concat(make_caster::name...) + _("]"); - - template using cast_op_type = type; - - operator type() & { return implicit_cast(indices{}); } - operator type() && { return std::move(*this).implicit_cast(indices{}); } - -protected: - template - type implicit_cast(index_sequence) & { return type(cast_op(std::get(subcasters))...); } - template - type implicit_cast(index_sequence) && { return type(cast_op(std::move(std::get(subcasters)))...); } - - static constexpr bool load_impl(const sequence &, bool, index_sequence<>) { return true; } - - template - bool load_impl(const sequence &seq, bool convert, index_sequence) { - for (bool r : {std::get(subcasters).load(seq[Is], convert)...}) - if (!r) - return false; - return true; - } - - /* Implementation: Convert a C++ tuple into a Python tuple */ - template - static handle cast_impl(T &&src, return_value_policy policy, handle parent, index_sequence) { - std::array entries{{ - reinterpret_steal(make_caster::cast(std::get(std::forward(src)), policy, parent))... - }}; - for (const auto &entry: entries) - if (!entry) - return handle(); - tuple result(size); - int counter = 0; - for (auto & entry: entries) - PyTuple_SET_ITEM(result.ptr(), counter++, entry.release().ptr()); - return result.release(); - } - - Tuple...> subcasters; -}; - -template class type_caster> - : public tuple_caster {}; - -template class type_caster> - : public tuple_caster {}; - -/// Helper class which abstracts away certain actions. Users can provide specializations for -/// custom holders, but it's only necessary if the type has a non-standard interface. -template -struct holder_helper { - static auto get(const T &p) -> decltype(p.get()) { return p.get(); } -}; - -/// Type caster for holder types like std::shared_ptr, etc. -template -struct copyable_holder_caster : public type_caster_base { -public: - using base = type_caster_base; - static_assert(std::is_base_of>::value, - "Holder classes are only supported for custom types"); - using base::base; - using base::cast; - using base::typeinfo; - using base::value; - - bool load(handle src, bool convert) { - return base::template load_impl>(src, convert); - } - - explicit operator type*() { return this->value; } - explicit operator type&() { return *(this->value); } - explicit operator holder_type*() { return std::addressof(holder); } - - // Workaround for Intel compiler bug - // see pybind11 issue 94 - #if defined(__ICC) || defined(__INTEL_COMPILER) - operator holder_type&() { return holder; } - #else - explicit operator holder_type&() { return holder; } - #endif - - static handle cast(const holder_type &src, return_value_policy, handle) { - const auto *ptr = holder_helper::get(src); - return type_caster_base::cast_holder(ptr, &src); - } - -protected: - friend class type_caster_generic; - void check_holder_compat() { - if (typeinfo->default_holder) - throw cast_error("Unable to load a custom holder type from a default-holder instance"); - } - - bool load_value(value_and_holder &&v_h) { - if (v_h.holder_constructed()) { - value = v_h.value_ptr(); - holder = v_h.template holder(); - return true; - } else { - throw cast_error("Unable to cast from non-held to held instance (T& to Holder) " -#if defined(NDEBUG) - "(compile in debug mode for type information)"); -#else - "of type '" + type_id() + "''"); -#endif - } - } - - template ::value, int> = 0> - bool try_implicit_casts(handle, bool) { return false; } - - template ::value, int> = 0> - bool try_implicit_casts(handle src, bool convert) { - for (auto &cast : typeinfo->implicit_casts) { - copyable_holder_caster sub_caster(*cast.first); - if (sub_caster.load(src, convert)) { - value = cast.second(sub_caster.value); - holder = holder_type(sub_caster.holder, (type *) value); - return true; - } - } - return false; - } - - static bool try_direct_conversions(handle) { return false; } - - - holder_type holder; -}; - -/// Specialize for the common std::shared_ptr, so users don't need to -template -class type_caster> : public copyable_holder_caster> { }; - -template -struct move_only_holder_caster { - static_assert(std::is_base_of, type_caster>::value, - "Holder classes are only supported for custom types"); - - static handle cast(holder_type &&src, return_value_policy, handle) { - auto *ptr = holder_helper::get(src); - return type_caster_base::cast_holder(ptr, std::addressof(src)); - } - static constexpr auto name = type_caster_base::name; -}; - -template -class type_caster> - : public move_only_holder_caster> { }; - -template -using type_caster_holder = conditional_t::value, - copyable_holder_caster, - move_only_holder_caster>; - -template struct always_construct_holder { static constexpr bool value = Value; }; - -/// Create a specialization for custom holder types (silently ignores std::shared_ptr) -#define PYBIND11_DECLARE_HOLDER_TYPE(type, holder_type, ...) \ - namespace pybind11 { namespace detail { \ - template \ - struct always_construct_holder : always_construct_holder { }; \ - template \ - class type_caster::value>> \ - : public type_caster_holder { }; \ - }} - -// PYBIND11_DECLARE_HOLDER_TYPE holder types: -template struct is_holder_type : - std::is_base_of, detail::type_caster> {}; -// Specialization for always-supported unique_ptr holders: -template struct is_holder_type> : - std::true_type {}; - -template struct handle_type_name { static constexpr auto name = _(); }; -template <> struct handle_type_name { static constexpr auto name = _(PYBIND11_BYTES_NAME); }; -template <> struct handle_type_name { static constexpr auto name = _("*args"); }; -template <> struct handle_type_name { static constexpr auto name = _("**kwargs"); }; - -template -struct pyobject_caster { - template ::value, int> = 0> - bool load(handle src, bool /* convert */) { value = src; return static_cast(value); } - - template ::value, int> = 0> - bool load(handle src, bool /* convert */) { - if (!isinstance(src)) - return false; - value = reinterpret_borrow(src); - return true; - } - - static handle cast(const handle &src, return_value_policy /* policy */, handle /* parent */) { - return src.inc_ref(); - } - PYBIND11_TYPE_CASTER(type, handle_type_name::name); -}; - -template -class type_caster::value>> : public pyobject_caster { }; - -// Our conditions for enabling moving are quite restrictive: -// At compile time: -// - T needs to be a non-const, non-pointer, non-reference type -// - type_caster::operator T&() must exist -// - the type must be move constructible (obviously) -// At run-time: -// - if the type is non-copy-constructible, the object must be the sole owner of the type (i.e. it -// must have ref_count() == 1)h -// If any of the above are not satisfied, we fall back to copying. -template using move_is_plain_type = satisfies_none_of; -template struct move_always : std::false_type {}; -template struct move_always, - negation>, - std::is_move_constructible, - std::is_same>().operator T&()), T&> ->::value>> : std::true_type {}; -template struct move_if_unreferenced : std::false_type {}; -template struct move_if_unreferenced, - negation>, - std::is_move_constructible, - std::is_same>().operator T&()), T&> ->::value>> : std::true_type {}; -template using move_never = none_of, move_if_unreferenced>; - -// Detect whether returning a `type` from a cast on type's type_caster is going to result in a -// reference or pointer to a local variable of the type_caster. Basically, only -// non-reference/pointer `type`s and reference/pointers from a type_caster_generic are safe; -// everything else returns a reference/pointer to a local variable. -template using cast_is_temporary_value_reference = bool_constant< - (std::is_reference::value || std::is_pointer::value) && - !std::is_base_of>::value && - !std::is_same, void>::value ->; - -// When a value returned from a C++ function is being cast back to Python, we almost always want to -// force `policy = move`, regardless of the return value policy the function/method was declared -// with. -template struct return_value_policy_override { - static return_value_policy policy(return_value_policy p) { return p; } -}; - -template struct return_value_policy_override>::value, void>> { - static return_value_policy policy(return_value_policy p) { - return !std::is_lvalue_reference::value && - !std::is_pointer::value - ? return_value_policy::move : p; - } -}; - -// Basic python -> C++ casting; throws if casting fails -template type_caster &load_type(type_caster &conv, const handle &handle) { - if (!conv.load(handle, true)) { -#if defined(NDEBUG) - throw cast_error("Unable to cast Python instance to C++ type (compile in debug mode for details)"); -#else - throw cast_error("Unable to cast Python instance of type " + - (std::string) str(handle.get_type()) + " to C++ type '" + type_id() + "'"); -#endif - } - return conv; -} -// Wrapper around the above that also constructs and returns a type_caster -template make_caster load_type(const handle &handle) { - make_caster conv; - load_type(conv, handle); - return conv; -} - -NAMESPACE_END(detail) - -// pytype -> C++ type -template ::value, int> = 0> -T cast(const handle &handle) { - using namespace detail; - static_assert(!cast_is_temporary_value_reference::value, - "Unable to cast type to reference: value is local to type caster"); - return cast_op(load_type(handle)); -} - -// pytype -> pytype (calls converting constructor) -template ::value, int> = 0> -T cast(const handle &handle) { return T(reinterpret_borrow(handle)); } - -// C++ type -> py::object -template ::value, int> = 0> -object cast(const T &value, return_value_policy policy = return_value_policy::automatic_reference, - handle parent = handle()) { - if (policy == return_value_policy::automatic) - policy = std::is_pointer::value ? return_value_policy::take_ownership : return_value_policy::copy; - else if (policy == return_value_policy::automatic_reference) - policy = std::is_pointer::value ? return_value_policy::reference : return_value_policy::copy; - return reinterpret_steal(detail::make_caster::cast(value, policy, parent)); -} - -template T handle::cast() const { return pybind11::cast(*this); } -template <> inline void handle::cast() const { return; } - -template -detail::enable_if_t::value, T> move(object &&obj) { - if (obj.ref_count() > 1) -#if defined(NDEBUG) - throw cast_error("Unable to cast Python instance to C++ rvalue: instance has multiple references" - " (compile in debug mode for details)"); -#else - throw cast_error("Unable to move from Python " + (std::string) str(obj.get_type()) + - " instance to C++ " + type_id() + " instance: instance has multiple references"); -#endif - - // Move into a temporary and return that, because the reference may be a local value of `conv` - T ret = std::move(detail::load_type(obj).operator T&()); - return ret; -} - -// Calling cast() on an rvalue calls pybind::cast with the object rvalue, which does: -// - If we have to move (because T has no copy constructor), do it. This will fail if the moved -// object has multiple references, but trying to copy will fail to compile. -// - If both movable and copyable, check ref count: if 1, move; otherwise copy -// - Otherwise (not movable), copy. -template detail::enable_if_t::value, T> cast(object &&object) { - return move(std::move(object)); -} -template detail::enable_if_t::value, T> cast(object &&object) { - if (object.ref_count() > 1) - return cast(object); - else - return move(std::move(object)); -} -template detail::enable_if_t::value, T> cast(object &&object) { - return cast(object); -} - -template T object::cast() const & { return pybind11::cast(*this); } -template T object::cast() && { return pybind11::cast(std::move(*this)); } -template <> inline void object::cast() const & { return; } -template <> inline void object::cast() && { return; } - -NAMESPACE_BEGIN(detail) - -// Declared in pytypes.h: -template ::value, int>> -object object_or_cast(T &&o) { return pybind11::cast(std::forward(o)); } - -struct overload_unused {}; // Placeholder type for the unneeded (and dead code) static variable in the OVERLOAD_INT macro -template using overload_caster_t = conditional_t< - cast_is_temporary_value_reference::value, make_caster, overload_unused>; - -// Trampoline use: for reference/pointer types to value-converted values, we do a value cast, then -// store the result in the given variable. For other types, this is a no-op. -template enable_if_t::value, T> cast_ref(object &&o, make_caster &caster) { - return cast_op(load_type(caster, o)); -} -template enable_if_t::value, T> cast_ref(object &&, overload_unused &) { - pybind11_fail("Internal error: cast_ref fallback invoked"); } - -// Trampoline use: Having a pybind11::cast with an invalid reference type is going to static_assert, even -// though if it's in dead code, so we provide a "trampoline" to pybind11::cast that only does anything in -// cases where pybind11::cast is valid. -template enable_if_t::value, T> cast_safe(object &&o) { - return pybind11::cast(std::move(o)); } -template enable_if_t::value, T> cast_safe(object &&) { - pybind11_fail("Internal error: cast_safe fallback invoked"); } -template <> inline void cast_safe(object &&) {} - -NAMESPACE_END(detail) - -template -tuple make_tuple() { return tuple(0); } - -template tuple make_tuple(Args&&... args_) { - constexpr size_t size = sizeof...(Args); - std::array args { - { reinterpret_steal(detail::make_caster::cast( - std::forward(args_), policy, nullptr))... } - }; - for (size_t i = 0; i < args.size(); i++) { - if (!args[i]) { -#if defined(NDEBUG) - throw cast_error("make_tuple(): unable to convert arguments to Python object (compile in debug mode for details)"); -#else - std::array argtypes { {type_id()...} }; - throw cast_error("make_tuple(): unable to convert argument of type '" + - argtypes[i] + "' to Python object"); -#endif - } - } - tuple result(size); - int counter = 0; - for (auto &arg_value : args) - PyTuple_SET_ITEM(result.ptr(), counter++, arg_value.release().ptr()); - return result; -} - -/// \ingroup annotations -/// Annotation for arguments -struct arg { - /// Constructs an argument with the name of the argument; if null or omitted, this is a positional argument. - constexpr explicit arg(const char *name = nullptr) : name(name), flag_noconvert(false), flag_none(true) { } - /// Assign a value to this argument - template arg_v operator=(T &&value) const; - /// Indicate that the type should not be converted in the type caster - arg &noconvert(bool flag = true) { flag_noconvert = flag; return *this; } - /// Indicates that the argument should/shouldn't allow None (e.g. for nullable pointer args) - arg &none(bool flag = true) { flag_none = flag; return *this; } - - const char *name; ///< If non-null, this is a named kwargs argument - bool flag_noconvert : 1; ///< If set, do not allow conversion (requires a supporting type caster!) - bool flag_none : 1; ///< If set (the default), allow None to be passed to this argument -}; - -/// \ingroup annotations -/// Annotation for arguments with values -struct arg_v : arg { -private: - template - arg_v(arg &&base, T &&x, const char *descr = nullptr) - : arg(base), - value(reinterpret_steal( - detail::make_caster::cast(x, return_value_policy::automatic, {}) - )), - descr(descr) -#if !defined(NDEBUG) - , type(type_id()) -#endif - { } - -public: - /// Direct construction with name, default, and description - template - arg_v(const char *name, T &&x, const char *descr = nullptr) - : arg_v(arg(name), std::forward(x), descr) { } - - /// Called internally when invoking `py::arg("a") = value` - template - arg_v(const arg &base, T &&x, const char *descr = nullptr) - : arg_v(arg(base), std::forward(x), descr) { } - - /// Same as `arg::noconvert()`, but returns *this as arg_v&, not arg& - arg_v &noconvert(bool flag = true) { arg::noconvert(flag); return *this; } - - /// Same as `arg::nonone()`, but returns *this as arg_v&, not arg& - arg_v &none(bool flag = true) { arg::none(flag); return *this; } - - /// The default value - object value; - /// The (optional) description of the default value - const char *descr; -#if !defined(NDEBUG) - /// The C++ type name of the default value (only available when compiled in debug mode) - std::string type; -#endif -}; - -template -arg_v arg::operator=(T &&value) const { return {std::move(*this), std::forward(value)}; } - -/// Alias for backward compatibility -- to be removed in version 2.0 -template using arg_t = arg_v; - -inline namespace literals { -/** \rst - String literal version of `arg` - \endrst */ -constexpr arg operator"" _a(const char *name, size_t) { return arg(name); } -} - -NAMESPACE_BEGIN(detail) - -// forward declaration (definition in attr.h) -struct function_record; - -/// Internal data associated with a single function call -struct function_call { - function_call(const function_record &f, handle p); // Implementation in attr.h - - /// The function data: - const function_record &func; - - /// Arguments passed to the function: - std::vector args; - - /// The `convert` value the arguments should be loaded with - std::vector args_convert; - - /// Extra references for the optional `py::args` and/or `py::kwargs` arguments (which, if - /// present, are also in `args` but without a reference). - object args_ref, kwargs_ref; - - /// The parent, if any - handle parent; - - /// If this is a call to an initializer, this argument contains `self` - handle init_self; -}; - - -/// Helper class which loads arguments for C++ functions called from Python -template -class argument_loader { - using indices = make_index_sequence; - - template using argument_is_args = std::is_same, args>; - template using argument_is_kwargs = std::is_same, kwargs>; - // Get args/kwargs argument positions relative to the end of the argument list: - static constexpr auto args_pos = constexpr_first() - (int) sizeof...(Args), - kwargs_pos = constexpr_first() - (int) sizeof...(Args); - - static constexpr bool args_kwargs_are_last = kwargs_pos >= - 1 && args_pos >= kwargs_pos - 1; - - static_assert(args_kwargs_are_last, "py::args/py::kwargs are only permitted as the last argument(s) of a function"); - -public: - static constexpr bool has_kwargs = kwargs_pos < 0; - static constexpr bool has_args = args_pos < 0; - - static constexpr auto arg_names = concat(type_descr(make_caster::name)...); - - bool load_args(function_call &call) { - return load_impl_sequence(call, indices{}); - } - - template - enable_if_t::value, Return> call(Func &&f) && { - return std::move(*this).template call_impl(std::forward(f), indices{}, Guard{}); - } - - template - enable_if_t::value, void_type> call(Func &&f) && { - std::move(*this).template call_impl(std::forward(f), indices{}, Guard{}); - return void_type(); - } - -private: - - static bool load_impl_sequence(function_call &, index_sequence<>) { return true; } - - template - bool load_impl_sequence(function_call &call, index_sequence) { - for (bool r : {std::get(argcasters).load(call.args[Is], call.args_convert[Is])...}) - if (!r) - return false; - return true; - } - - template - Return call_impl(Func &&f, index_sequence, Guard &&) { - return std::forward(f)(cast_op(std::move(std::get(argcasters)))...); - } - - std::tuple...> argcasters; -}; - -/// Helper class which collects only positional arguments for a Python function call. -/// A fancier version below can collect any argument, but this one is optimal for simple calls. -template -class simple_collector { -public: - template - explicit simple_collector(Ts &&...values) - : m_args(pybind11::make_tuple(std::forward(values)...)) { } - - const tuple &args() const & { return m_args; } - dict kwargs() const { return {}; } - - tuple args() && { return std::move(m_args); } - - /// Call a Python function and pass the collected arguments - object call(PyObject *ptr) const { - PyObject *result = PyObject_CallObject(ptr, m_args.ptr()); - if (!result) - throw error_already_set(); - return reinterpret_steal(result); - } - -private: - tuple m_args; -}; - -/// Helper class which collects positional, keyword, * and ** arguments for a Python function call -template -class unpacking_collector { -public: - template - explicit unpacking_collector(Ts &&...values) { - // Tuples aren't (easily) resizable so a list is needed for collection, - // but the actual function call strictly requires a tuple. - auto args_list = list(); - int _[] = { 0, (process(args_list, std::forward(values)), 0)... }; - ignore_unused(_); - - m_args = std::move(args_list); - } - - const tuple &args() const & { return m_args; } - const dict &kwargs() const & { return m_kwargs; } - - tuple args() && { return std::move(m_args); } - dict kwargs() && { return std::move(m_kwargs); } - - /// Call a Python function and pass the collected arguments - object call(PyObject *ptr) const { - PyObject *result = PyObject_Call(ptr, m_args.ptr(), m_kwargs.ptr()); - if (!result) - throw error_already_set(); - return reinterpret_steal(result); - } - -private: - template - void process(list &args_list, T &&x) { - auto o = reinterpret_steal(detail::make_caster::cast(std::forward(x), policy, {})); - if (!o) { -#if defined(NDEBUG) - argument_cast_error(); -#else - argument_cast_error(std::to_string(args_list.size()), type_id()); -#endif - } - args_list.append(o); - } - - void process(list &args_list, detail::args_proxy ap) { - for (const auto &a : ap) - args_list.append(a); - } - - void process(list &/*args_list*/, arg_v a) { - if (!a.name) -#if defined(NDEBUG) - nameless_argument_error(); -#else - nameless_argument_error(a.type); -#endif - - if (m_kwargs.contains(a.name)) { -#if defined(NDEBUG) - multiple_values_error(); -#else - multiple_values_error(a.name); -#endif - } - if (!a.value) { -#if defined(NDEBUG) - argument_cast_error(); -#else - argument_cast_error(a.name, a.type); -#endif - } - m_kwargs[a.name] = a.value; - } - - void process(list &/*args_list*/, detail::kwargs_proxy kp) { - if (!kp) - return; - for (const auto &k : reinterpret_borrow(kp)) { - if (m_kwargs.contains(k.first)) { -#if defined(NDEBUG) - multiple_values_error(); -#else - multiple_values_error(str(k.first)); -#endif - } - m_kwargs[k.first] = k.second; - } - } - - [[noreturn]] static void nameless_argument_error() { - throw type_error("Got kwargs without a name; only named arguments " - "may be passed via py::arg() to a python function call. " - "(compile in debug mode for details)"); - } - [[noreturn]] static void nameless_argument_error(std::string type) { - throw type_error("Got kwargs without a name of type '" + type + "'; only named " - "arguments may be passed via py::arg() to a python function call. "); - } - [[noreturn]] static void multiple_values_error() { - throw type_error("Got multiple values for keyword argument " - "(compile in debug mode for details)"); - } - - [[noreturn]] static void multiple_values_error(std::string name) { - throw type_error("Got multiple values for keyword argument '" + name + "'"); - } - - [[noreturn]] static void argument_cast_error() { - throw cast_error("Unable to convert call argument to Python object " - "(compile in debug mode for details)"); - } - - [[noreturn]] static void argument_cast_error(std::string name, std::string type) { - throw cast_error("Unable to convert call argument '" + name - + "' of type '" + type + "' to Python object"); - } - -private: - tuple m_args; - dict m_kwargs; -}; - -/// Collect only positional arguments for a Python function call -template ...>::value>> -simple_collector collect_arguments(Args &&...args) { - return simple_collector(std::forward(args)...); -} - -/// Collect all arguments, including keywords and unpacking (only instantiated when needed) -template ...>::value>> -unpacking_collector collect_arguments(Args &&...args) { - // Following argument order rules for generalized unpacking according to PEP 448 - static_assert( - constexpr_last() < constexpr_first() - && constexpr_last() < constexpr_first(), - "Invalid function call: positional args must precede keywords and ** unpacking; " - "* unpacking must precede ** unpacking" - ); - return unpacking_collector(std::forward(args)...); -} - -template -template -object object_api::operator()(Args &&...args) const { - return detail::collect_arguments(std::forward(args)...).call(derived().ptr()); -} - -template -template -object object_api::call(Args &&...args) const { - return operator()(std::forward(args)...); -} - -NAMESPACE_END(detail) - -#define PYBIND11_MAKE_OPAQUE(...) \ - namespace pybind11 { namespace detail { \ - template<> class type_caster<__VA_ARGS__> : public type_caster_base<__VA_ARGS__> { }; \ - }} - -/// Lets you pass a type containing a `,` through a macro parameter without needing a separate -/// typedef, e.g.: `PYBIND11_OVERLOAD(PYBIND11_TYPE(ReturnType), PYBIND11_TYPE(Parent), f, arg)` -#define PYBIND11_TYPE(...) __VA_ARGS__ - -NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/ptocr/postprocess/piexlmerge/include/pybind11/chrono.h b/ptocr/postprocess/piexlmerge/include/pybind11/chrono.h deleted file mode 100644 index 95ada76..0000000 --- a/ptocr/postprocess/piexlmerge/include/pybind11/chrono.h +++ /dev/null @@ -1,162 +0,0 @@ -/* - pybind11/chrono.h: Transparent conversion between std::chrono and python's datetime - - Copyright (c) 2016 Trent Houliston and - Wenzel Jakob - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#include "pybind11.h" -#include -#include -#include -#include - -// Backport the PyDateTime_DELTA functions from Python3.3 if required -#ifndef PyDateTime_DELTA_GET_DAYS -#define PyDateTime_DELTA_GET_DAYS(o) (((PyDateTime_Delta*)o)->days) -#endif -#ifndef PyDateTime_DELTA_GET_SECONDS -#define PyDateTime_DELTA_GET_SECONDS(o) (((PyDateTime_Delta*)o)->seconds) -#endif -#ifndef PyDateTime_DELTA_GET_MICROSECONDS -#define PyDateTime_DELTA_GET_MICROSECONDS(o) (((PyDateTime_Delta*)o)->microseconds) -#endif - -NAMESPACE_BEGIN(PYBIND11_NAMESPACE) -NAMESPACE_BEGIN(detail) - -template class duration_caster { -public: - typedef typename type::rep rep; - typedef typename type::period period; - - typedef std::chrono::duration> days; - - bool load(handle src, bool) { - using namespace std::chrono; - - // Lazy initialise the PyDateTime import - if (!PyDateTimeAPI) { PyDateTime_IMPORT; } - - if (!src) return false; - // If invoked with datetime.delta object - if (PyDelta_Check(src.ptr())) { - value = type(duration_cast>( - days(PyDateTime_DELTA_GET_DAYS(src.ptr())) - + seconds(PyDateTime_DELTA_GET_SECONDS(src.ptr())) - + microseconds(PyDateTime_DELTA_GET_MICROSECONDS(src.ptr())))); - return true; - } - // If invoked with a float we assume it is seconds and convert - else if (PyFloat_Check(src.ptr())) { - value = type(duration_cast>(duration(PyFloat_AsDouble(src.ptr())))); - return true; - } - else return false; - } - - // If this is a duration just return it back - static const std::chrono::duration& get_duration(const std::chrono::duration &src) { - return src; - } - - // If this is a time_point get the time_since_epoch - template static std::chrono::duration get_duration(const std::chrono::time_point> &src) { - return src.time_since_epoch(); - } - - static handle cast(const type &src, return_value_policy /* policy */, handle /* parent */) { - using namespace std::chrono; - - // Use overloaded function to get our duration from our source - // Works out if it is a duration or time_point and get the duration - auto d = get_duration(src); - - // Lazy initialise the PyDateTime import - if (!PyDateTimeAPI) { PyDateTime_IMPORT; } - - // Declare these special duration types so the conversions happen with the correct primitive types (int) - using dd_t = duration>; - using ss_t = duration>; - using us_t = duration; - - auto dd = duration_cast(d); - auto subd = d - dd; - auto ss = duration_cast(subd); - auto us = duration_cast(subd - ss); - return PyDelta_FromDSU(dd.count(), ss.count(), us.count()); - } - - PYBIND11_TYPE_CASTER(type, _("datetime.timedelta")); -}; - -// This is for casting times on the system clock into datetime.datetime instances -template class type_caster> { -public: - typedef std::chrono::time_point type; - bool load(handle src, bool) { - using namespace std::chrono; - - // Lazy initialise the PyDateTime import - if (!PyDateTimeAPI) { PyDateTime_IMPORT; } - - if (!src) return false; - if (PyDateTime_Check(src.ptr())) { - std::tm cal; - cal.tm_sec = PyDateTime_DATE_GET_SECOND(src.ptr()); - cal.tm_min = PyDateTime_DATE_GET_MINUTE(src.ptr()); - cal.tm_hour = PyDateTime_DATE_GET_HOUR(src.ptr()); - cal.tm_mday = PyDateTime_GET_DAY(src.ptr()); - cal.tm_mon = PyDateTime_GET_MONTH(src.ptr()) - 1; - cal.tm_year = PyDateTime_GET_YEAR(src.ptr()) - 1900; - cal.tm_isdst = -1; - - value = system_clock::from_time_t(std::mktime(&cal)) + microseconds(PyDateTime_DATE_GET_MICROSECOND(src.ptr())); - return true; - } - else return false; - } - - static handle cast(const std::chrono::time_point &src, return_value_policy /* policy */, handle /* parent */) { - using namespace std::chrono; - - // Lazy initialise the PyDateTime import - if (!PyDateTimeAPI) { PyDateTime_IMPORT; } - - std::time_t tt = system_clock::to_time_t(src); - // this function uses static memory so it's best to copy it out asap just in case - // otherwise other code that is using localtime may break this (not just python code) - std::tm localtime = *std::localtime(&tt); - - // Declare these special duration types so the conversions happen with the correct primitive types (int) - using us_t = duration; - - return PyDateTime_FromDateAndTime(localtime.tm_year + 1900, - localtime.tm_mon + 1, - localtime.tm_mday, - localtime.tm_hour, - localtime.tm_min, - localtime.tm_sec, - (duration_cast(src.time_since_epoch() % seconds(1))).count()); - } - PYBIND11_TYPE_CASTER(type, _("datetime.datetime")); -}; - -// Other clocks that are not the system clock are not measured as datetime.datetime objects -// since they are not measured on calendar time. So instead we just make them timedeltas -// Or if they have passed us a time as a float we convert that -template class type_caster> -: public duration_caster> { -}; - -template class type_caster> -: public duration_caster> { -}; - -NAMESPACE_END(detail) -NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/ptocr/postprocess/piexlmerge/include/pybind11/class_support.h b/ptocr/postprocess/piexlmerge/include/pybind11/class_support.h deleted file mode 100644 index 8e18c4c..0000000 --- a/ptocr/postprocess/piexlmerge/include/pybind11/class_support.h +++ /dev/null @@ -1,603 +0,0 @@ -/* - pybind11/class_support.h: Python C API implementation details for py::class_ - - Copyright (c) 2017 Wenzel Jakob - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#include "attr.h" - -NAMESPACE_BEGIN(pybind11) -NAMESPACE_BEGIN(detail) - -inline PyTypeObject *type_incref(PyTypeObject *type) { - Py_INCREF(type); - return type; -} - -#if !defined(PYPY_VERSION) - -/// `pybind11_static_property.__get__()`: Always pass the class instead of the instance. -extern "C" inline PyObject *pybind11_static_get(PyObject *self, PyObject * /*ob*/, PyObject *cls) { - return PyProperty_Type.tp_descr_get(self, cls, cls); -} - -/// `pybind11_static_property.__set__()`: Just like the above `__get__()`. -extern "C" inline int pybind11_static_set(PyObject *self, PyObject *obj, PyObject *value) { - PyObject *cls = PyType_Check(obj) ? obj : (PyObject *) Py_TYPE(obj); - return PyProperty_Type.tp_descr_set(self, cls, value); -} - -/** A `static_property` is the same as a `property` but the `__get__()` and `__set__()` - methods are modified to always use the object type instead of a concrete instance. - Return value: New reference. */ -inline PyTypeObject *make_static_property_type() { - constexpr auto *name = "pybind11_static_property"; - auto name_obj = reinterpret_steal(PYBIND11_FROM_STRING(name)); - - /* Danger zone: from now (and until PyType_Ready), make sure to - issue no Python C API calls which could potentially invoke the - garbage collector (the GC will call type_traverse(), which will in - turn find the newly constructed type in an invalid state) */ - auto heap_type = (PyHeapTypeObject *) PyType_Type.tp_alloc(&PyType_Type, 0); - if (!heap_type) - pybind11_fail("make_static_property_type(): error allocating type!"); - - heap_type->ht_name = name_obj.inc_ref().ptr(); -#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 3 - heap_type->ht_qualname = name_obj.inc_ref().ptr(); -#endif - - auto type = &heap_type->ht_type; - type->tp_name = name; - type->tp_base = type_incref(&PyProperty_Type); - type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE; - type->tp_descr_get = pybind11_static_get; - type->tp_descr_set = pybind11_static_set; - - if (PyType_Ready(type) < 0) - pybind11_fail("make_static_property_type(): failure in PyType_Ready()!"); - - setattr((PyObject *) type, "__module__", str("pybind11_builtins")); - - return type; -} - -#else // PYPY - -/** PyPy has some issues with the above C API, so we evaluate Python code instead. - This function will only be called once so performance isn't really a concern. - Return value: New reference. */ -inline PyTypeObject *make_static_property_type() { - auto d = dict(); - PyObject *result = PyRun_String(R"(\ - class pybind11_static_property(property): - def __get__(self, obj, cls): - return property.__get__(self, cls, cls) - - def __set__(self, obj, value): - cls = obj if isinstance(obj, type) else type(obj) - property.__set__(self, cls, value) - )", Py_file_input, d.ptr(), d.ptr() - ); - if (result == nullptr) - throw error_already_set(); - Py_DECREF(result); - return (PyTypeObject *) d["pybind11_static_property"].cast().release().ptr(); -} - -#endif // PYPY - -/** Types with static properties need to handle `Type.static_prop = x` in a specific way. - By default, Python replaces the `static_property` itself, but for wrapped C++ types - we need to call `static_property.__set__()` in order to propagate the new value to - the underlying C++ data structure. */ -extern "C" inline int pybind11_meta_setattro(PyObject* obj, PyObject* name, PyObject* value) { - // Use `_PyType_Lookup()` instead of `PyObject_GetAttr()` in order to get the raw - // descriptor (`property`) instead of calling `tp_descr_get` (`property.__get__()`). - PyObject *descr = _PyType_Lookup((PyTypeObject *) obj, name); - - // The following assignment combinations are possible: - // 1. `Type.static_prop = value` --> descr_set: `Type.static_prop.__set__(value)` - // 2. `Type.static_prop = other_static_prop` --> setattro: replace existing `static_prop` - // 3. `Type.regular_attribute = value` --> setattro: regular attribute assignment - const auto static_prop = (PyObject *) get_internals().static_property_type; - const auto call_descr_set = descr && PyObject_IsInstance(descr, static_prop) - && !PyObject_IsInstance(value, static_prop); - if (call_descr_set) { - // Call `static_property.__set__()` instead of replacing the `static_property`. -#if !defined(PYPY_VERSION) - return Py_TYPE(descr)->tp_descr_set(descr, obj, value); -#else - if (PyObject *result = PyObject_CallMethod(descr, "__set__", "OO", obj, value)) { - Py_DECREF(result); - return 0; - } else { - return -1; - } -#endif - } else { - // Replace existing attribute. - return PyType_Type.tp_setattro(obj, name, value); - } -} - -#if PY_MAJOR_VERSION >= 3 -/** - * Python 3's PyInstanceMethod_Type hides itself via its tp_descr_get, which prevents aliasing - * methods via cls.attr("m2") = cls.attr("m1"): instead the tp_descr_get returns a plain function, - * when called on a class, or a PyMethod, when called on an instance. Override that behaviour here - * to do a special case bypass for PyInstanceMethod_Types. - */ -extern "C" inline PyObject *pybind11_meta_getattro(PyObject *obj, PyObject *name) { - PyObject *descr = _PyType_Lookup((PyTypeObject *) obj, name); - if (descr && PyInstanceMethod_Check(descr)) { - Py_INCREF(descr); - return descr; - } - else { - return PyType_Type.tp_getattro(obj, name); - } -} -#endif - -/** This metaclass is assigned by default to all pybind11 types and is required in order - for static properties to function correctly. Users may override this using `py::metaclass`. - Return value: New reference. */ -inline PyTypeObject* make_default_metaclass() { - constexpr auto *name = "pybind11_type"; - auto name_obj = reinterpret_steal(PYBIND11_FROM_STRING(name)); - - /* Danger zone: from now (and until PyType_Ready), make sure to - issue no Python C API calls which could potentially invoke the - garbage collector (the GC will call type_traverse(), which will in - turn find the newly constructed type in an invalid state) */ - auto heap_type = (PyHeapTypeObject *) PyType_Type.tp_alloc(&PyType_Type, 0); - if (!heap_type) - pybind11_fail("make_default_metaclass(): error allocating metaclass!"); - - heap_type->ht_name = name_obj.inc_ref().ptr(); -#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 3 - heap_type->ht_qualname = name_obj.inc_ref().ptr(); -#endif - - auto type = &heap_type->ht_type; - type->tp_name = name; - type->tp_base = type_incref(&PyType_Type); - type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE; - - type->tp_setattro = pybind11_meta_setattro; -#if PY_MAJOR_VERSION >= 3 - type->tp_getattro = pybind11_meta_getattro; -#endif - - if (PyType_Ready(type) < 0) - pybind11_fail("make_default_metaclass(): failure in PyType_Ready()!"); - - setattr((PyObject *) type, "__module__", str("pybind11_builtins")); - - return type; -} - -/// For multiple inheritance types we need to recursively register/deregister base pointers for any -/// base classes with pointers that are difference from the instance value pointer so that we can -/// correctly recognize an offset base class pointer. This calls a function with any offset base ptrs. -inline void traverse_offset_bases(void *valueptr, const detail::type_info *tinfo, instance *self, - bool (*f)(void * /*parentptr*/, instance * /*self*/)) { - for (handle h : reinterpret_borrow(tinfo->type->tp_bases)) { - if (auto parent_tinfo = get_type_info((PyTypeObject *) h.ptr())) { - for (auto &c : parent_tinfo->implicit_casts) { - if (c.first == tinfo->cpptype) { - auto *parentptr = c.second(valueptr); - if (parentptr != valueptr) - f(parentptr, self); - traverse_offset_bases(parentptr, parent_tinfo, self, f); - break; - } - } - } - } -} - -inline bool register_instance_impl(void *ptr, instance *self) { - get_internals().registered_instances.emplace(ptr, self); - return true; // unused, but gives the same signature as the deregister func -} -inline bool deregister_instance_impl(void *ptr, instance *self) { - auto ®istered_instances = get_internals().registered_instances; - auto range = registered_instances.equal_range(ptr); - for (auto it = range.first; it != range.second; ++it) { - if (Py_TYPE(self) == Py_TYPE(it->second)) { - registered_instances.erase(it); - return true; - } - } - return false; -} - -inline void register_instance(instance *self, void *valptr, const type_info *tinfo) { - register_instance_impl(valptr, self); - if (!tinfo->simple_ancestors) - traverse_offset_bases(valptr, tinfo, self, register_instance_impl); -} - -inline bool deregister_instance(instance *self, void *valptr, const type_info *tinfo) { - bool ret = deregister_instance_impl(valptr, self); - if (!tinfo->simple_ancestors) - traverse_offset_bases(valptr, tinfo, self, deregister_instance_impl); - return ret; -} - -/// Instance creation function for all pybind11 types. It only allocates space for the C++ object -/// (or multiple objects, for Python-side inheritance from multiple pybind11 types), but doesn't -/// call the constructor -- an `__init__` function must do that (followed by an `init_instance` -/// to set up the holder and register the instance). -inline PyObject *make_new_instance(PyTypeObject *type, bool allocate_value /*= true (in cast.h)*/) { -#if defined(PYPY_VERSION) - // PyPy gets tp_basicsize wrong (issue 2482) under multiple inheritance when the first inherited - // object is a a plain Python type (i.e. not derived from an extension type). Fix it. - ssize_t instance_size = static_cast(sizeof(instance)); - if (type->tp_basicsize < instance_size) { - type->tp_basicsize = instance_size; - } -#endif - PyObject *self = type->tp_alloc(type, 0); - auto inst = reinterpret_cast(self); - // Allocate the value/holder internals: - inst->allocate_layout(); - - inst->owned = true; - // Allocate (if requested) the value pointers; otherwise leave them as nullptr - if (allocate_value) { - for (auto &v_h : values_and_holders(inst)) { - void *&vptr = v_h.value_ptr(); - vptr = v_h.type->operator_new(v_h.type->type_size); - } - } - - return self; -} - -/// Instance creation function for all pybind11 types. It only allocates space for the -/// C++ object, but doesn't call the constructor -- an `__init__` function must do that. -extern "C" inline PyObject *pybind11_object_new(PyTypeObject *type, PyObject *, PyObject *) { - return make_new_instance(type); -} - -/// An `__init__` function constructs the C++ object. Users should provide at least one -/// of these using `py::init` or directly with `.def(__init__, ...)`. Otherwise, the -/// following default function will be used which simply throws an exception. -extern "C" inline int pybind11_object_init(PyObject *self, PyObject *, PyObject *) { - PyTypeObject *type = Py_TYPE(self); - std::string msg; -#if defined(PYPY_VERSION) - msg += handle((PyObject *) type).attr("__module__").cast() + "."; -#endif - msg += type->tp_name; - msg += ": No constructor defined!"; - PyErr_SetString(PyExc_TypeError, msg.c_str()); - return -1; -} - -inline void add_patient(PyObject *nurse, PyObject *patient) { - auto &internals = get_internals(); - auto instance = reinterpret_cast(nurse); - instance->has_patients = true; - Py_INCREF(patient); - internals.patients[nurse].push_back(patient); -} - -inline void clear_patients(PyObject *self) { - auto instance = reinterpret_cast(self); - auto &internals = get_internals(); - auto pos = internals.patients.find(self); - assert(pos != internals.patients.end()); - // Clearing the patients can cause more Python code to run, which - // can invalidate the iterator. Extract the vector of patients - // from the unordered_map first. - auto patients = std::move(pos->second); - internals.patients.erase(pos); - instance->has_patients = false; - for (PyObject *&patient : patients) - Py_CLEAR(patient); -} - -/// Clears all internal data from the instance and removes it from registered instances in -/// preparation for deallocation. -inline void clear_instance(PyObject *self) { - auto instance = reinterpret_cast(self); - - // Deallocate any values/holders, if present: - for (auto &v_h : values_and_holders(instance)) { - if (v_h) { - - // We have to deregister before we call dealloc because, for virtual MI types, we still - // need to be able to get the parent pointers. - if (v_h.instance_registered() && !deregister_instance(instance, v_h.value_ptr(), v_h.type)) - pybind11_fail("pybind11_object_dealloc(): Tried to deallocate unregistered instance!"); - - if (instance->owned || v_h.holder_constructed()) - v_h.type->dealloc(v_h); - } - } - // Deallocate the value/holder layout internals: - instance->deallocate_layout(); - - if (instance->weakrefs) - PyObject_ClearWeakRefs(self); - - PyObject **dict_ptr = _PyObject_GetDictPtr(self); - if (dict_ptr) - Py_CLEAR(*dict_ptr); - - if (instance->has_patients) - clear_patients(self); -} - -/// Instance destructor function for all pybind11 types. It calls `type_info.dealloc` -/// to destroy the C++ object itself, while the rest is Python bookkeeping. -extern "C" inline void pybind11_object_dealloc(PyObject *self) { - clear_instance(self); - Py_TYPE(self)->tp_free(self); -} - -/** Create the type which can be used as a common base for all classes. This is - needed in order to satisfy Python's requirements for multiple inheritance. - Return value: New reference. */ -inline PyObject *make_object_base_type(PyTypeObject *metaclass) { - constexpr auto *name = "pybind11_object"; - auto name_obj = reinterpret_steal(PYBIND11_FROM_STRING(name)); - - /* Danger zone: from now (and until PyType_Ready), make sure to - issue no Python C API calls which could potentially invoke the - garbage collector (the GC will call type_traverse(), which will in - turn find the newly constructed type in an invalid state) */ - auto heap_type = (PyHeapTypeObject *) metaclass->tp_alloc(metaclass, 0); - if (!heap_type) - pybind11_fail("make_object_base_type(): error allocating type!"); - - heap_type->ht_name = name_obj.inc_ref().ptr(); -#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 3 - heap_type->ht_qualname = name_obj.inc_ref().ptr(); -#endif - - auto type = &heap_type->ht_type; - type->tp_name = name; - type->tp_base = type_incref(&PyBaseObject_Type); - type->tp_basicsize = static_cast(sizeof(instance)); - type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE; - - type->tp_new = pybind11_object_new; - type->tp_init = pybind11_object_init; - type->tp_dealloc = pybind11_object_dealloc; - - /* Support weak references (needed for the keep_alive feature) */ - type->tp_weaklistoffset = offsetof(instance, weakrefs); - - if (PyType_Ready(type) < 0) - pybind11_fail("PyType_Ready failed in make_object_base_type():" + error_string()); - - setattr((PyObject *) type, "__module__", str("pybind11_builtins")); - - assert(!PyType_HasFeature(type, Py_TPFLAGS_HAVE_GC)); - return (PyObject *) heap_type; -} - -/// dynamic_attr: Support for `d = instance.__dict__`. -extern "C" inline PyObject *pybind11_get_dict(PyObject *self, void *) { - PyObject *&dict = *_PyObject_GetDictPtr(self); - if (!dict) - dict = PyDict_New(); - Py_XINCREF(dict); - return dict; -} - -/// dynamic_attr: Support for `instance.__dict__ = dict()`. -extern "C" inline int pybind11_set_dict(PyObject *self, PyObject *new_dict, void *) { - if (!PyDict_Check(new_dict)) { - PyErr_Format(PyExc_TypeError, "__dict__ must be set to a dictionary, not a '%.200s'", - Py_TYPE(new_dict)->tp_name); - return -1; - } - PyObject *&dict = *_PyObject_GetDictPtr(self); - Py_INCREF(new_dict); - Py_CLEAR(dict); - dict = new_dict; - return 0; -} - -/// dynamic_attr: Allow the garbage collector to traverse the internal instance `__dict__`. -extern "C" inline int pybind11_traverse(PyObject *self, visitproc visit, void *arg) { - PyObject *&dict = *_PyObject_GetDictPtr(self); - Py_VISIT(dict); - return 0; -} - -/// dynamic_attr: Allow the GC to clear the dictionary. -extern "C" inline int pybind11_clear(PyObject *self) { - PyObject *&dict = *_PyObject_GetDictPtr(self); - Py_CLEAR(dict); - return 0; -} - -/// Give instances of this type a `__dict__` and opt into garbage collection. -inline void enable_dynamic_attributes(PyHeapTypeObject *heap_type) { - auto type = &heap_type->ht_type; -#if defined(PYPY_VERSION) - pybind11_fail(std::string(type->tp_name) + ": dynamic attributes are " - "currently not supported in " - "conjunction with PyPy!"); -#endif - type->tp_flags |= Py_TPFLAGS_HAVE_GC; - type->tp_dictoffset = type->tp_basicsize; // place dict at the end - type->tp_basicsize += (ssize_t)sizeof(PyObject *); // and allocate enough space for it - type->tp_traverse = pybind11_traverse; - type->tp_clear = pybind11_clear; - - static PyGetSetDef getset[] = { - {const_cast("__dict__"), pybind11_get_dict, pybind11_set_dict, nullptr, nullptr}, - {nullptr, nullptr, nullptr, nullptr, nullptr} - }; - type->tp_getset = getset; -} - -/// buffer_protocol: Fill in the view as specified by flags. -extern "C" inline int pybind11_getbuffer(PyObject *obj, Py_buffer *view, int flags) { - // Look for a `get_buffer` implementation in this type's info or any bases (following MRO). - type_info *tinfo = nullptr; - for (auto type : reinterpret_borrow(Py_TYPE(obj)->tp_mro)) { - tinfo = get_type_info((PyTypeObject *) type.ptr()); - if (tinfo && tinfo->get_buffer) - break; - } - if (view == nullptr || obj == nullptr || !tinfo || !tinfo->get_buffer) { - if (view) - view->obj = nullptr; - PyErr_SetString(PyExc_BufferError, "pybind11_getbuffer(): Internal error"); - return -1; - } - std::memset(view, 0, sizeof(Py_buffer)); - buffer_info *info = tinfo->get_buffer(obj, tinfo->get_buffer_data); - view->obj = obj; - view->ndim = 1; - view->internal = info; - view->buf = info->ptr; - view->itemsize = info->itemsize; - view->len = view->itemsize; - for (auto s : info->shape) - view->len *= s; - if ((flags & PyBUF_FORMAT) == PyBUF_FORMAT) - view->format = const_cast(info->format.c_str()); - if ((flags & PyBUF_STRIDES) == PyBUF_STRIDES) { - view->ndim = (int) info->ndim; - view->strides = &info->strides[0]; - view->shape = &info->shape[0]; - } - Py_INCREF(view->obj); - return 0; -} - -/// buffer_protocol: Release the resources of the buffer. -extern "C" inline void pybind11_releasebuffer(PyObject *, Py_buffer *view) { - delete (buffer_info *) view->internal; -} - -/// Give this type a buffer interface. -inline void enable_buffer_protocol(PyHeapTypeObject *heap_type) { - heap_type->ht_type.tp_as_buffer = &heap_type->as_buffer; -#if PY_MAJOR_VERSION < 3 - heap_type->ht_type.tp_flags |= Py_TPFLAGS_HAVE_NEWBUFFER; -#endif - - heap_type->as_buffer.bf_getbuffer = pybind11_getbuffer; - heap_type->as_buffer.bf_releasebuffer = pybind11_releasebuffer; -} - -/** Create a brand new Python type according to the `type_record` specification. - Return value: New reference. */ -inline PyObject* make_new_python_type(const type_record &rec) { - auto name = reinterpret_steal(PYBIND11_FROM_STRING(rec.name)); - -#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 3 - auto ht_qualname = name; - if (rec.scope && hasattr(rec.scope, "__qualname__")) { - ht_qualname = reinterpret_steal( - PyUnicode_FromFormat("%U.%U", rec.scope.attr("__qualname__").ptr(), name.ptr())); - } -#endif - - object module; - if (rec.scope) { - if (hasattr(rec.scope, "__module__")) - module = rec.scope.attr("__module__"); - else if (hasattr(rec.scope, "__name__")) - module = rec.scope.attr("__name__"); - } - -#if !defined(PYPY_VERSION) - const auto full_name = module ? str(module).cast() + "." + rec.name - : std::string(rec.name); -#else - const auto full_name = std::string(rec.name); -#endif - - char *tp_doc = nullptr; - if (rec.doc && options::show_user_defined_docstrings()) { - /* Allocate memory for docstring (using PyObject_MALLOC, since - Python will free this later on) */ - size_t size = strlen(rec.doc) + 1; - tp_doc = (char *) PyObject_MALLOC(size); - memcpy((void *) tp_doc, rec.doc, size); - } - - auto &internals = get_internals(); - auto bases = tuple(rec.bases); - auto base = (bases.size() == 0) ? internals.instance_base - : bases[0].ptr(); - - /* Danger zone: from now (and until PyType_Ready), make sure to - issue no Python C API calls which could potentially invoke the - garbage collector (the GC will call type_traverse(), which will in - turn find the newly constructed type in an invalid state) */ - auto metaclass = rec.metaclass.ptr() ? (PyTypeObject *) rec.metaclass.ptr() - : internals.default_metaclass; - - auto heap_type = (PyHeapTypeObject *) metaclass->tp_alloc(metaclass, 0); - if (!heap_type) - pybind11_fail(std::string(rec.name) + ": Unable to create type object!"); - - heap_type->ht_name = name.release().ptr(); -#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 3 - heap_type->ht_qualname = ht_qualname.release().ptr(); -#endif - - auto type = &heap_type->ht_type; - type->tp_name = strdup(full_name.c_str()); - type->tp_doc = tp_doc; - type->tp_base = type_incref((PyTypeObject *)base); - type->tp_basicsize = static_cast(sizeof(instance)); - if (bases.size() > 0) - type->tp_bases = bases.release().ptr(); - - /* Don't inherit base __init__ */ - type->tp_init = pybind11_object_init; - - /* Supported protocols */ - type->tp_as_number = &heap_type->as_number; - type->tp_as_sequence = &heap_type->as_sequence; - type->tp_as_mapping = &heap_type->as_mapping; - - /* Flags */ - type->tp_flags |= Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE; -#if PY_MAJOR_VERSION < 3 - type->tp_flags |= Py_TPFLAGS_CHECKTYPES; -#endif - - if (rec.dynamic_attr) - enable_dynamic_attributes(heap_type); - - if (rec.buffer_protocol) - enable_buffer_protocol(heap_type); - - if (PyType_Ready(type) < 0) - pybind11_fail(std::string(rec.name) + ": PyType_Ready failed (" + error_string() + ")!"); - - assert(rec.dynamic_attr ? PyType_HasFeature(type, Py_TPFLAGS_HAVE_GC) - : !PyType_HasFeature(type, Py_TPFLAGS_HAVE_GC)); - - /* Register type with the parent scope */ - if (rec.scope) - setattr(rec.scope, rec.name, (PyObject *) type); - - if (module) // Needed by pydoc - setattr((PyObject *) type, "__module__", module); - - return (PyObject *) type; -} - -NAMESPACE_END(detail) -NAMESPACE_END(pybind11) diff --git a/ptocr/postprocess/piexlmerge/include/pybind11/common.h b/ptocr/postprocess/piexlmerge/include/pybind11/common.h deleted file mode 100644 index 6c8a4f1..0000000 --- a/ptocr/postprocess/piexlmerge/include/pybind11/common.h +++ /dev/null @@ -1,2 +0,0 @@ -#include "detail/common.h" -#warning "Including 'common.h' is deprecated. It will be removed in v3.0. Use 'pybind11.h'." diff --git a/ptocr/postprocess/piexlmerge/include/pybind11/complex.h b/ptocr/postprocess/piexlmerge/include/pybind11/complex.h deleted file mode 100644 index 3f89638..0000000 --- a/ptocr/postprocess/piexlmerge/include/pybind11/complex.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - pybind11/complex.h: Complex number support - - Copyright (c) 2016 Wenzel Jakob - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#include "pybind11.h" -#include - -/// glibc defines I as a macro which breaks things, e.g., boost template names -#ifdef I -# undef I -#endif - -NAMESPACE_BEGIN(PYBIND11_NAMESPACE) - -template struct format_descriptor, detail::enable_if_t::value>> { - static constexpr const char c = format_descriptor::c; - static constexpr const char value[3] = { 'Z', c, '\0' }; - static std::string format() { return std::string(value); } -}; - -#ifndef PYBIND11_CPP17 - -template constexpr const char format_descriptor< - std::complex, detail::enable_if_t::value>>::value[3]; - -#endif - -NAMESPACE_BEGIN(detail) - -template struct is_fmt_numeric, detail::enable_if_t::value>> { - static constexpr bool value = true; - static constexpr int index = is_fmt_numeric::index + 3; -}; - -template class type_caster> { -public: - bool load(handle src, bool convert) { - if (!src) - return false; - if (!convert && !PyComplex_Check(src.ptr())) - return false; - Py_complex result = PyComplex_AsCComplex(src.ptr()); - if (result.real == -1.0 && PyErr_Occurred()) { - PyErr_Clear(); - return false; - } - value = std::complex((T) result.real, (T) result.imag); - return true; - } - - static handle cast(const std::complex &src, return_value_policy /* policy */, handle /* parent */) { - return PyComplex_FromDoubles((double) src.real(), (double) src.imag()); - } - - PYBIND11_TYPE_CASTER(std::complex, _("complex")); -}; -NAMESPACE_END(detail) -NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/ptocr/postprocess/piexlmerge/include/pybind11/descr.h b/ptocr/postprocess/piexlmerge/include/pybind11/descr.h deleted file mode 100644 index 23a099c..0000000 --- a/ptocr/postprocess/piexlmerge/include/pybind11/descr.h +++ /dev/null @@ -1,185 +0,0 @@ -/* - pybind11/descr.h: Helper type for concatenating type signatures - either at runtime (C++11) or compile time (C++14) - - Copyright (c) 2016 Wenzel Jakob - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#include "common.h" - -NAMESPACE_BEGIN(pybind11) -NAMESPACE_BEGIN(detail) - -/* Concatenate type signatures at compile time using C++14 */ -#if defined(PYBIND11_CPP14) && !defined(_MSC_VER) -#define PYBIND11_CONSTEXPR_DESCR - -template class descr { - template friend class descr; -public: - constexpr descr(char const (&text) [Size1+1], const std::type_info * const (&types)[Size2+1]) - : descr(text, types, - make_index_sequence(), - make_index_sequence()) { } - - constexpr const char *text() const { return m_text; } - constexpr const std::type_info * const * types() const { return m_types; } - - template - constexpr descr operator+(const descr &other) const { - return concat(other, - make_index_sequence(), - make_index_sequence(), - make_index_sequence(), - make_index_sequence()); - } - -protected: - template - constexpr descr( - char const (&text) [Size1+1], - const std::type_info * const (&types) [Size2+1], - index_sequence, index_sequence) - : m_text{text[Indices1]..., '\0'}, - m_types{types[Indices2]..., nullptr } {} - - template - constexpr descr - concat(const descr &other, - index_sequence, index_sequence, - index_sequence, index_sequence) const { - return descr( - { m_text[Indices1]..., other.m_text[OtherIndices1]..., '\0' }, - { m_types[Indices2]..., other.m_types[OtherIndices2]..., nullptr } - ); - } - -protected: - char m_text[Size1 + 1]; - const std::type_info * m_types[Size2 + 1]; -}; - -template constexpr descr _(char const(&text)[Size]) { - return descr(text, { nullptr }); -} - -template struct int_to_str : int_to_str { }; -template struct int_to_str<0, Digits...> { - static constexpr auto digits = descr({ ('0' + Digits)..., '\0' }, { nullptr }); -}; - -// Ternary description (like std::conditional) -template -constexpr enable_if_t> _(char const(&text1)[Size1], char const(&)[Size2]) { - return _(text1); -} -template -constexpr enable_if_t> _(char const(&)[Size1], char const(&text2)[Size2]) { - return _(text2); -} -template -constexpr enable_if_t> _(descr d, descr) { return d; } -template -constexpr enable_if_t> _(descr, descr d) { return d; } - -template auto constexpr _() -> decltype(int_to_str::digits) { - return int_to_str::digits; -} - -template constexpr descr<1, 1> _() { - return descr<1, 1>({ '%', '\0' }, { &typeid(Type), nullptr }); -} - -inline constexpr descr<0, 0> concat() { return _(""); } -template auto constexpr concat(descr descr) { return descr; } -template auto constexpr concat(descr descr, Args&&... args) { return descr + _(", ") + concat(args...); } -template auto constexpr type_descr(descr descr) { return _("{") + descr + _("}"); } - -#define PYBIND11_DESCR constexpr auto - -#else /* Simpler C++11 implementation based on run-time memory allocation and copying */ - -class descr { -public: - PYBIND11_NOINLINE descr(const char *text, const std::type_info * const * types) { - size_t nChars = len(text), nTypes = len(types); - m_text = new char[nChars]; - m_types = new const std::type_info *[nTypes]; - memcpy(m_text, text, nChars * sizeof(char)); - memcpy(m_types, types, nTypes * sizeof(const std::type_info *)); - } - - PYBIND11_NOINLINE descr operator+(descr &&d2) && { - descr r; - - size_t nChars1 = len(m_text), nTypes1 = len(m_types); - size_t nChars2 = len(d2.m_text), nTypes2 = len(d2.m_types); - - r.m_text = new char[nChars1 + nChars2 - 1]; - r.m_types = new const std::type_info *[nTypes1 + nTypes2 - 1]; - memcpy(r.m_text, m_text, (nChars1-1) * sizeof(char)); - memcpy(r.m_text + nChars1 - 1, d2.m_text, nChars2 * sizeof(char)); - memcpy(r.m_types, m_types, (nTypes1-1) * sizeof(std::type_info *)); - memcpy(r.m_types + nTypes1 - 1, d2.m_types, nTypes2 * sizeof(std::type_info *)); - - delete[] m_text; delete[] m_types; - delete[] d2.m_text; delete[] d2.m_types; - - return r; - } - - char *text() { return m_text; } - const std::type_info * * types() { return m_types; } - -protected: - PYBIND11_NOINLINE descr() { } - - template static size_t len(const T *ptr) { // return length including null termination - const T *it = ptr; - while (*it++ != (T) 0) - ; - return static_cast(it - ptr); - } - - const std::type_info **m_types = nullptr; - char *m_text = nullptr; -}; - -/* The 'PYBIND11_NOINLINE inline' combinations below are intentional to get the desired linkage while producing as little object code as possible */ - -PYBIND11_NOINLINE inline descr _(const char *text) { - const std::type_info *types[1] = { nullptr }; - return descr(text, types); -} - -template PYBIND11_NOINLINE enable_if_t _(const char *text1, const char *) { return _(text1); } -template PYBIND11_NOINLINE enable_if_t _(char const *, const char *text2) { return _(text2); } -template PYBIND11_NOINLINE enable_if_t _(descr d, descr) { return d; } -template PYBIND11_NOINLINE enable_if_t _(descr, descr d) { return d; } - -template PYBIND11_NOINLINE descr _() { - const std::type_info *types[2] = { &typeid(Type), nullptr }; - return descr("%", types); -} - -template PYBIND11_NOINLINE descr _() { - const std::type_info *types[1] = { nullptr }; - return descr(std::to_string(Size).c_str(), types); -} - -PYBIND11_NOINLINE inline descr concat() { return _(""); } -PYBIND11_NOINLINE inline descr concat(descr &&d) { return d; } -template PYBIND11_NOINLINE descr concat(descr &&d, Args&&... args) { return std::move(d) + _(", ") + concat(std::forward(args)...); } -PYBIND11_NOINLINE inline descr type_descr(descr&& d) { return _("{") + std::move(d) + _("}"); } - -#define PYBIND11_DESCR ::pybind11::detail::descr -#endif - -NAMESPACE_END(detail) -NAMESPACE_END(pybind11) diff --git a/ptocr/postprocess/piexlmerge/include/pybind11/detail/class.h b/ptocr/postprocess/piexlmerge/include/pybind11/detail/class.h deleted file mode 100644 index 7a5dd01..0000000 --- a/ptocr/postprocess/piexlmerge/include/pybind11/detail/class.h +++ /dev/null @@ -1,622 +0,0 @@ -/* - pybind11/detail/class.h: Python C API implementation details for py::class_ - - Copyright (c) 2017 Wenzel Jakob - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#include "../attr.h" - -NAMESPACE_BEGIN(PYBIND11_NAMESPACE) -NAMESPACE_BEGIN(detail) - -#if PY_VERSION_HEX >= 0x03030000 -# define PYBIND11_BUILTIN_QUALNAME -# define PYBIND11_SET_OLDPY_QUALNAME(obj, nameobj) -#else -// In pre-3.3 Python, we still set __qualname__ so that we can produce reliable function type -// signatures; in 3.3+ this macro expands to nothing: -# define PYBIND11_SET_OLDPY_QUALNAME(obj, nameobj) setattr((PyObject *) obj, "__qualname__", nameobj) -#endif - -inline PyTypeObject *type_incref(PyTypeObject *type) { - Py_INCREF(type); - return type; -} - -#if !defined(PYPY_VERSION) - -/// `pybind11_static_property.__get__()`: Always pass the class instead of the instance. -extern "C" inline PyObject *pybind11_static_get(PyObject *self, PyObject * /*ob*/, PyObject *cls) { - return PyProperty_Type.tp_descr_get(self, cls, cls); -} - -/// `pybind11_static_property.__set__()`: Just like the above `__get__()`. -extern "C" inline int pybind11_static_set(PyObject *self, PyObject *obj, PyObject *value) { - PyObject *cls = PyType_Check(obj) ? obj : (PyObject *) Py_TYPE(obj); - return PyProperty_Type.tp_descr_set(self, cls, value); -} - -/** A `static_property` is the same as a `property` but the `__get__()` and `__set__()` - methods are modified to always use the object type instead of a concrete instance. - Return value: New reference. */ -inline PyTypeObject *make_static_property_type() { - constexpr auto *name = "pybind11_static_property"; - auto name_obj = reinterpret_steal(PYBIND11_FROM_STRING(name)); - - /* Danger zone: from now (and until PyType_Ready), make sure to - issue no Python C API calls which could potentially invoke the - garbage collector (the GC will call type_traverse(), which will in - turn find the newly constructed type in an invalid state) */ - auto heap_type = (PyHeapTypeObject *) PyType_Type.tp_alloc(&PyType_Type, 0); - if (!heap_type) - pybind11_fail("make_static_property_type(): error allocating type!"); - - heap_type->ht_name = name_obj.inc_ref().ptr(); -#ifdef PYBIND11_BUILTIN_QUALNAME - heap_type->ht_qualname = name_obj.inc_ref().ptr(); -#endif - - auto type = &heap_type->ht_type; - type->tp_name = name; - type->tp_base = type_incref(&PyProperty_Type); - type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE; - type->tp_descr_get = pybind11_static_get; - type->tp_descr_set = pybind11_static_set; - - if (PyType_Ready(type) < 0) - pybind11_fail("make_static_property_type(): failure in PyType_Ready()!"); - - setattr((PyObject *) type, "__module__", str("pybind11_builtins")); - PYBIND11_SET_OLDPY_QUALNAME(type, name_obj); - - return type; -} - -#else // PYPY - -/** PyPy has some issues with the above C API, so we evaluate Python code instead. - This function will only be called once so performance isn't really a concern. - Return value: New reference. */ -inline PyTypeObject *make_static_property_type() { - auto d = dict(); - PyObject *result = PyRun_String(R"(\ - class pybind11_static_property(property): - def __get__(self, obj, cls): - return property.__get__(self, cls, cls) - - def __set__(self, obj, value): - cls = obj if isinstance(obj, type) else type(obj) - property.__set__(self, cls, value) - )", Py_file_input, d.ptr(), d.ptr() - ); - if (result == nullptr) - throw error_already_set(); - Py_DECREF(result); - return (PyTypeObject *) d["pybind11_static_property"].cast().release().ptr(); -} - -#endif // PYPY - -/** Types with static properties need to handle `Type.static_prop = x` in a specific way. - By default, Python replaces the `static_property` itself, but for wrapped C++ types - we need to call `static_property.__set__()` in order to propagate the new value to - the underlying C++ data structure. */ -extern "C" inline int pybind11_meta_setattro(PyObject* obj, PyObject* name, PyObject* value) { - // Use `_PyType_Lookup()` instead of `PyObject_GetAttr()` in order to get the raw - // descriptor (`property`) instead of calling `tp_descr_get` (`property.__get__()`). - PyObject *descr = _PyType_Lookup((PyTypeObject *) obj, name); - - // The following assignment combinations are possible: - // 1. `Type.static_prop = value` --> descr_set: `Type.static_prop.__set__(value)` - // 2. `Type.static_prop = other_static_prop` --> setattro: replace existing `static_prop` - // 3. `Type.regular_attribute = value` --> setattro: regular attribute assignment - const auto static_prop = (PyObject *) get_internals().static_property_type; - const auto call_descr_set = descr && PyObject_IsInstance(descr, static_prop) - && !PyObject_IsInstance(value, static_prop); - if (call_descr_set) { - // Call `static_property.__set__()` instead of replacing the `static_property`. -#if !defined(PYPY_VERSION) - return Py_TYPE(descr)->tp_descr_set(descr, obj, value); -#else - if (PyObject *result = PyObject_CallMethod(descr, "__set__", "OO", obj, value)) { - Py_DECREF(result); - return 0; - } else { - return -1; - } -#endif - } else { - // Replace existing attribute. - return PyType_Type.tp_setattro(obj, name, value); - } -} - -#if PY_MAJOR_VERSION >= 3 -/** - * Python 3's PyInstanceMethod_Type hides itself via its tp_descr_get, which prevents aliasing - * methods via cls.attr("m2") = cls.attr("m1"): instead the tp_descr_get returns a plain function, - * when called on a class, or a PyMethod, when called on an instance. Override that behaviour here - * to do a special case bypass for PyInstanceMethod_Types. - */ -extern "C" inline PyObject *pybind11_meta_getattro(PyObject *obj, PyObject *name) { - PyObject *descr = _PyType_Lookup((PyTypeObject *) obj, name); - if (descr && PyInstanceMethod_Check(descr)) { - Py_INCREF(descr); - return descr; - } - else { - return PyType_Type.tp_getattro(obj, name); - } -} -#endif - -/** This metaclass is assigned by default to all pybind11 types and is required in order - for static properties to function correctly. Users may override this using `py::metaclass`. - Return value: New reference. */ -inline PyTypeObject* make_default_metaclass() { - constexpr auto *name = "pybind11_type"; - auto name_obj = reinterpret_steal(PYBIND11_FROM_STRING(name)); - - /* Danger zone: from now (and until PyType_Ready), make sure to - issue no Python C API calls which could potentially invoke the - garbage collector (the GC will call type_traverse(), which will in - turn find the newly constructed type in an invalid state) */ - auto heap_type = (PyHeapTypeObject *) PyType_Type.tp_alloc(&PyType_Type, 0); - if (!heap_type) - pybind11_fail("make_default_metaclass(): error allocating metaclass!"); - - heap_type->ht_name = name_obj.inc_ref().ptr(); -#ifdef PYBIND11_BUILTIN_QUALNAME - heap_type->ht_qualname = name_obj.inc_ref().ptr(); -#endif - - auto type = &heap_type->ht_type; - type->tp_name = name; - type->tp_base = type_incref(&PyType_Type); - type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE; - - type->tp_setattro = pybind11_meta_setattro; -#if PY_MAJOR_VERSION >= 3 - type->tp_getattro = pybind11_meta_getattro; -#endif - - if (PyType_Ready(type) < 0) - pybind11_fail("make_default_metaclass(): failure in PyType_Ready()!"); - - setattr((PyObject *) type, "__module__", str("pybind11_builtins")); - PYBIND11_SET_OLDPY_QUALNAME(type, name_obj); - - return type; -} - -/// For multiple inheritance types we need to recursively register/deregister base pointers for any -/// base classes with pointers that are difference from the instance value pointer so that we can -/// correctly recognize an offset base class pointer. This calls a function with any offset base ptrs. -inline void traverse_offset_bases(void *valueptr, const detail::type_info *tinfo, instance *self, - bool (*f)(void * /*parentptr*/, instance * /*self*/)) { - for (handle h : reinterpret_borrow(tinfo->type->tp_bases)) { - if (auto parent_tinfo = get_type_info((PyTypeObject *) h.ptr())) { - for (auto &c : parent_tinfo->implicit_casts) { - if (c.first == tinfo->cpptype) { - auto *parentptr = c.second(valueptr); - if (parentptr != valueptr) - f(parentptr, self); - traverse_offset_bases(parentptr, parent_tinfo, self, f); - break; - } - } - } - } -} - -inline bool register_instance_impl(void *ptr, instance *self) { - get_internals().registered_instances.emplace(ptr, self); - return true; // unused, but gives the same signature as the deregister func -} -inline bool deregister_instance_impl(void *ptr, instance *self) { - auto ®istered_instances = get_internals().registered_instances; - auto range = registered_instances.equal_range(ptr); - for (auto it = range.first; it != range.second; ++it) { - if (Py_TYPE(self) == Py_TYPE(it->second)) { - registered_instances.erase(it); - return true; - } - } - return false; -} - -inline void register_instance(instance *self, void *valptr, const type_info *tinfo) { - register_instance_impl(valptr, self); - if (!tinfo->simple_ancestors) - traverse_offset_bases(valptr, tinfo, self, register_instance_impl); -} - -inline bool deregister_instance(instance *self, void *valptr, const type_info *tinfo) { - bool ret = deregister_instance_impl(valptr, self); - if (!tinfo->simple_ancestors) - traverse_offset_bases(valptr, tinfo, self, deregister_instance_impl); - return ret; -} - -/// Instance creation function for all pybind11 types. It allocates the internal instance layout for -/// holding C++ objects and holders. Allocation is done lazily (the first time the instance is cast -/// to a reference or pointer), and initialization is done by an `__init__` function. -inline PyObject *make_new_instance(PyTypeObject *type) { -#if defined(PYPY_VERSION) - // PyPy gets tp_basicsize wrong (issue 2482) under multiple inheritance when the first inherited - // object is a a plain Python type (i.e. not derived from an extension type). Fix it. - ssize_t instance_size = static_cast(sizeof(instance)); - if (type->tp_basicsize < instance_size) { - type->tp_basicsize = instance_size; - } -#endif - PyObject *self = type->tp_alloc(type, 0); - auto inst = reinterpret_cast(self); - // Allocate the value/holder internals: - inst->allocate_layout(); - - inst->owned = true; - - return self; -} - -/// Instance creation function for all pybind11 types. It only allocates space for the -/// C++ object, but doesn't call the constructor -- an `__init__` function must do that. -extern "C" inline PyObject *pybind11_object_new(PyTypeObject *type, PyObject *, PyObject *) { - return make_new_instance(type); -} - -/// An `__init__` function constructs the C++ object. Users should provide at least one -/// of these using `py::init` or directly with `.def(__init__, ...)`. Otherwise, the -/// following default function will be used which simply throws an exception. -extern "C" inline int pybind11_object_init(PyObject *self, PyObject *, PyObject *) { - PyTypeObject *type = Py_TYPE(self); - std::string msg; -#if defined(PYPY_VERSION) - msg += handle((PyObject *) type).attr("__module__").cast() + "."; -#endif - msg += type->tp_name; - msg += ": No constructor defined!"; - PyErr_SetString(PyExc_TypeError, msg.c_str()); - return -1; -} - -inline void add_patient(PyObject *nurse, PyObject *patient) { - auto &internals = get_internals(); - auto instance = reinterpret_cast(nurse); - instance->has_patients = true; - Py_INCREF(patient); - internals.patients[nurse].push_back(patient); -} - -inline void clear_patients(PyObject *self) { - auto instance = reinterpret_cast(self); - auto &internals = get_internals(); - auto pos = internals.patients.find(self); - assert(pos != internals.patients.end()); - // Clearing the patients can cause more Python code to run, which - // can invalidate the iterator. Extract the vector of patients - // from the unordered_map first. - auto patients = std::move(pos->second); - internals.patients.erase(pos); - instance->has_patients = false; - for (PyObject *&patient : patients) - Py_CLEAR(patient); -} - -/// Clears all internal data from the instance and removes it from registered instances in -/// preparation for deallocation. -inline void clear_instance(PyObject *self) { - auto instance = reinterpret_cast(self); - - // Deallocate any values/holders, if present: - for (auto &v_h : values_and_holders(instance)) { - if (v_h) { - - // We have to deregister before we call dealloc because, for virtual MI types, we still - // need to be able to get the parent pointers. - if (v_h.instance_registered() && !deregister_instance(instance, v_h.value_ptr(), v_h.type)) - pybind11_fail("pybind11_object_dealloc(): Tried to deallocate unregistered instance!"); - - if (instance->owned || v_h.holder_constructed()) - v_h.type->dealloc(v_h); - } - } - // Deallocate the value/holder layout internals: - instance->deallocate_layout(); - - if (instance->weakrefs) - PyObject_ClearWeakRefs(self); - - PyObject **dict_ptr = _PyObject_GetDictPtr(self); - if (dict_ptr) - Py_CLEAR(*dict_ptr); - - if (instance->has_patients) - clear_patients(self); -} - -/// Instance destructor function for all pybind11 types. It calls `type_info.dealloc` -/// to destroy the C++ object itself, while the rest is Python bookkeeping. -extern "C" inline void pybind11_object_dealloc(PyObject *self) { - clear_instance(self); - - auto type = Py_TYPE(self); - type->tp_free(self); - - // `type->tp_dealloc != pybind11_object_dealloc` means that we're being called - // as part of a derived type's dealloc, in which case we're not allowed to decref - // the type here. For cross-module compatibility, we shouldn't compare directly - // with `pybind11_object_dealloc`, but with the common one stashed in internals. - auto pybind11_object_type = (PyTypeObject *) get_internals().instance_base; - if (type->tp_dealloc == pybind11_object_type->tp_dealloc) - Py_DECREF(type); -} - -/** Create the type which can be used as a common base for all classes. This is - needed in order to satisfy Python's requirements for multiple inheritance. - Return value: New reference. */ -inline PyObject *make_object_base_type(PyTypeObject *metaclass) { - constexpr auto *name = "pybind11_object"; - auto name_obj = reinterpret_steal(PYBIND11_FROM_STRING(name)); - - /* Danger zone: from now (and until PyType_Ready), make sure to - issue no Python C API calls which could potentially invoke the - garbage collector (the GC will call type_traverse(), which will in - turn find the newly constructed type in an invalid state) */ - auto heap_type = (PyHeapTypeObject *) metaclass->tp_alloc(metaclass, 0); - if (!heap_type) - pybind11_fail("make_object_base_type(): error allocating type!"); - - heap_type->ht_name = name_obj.inc_ref().ptr(); -#ifdef PYBIND11_BUILTIN_QUALNAME - heap_type->ht_qualname = name_obj.inc_ref().ptr(); -#endif - - auto type = &heap_type->ht_type; - type->tp_name = name; - type->tp_base = type_incref(&PyBaseObject_Type); - type->tp_basicsize = static_cast(sizeof(instance)); - type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE; - - type->tp_new = pybind11_object_new; - type->tp_init = pybind11_object_init; - type->tp_dealloc = pybind11_object_dealloc; - - /* Support weak references (needed for the keep_alive feature) */ - type->tp_weaklistoffset = offsetof(instance, weakrefs); - - if (PyType_Ready(type) < 0) - pybind11_fail("PyType_Ready failed in make_object_base_type():" + error_string()); - - setattr((PyObject *) type, "__module__", str("pybind11_builtins")); - PYBIND11_SET_OLDPY_QUALNAME(type, name_obj); - - assert(!PyType_HasFeature(type, Py_TPFLAGS_HAVE_GC)); - return (PyObject *) heap_type; -} - -/// dynamic_attr: Support for `d = instance.__dict__`. -extern "C" inline PyObject *pybind11_get_dict(PyObject *self, void *) { - PyObject *&dict = *_PyObject_GetDictPtr(self); - if (!dict) - dict = PyDict_New(); - Py_XINCREF(dict); - return dict; -} - -/// dynamic_attr: Support for `instance.__dict__ = dict()`. -extern "C" inline int pybind11_set_dict(PyObject *self, PyObject *new_dict, void *) { - if (!PyDict_Check(new_dict)) { - PyErr_Format(PyExc_TypeError, "__dict__ must be set to a dictionary, not a '%.200s'", - Py_TYPE(new_dict)->tp_name); - return -1; - } - PyObject *&dict = *_PyObject_GetDictPtr(self); - Py_INCREF(new_dict); - Py_CLEAR(dict); - dict = new_dict; - return 0; -} - -/// dynamic_attr: Allow the garbage collector to traverse the internal instance `__dict__`. -extern "C" inline int pybind11_traverse(PyObject *self, visitproc visit, void *arg) { - PyObject *&dict = *_PyObject_GetDictPtr(self); - Py_VISIT(dict); - return 0; -} - -/// dynamic_attr: Allow the GC to clear the dictionary. -extern "C" inline int pybind11_clear(PyObject *self) { - PyObject *&dict = *_PyObject_GetDictPtr(self); - Py_CLEAR(dict); - return 0; -} - -/// Give instances of this type a `__dict__` and opt into garbage collection. -inline void enable_dynamic_attributes(PyHeapTypeObject *heap_type) { - auto type = &heap_type->ht_type; -#if defined(PYPY_VERSION) - pybind11_fail(std::string(type->tp_name) + ": dynamic attributes are " - "currently not supported in " - "conjunction with PyPy!"); -#endif - type->tp_flags |= Py_TPFLAGS_HAVE_GC; - type->tp_dictoffset = type->tp_basicsize; // place dict at the end - type->tp_basicsize += (ssize_t)sizeof(PyObject *); // and allocate enough space for it - type->tp_traverse = pybind11_traverse; - type->tp_clear = pybind11_clear; - - static PyGetSetDef getset[] = { - {const_cast("__dict__"), pybind11_get_dict, pybind11_set_dict, nullptr, nullptr}, - {nullptr, nullptr, nullptr, nullptr, nullptr} - }; - type->tp_getset = getset; -} - -/// buffer_protocol: Fill in the view as specified by flags. -extern "C" inline int pybind11_getbuffer(PyObject *obj, Py_buffer *view, int flags) { - // Look for a `get_buffer` implementation in this type's info or any bases (following MRO). - type_info *tinfo = nullptr; - for (auto type : reinterpret_borrow(Py_TYPE(obj)->tp_mro)) { - tinfo = get_type_info((PyTypeObject *) type.ptr()); - if (tinfo && tinfo->get_buffer) - break; - } - if (view == nullptr || obj == nullptr || !tinfo || !tinfo->get_buffer) { - if (view) - view->obj = nullptr; - PyErr_SetString(PyExc_BufferError, "pybind11_getbuffer(): Internal error"); - return -1; - } - std::memset(view, 0, sizeof(Py_buffer)); - buffer_info *info = tinfo->get_buffer(obj, tinfo->get_buffer_data); - view->obj = obj; - view->ndim = 1; - view->internal = info; - view->buf = info->ptr; - view->itemsize = info->itemsize; - view->len = view->itemsize; - for (auto s : info->shape) - view->len *= s; - if ((flags & PyBUF_FORMAT) == PyBUF_FORMAT) - view->format = const_cast(info->format.c_str()); - if ((flags & PyBUF_STRIDES) == PyBUF_STRIDES) { - view->ndim = (int) info->ndim; - view->strides = &info->strides[0]; - view->shape = &info->shape[0]; - } - Py_INCREF(view->obj); - return 0; -} - -/// buffer_protocol: Release the resources of the buffer. -extern "C" inline void pybind11_releasebuffer(PyObject *, Py_buffer *view) { - delete (buffer_info *) view->internal; -} - -/// Give this type a buffer interface. -inline void enable_buffer_protocol(PyHeapTypeObject *heap_type) { - heap_type->ht_type.tp_as_buffer = &heap_type->as_buffer; -#if PY_MAJOR_VERSION < 3 - heap_type->ht_type.tp_flags |= Py_TPFLAGS_HAVE_NEWBUFFER; -#endif - - heap_type->as_buffer.bf_getbuffer = pybind11_getbuffer; - heap_type->as_buffer.bf_releasebuffer = pybind11_releasebuffer; -} - -/** Create a brand new Python type according to the `type_record` specification. - Return value: New reference. */ -inline PyObject* make_new_python_type(const type_record &rec) { - auto name = reinterpret_steal(PYBIND11_FROM_STRING(rec.name)); - - auto qualname = name; - if (rec.scope && !PyModule_Check(rec.scope.ptr()) && hasattr(rec.scope, "__qualname__")) { -#if PY_MAJOR_VERSION >= 3 - qualname = reinterpret_steal( - PyUnicode_FromFormat("%U.%U", rec.scope.attr("__qualname__").ptr(), name.ptr())); -#else - qualname = str(rec.scope.attr("__qualname__").cast() + "." + rec.name); -#endif - } - - object module; - if (rec.scope) { - if (hasattr(rec.scope, "__module__")) - module = rec.scope.attr("__module__"); - else if (hasattr(rec.scope, "__name__")) - module = rec.scope.attr("__name__"); - } - - auto full_name = c_str( -#if !defined(PYPY_VERSION) - module ? str(module).cast() + "." + rec.name : -#endif - rec.name); - - char *tp_doc = nullptr; - if (rec.doc && options::show_user_defined_docstrings()) { - /* Allocate memory for docstring (using PyObject_MALLOC, since - Python will free this later on) */ - size_t size = strlen(rec.doc) + 1; - tp_doc = (char *) PyObject_MALLOC(size); - memcpy((void *) tp_doc, rec.doc, size); - } - - auto &internals = get_internals(); - auto bases = tuple(rec.bases); - auto base = (bases.size() == 0) ? internals.instance_base - : bases[0].ptr(); - - /* Danger zone: from now (and until PyType_Ready), make sure to - issue no Python C API calls which could potentially invoke the - garbage collector (the GC will call type_traverse(), which will in - turn find the newly constructed type in an invalid state) */ - auto metaclass = rec.metaclass.ptr() ? (PyTypeObject *) rec.metaclass.ptr() - : internals.default_metaclass; - - auto heap_type = (PyHeapTypeObject *) metaclass->tp_alloc(metaclass, 0); - if (!heap_type) - pybind11_fail(std::string(rec.name) + ": Unable to create type object!"); - - heap_type->ht_name = name.release().ptr(); -#ifdef PYBIND11_BUILTIN_QUALNAME - heap_type->ht_qualname = qualname.inc_ref().ptr(); -#endif - - auto type = &heap_type->ht_type; - type->tp_name = full_name; - type->tp_doc = tp_doc; - type->tp_base = type_incref((PyTypeObject *)base); - type->tp_basicsize = static_cast(sizeof(instance)); - if (bases.size() > 0) - type->tp_bases = bases.release().ptr(); - - /* Don't inherit base __init__ */ - type->tp_init = pybind11_object_init; - - /* Supported protocols */ - type->tp_as_number = &heap_type->as_number; - type->tp_as_sequence = &heap_type->as_sequence; - type->tp_as_mapping = &heap_type->as_mapping; - - /* Flags */ - type->tp_flags |= Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE; -#if PY_MAJOR_VERSION < 3 - type->tp_flags |= Py_TPFLAGS_CHECKTYPES; -#endif - - if (rec.dynamic_attr) - enable_dynamic_attributes(heap_type); - - if (rec.buffer_protocol) - enable_buffer_protocol(heap_type); - - if (PyType_Ready(type) < 0) - pybind11_fail(std::string(rec.name) + ": PyType_Ready failed (" + error_string() + ")!"); - - assert(rec.dynamic_attr ? PyType_HasFeature(type, Py_TPFLAGS_HAVE_GC) - : !PyType_HasFeature(type, Py_TPFLAGS_HAVE_GC)); - - /* Register type with the parent scope */ - if (rec.scope) - setattr(rec.scope, rec.name, (PyObject *) type); - else - Py_INCREF(type); // Keep it alive forever (reference leak) - - if (module) // Needed by pydoc - setattr((PyObject *) type, "__module__", module); - - PYBIND11_SET_OLDPY_QUALNAME(type, qualname); - - return (PyObject *) type; -} - -NAMESPACE_END(detail) -NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/ptocr/postprocess/piexlmerge/include/pybind11/detail/common.h b/ptocr/postprocess/piexlmerge/include/pybind11/detail/common.h deleted file mode 100644 index 5ff7485..0000000 --- a/ptocr/postprocess/piexlmerge/include/pybind11/detail/common.h +++ /dev/null @@ -1,807 +0,0 @@ -/* - pybind11/detail/common.h -- Basic macros - - Copyright (c) 2016 Wenzel Jakob - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#if !defined(NAMESPACE_BEGIN) -# define NAMESPACE_BEGIN(name) namespace name { -#endif -#if !defined(NAMESPACE_END) -# define NAMESPACE_END(name) } -#endif - -// Robust support for some features and loading modules compiled against different pybind versions -// requires forcing hidden visibility on pybind code, so we enforce this by setting the attribute on -// the main `pybind11` namespace. -#if !defined(PYBIND11_NAMESPACE) -# ifdef __GNUG__ -# define PYBIND11_NAMESPACE pybind11 __attribute__((visibility("hidden"))) -# else -# define PYBIND11_NAMESPACE pybind11 -# endif -#endif - -#if !(defined(_MSC_VER) && __cplusplus == 199711L) && !defined(__INTEL_COMPILER) -# if __cplusplus >= 201402L -# define PYBIND11_CPP14 -# if __cplusplus >= 201703L -# define PYBIND11_CPP17 -# endif -# endif -#elif defined(_MSC_VER) && __cplusplus == 199711L -// MSVC sets _MSVC_LANG rather than __cplusplus (supposedly until the standard is fully implemented) -// Unless you use the /Zc:__cplusplus flag on Visual Studio 2017 15.7 Preview 3 or newer -# if _MSVC_LANG >= 201402L -# define PYBIND11_CPP14 -# if _MSVC_LANG > 201402L && _MSC_VER >= 1910 -# define PYBIND11_CPP17 -# endif -# endif -#endif - -// Compiler version assertions -#if defined(__INTEL_COMPILER) -# if __INTEL_COMPILER < 1700 -# error pybind11 requires Intel C++ compiler v17 or newer -# endif -#elif defined(__clang__) && !defined(__apple_build_version__) -# if __clang_major__ < 3 || (__clang_major__ == 3 && __clang_minor__ < 3) -# error pybind11 requires clang 3.3 or newer -# endif -#elif defined(__clang__) -// Apple changes clang version macros to its Xcode version; the first Xcode release based on -// (upstream) clang 3.3 was Xcode 5: -# if __clang_major__ < 5 -# error pybind11 requires Xcode/clang 5.0 or newer -# endif -#elif defined(__GNUG__) -# if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 8) -# error pybind11 requires gcc 4.8 or newer -# endif -#elif defined(_MSC_VER) -// Pybind hits various compiler bugs in 2015u2 and earlier, and also makes use of some stl features -// (e.g. std::negation) added in 2015u3: -# if _MSC_FULL_VER < 190024210 -# error pybind11 requires MSVC 2015 update 3 or newer -# endif -#endif - -#if !defined(PYBIND11_EXPORT) -# if defined(WIN32) || defined(_WIN32) -# define PYBIND11_EXPORT __declspec(dllexport) -# else -# define PYBIND11_EXPORT __attribute__ ((visibility("default"))) -# endif -#endif - -#if defined(_MSC_VER) -# define PYBIND11_NOINLINE __declspec(noinline) -#else -# define PYBIND11_NOINLINE __attribute__ ((noinline)) -#endif - -#if defined(PYBIND11_CPP14) -# define PYBIND11_DEPRECATED(reason) [[deprecated(reason)]] -#else -# define PYBIND11_DEPRECATED(reason) __attribute__((deprecated(reason))) -#endif - -#define PYBIND11_VERSION_MAJOR 2 -#define PYBIND11_VERSION_MINOR 3 -#define PYBIND11_VERSION_PATCH dev0 - -/// Include Python header, disable linking to pythonX_d.lib on Windows in debug mode -#if defined(_MSC_VER) -# if (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION < 4) -# define HAVE_ROUND 1 -# endif -# pragma warning(push) -# pragma warning(disable: 4510 4610 4512 4005) -# if defined(_DEBUG) -# define PYBIND11_DEBUG_MARKER -# undef _DEBUG -# endif -#endif - -#include -#include -#include - -#if defined(_WIN32) && (defined(min) || defined(max)) -# error Macro clash with min and max -- define NOMINMAX when compiling your program on Windows -#endif - -#if defined(isalnum) -# undef isalnum -# undef isalpha -# undef islower -# undef isspace -# undef isupper -# undef tolower -# undef toupper -#endif - -#if defined(_MSC_VER) -# if defined(PYBIND11_DEBUG_MARKER) -# define _DEBUG -# undef PYBIND11_DEBUG_MARKER -# endif -# pragma warning(pop) -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if PY_MAJOR_VERSION >= 3 /// Compatibility macros for various Python versions -#define PYBIND11_INSTANCE_METHOD_NEW(ptr, class_) PyInstanceMethod_New(ptr) -#define PYBIND11_INSTANCE_METHOD_CHECK PyInstanceMethod_Check -#define PYBIND11_INSTANCE_METHOD_GET_FUNCTION PyInstanceMethod_GET_FUNCTION -#define PYBIND11_BYTES_CHECK PyBytes_Check -#define PYBIND11_BYTES_FROM_STRING PyBytes_FromString -#define PYBIND11_BYTES_FROM_STRING_AND_SIZE PyBytes_FromStringAndSize -#define PYBIND11_BYTES_AS_STRING_AND_SIZE PyBytes_AsStringAndSize -#define PYBIND11_BYTES_AS_STRING PyBytes_AsString -#define PYBIND11_BYTES_SIZE PyBytes_Size -#define PYBIND11_LONG_CHECK(o) PyLong_Check(o) -#define PYBIND11_LONG_AS_LONGLONG(o) PyLong_AsLongLong(o) -#define PYBIND11_LONG_FROM_SIGNED(o) PyLong_FromSsize_t((ssize_t) o) -#define PYBIND11_LONG_FROM_UNSIGNED(o) PyLong_FromSize_t((size_t) o) -#define PYBIND11_BYTES_NAME "bytes" -#define PYBIND11_STRING_NAME "str" -#define PYBIND11_SLICE_OBJECT PyObject -#define PYBIND11_FROM_STRING PyUnicode_FromString -#define PYBIND11_STR_TYPE ::pybind11::str -#define PYBIND11_BOOL_ATTR "__bool__" -#define PYBIND11_NB_BOOL(ptr) ((ptr)->nb_bool) -#define PYBIND11_PLUGIN_IMPL(name) \ - extern "C" PYBIND11_EXPORT PyObject *PyInit_##name() - -#else -#define PYBIND11_INSTANCE_METHOD_NEW(ptr, class_) PyMethod_New(ptr, nullptr, class_) -#define PYBIND11_INSTANCE_METHOD_CHECK PyMethod_Check -#define PYBIND11_INSTANCE_METHOD_GET_FUNCTION PyMethod_GET_FUNCTION -#define PYBIND11_BYTES_CHECK PyString_Check -#define PYBIND11_BYTES_FROM_STRING PyString_FromString -#define PYBIND11_BYTES_FROM_STRING_AND_SIZE PyString_FromStringAndSize -#define PYBIND11_BYTES_AS_STRING_AND_SIZE PyString_AsStringAndSize -#define PYBIND11_BYTES_AS_STRING PyString_AsString -#define PYBIND11_BYTES_SIZE PyString_Size -#define PYBIND11_LONG_CHECK(o) (PyInt_Check(o) || PyLong_Check(o)) -#define PYBIND11_LONG_AS_LONGLONG(o) (PyInt_Check(o) ? (long long) PyLong_AsLong(o) : PyLong_AsLongLong(o)) -#define PYBIND11_LONG_FROM_SIGNED(o) PyInt_FromSsize_t((ssize_t) o) // Returns long if needed. -#define PYBIND11_LONG_FROM_UNSIGNED(o) PyInt_FromSize_t((size_t) o) // Returns long if needed. -#define PYBIND11_BYTES_NAME "str" -#define PYBIND11_STRING_NAME "unicode" -#define PYBIND11_SLICE_OBJECT PySliceObject -#define PYBIND11_FROM_STRING PyString_FromString -#define PYBIND11_STR_TYPE ::pybind11::bytes -#define PYBIND11_BOOL_ATTR "__nonzero__" -#define PYBIND11_NB_BOOL(ptr) ((ptr)->nb_nonzero) -#define PYBIND11_PLUGIN_IMPL(name) \ - static PyObject *pybind11_init_wrapper(); \ - extern "C" PYBIND11_EXPORT void init##name() { \ - (void)pybind11_init_wrapper(); \ - } \ - PyObject *pybind11_init_wrapper() -#endif - -#if PY_VERSION_HEX >= 0x03050000 && PY_VERSION_HEX < 0x03050200 -extern "C" { - struct _Py_atomic_address { void *value; }; - PyAPI_DATA(_Py_atomic_address) _PyThreadState_Current; -} -#endif - -#define PYBIND11_TRY_NEXT_OVERLOAD ((PyObject *) 1) // special failure return code -#define PYBIND11_STRINGIFY(x) #x -#define PYBIND11_TOSTRING(x) PYBIND11_STRINGIFY(x) -#define PYBIND11_CONCAT(first, second) first##second - -#define PYBIND11_CHECK_PYTHON_VERSION \ - { \ - const char *compiled_ver = PYBIND11_TOSTRING(PY_MAJOR_VERSION) \ - "." PYBIND11_TOSTRING(PY_MINOR_VERSION); \ - const char *runtime_ver = Py_GetVersion(); \ - size_t len = std::strlen(compiled_ver); \ - if (std::strncmp(runtime_ver, compiled_ver, len) != 0 \ - || (runtime_ver[len] >= '0' && runtime_ver[len] <= '9')) { \ - PyErr_Format(PyExc_ImportError, \ - "Python version mismatch: module was compiled for Python %s, " \ - "but the interpreter version is incompatible: %s.", \ - compiled_ver, runtime_ver); \ - return nullptr; \ - } \ - } - -#define PYBIND11_CATCH_INIT_EXCEPTIONS \ - catch (pybind11::error_already_set &e) { \ - PyErr_SetString(PyExc_ImportError, e.what()); \ - return nullptr; \ - } catch (const std::exception &e) { \ - PyErr_SetString(PyExc_ImportError, e.what()); \ - return nullptr; \ - } \ - -/** \rst - ***Deprecated in favor of PYBIND11_MODULE*** - - This macro creates the entry point that will be invoked when the Python interpreter - imports a plugin library. Please create a `module` in the function body and return - the pointer to its underlying Python object at the end. - - .. code-block:: cpp - - PYBIND11_PLUGIN(example) { - pybind11::module m("example", "pybind11 example plugin"); - /// Set up bindings here - return m.ptr(); - } -\endrst */ -#define PYBIND11_PLUGIN(name) \ - PYBIND11_DEPRECATED("PYBIND11_PLUGIN is deprecated, use PYBIND11_MODULE") \ - static PyObject *pybind11_init(); \ - PYBIND11_PLUGIN_IMPL(name) { \ - PYBIND11_CHECK_PYTHON_VERSION \ - try { \ - return pybind11_init(); \ - } PYBIND11_CATCH_INIT_EXCEPTIONS \ - } \ - PyObject *pybind11_init() - -/** \rst - This macro creates the entry point that will be invoked when the Python interpreter - imports an extension module. The module name is given as the fist argument and it - should not be in quotes. The second macro argument defines a variable of type - `py::module` which can be used to initialize the module. - - .. code-block:: cpp - - PYBIND11_MODULE(example, m) { - m.doc() = "pybind11 example module"; - - // Add bindings here - m.def("foo", []() { - return "Hello, World!"; - }); - } -\endrst */ -#define PYBIND11_MODULE(name, variable) \ - static void PYBIND11_CONCAT(pybind11_init_, name)(pybind11::module &); \ - PYBIND11_PLUGIN_IMPL(name) { \ - PYBIND11_CHECK_PYTHON_VERSION \ - auto m = pybind11::module(PYBIND11_TOSTRING(name)); \ - try { \ - PYBIND11_CONCAT(pybind11_init_, name)(m); \ - return m.ptr(); \ - } PYBIND11_CATCH_INIT_EXCEPTIONS \ - } \ - void PYBIND11_CONCAT(pybind11_init_, name)(pybind11::module &variable) - - -NAMESPACE_BEGIN(PYBIND11_NAMESPACE) - -using ssize_t = Py_ssize_t; -using size_t = std::size_t; - -/// Approach used to cast a previously unknown C++ instance into a Python object -enum class return_value_policy : uint8_t { - /** This is the default return value policy, which falls back to the policy - return_value_policy::take_ownership when the return value is a pointer. - Otherwise, it uses return_value::move or return_value::copy for rvalue - and lvalue references, respectively. See below for a description of what - all of these different policies do. */ - automatic = 0, - - /** As above, but use policy return_value_policy::reference when the return - value is a pointer. This is the default conversion policy for function - arguments when calling Python functions manually from C++ code (i.e. via - handle::operator()). You probably won't need to use this. */ - automatic_reference, - - /** Reference an existing object (i.e. do not create a new copy) and take - ownership. Python will call the destructor and delete operator when the - object’s reference count reaches zero. Undefined behavior ensues when - the C++ side does the same.. */ - take_ownership, - - /** Create a new copy of the returned object, which will be owned by - Python. This policy is comparably safe because the lifetimes of the two - instances are decoupled. */ - copy, - - /** Use std::move to move the return value contents into a new instance - that will be owned by Python. This policy is comparably safe because the - lifetimes of the two instances (move source and destination) are - decoupled. */ - move, - - /** Reference an existing object, but do not take ownership. The C++ side - is responsible for managing the object’s lifetime and deallocating it - when it is no longer used. Warning: undefined behavior will ensue when - the C++ side deletes an object that is still referenced and used by - Python. */ - reference, - - /** This policy only applies to methods and properties. It references the - object without taking ownership similar to the above - return_value_policy::reference policy. In contrast to that policy, the - function or property’s implicit this argument (called the parent) is - considered to be the the owner of the return value (the child). - pybind11 then couples the lifetime of the parent to the child via a - reference relationship that ensures that the parent cannot be garbage - collected while Python is still using the child. More advanced - variations of this scheme are also possible using combinations of - return_value_policy::reference and the keep_alive call policy */ - reference_internal -}; - -NAMESPACE_BEGIN(detail) - -inline static constexpr int log2(size_t n, int k = 0) { return (n <= 1) ? k : log2(n >> 1, k + 1); } - -// Returns the size as a multiple of sizeof(void *), rounded up. -inline static constexpr size_t size_in_ptrs(size_t s) { return 1 + ((s - 1) >> log2(sizeof(void *))); } - -/** - * The space to allocate for simple layout instance holders (see below) in multiple of the size of - * a pointer (e.g. 2 means 16 bytes on 64-bit architectures). The default is the minimum required - * to holder either a std::unique_ptr or std::shared_ptr (which is almost always - * sizeof(std::shared_ptr)). - */ -constexpr size_t instance_simple_holder_in_ptrs() { - static_assert(sizeof(std::shared_ptr) >= sizeof(std::unique_ptr), - "pybind assumes std::shared_ptrs are at least as big as std::unique_ptrs"); - return size_in_ptrs(sizeof(std::shared_ptr)); -} - -// Forward declarations -struct type_info; -struct value_and_holder; - -struct nonsimple_values_and_holders { - void **values_and_holders; - uint8_t *status; -}; - -/// The 'instance' type which needs to be standard layout (need to be able to use 'offsetof') -struct instance { - PyObject_HEAD - /// Storage for pointers and holder; see simple_layout, below, for a description - union { - void *simple_value_holder[1 + instance_simple_holder_in_ptrs()]; - nonsimple_values_and_holders nonsimple; - }; - /// Weak references - PyObject *weakrefs; - /// If true, the pointer is owned which means we're free to manage it with a holder. - bool owned : 1; - /** - * An instance has two possible value/holder layouts. - * - * Simple layout (when this flag is true), means the `simple_value_holder` is set with a pointer - * and the holder object governing that pointer, i.e. [val1*][holder]. This layout is applied - * whenever there is no python-side multiple inheritance of bound C++ types *and* the type's - * holder will fit in the default space (which is large enough to hold either a std::unique_ptr - * or std::shared_ptr). - * - * Non-simple layout applies when using custom holders that require more space than `shared_ptr` - * (which is typically the size of two pointers), or when multiple inheritance is used on the - * python side. Non-simple layout allocates the required amount of memory to have multiple - * bound C++ classes as parents. Under this layout, `nonsimple.values_and_holders` is set to a - * pointer to allocated space of the required space to hold a sequence of value pointers and - * holders followed `status`, a set of bit flags (1 byte each), i.e. - * [val1*][holder1][val2*][holder2]...[bb...] where each [block] is rounded up to a multiple of - * `sizeof(void *)`. `nonsimple.status` is, for convenience, a pointer to the - * beginning of the [bb...] block (but not independently allocated). - * - * Status bits indicate whether the associated holder is constructed (& - * status_holder_constructed) and whether the value pointer is registered (& - * status_instance_registered) in `registered_instances`. - */ - bool simple_layout : 1; - /// For simple layout, tracks whether the holder has been constructed - bool simple_holder_constructed : 1; - /// For simple layout, tracks whether the instance is registered in `registered_instances` - bool simple_instance_registered : 1; - /// If true, get_internals().patients has an entry for this object - bool has_patients : 1; - - /// Initializes all of the above type/values/holders data (but not the instance values themselves) - void allocate_layout(); - - /// Destroys/deallocates all of the above - void deallocate_layout(); - - /// Returns the value_and_holder wrapper for the given type (or the first, if `find_type` - /// omitted). Returns a default-constructed (with `.inst = nullptr`) object on failure if - /// `throw_if_missing` is false. - value_and_holder get_value_and_holder(const type_info *find_type = nullptr, bool throw_if_missing = true); - - /// Bit values for the non-simple status flags - static constexpr uint8_t status_holder_constructed = 1; - static constexpr uint8_t status_instance_registered = 2; -}; - -static_assert(std::is_standard_layout::value, "Internal error: `pybind11::detail::instance` is not standard layout!"); - -/// from __cpp_future__ import (convenient aliases from C++14/17) -#if defined(PYBIND11_CPP14) && (!defined(_MSC_VER) || _MSC_VER >= 1910) -using std::enable_if_t; -using std::conditional_t; -using std::remove_cv_t; -using std::remove_reference_t; -#else -template using enable_if_t = typename std::enable_if::type; -template using conditional_t = typename std::conditional::type; -template using remove_cv_t = typename std::remove_cv::type; -template using remove_reference_t = typename std::remove_reference::type; -#endif - -/// Index sequences -#if defined(PYBIND11_CPP14) -using std::index_sequence; -using std::make_index_sequence; -#else -template struct index_sequence { }; -template struct make_index_sequence_impl : make_index_sequence_impl { }; -template struct make_index_sequence_impl <0, S...> { typedef index_sequence type; }; -template using make_index_sequence = typename make_index_sequence_impl::type; -#endif - -/// Make an index sequence of the indices of true arguments -template struct select_indices_impl { using type = ISeq; }; -template struct select_indices_impl, I, B, Bs...> - : select_indices_impl, index_sequence>, I + 1, Bs...> {}; -template using select_indices = typename select_indices_impl, 0, Bs...>::type; - -/// Backports of std::bool_constant and std::negation to accommodate older compilers -template using bool_constant = std::integral_constant; -template struct negation : bool_constant { }; - -template struct void_t_impl { using type = void; }; -template using void_t = typename void_t_impl::type; - -/// Compile-time all/any/none of that check the boolean value of all template types -#if defined(__cpp_fold_expressions) && !(defined(_MSC_VER) && (_MSC_VER < 1916)) -template using all_of = bool_constant<(Ts::value && ...)>; -template using any_of = bool_constant<(Ts::value || ...)>; -#elif !defined(_MSC_VER) -template struct bools {}; -template using all_of = std::is_same< - bools, - bools>; -template using any_of = negation...>>; -#else -// MSVC has trouble with the above, but supports std::conjunction, which we can use instead (albeit -// at a slight loss of compilation efficiency). -template using all_of = std::conjunction; -template using any_of = std::disjunction; -#endif -template using none_of = negation>; - -template class... Predicates> using satisfies_all_of = all_of...>; -template class... Predicates> using satisfies_any_of = any_of...>; -template class... Predicates> using satisfies_none_of = none_of...>; - -/// Strip the class from a method type -template struct remove_class { }; -template struct remove_class { typedef R type(A...); }; -template struct remove_class { typedef R type(A...); }; - -/// Helper template to strip away type modifiers -template struct intrinsic_type { typedef T type; }; -template struct intrinsic_type { typedef typename intrinsic_type::type type; }; -template struct intrinsic_type { typedef typename intrinsic_type::type type; }; -template struct intrinsic_type { typedef typename intrinsic_type::type type; }; -template struct intrinsic_type { typedef typename intrinsic_type::type type; }; -template struct intrinsic_type { typedef typename intrinsic_type::type type; }; -template struct intrinsic_type { typedef typename intrinsic_type::type type; }; -template using intrinsic_t = typename intrinsic_type::type; - -/// Helper type to replace 'void' in some expressions -struct void_type { }; - -/// Helper template which holds a list of types -template struct type_list { }; - -/// Compile-time integer sum -#ifdef __cpp_fold_expressions -template constexpr size_t constexpr_sum(Ts... ns) { return (0 + ... + size_t{ns}); } -#else -constexpr size_t constexpr_sum() { return 0; } -template -constexpr size_t constexpr_sum(T n, Ts... ns) { return size_t{n} + constexpr_sum(ns...); } -#endif - -NAMESPACE_BEGIN(constexpr_impl) -/// Implementation details for constexpr functions -constexpr int first(int i) { return i; } -template -constexpr int first(int i, T v, Ts... vs) { return v ? i : first(i + 1, vs...); } - -constexpr int last(int /*i*/, int result) { return result; } -template -constexpr int last(int i, int result, T v, Ts... vs) { return last(i + 1, v ? i : result, vs...); } -NAMESPACE_END(constexpr_impl) - -/// Return the index of the first type in Ts which satisfies Predicate. Returns sizeof...(Ts) if -/// none match. -template class Predicate, typename... Ts> -constexpr int constexpr_first() { return constexpr_impl::first(0, Predicate::value...); } - -/// Return the index of the last type in Ts which satisfies Predicate, or -1 if none match. -template class Predicate, typename... Ts> -constexpr int constexpr_last() { return constexpr_impl::last(0, -1, Predicate::value...); } - -/// Return the Nth element from the parameter pack -template -struct pack_element { using type = typename pack_element::type; }; -template -struct pack_element<0, T, Ts...> { using type = T; }; - -/// Return the one and only type which matches the predicate, or Default if none match. -/// If more than one type matches the predicate, fail at compile-time. -template class Predicate, typename Default, typename... Ts> -struct exactly_one { - static constexpr auto found = constexpr_sum(Predicate::value...); - static_assert(found <= 1, "Found more than one type matching the predicate"); - - static constexpr auto index = found ? constexpr_first() : 0; - using type = conditional_t::type, Default>; -}; -template class P, typename Default> -struct exactly_one { using type = Default; }; - -template class Predicate, typename Default, typename... Ts> -using exactly_one_t = typename exactly_one::type; - -/// Defer the evaluation of type T until types Us are instantiated -template struct deferred_type { using type = T; }; -template using deferred_t = typename deferred_type::type; - -/// Like is_base_of, but requires a strict base (i.e. `is_strict_base_of::value == false`, -/// unlike `std::is_base_of`) -template using is_strict_base_of = bool_constant< - std::is_base_of::value && !std::is_same::value>; - -/// Like is_base_of, but also requires that the base type is accessible (i.e. that a Derived pointer -/// can be converted to a Base pointer) -template using is_accessible_base_of = bool_constant< - std::is_base_of::value && std::is_convertible::value>; - -template class Base> -struct is_template_base_of_impl { - template static std::true_type check(Base *); - static std::false_type check(...); -}; - -/// Check if a template is the base of a type. For example: -/// `is_template_base_of` is true if `struct T : Base {}` where U can be anything -template class Base, typename T> -#if !defined(_MSC_VER) -using is_template_base_of = decltype(is_template_base_of_impl::check((intrinsic_t*)nullptr)); -#else // MSVC2015 has trouble with decltype in template aliases -struct is_template_base_of : decltype(is_template_base_of_impl::check((intrinsic_t*)nullptr)) { }; -#endif - -/// Check if T is an instantiation of the template `Class`. For example: -/// `is_instantiation` is true if `T == shared_ptr` where U can be anything. -template class Class, typename T> -struct is_instantiation : std::false_type { }; -template class Class, typename... Us> -struct is_instantiation> : std::true_type { }; - -/// Check if T is std::shared_ptr where U can be anything -template using is_shared_ptr = is_instantiation; - -/// Check if T looks like an input iterator -template struct is_input_iterator : std::false_type {}; -template -struct is_input_iterator()), decltype(++std::declval())>> - : std::true_type {}; - -template using is_function_pointer = bool_constant< - std::is_pointer::value && std::is_function::type>::value>; - -template struct strip_function_object { - using type = typename remove_class::type; -}; - -// Extracts the function signature from a function, function pointer or lambda. -template > -using function_signature_t = conditional_t< - std::is_function::value, - F, - typename conditional_t< - std::is_pointer::value || std::is_member_pointer::value, - std::remove_pointer, - strip_function_object - >::type ->; - -/// Returns true if the type looks like a lambda: that is, isn't a function, pointer or member -/// pointer. Note that this can catch all sorts of other things, too; this is intended to be used -/// in a place where passing a lambda makes sense. -template using is_lambda = satisfies_none_of, - std::is_function, std::is_pointer, std::is_member_pointer>; - -/// Ignore that a variable is unused in compiler warnings -inline void ignore_unused(const int *) { } - -/// Apply a function over each element of a parameter pack -#ifdef __cpp_fold_expressions -#define PYBIND11_EXPAND_SIDE_EFFECTS(PATTERN) (((PATTERN), void()), ...) -#else -using expand_side_effects = bool[]; -#define PYBIND11_EXPAND_SIDE_EFFECTS(PATTERN) pybind11::detail::expand_side_effects{ ((PATTERN), void(), false)..., false } -#endif - -NAMESPACE_END(detail) - -/// C++ bindings of builtin Python exceptions -class builtin_exception : public std::runtime_error { -public: - using std::runtime_error::runtime_error; - /// Set the error using the Python C API - virtual void set_error() const = 0; -}; - -#define PYBIND11_RUNTIME_EXCEPTION(name, type) \ - class name : public builtin_exception { public: \ - using builtin_exception::builtin_exception; \ - name() : name("") { } \ - void set_error() const override { PyErr_SetString(type, what()); } \ - }; - -PYBIND11_RUNTIME_EXCEPTION(stop_iteration, PyExc_StopIteration) -PYBIND11_RUNTIME_EXCEPTION(index_error, PyExc_IndexError) -PYBIND11_RUNTIME_EXCEPTION(key_error, PyExc_KeyError) -PYBIND11_RUNTIME_EXCEPTION(value_error, PyExc_ValueError) -PYBIND11_RUNTIME_EXCEPTION(type_error, PyExc_TypeError) -PYBIND11_RUNTIME_EXCEPTION(cast_error, PyExc_RuntimeError) /// Thrown when pybind11::cast or handle::call fail due to a type casting error -PYBIND11_RUNTIME_EXCEPTION(reference_cast_error, PyExc_RuntimeError) /// Used internally - -[[noreturn]] PYBIND11_NOINLINE inline void pybind11_fail(const char *reason) { throw std::runtime_error(reason); } -[[noreturn]] PYBIND11_NOINLINE inline void pybind11_fail(const std::string &reason) { throw std::runtime_error(reason); } - -template struct format_descriptor { }; - -NAMESPACE_BEGIN(detail) -// Returns the index of the given type in the type char array below, and in the list in numpy.h -// The order here is: bool; 8 ints ((signed,unsigned)x(8,16,32,64)bits); float,double,long double; -// complex float,double,long double. Note that the long double types only participate when long -// double is actually longer than double (it isn't under MSVC). -// NB: not only the string below but also complex.h and numpy.h rely on this order. -template struct is_fmt_numeric { static constexpr bool value = false; }; -template struct is_fmt_numeric::value>> { - static constexpr bool value = true; - static constexpr int index = std::is_same::value ? 0 : 1 + ( - std::is_integral::value ? detail::log2(sizeof(T))*2 + std::is_unsigned::value : 8 + ( - std::is_same::value ? 1 : std::is_same::value ? 2 : 0)); -}; -NAMESPACE_END(detail) - -template struct format_descriptor::value>> { - static constexpr const char c = "?bBhHiIqQfdg"[detail::is_fmt_numeric::index]; - static constexpr const char value[2] = { c, '\0' }; - static std::string format() { return std::string(1, c); } -}; - -#if !defined(PYBIND11_CPP17) - -template constexpr const char format_descriptor< - T, detail::enable_if_t::value>>::value[2]; - -#endif - -/// RAII wrapper that temporarily clears any Python error state -struct error_scope { - PyObject *type, *value, *trace; - error_scope() { PyErr_Fetch(&type, &value, &trace); } - ~error_scope() { PyErr_Restore(type, value, trace); } -}; - -/// Dummy destructor wrapper that can be used to expose classes with a private destructor -struct nodelete { template void operator()(T*) { } }; - -// overload_cast requires variable templates: C++14 -#if defined(PYBIND11_CPP14) -#define PYBIND11_OVERLOAD_CAST 1 - -NAMESPACE_BEGIN(detail) -template -struct overload_cast_impl { - constexpr overload_cast_impl() {} // MSVC 2015 needs this - - template - constexpr auto operator()(Return (*pf)(Args...)) const noexcept - -> decltype(pf) { return pf; } - - template - constexpr auto operator()(Return (Class::*pmf)(Args...), std::false_type = {}) const noexcept - -> decltype(pmf) { return pmf; } - - template - constexpr auto operator()(Return (Class::*pmf)(Args...) const, std::true_type) const noexcept - -> decltype(pmf) { return pmf; } -}; -NAMESPACE_END(detail) - -/// Syntax sugar for resolving overloaded function pointers: -/// - regular: static_cast(&Class::func) -/// - sweet: overload_cast(&Class::func) -template -static constexpr detail::overload_cast_impl overload_cast = {}; -// MSVC 2015 only accepts this particular initialization syntax for this variable template. - -/// Const member function selector for overload_cast -/// - regular: static_cast(&Class::func) -/// - sweet: overload_cast(&Class::func, const_) -static constexpr auto const_ = std::true_type{}; - -#else // no overload_cast: providing something that static_assert-fails: -template struct overload_cast { - static_assert(detail::deferred_t::value, - "pybind11::overload_cast<...> requires compiling in C++14 mode"); -}; -#endif // overload_cast - -NAMESPACE_BEGIN(detail) - -// Adaptor for converting arbitrary container arguments into a vector; implicitly convertible from -// any standard container (or C-style array) supporting std::begin/std::end, any singleton -// arithmetic type (if T is arithmetic), or explicitly constructible from an iterator pair. -template -class any_container { - std::vector v; -public: - any_container() = default; - - // Can construct from a pair of iterators - template ::value>> - any_container(It first, It last) : v(first, last) { } - - // Implicit conversion constructor from any arbitrary container type with values convertible to T - template ())), T>::value>> - any_container(const Container &c) : any_container(std::begin(c), std::end(c)) { } - - // initializer_list's aren't deducible, so don't get matched by the above template; we need this - // to explicitly allow implicit conversion from one: - template ::value>> - any_container(const std::initializer_list &c) : any_container(c.begin(), c.end()) { } - - // Avoid copying if given an rvalue vector of the correct type. - any_container(std::vector &&v) : v(std::move(v)) { } - - // Moves the vector out of an rvalue any_container - operator std::vector &&() && { return std::move(v); } - - // Dereferencing obtains a reference to the underlying vector - std::vector &operator*() { return v; } - const std::vector &operator*() const { return v; } - - // -> lets you call methods on the underlying vector - std::vector *operator->() { return &v; } - const std::vector *operator->() const { return &v; } -}; - -NAMESPACE_END(detail) - - - -NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/ptocr/postprocess/piexlmerge/include/pybind11/detail/descr.h b/ptocr/postprocess/piexlmerge/include/pybind11/detail/descr.h deleted file mode 100644 index 8d404e5..0000000 --- a/ptocr/postprocess/piexlmerge/include/pybind11/detail/descr.h +++ /dev/null @@ -1,100 +0,0 @@ -/* - pybind11/detail/descr.h: Helper type for concatenating type signatures at compile time - - Copyright (c) 2016 Wenzel Jakob - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#include "common.h" - -NAMESPACE_BEGIN(PYBIND11_NAMESPACE) -NAMESPACE_BEGIN(detail) - -#if !defined(_MSC_VER) -# define PYBIND11_DESCR_CONSTEXPR static constexpr -#else -# define PYBIND11_DESCR_CONSTEXPR const -#endif - -/* Concatenate type signatures at compile time */ -template -struct descr { - char text[N + 1]; - - constexpr descr() : text{'\0'} { } - constexpr descr(char const (&s)[N+1]) : descr(s, make_index_sequence()) { } - - template - constexpr descr(char const (&s)[N+1], index_sequence) : text{s[Is]..., '\0'} { } - - template - constexpr descr(char c, Chars... cs) : text{c, static_cast(cs)..., '\0'} { } - - static constexpr std::array types() { - return {{&typeid(Ts)..., nullptr}}; - } -}; - -template -constexpr descr plus_impl(const descr &a, const descr &b, - index_sequence, index_sequence) { - return {a.text[Is1]..., b.text[Is2]...}; -} - -template -constexpr descr operator+(const descr &a, const descr &b) { - return plus_impl(a, b, make_index_sequence(), make_index_sequence()); -} - -template -constexpr descr _(char const(&text)[N]) { return descr(text); } -constexpr descr<0> _(char const(&)[1]) { return {}; } - -template struct int_to_str : int_to_str { }; -template struct int_to_str<0, Digits...> { - static constexpr auto digits = descr(('0' + Digits)...); -}; - -// Ternary description (like std::conditional) -template -constexpr enable_if_t> _(char const(&text1)[N1], char const(&)[N2]) { - return _(text1); -} -template -constexpr enable_if_t> _(char const(&)[N1], char const(&text2)[N2]) { - return _(text2); -} - -template -constexpr enable_if_t _(const T1 &d, const T2 &) { return d; } -template -constexpr enable_if_t _(const T1 &, const T2 &d) { return d; } - -template auto constexpr _() -> decltype(int_to_str::digits) { - return int_to_str::digits; -} - -template constexpr descr<1, Type> _() { return {'%'}; } - -constexpr descr<0> concat() { return {}; } - -template -constexpr descr concat(const descr &descr) { return descr; } - -template -constexpr auto concat(const descr &d, const Args &...args) - -> decltype(std::declval>() + concat(args...)) { - return d + _(", ") + concat(args...); -} - -template -constexpr descr type_descr(const descr &descr) { - return _("{") + descr + _("}"); -} - -NAMESPACE_END(detail) -NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/ptocr/postprocess/piexlmerge/include/pybind11/detail/init.h b/ptocr/postprocess/piexlmerge/include/pybind11/detail/init.h deleted file mode 100644 index acfe00b..0000000 --- a/ptocr/postprocess/piexlmerge/include/pybind11/detail/init.h +++ /dev/null @@ -1,335 +0,0 @@ -/* - pybind11/detail/init.h: init factory function implementation and support code. - - Copyright (c) 2017 Jason Rhinelander - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#include "class.h" - -NAMESPACE_BEGIN(PYBIND11_NAMESPACE) -NAMESPACE_BEGIN(detail) - -template <> -class type_caster { -public: - bool load(handle h, bool) { - value = reinterpret_cast(h.ptr()); - return true; - } - - template using cast_op_type = value_and_holder &; - operator value_and_holder &() { return *value; } - static constexpr auto name = _(); - -private: - value_and_holder *value = nullptr; -}; - -NAMESPACE_BEGIN(initimpl) - -inline void no_nullptr(void *ptr) { - if (!ptr) throw type_error("pybind11::init(): factory function returned nullptr"); -} - -// Implementing functions for all forms of py::init<...> and py::init(...) -template using Cpp = typename Class::type; -template using Alias = typename Class::type_alias; -template using Holder = typename Class::holder_type; - -template using is_alias_constructible = std::is_constructible, Cpp &&>; - -// Takes a Cpp pointer and returns true if it actually is a polymorphic Alias instance. -template = 0> -bool is_alias(Cpp *ptr) { - return dynamic_cast *>(ptr) != nullptr; -} -// Failing fallback version of the above for a no-alias class (always returns false) -template -constexpr bool is_alias(void *) { return false; } - -// Constructs and returns a new object; if the given arguments don't map to a constructor, we fall -// back to brace aggregate initiailization so that for aggregate initialization can be used with -// py::init, e.g. `py::init` to initialize a `struct T { int a; int b; }`. For -// non-aggregate types, we need to use an ordinary T(...) constructor (invoking as `T{...}` usually -// works, but will not do the expected thing when `T` has an `initializer_list` constructor). -template ::value, int> = 0> -inline Class *construct_or_initialize(Args &&...args) { return new Class(std::forward(args)...); } -template ::value, int> = 0> -inline Class *construct_or_initialize(Args &&...args) { return new Class{std::forward(args)...}; } - -// Attempts to constructs an alias using a `Alias(Cpp &&)` constructor. This allows types with -// an alias to provide only a single Cpp factory function as long as the Alias can be -// constructed from an rvalue reference of the base Cpp type. This means that Alias classes -// can, when appropriate, simply define a `Alias(Cpp &&)` constructor rather than needing to -// inherit all the base class constructors. -template -void construct_alias_from_cpp(std::true_type /*is_alias_constructible*/, - value_and_holder &v_h, Cpp &&base) { - v_h.value_ptr() = new Alias(std::move(base)); -} -template -[[noreturn]] void construct_alias_from_cpp(std::false_type /*!is_alias_constructible*/, - value_and_holder &, Cpp &&) { - throw type_error("pybind11::init(): unable to convert returned instance to required " - "alias class: no `Alias(Class &&)` constructor available"); -} - -// Error-generating fallback for factories that don't match one of the below construction -// mechanisms. -template -void construct(...) { - static_assert(!std::is_same::value /* always false */, - "pybind11::init(): init function must return a compatible pointer, " - "holder, or value"); -} - -// Pointer return v1: the factory function returns a class pointer for a registered class. -// If we don't need an alias (because this class doesn't have one, or because the final type is -// inherited on the Python side) we can simply take over ownership. Otherwise we need to try to -// construct an Alias from the returned base instance. -template -void construct(value_and_holder &v_h, Cpp *ptr, bool need_alias) { - no_nullptr(ptr); - if (Class::has_alias && need_alias && !is_alias(ptr)) { - // We're going to try to construct an alias by moving the cpp type. Whether or not - // that succeeds, we still need to destroy the original cpp pointer (either the - // moved away leftover, if the alias construction works, or the value itself if we - // throw an error), but we can't just call `delete ptr`: it might have a special - // deleter, or might be shared_from_this. So we construct a holder around it as if - // it was a normal instance, then steal the holder away into a local variable; thus - // the holder and destruction happens when we leave the C++ scope, and the holder - // class gets to handle the destruction however it likes. - v_h.value_ptr() = ptr; - v_h.set_instance_registered(true); // To prevent init_instance from registering it - v_h.type->init_instance(v_h.inst, nullptr); // Set up the holder - Holder temp_holder(std::move(v_h.holder>())); // Steal the holder - v_h.type->dealloc(v_h); // Destroys the moved-out holder remains, resets value ptr to null - v_h.set_instance_registered(false); - - construct_alias_from_cpp(is_alias_constructible{}, v_h, std::move(*ptr)); - } else { - // Otherwise the type isn't inherited, so we don't need an Alias - v_h.value_ptr() = ptr; - } -} - -// Pointer return v2: a factory that always returns an alias instance ptr. We simply take over -// ownership of the pointer. -template = 0> -void construct(value_and_holder &v_h, Alias *alias_ptr, bool) { - no_nullptr(alias_ptr); - v_h.value_ptr() = static_cast *>(alias_ptr); -} - -// Holder return: copy its pointer, and move or copy the returned holder into the new instance's -// holder. This also handles types like std::shared_ptr and std::unique_ptr where T is a -// derived type (through those holder's implicit conversion from derived class holder constructors). -template -void construct(value_and_holder &v_h, Holder holder, bool need_alias) { - auto *ptr = holder_helper>::get(holder); - // If we need an alias, check that the held pointer is actually an alias instance - if (Class::has_alias && need_alias && !is_alias(ptr)) - throw type_error("pybind11::init(): construction failed: returned holder-wrapped instance " - "is not an alias instance"); - - v_h.value_ptr() = ptr; - v_h.type->init_instance(v_h.inst, &holder); -} - -// return-by-value version 1: returning a cpp class by value. If the class has an alias and an -// alias is required the alias must have an `Alias(Cpp &&)` constructor so that we can construct -// the alias from the base when needed (i.e. because of Python-side inheritance). When we don't -// need it, we simply move-construct the cpp value into a new instance. -template -void construct(value_and_holder &v_h, Cpp &&result, bool need_alias) { - static_assert(std::is_move_constructible>::value, - "pybind11::init() return-by-value factory function requires a movable class"); - if (Class::has_alias && need_alias) - construct_alias_from_cpp(is_alias_constructible{}, v_h, std::move(result)); - else - v_h.value_ptr() = new Cpp(std::move(result)); -} - -// return-by-value version 2: returning a value of the alias type itself. We move-construct an -// Alias instance (even if no the python-side inheritance is involved). The is intended for -// cases where Alias initialization is always desired. -template -void construct(value_and_holder &v_h, Alias &&result, bool) { - static_assert(std::is_move_constructible>::value, - "pybind11::init() return-by-alias-value factory function requires a movable alias class"); - v_h.value_ptr() = new Alias(std::move(result)); -} - -// Implementing class for py::init<...>() -template -struct constructor { - template = 0> - static void execute(Class &cl, const Extra&... extra) { - cl.def("__init__", [](value_and_holder &v_h, Args... args) { - v_h.value_ptr() = construct_or_initialize>(std::forward(args)...); - }, is_new_style_constructor(), extra...); - } - - template , Args...>::value, int> = 0> - static void execute(Class &cl, const Extra&... extra) { - cl.def("__init__", [](value_and_holder &v_h, Args... args) { - if (Py_TYPE(v_h.inst) == v_h.type->type) - v_h.value_ptr() = construct_or_initialize>(std::forward(args)...); - else - v_h.value_ptr() = construct_or_initialize>(std::forward(args)...); - }, is_new_style_constructor(), extra...); - } - - template , Args...>::value, int> = 0> - static void execute(Class &cl, const Extra&... extra) { - cl.def("__init__", [](value_and_holder &v_h, Args... args) { - v_h.value_ptr() = construct_or_initialize>(std::forward(args)...); - }, is_new_style_constructor(), extra...); - } -}; - -// Implementing class for py::init_alias<...>() -template struct alias_constructor { - template , Args...>::value, int> = 0> - static void execute(Class &cl, const Extra&... extra) { - cl.def("__init__", [](value_and_holder &v_h, Args... args) { - v_h.value_ptr() = construct_or_initialize>(std::forward(args)...); - }, is_new_style_constructor(), extra...); - } -}; - -// Implementation class for py::init(Func) and py::init(Func, AliasFunc) -template , typename = function_signature_t> -struct factory; - -// Specialization for py::init(Func) -template -struct factory { - remove_reference_t class_factory; - - factory(Func &&f) : class_factory(std::forward(f)) { } - - // The given class either has no alias or has no separate alias factory; - // this always constructs the class itself. If the class is registered with an alias - // type and an alias instance is needed (i.e. because the final type is a Python class - // inheriting from the C++ type) the returned value needs to either already be an alias - // instance, or the alias needs to be constructible from a `Class &&` argument. - template - void execute(Class &cl, const Extra &...extra) && { - #if defined(PYBIND11_CPP14) - cl.def("__init__", [func = std::move(class_factory)] - #else - auto &func = class_factory; - cl.def("__init__", [func] - #endif - (value_and_holder &v_h, Args... args) { - construct(v_h, func(std::forward(args)...), - Py_TYPE(v_h.inst) != v_h.type->type); - }, is_new_style_constructor(), extra...); - } -}; - -// Specialization for py::init(Func, AliasFunc) -template -struct factory { - static_assert(sizeof...(CArgs) == sizeof...(AArgs), - "pybind11::init(class_factory, alias_factory): class and alias factories " - "must have identical argument signatures"); - static_assert(all_of...>::value, - "pybind11::init(class_factory, alias_factory): class and alias factories " - "must have identical argument signatures"); - - remove_reference_t class_factory; - remove_reference_t alias_factory; - - factory(CFunc &&c, AFunc &&a) - : class_factory(std::forward(c)), alias_factory(std::forward(a)) { } - - // The class factory is called when the `self` type passed to `__init__` is the direct - // class (i.e. not inherited), the alias factory when `self` is a Python-side subtype. - template - void execute(Class &cl, const Extra&... extra) && { - static_assert(Class::has_alias, "The two-argument version of `py::init()` can " - "only be used if the class has an alias"); - #if defined(PYBIND11_CPP14) - cl.def("__init__", [class_func = std::move(class_factory), alias_func = std::move(alias_factory)] - #else - auto &class_func = class_factory; - auto &alias_func = alias_factory; - cl.def("__init__", [class_func, alias_func] - #endif - (value_and_holder &v_h, CArgs... args) { - if (Py_TYPE(v_h.inst) == v_h.type->type) - // If the instance type equals the registered type we don't have inheritance, so - // don't need the alias and can construct using the class function: - construct(v_h, class_func(std::forward(args)...), false); - else - construct(v_h, alias_func(std::forward(args)...), true); - }, is_new_style_constructor(), extra...); - } -}; - -/// Set just the C++ state. Same as `__init__`. -template -void setstate(value_and_holder &v_h, T &&result, bool need_alias) { - construct(v_h, std::forward(result), need_alias); -} - -/// Set both the C++ and Python states -template ::value, int> = 0> -void setstate(value_and_holder &v_h, std::pair &&result, bool need_alias) { - construct(v_h, std::move(result.first), need_alias); - setattr((PyObject *) v_h.inst, "__dict__", result.second); -} - -/// Implementation for py::pickle(GetState, SetState) -template , typename = function_signature_t> -struct pickle_factory; - -template -struct pickle_factory { - static_assert(std::is_same, intrinsic_t>::value, - "The type returned by `__getstate__` must be the same " - "as the argument accepted by `__setstate__`"); - - remove_reference_t get; - remove_reference_t set; - - pickle_factory(Get get, Set set) - : get(std::forward(get)), set(std::forward(set)) { } - - template - void execute(Class &cl, const Extra &...extra) && { - cl.def("__getstate__", std::move(get)); - -#if defined(PYBIND11_CPP14) - cl.def("__setstate__", [func = std::move(set)] -#else - auto &func = set; - cl.def("__setstate__", [func] -#endif - (value_and_holder &v_h, ArgState state) { - setstate(v_h, func(std::forward(state)), - Py_TYPE(v_h.inst) != v_h.type->type); - }, is_new_style_constructor(), extra...); - } -}; - -NAMESPACE_END(initimpl) -NAMESPACE_END(detail) -NAMESPACE_END(pybind11) diff --git a/ptocr/postprocess/piexlmerge/include/pybind11/detail/internals.h b/ptocr/postprocess/piexlmerge/include/pybind11/detail/internals.h deleted file mode 100644 index 6d7dc5c..0000000 --- a/ptocr/postprocess/piexlmerge/include/pybind11/detail/internals.h +++ /dev/null @@ -1,291 +0,0 @@ -/* - pybind11/detail/internals.h: Internal data structure and related functions - - Copyright (c) 2017 Wenzel Jakob - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#include "../pytypes.h" - -NAMESPACE_BEGIN(PYBIND11_NAMESPACE) -NAMESPACE_BEGIN(detail) -// Forward declarations -inline PyTypeObject *make_static_property_type(); -inline PyTypeObject *make_default_metaclass(); -inline PyObject *make_object_base_type(PyTypeObject *metaclass); - -// The old Python Thread Local Storage (TLS) API is deprecated in Python 3.7 in favor of the new -// Thread Specific Storage (TSS) API. -#if PY_VERSION_HEX >= 0x03070000 -# define PYBIND11_TLS_KEY_INIT(var) Py_tss_t *var = nullptr -# define PYBIND11_TLS_GET_VALUE(key) PyThread_tss_get((key)) -# define PYBIND11_TLS_REPLACE_VALUE(key, value) PyThread_tss_set((key), (tstate)) -# define PYBIND11_TLS_DELETE_VALUE(key) PyThread_tss_set((key), nullptr) -#else - // Usually an int but a long on Cygwin64 with Python 3.x -# define PYBIND11_TLS_KEY_INIT(var) decltype(PyThread_create_key()) var = 0 -# define PYBIND11_TLS_GET_VALUE(key) PyThread_get_key_value((key)) -# if PY_MAJOR_VERSION < 3 -# define PYBIND11_TLS_DELETE_VALUE(key) \ - PyThread_delete_key_value(key) -# define PYBIND11_TLS_REPLACE_VALUE(key, value) \ - do { \ - PyThread_delete_key_value((key)); \ - PyThread_set_key_value((key), (value)); \ - } while (false) -# else -# define PYBIND11_TLS_DELETE_VALUE(key) \ - PyThread_set_key_value((key), nullptr) -# define PYBIND11_TLS_REPLACE_VALUE(key, value) \ - PyThread_set_key_value((key), (value)) -# endif -#endif - -// Python loads modules by default with dlopen with the RTLD_LOCAL flag; under libc++ and possibly -// other STLs, this means `typeid(A)` from one module won't equal `typeid(A)` from another module -// even when `A` is the same, non-hidden-visibility type (e.g. from a common include). Under -// libstdc++, this doesn't happen: equality and the type_index hash are based on the type name, -// which works. If not under a known-good stl, provide our own name-based hash and equality -// functions that use the type name. -#if defined(__GLIBCXX__) -inline bool same_type(const std::type_info &lhs, const std::type_info &rhs) { return lhs == rhs; } -using type_hash = std::hash; -using type_equal_to = std::equal_to; -#else -inline bool same_type(const std::type_info &lhs, const std::type_info &rhs) { - return lhs.name() == rhs.name() || std::strcmp(lhs.name(), rhs.name()) == 0; -} - -struct type_hash { - size_t operator()(const std::type_index &t) const { - size_t hash = 5381; - const char *ptr = t.name(); - while (auto c = static_cast(*ptr++)) - hash = (hash * 33) ^ c; - return hash; - } -}; - -struct type_equal_to { - bool operator()(const std::type_index &lhs, const std::type_index &rhs) const { - return lhs.name() == rhs.name() || std::strcmp(lhs.name(), rhs.name()) == 0; - } -}; -#endif - -template -using type_map = std::unordered_map; - -struct overload_hash { - inline size_t operator()(const std::pair& v) const { - size_t value = std::hash()(v.first); - value ^= std::hash()(v.second) + 0x9e3779b9 + (value<<6) + (value>>2); - return value; - } -}; - -/// Internal data structure used to track registered instances and types. -/// Whenever binary incompatible changes are made to this structure, -/// `PYBIND11_INTERNALS_VERSION` must be incremented. -struct internals { - type_map registered_types_cpp; // std::type_index -> pybind11's type information - std::unordered_map> registered_types_py; // PyTypeObject* -> base type_info(s) - std::unordered_multimap registered_instances; // void * -> instance* - std::unordered_set, overload_hash> inactive_overload_cache; - type_map> direct_conversions; - std::unordered_map> patients; - std::forward_list registered_exception_translators; - std::unordered_map shared_data; // Custom data to be shared across extensions - std::vector loader_patient_stack; // Used by `loader_life_support` - std::forward_list static_strings; // Stores the std::strings backing detail::c_str() - PyTypeObject *static_property_type; - PyTypeObject *default_metaclass; - PyObject *instance_base; -#if defined(WITH_THREAD) - PYBIND11_TLS_KEY_INIT(tstate); - PyInterpreterState *istate = nullptr; -#endif -}; - -/// Additional type information which does not fit into the PyTypeObject. -/// Changes to this struct also require bumping `PYBIND11_INTERNALS_VERSION`. -struct type_info { - PyTypeObject *type; - const std::type_info *cpptype; - size_t type_size, type_align, holder_size_in_ptrs; - void *(*operator_new)(size_t); - void (*init_instance)(instance *, const void *); - void (*dealloc)(value_and_holder &v_h); - std::vector implicit_conversions; - std::vector> implicit_casts; - std::vector *direct_conversions; - buffer_info *(*get_buffer)(PyObject *, void *) = nullptr; - void *get_buffer_data = nullptr; - void *(*module_local_load)(PyObject *, const type_info *) = nullptr; - /* A simple type never occurs as a (direct or indirect) parent - * of a class that makes use of multiple inheritance */ - bool simple_type : 1; - /* True if there is no multiple inheritance in this type's inheritance tree */ - bool simple_ancestors : 1; - /* for base vs derived holder_type checks */ - bool default_holder : 1; - /* true if this is a type registered with py::module_local */ - bool module_local : 1; -}; - -/// Tracks the `internals` and `type_info` ABI version independent of the main library version -#define PYBIND11_INTERNALS_VERSION 3 - -#if defined(_DEBUG) -# define PYBIND11_BUILD_TYPE "_debug" -#else -# define PYBIND11_BUILD_TYPE "" -#endif - -#if defined(WITH_THREAD) -# define PYBIND11_INTERNALS_KIND "" -#else -# define PYBIND11_INTERNALS_KIND "_without_thread" -#endif - -#define PYBIND11_INTERNALS_ID "__pybind11_internals_v" \ - PYBIND11_TOSTRING(PYBIND11_INTERNALS_VERSION) PYBIND11_INTERNALS_KIND PYBIND11_BUILD_TYPE "__" - -#define PYBIND11_MODULE_LOCAL_ID "__pybind11_module_local_v" \ - PYBIND11_TOSTRING(PYBIND11_INTERNALS_VERSION) PYBIND11_INTERNALS_KIND PYBIND11_BUILD_TYPE "__" - -/// Each module locally stores a pointer to the `internals` data. The data -/// itself is shared among modules with the same `PYBIND11_INTERNALS_ID`. -inline internals **&get_internals_pp() { - static internals **internals_pp = nullptr; - return internals_pp; -} - -/// Return a reference to the current `internals` data -PYBIND11_NOINLINE inline internals &get_internals() { - auto **&internals_pp = get_internals_pp(); - if (internals_pp && *internals_pp) - return **internals_pp; - - constexpr auto *id = PYBIND11_INTERNALS_ID; - auto builtins = handle(PyEval_GetBuiltins()); - if (builtins.contains(id) && isinstance(builtins[id])) { - internals_pp = static_cast(capsule(builtins[id])); - - // We loaded builtins through python's builtins, which means that our `error_already_set` - // and `builtin_exception` may be different local classes than the ones set up in the - // initial exception translator, below, so add another for our local exception classes. - // - // libstdc++ doesn't require this (types there are identified only by name) -#if !defined(__GLIBCXX__) - (*internals_pp)->registered_exception_translators.push_front( - [](std::exception_ptr p) -> void { - try { - if (p) std::rethrow_exception(p); - } catch (error_already_set &e) { e.restore(); return; - } catch (const builtin_exception &e) { e.set_error(); return; - } - } - ); -#endif - } else { - if (!internals_pp) internals_pp = new internals*(); - auto *&internals_ptr = *internals_pp; - internals_ptr = new internals(); -#if defined(WITH_THREAD) - PyEval_InitThreads(); - PyThreadState *tstate = PyThreadState_Get(); - #if PY_VERSION_HEX >= 0x03070000 - internals_ptr->tstate = PyThread_tss_alloc(); - if (!internals_ptr->tstate || PyThread_tss_create(internals_ptr->tstate)) - pybind11_fail("get_internals: could not successfully initialize the TSS key!"); - PyThread_tss_set(internals_ptr->tstate, tstate); - #else - internals_ptr->tstate = PyThread_create_key(); - if (internals_ptr->tstate == -1) - pybind11_fail("get_internals: could not successfully initialize the TLS key!"); - PyThread_set_key_value(internals_ptr->tstate, tstate); - #endif - internals_ptr->istate = tstate->interp; -#endif - builtins[id] = capsule(internals_pp); - internals_ptr->registered_exception_translators.push_front( - [](std::exception_ptr p) -> void { - try { - if (p) std::rethrow_exception(p); - } catch (error_already_set &e) { e.restore(); return; - } catch (const builtin_exception &e) { e.set_error(); return; - } catch (const std::bad_alloc &e) { PyErr_SetString(PyExc_MemoryError, e.what()); return; - } catch (const std::domain_error &e) { PyErr_SetString(PyExc_ValueError, e.what()); return; - } catch (const std::invalid_argument &e) { PyErr_SetString(PyExc_ValueError, e.what()); return; - } catch (const std::length_error &e) { PyErr_SetString(PyExc_ValueError, e.what()); return; - } catch (const std::out_of_range &e) { PyErr_SetString(PyExc_IndexError, e.what()); return; - } catch (const std::range_error &e) { PyErr_SetString(PyExc_ValueError, e.what()); return; - } catch (const std::exception &e) { PyErr_SetString(PyExc_RuntimeError, e.what()); return; - } catch (...) { - PyErr_SetString(PyExc_RuntimeError, "Caught an unknown exception!"); - return; - } - } - ); - internals_ptr->static_property_type = make_static_property_type(); - internals_ptr->default_metaclass = make_default_metaclass(); - internals_ptr->instance_base = make_object_base_type(internals_ptr->default_metaclass); - } - return **internals_pp; -} - -/// Works like `internals.registered_types_cpp`, but for module-local registered types: -inline type_map ®istered_local_types_cpp() { - static type_map locals{}; - return locals; -} - -/// Constructs a std::string with the given arguments, stores it in `internals`, and returns its -/// `c_str()`. Such strings objects have a long storage duration -- the internal strings are only -/// cleared when the program exits or after interpreter shutdown (when embedding), and so are -/// suitable for c-style strings needed by Python internals (such as PyTypeObject's tp_name). -template -const char *c_str(Args &&...args) { - auto &strings = get_internals().static_strings; - strings.emplace_front(std::forward(args)...); - return strings.front().c_str(); -} - -NAMESPACE_END(detail) - -/// Returns a named pointer that is shared among all extension modules (using the same -/// pybind11 version) running in the current interpreter. Names starting with underscores -/// are reserved for internal usage. Returns `nullptr` if no matching entry was found. -inline PYBIND11_NOINLINE void *get_shared_data(const std::string &name) { - auto &internals = detail::get_internals(); - auto it = internals.shared_data.find(name); - return it != internals.shared_data.end() ? it->second : nullptr; -} - -/// Set the shared data that can be later recovered by `get_shared_data()`. -inline PYBIND11_NOINLINE void *set_shared_data(const std::string &name, void *data) { - detail::get_internals().shared_data[name] = data; - return data; -} - -/// Returns a typed reference to a shared data entry (by using `get_shared_data()`) if -/// such entry exists. Otherwise, a new object of default-constructible type `T` is -/// added to the shared data under the given name and a reference to it is returned. -template -T &get_or_create_shared_data(const std::string &name) { - auto &internals = detail::get_internals(); - auto it = internals.shared_data.find(name); - T *ptr = (T *) (it != internals.shared_data.end() ? it->second : nullptr); - if (!ptr) { - ptr = new T(); - internals.shared_data[name] = ptr; - } - return *ptr; -} - -NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/ptocr/postprocess/piexlmerge/include/pybind11/detail/typeid.h b/ptocr/postprocess/piexlmerge/include/pybind11/detail/typeid.h deleted file mode 100644 index 6f36aab..0000000 --- a/ptocr/postprocess/piexlmerge/include/pybind11/detail/typeid.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - pybind11/detail/typeid.h: Compiler-independent access to type identifiers - - Copyright (c) 2016 Wenzel Jakob - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#include -#include - -#if defined(__GNUG__) -#include -#endif - -NAMESPACE_BEGIN(PYBIND11_NAMESPACE) -NAMESPACE_BEGIN(detail) -/// Erase all occurrences of a substring -inline void erase_all(std::string &string, const std::string &search) { - for (size_t pos = 0;;) { - pos = string.find(search, pos); - if (pos == std::string::npos) break; - string.erase(pos, search.length()); - } -} - -PYBIND11_NOINLINE inline void clean_type_id(std::string &name) { -#if defined(__GNUG__) - int status = 0; - std::unique_ptr res { - abi::__cxa_demangle(name.c_str(), nullptr, nullptr, &status), std::free }; - if (status == 0) - name = res.get(); -#else - detail::erase_all(name, "class "); - detail::erase_all(name, "struct "); - detail::erase_all(name, "enum "); -#endif - detail::erase_all(name, "pybind11::"); -} -NAMESPACE_END(detail) - -/// Return a string representation of a C++ type -template static std::string type_id() { - std::string name(typeid(T).name()); - detail::clean_type_id(name); - return name; -} - -NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/ptocr/postprocess/piexlmerge/include/pybind11/eigen.h b/ptocr/postprocess/piexlmerge/include/pybind11/eigen.h deleted file mode 100644 index d963d96..0000000 --- a/ptocr/postprocess/piexlmerge/include/pybind11/eigen.h +++ /dev/null @@ -1,607 +0,0 @@ -/* - pybind11/eigen.h: Transparent conversion for dense and sparse Eigen matrices - - Copyright (c) 2016 Wenzel Jakob - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#include "numpy.h" - -#if defined(__INTEL_COMPILER) -# pragma warning(disable: 1682) // implicit conversion of a 64-bit integral type to a smaller integral type (potential portability problem) -#elif defined(__GNUG__) || defined(__clang__) -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wconversion" -# pragma GCC diagnostic ignored "-Wdeprecated-declarations" -# ifdef __clang__ -// Eigen generates a bunch of implicit-copy-constructor-is-deprecated warnings with -Wdeprecated -// under Clang, so disable that warning here: -# pragma GCC diagnostic ignored "-Wdeprecated" -# endif -# if __GNUC__ >= 7 -# pragma GCC diagnostic ignored "-Wint-in-bool-context" -# endif -#endif - -#if defined(_MSC_VER) -# pragma warning(push) -# pragma warning(disable: 4127) // warning C4127: Conditional expression is constant -# pragma warning(disable: 4996) // warning C4996: std::unary_negate is deprecated in C++17 -#endif - -#include -#include - -// Eigen prior to 3.2.7 doesn't have proper move constructors--but worse, some classes get implicit -// move constructors that break things. We could detect this an explicitly copy, but an extra copy -// of matrices seems highly undesirable. -static_assert(EIGEN_VERSION_AT_LEAST(3,2,7), "Eigen support in pybind11 requires Eigen >= 3.2.7"); - -NAMESPACE_BEGIN(PYBIND11_NAMESPACE) - -// Provide a convenience alias for easier pass-by-ref usage with fully dynamic strides: -using EigenDStride = Eigen::Stride; -template using EigenDRef = Eigen::Ref; -template using EigenDMap = Eigen::Map; - -NAMESPACE_BEGIN(detail) - -#if EIGEN_VERSION_AT_LEAST(3,3,0) -using EigenIndex = Eigen::Index; -#else -using EigenIndex = EIGEN_DEFAULT_DENSE_INDEX_TYPE; -#endif - -// Matches Eigen::Map, Eigen::Ref, blocks, etc: -template using is_eigen_dense_map = all_of, std::is_base_of, T>>; -template using is_eigen_mutable_map = std::is_base_of, T>; -template using is_eigen_dense_plain = all_of>, is_template_base_of>; -template using is_eigen_sparse = is_template_base_of; -// Test for objects inheriting from EigenBase that aren't captured by the above. This -// basically covers anything that can be assigned to a dense matrix but that don't have a typical -// matrix data layout that can be copied from their .data(). For example, DiagonalMatrix and -// SelfAdjointView fall into this category. -template using is_eigen_other = all_of< - is_template_base_of, - negation, is_eigen_dense_plain, is_eigen_sparse>> ->; - -// Captures numpy/eigen conformability status (returned by EigenProps::conformable()): -template struct EigenConformable { - bool conformable = false; - EigenIndex rows = 0, cols = 0; - EigenDStride stride{0, 0}; // Only valid if negativestrides is false! - bool negativestrides = false; // If true, do not use stride! - - EigenConformable(bool fits = false) : conformable{fits} {} - // Matrix type: - EigenConformable(EigenIndex r, EigenIndex c, - EigenIndex rstride, EigenIndex cstride) : - conformable{true}, rows{r}, cols{c} { - // TODO: when Eigen bug #747 is fixed, remove the tests for non-negativity. http://eigen.tuxfamily.org/bz/show_bug.cgi?id=747 - if (rstride < 0 || cstride < 0) { - negativestrides = true; - } else { - stride = {EigenRowMajor ? rstride : cstride /* outer stride */, - EigenRowMajor ? cstride : rstride /* inner stride */ }; - } - } - // Vector type: - EigenConformable(EigenIndex r, EigenIndex c, EigenIndex stride) - : EigenConformable(r, c, r == 1 ? c*stride : stride, c == 1 ? r : r*stride) {} - - template bool stride_compatible() const { - // To have compatible strides, we need (on both dimensions) one of fully dynamic strides, - // matching strides, or a dimension size of 1 (in which case the stride value is irrelevant) - return - !negativestrides && - (props::inner_stride == Eigen::Dynamic || props::inner_stride == stride.inner() || - (EigenRowMajor ? cols : rows) == 1) && - (props::outer_stride == Eigen::Dynamic || props::outer_stride == stride.outer() || - (EigenRowMajor ? rows : cols) == 1); - } - operator bool() const { return conformable; } -}; - -template struct eigen_extract_stride { using type = Type; }; -template -struct eigen_extract_stride> { using type = StrideType; }; -template -struct eigen_extract_stride> { using type = StrideType; }; - -// Helper struct for extracting information from an Eigen type -template struct EigenProps { - using Type = Type_; - using Scalar = typename Type::Scalar; - using StrideType = typename eigen_extract_stride::type; - static constexpr EigenIndex - rows = Type::RowsAtCompileTime, - cols = Type::ColsAtCompileTime, - size = Type::SizeAtCompileTime; - static constexpr bool - row_major = Type::IsRowMajor, - vector = Type::IsVectorAtCompileTime, // At least one dimension has fixed size 1 - fixed_rows = rows != Eigen::Dynamic, - fixed_cols = cols != Eigen::Dynamic, - fixed = size != Eigen::Dynamic, // Fully-fixed size - dynamic = !fixed_rows && !fixed_cols; // Fully-dynamic size - - template using if_zero = std::integral_constant; - static constexpr EigenIndex inner_stride = if_zero::value, - outer_stride = if_zero::value; - static constexpr bool dynamic_stride = inner_stride == Eigen::Dynamic && outer_stride == Eigen::Dynamic; - static constexpr bool requires_row_major = !dynamic_stride && !vector && (row_major ? inner_stride : outer_stride) == 1; - static constexpr bool requires_col_major = !dynamic_stride && !vector && (row_major ? outer_stride : inner_stride) == 1; - - // Takes an input array and determines whether we can make it fit into the Eigen type. If - // the array is a vector, we attempt to fit it into either an Eigen 1xN or Nx1 vector - // (preferring the latter if it will fit in either, i.e. for a fully dynamic matrix type). - static EigenConformable conformable(const array &a) { - const auto dims = a.ndim(); - if (dims < 1 || dims > 2) - return false; - - if (dims == 2) { // Matrix type: require exact match (or dynamic) - - EigenIndex - np_rows = a.shape(0), - np_cols = a.shape(1), - np_rstride = a.strides(0) / static_cast(sizeof(Scalar)), - np_cstride = a.strides(1) / static_cast(sizeof(Scalar)); - if ((fixed_rows && np_rows != rows) || (fixed_cols && np_cols != cols)) - return false; - - return {np_rows, np_cols, np_rstride, np_cstride}; - } - - // Otherwise we're storing an n-vector. Only one of the strides will be used, but whichever - // is used, we want the (single) numpy stride value. - const EigenIndex n = a.shape(0), - stride = a.strides(0) / static_cast(sizeof(Scalar)); - - if (vector) { // Eigen type is a compile-time vector - if (fixed && size != n) - return false; // Vector size mismatch - return {rows == 1 ? 1 : n, cols == 1 ? 1 : n, stride}; - } - else if (fixed) { - // The type has a fixed size, but is not a vector: abort - return false; - } - else if (fixed_cols) { - // Since this isn't a vector, cols must be != 1. We allow this only if it exactly - // equals the number of elements (rows is Dynamic, and so 1 row is allowed). - if (cols != n) return false; - return {1, n, stride}; - } - else { - // Otherwise it's either fully dynamic, or column dynamic; both become a column vector - if (fixed_rows && rows != n) return false; - return {n, 1, stride}; - } - } - - static constexpr bool show_writeable = is_eigen_dense_map::value && is_eigen_mutable_map::value; - static constexpr bool show_order = is_eigen_dense_map::value; - static constexpr bool show_c_contiguous = show_order && requires_row_major; - static constexpr bool show_f_contiguous = !show_c_contiguous && show_order && requires_col_major; - - static constexpr auto descriptor = - _("numpy.ndarray[") + npy_format_descriptor::name + - _("[") + _(_<(size_t) rows>(), _("m")) + - _(", ") + _(_<(size_t) cols>(), _("n")) + - _("]") + - // For a reference type (e.g. Ref) we have other constraints that might need to be - // satisfied: writeable=True (for a mutable reference), and, depending on the map's stride - // options, possibly f_contiguous or c_contiguous. We include them in the descriptor output - // to provide some hint as to why a TypeError is occurring (otherwise it can be confusing to - // see that a function accepts a 'numpy.ndarray[float64[3,2]]' and an error message that you - // *gave* a numpy.ndarray of the right type and dimensions. - _(", flags.writeable", "") + - _(", flags.c_contiguous", "") + - _(", flags.f_contiguous", "") + - _("]"); -}; - -// Casts an Eigen type to numpy array. If given a base, the numpy array references the src data, -// otherwise it'll make a copy. writeable lets you turn off the writeable flag for the array. -template handle eigen_array_cast(typename props::Type const &src, handle base = handle(), bool writeable = true) { - constexpr ssize_t elem_size = sizeof(typename props::Scalar); - array a; - if (props::vector) - a = array({ src.size() }, { elem_size * src.innerStride() }, src.data(), base); - else - a = array({ src.rows(), src.cols() }, { elem_size * src.rowStride(), elem_size * src.colStride() }, - src.data(), base); - - if (!writeable) - array_proxy(a.ptr())->flags &= ~detail::npy_api::NPY_ARRAY_WRITEABLE_; - - return a.release(); -} - -// Takes an lvalue ref to some Eigen type and a (python) base object, creating a numpy array that -// reference the Eigen object's data with `base` as the python-registered base class (if omitted, -// the base will be set to None, and lifetime management is up to the caller). The numpy array is -// non-writeable if the given type is const. -template -handle eigen_ref_array(Type &src, handle parent = none()) { - // none here is to get past array's should-we-copy detection, which currently always - // copies when there is no base. Setting the base to None should be harmless. - return eigen_array_cast(src, parent, !std::is_const::value); -} - -// Takes a pointer to some dense, plain Eigen type, builds a capsule around it, then returns a numpy -// array that references the encapsulated data with a python-side reference to the capsule to tie -// its destruction to that of any dependent python objects. Const-ness is determined by whether or -// not the Type of the pointer given is const. -template ::value>> -handle eigen_encapsulate(Type *src) { - capsule base(src, [](void *o) { delete static_cast(o); }); - return eigen_ref_array(*src, base); -} - -// Type caster for regular, dense matrix types (e.g. MatrixXd), but not maps/refs/etc. of dense -// types. -template -struct type_caster::value>> { - using Scalar = typename Type::Scalar; - using props = EigenProps; - - bool load(handle src, bool convert) { - // If we're in no-convert mode, only load if given an array of the correct type - if (!convert && !isinstance>(src)) - return false; - - // Coerce into an array, but don't do type conversion yet; the copy below handles it. - auto buf = array::ensure(src); - - if (!buf) - return false; - - auto dims = buf.ndim(); - if (dims < 1 || dims > 2) - return false; - - auto fits = props::conformable(buf); - if (!fits) - return false; - - // Allocate the new type, then build a numpy reference into it - value = Type(fits.rows, fits.cols); - auto ref = reinterpret_steal(eigen_ref_array(value)); - if (dims == 1) ref = ref.squeeze(); - else if (ref.ndim() == 1) buf = buf.squeeze(); - - int result = detail::npy_api::get().PyArray_CopyInto_(ref.ptr(), buf.ptr()); - - if (result < 0) { // Copy failed! - PyErr_Clear(); - return false; - } - - return true; - } - -private: - - // Cast implementation - template - static handle cast_impl(CType *src, return_value_policy policy, handle parent) { - switch (policy) { - case return_value_policy::take_ownership: - case return_value_policy::automatic: - return eigen_encapsulate(src); - case return_value_policy::move: - return eigen_encapsulate(new CType(std::move(*src))); - case return_value_policy::copy: - return eigen_array_cast(*src); - case return_value_policy::reference: - case return_value_policy::automatic_reference: - return eigen_ref_array(*src); - case return_value_policy::reference_internal: - return eigen_ref_array(*src, parent); - default: - throw cast_error("unhandled return_value_policy: should not happen!"); - }; - } - -public: - - // Normal returned non-reference, non-const value: - static handle cast(Type &&src, return_value_policy /* policy */, handle parent) { - return cast_impl(&src, return_value_policy::move, parent); - } - // If you return a non-reference const, we mark the numpy array readonly: - static handle cast(const Type &&src, return_value_policy /* policy */, handle parent) { - return cast_impl(&src, return_value_policy::move, parent); - } - // lvalue reference return; default (automatic) becomes copy - static handle cast(Type &src, return_value_policy policy, handle parent) { - if (policy == return_value_policy::automatic || policy == return_value_policy::automatic_reference) - policy = return_value_policy::copy; - return cast_impl(&src, policy, parent); - } - // const lvalue reference return; default (automatic) becomes copy - static handle cast(const Type &src, return_value_policy policy, handle parent) { - if (policy == return_value_policy::automatic || policy == return_value_policy::automatic_reference) - policy = return_value_policy::copy; - return cast(&src, policy, parent); - } - // non-const pointer return - static handle cast(Type *src, return_value_policy policy, handle parent) { - return cast_impl(src, policy, parent); - } - // const pointer return - static handle cast(const Type *src, return_value_policy policy, handle parent) { - return cast_impl(src, policy, parent); - } - - static constexpr auto name = props::descriptor; - - operator Type*() { return &value; } - operator Type&() { return value; } - operator Type&&() && { return std::move(value); } - template using cast_op_type = movable_cast_op_type; - -private: - Type value; -}; - -// Base class for casting reference/map/block/etc. objects back to python. -template struct eigen_map_caster { -private: - using props = EigenProps; - -public: - - // Directly referencing a ref/map's data is a bit dangerous (whatever the map/ref points to has - // to stay around), but we'll allow it under the assumption that you know what you're doing (and - // have an appropriate keep_alive in place). We return a numpy array pointing directly at the - // ref's data (The numpy array ends up read-only if the ref was to a const matrix type.) Note - // that this means you need to ensure you don't destroy the object in some other way (e.g. with - // an appropriate keep_alive, or with a reference to a statically allocated matrix). - static handle cast(const MapType &src, return_value_policy policy, handle parent) { - switch (policy) { - case return_value_policy::copy: - return eigen_array_cast(src); - case return_value_policy::reference_internal: - return eigen_array_cast(src, parent, is_eigen_mutable_map::value); - case return_value_policy::reference: - case return_value_policy::automatic: - case return_value_policy::automatic_reference: - return eigen_array_cast(src, none(), is_eigen_mutable_map::value); - default: - // move, take_ownership don't make any sense for a ref/map: - pybind11_fail("Invalid return_value_policy for Eigen Map/Ref/Block type"); - } - } - - static constexpr auto name = props::descriptor; - - // Explicitly delete these: support python -> C++ conversion on these (i.e. these can be return - // types but not bound arguments). We still provide them (with an explicitly delete) so that - // you end up here if you try anyway. - bool load(handle, bool) = delete; - operator MapType() = delete; - template using cast_op_type = MapType; -}; - -// We can return any map-like object (but can only load Refs, specialized next): -template struct type_caster::value>> - : eigen_map_caster {}; - -// Loader for Ref<...> arguments. See the documentation for info on how to make this work without -// copying (it requires some extra effort in many cases). -template -struct type_caster< - Eigen::Ref, - enable_if_t>::value> -> : public eigen_map_caster> { -private: - using Type = Eigen::Ref; - using props = EigenProps; - using Scalar = typename props::Scalar; - using MapType = Eigen::Map; - using Array = array_t; - static constexpr bool need_writeable = is_eigen_mutable_map::value; - // Delay construction (these have no default constructor) - std::unique_ptr map; - std::unique_ptr ref; - // Our array. When possible, this is just a numpy array pointing to the source data, but - // sometimes we can't avoid copying (e.g. input is not a numpy array at all, has an incompatible - // layout, or is an array of a type that needs to be converted). Using a numpy temporary - // (rather than an Eigen temporary) saves an extra copy when we need both type conversion and - // storage order conversion. (Note that we refuse to use this temporary copy when loading an - // argument for a Ref with M non-const, i.e. a read-write reference). - Array copy_or_ref; -public: - bool load(handle src, bool convert) { - // First check whether what we have is already an array of the right type. If not, we can't - // avoid a copy (because the copy is also going to do type conversion). - bool need_copy = !isinstance(src); - - EigenConformable fits; - if (!need_copy) { - // We don't need a converting copy, but we also need to check whether the strides are - // compatible with the Ref's stride requirements - Array aref = reinterpret_borrow(src); - - if (aref && (!need_writeable || aref.writeable())) { - fits = props::conformable(aref); - if (!fits) return false; // Incompatible dimensions - if (!fits.template stride_compatible()) - need_copy = true; - else - copy_or_ref = std::move(aref); - } - else { - need_copy = true; - } - } - - if (need_copy) { - // We need to copy: If we need a mutable reference, or we're not supposed to convert - // (either because we're in the no-convert overload pass, or because we're explicitly - // instructed not to copy (via `py::arg().noconvert()`) we have to fail loading. - if (!convert || need_writeable) return false; - - Array copy = Array::ensure(src); - if (!copy) return false; - fits = props::conformable(copy); - if (!fits || !fits.template stride_compatible()) - return false; - copy_or_ref = std::move(copy); - loader_life_support::add_patient(copy_or_ref); - } - - ref.reset(); - map.reset(new MapType(data(copy_or_ref), fits.rows, fits.cols, make_stride(fits.stride.outer(), fits.stride.inner()))); - ref.reset(new Type(*map)); - - return true; - } - - operator Type*() { return ref.get(); } - operator Type&() { return *ref; } - template using cast_op_type = pybind11::detail::cast_op_type<_T>; - -private: - template ::value, int> = 0> - Scalar *data(Array &a) { return a.mutable_data(); } - - template ::value, int> = 0> - const Scalar *data(Array &a) { return a.data(); } - - // Attempt to figure out a constructor of `Stride` that will work. - // If both strides are fixed, use a default constructor: - template using stride_ctor_default = bool_constant< - S::InnerStrideAtCompileTime != Eigen::Dynamic && S::OuterStrideAtCompileTime != Eigen::Dynamic && - std::is_default_constructible::value>; - // Otherwise, if there is a two-index constructor, assume it is (outer,inner) like - // Eigen::Stride, and use it: - template using stride_ctor_dual = bool_constant< - !stride_ctor_default::value && std::is_constructible::value>; - // Otherwise, if there is a one-index constructor, and just one of the strides is dynamic, use - // it (passing whichever stride is dynamic). - template using stride_ctor_outer = bool_constant< - !any_of, stride_ctor_dual>::value && - S::OuterStrideAtCompileTime == Eigen::Dynamic && S::InnerStrideAtCompileTime != Eigen::Dynamic && - std::is_constructible::value>; - template using stride_ctor_inner = bool_constant< - !any_of, stride_ctor_dual>::value && - S::InnerStrideAtCompileTime == Eigen::Dynamic && S::OuterStrideAtCompileTime != Eigen::Dynamic && - std::is_constructible::value>; - - template ::value, int> = 0> - static S make_stride(EigenIndex, EigenIndex) { return S(); } - template ::value, int> = 0> - static S make_stride(EigenIndex outer, EigenIndex inner) { return S(outer, inner); } - template ::value, int> = 0> - static S make_stride(EigenIndex outer, EigenIndex) { return S(outer); } - template ::value, int> = 0> - static S make_stride(EigenIndex, EigenIndex inner) { return S(inner); } - -}; - -// type_caster for special matrix types (e.g. DiagonalMatrix), which are EigenBase, but not -// EigenDense (i.e. they don't have a data(), at least not with the usual matrix layout). -// load() is not supported, but we can cast them into the python domain by first copying to a -// regular Eigen::Matrix, then casting that. -template -struct type_caster::value>> { -protected: - using Matrix = Eigen::Matrix; - using props = EigenProps; -public: - static handle cast(const Type &src, return_value_policy /* policy */, handle /* parent */) { - handle h = eigen_encapsulate(new Matrix(src)); - return h; - } - static handle cast(const Type *src, return_value_policy policy, handle parent) { return cast(*src, policy, parent); } - - static constexpr auto name = props::descriptor; - - // Explicitly delete these: support python -> C++ conversion on these (i.e. these can be return - // types but not bound arguments). We still provide them (with an explicitly delete) so that - // you end up here if you try anyway. - bool load(handle, bool) = delete; - operator Type() = delete; - template using cast_op_type = Type; -}; - -template -struct type_caster::value>> { - typedef typename Type::Scalar Scalar; - typedef remove_reference_t().outerIndexPtr())> StorageIndex; - typedef typename Type::Index Index; - static constexpr bool rowMajor = Type::IsRowMajor; - - bool load(handle src, bool) { - if (!src) - return false; - - auto obj = reinterpret_borrow(src); - object sparse_module = module::import("scipy.sparse"); - object matrix_type = sparse_module.attr( - rowMajor ? "csr_matrix" : "csc_matrix"); - - if (!obj.get_type().is(matrix_type)) { - try { - obj = matrix_type(obj); - } catch (const error_already_set &) { - return false; - } - } - - auto values = array_t((object) obj.attr("data")); - auto innerIndices = array_t((object) obj.attr("indices")); - auto outerIndices = array_t((object) obj.attr("indptr")); - auto shape = pybind11::tuple((pybind11::object) obj.attr("shape")); - auto nnz = obj.attr("nnz").cast(); - - if (!values || !innerIndices || !outerIndices) - return false; - - value = Eigen::MappedSparseMatrix( - shape[0].cast(), shape[1].cast(), nnz, - outerIndices.mutable_data(), innerIndices.mutable_data(), values.mutable_data()); - - return true; - } - - static handle cast(const Type &src, return_value_policy /* policy */, handle /* parent */) { - const_cast(src).makeCompressed(); - - object matrix_type = module::import("scipy.sparse").attr( - rowMajor ? "csr_matrix" : "csc_matrix"); - - array data(src.nonZeros(), src.valuePtr()); - array outerIndices((rowMajor ? src.rows() : src.cols()) + 1, src.outerIndexPtr()); - array innerIndices(src.nonZeros(), src.innerIndexPtr()); - - return matrix_type( - std::make_tuple(data, innerIndices, outerIndices), - std::make_pair(src.rows(), src.cols()) - ).release(); - } - - PYBIND11_TYPE_CASTER(Type, _<(Type::IsRowMajor) != 0>("scipy.sparse.csr_matrix[", "scipy.sparse.csc_matrix[") - + npy_format_descriptor::name + _("]")); -}; - -NAMESPACE_END(detail) -NAMESPACE_END(PYBIND11_NAMESPACE) - -#if defined(__GNUG__) || defined(__clang__) -# pragma GCC diagnostic pop -#elif defined(_MSC_VER) -# pragma warning(pop) -#endif diff --git a/ptocr/postprocess/piexlmerge/include/pybind11/embed.h b/ptocr/postprocess/piexlmerge/include/pybind11/embed.h deleted file mode 100644 index 7265588..0000000 --- a/ptocr/postprocess/piexlmerge/include/pybind11/embed.h +++ /dev/null @@ -1,200 +0,0 @@ -/* - pybind11/embed.h: Support for embedding the interpreter - - Copyright (c) 2017 Wenzel Jakob - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#include "pybind11.h" -#include "eval.h" - -#if defined(PYPY_VERSION) -# error Embedding the interpreter is not supported with PyPy -#endif - -#if PY_MAJOR_VERSION >= 3 -# define PYBIND11_EMBEDDED_MODULE_IMPL(name) \ - extern "C" PyObject *pybind11_init_impl_##name() { \ - return pybind11_init_wrapper_##name(); \ - } -#else -# define PYBIND11_EMBEDDED_MODULE_IMPL(name) \ - extern "C" void pybind11_init_impl_##name() { \ - pybind11_init_wrapper_##name(); \ - } -#endif - -/** \rst - Add a new module to the table of builtins for the interpreter. Must be - defined in global scope. The first macro parameter is the name of the - module (without quotes). The second parameter is the variable which will - be used as the interface to add functions and classes to the module. - - .. code-block:: cpp - - PYBIND11_EMBEDDED_MODULE(example, m) { - // ... initialize functions and classes here - m.def("foo", []() { - return "Hello, World!"; - }); - } - \endrst */ -#define PYBIND11_EMBEDDED_MODULE(name, variable) \ - static void PYBIND11_CONCAT(pybind11_init_, name)(pybind11::module &); \ - static PyObject PYBIND11_CONCAT(*pybind11_init_wrapper_, name)() { \ - auto m = pybind11::module(PYBIND11_TOSTRING(name)); \ - try { \ - PYBIND11_CONCAT(pybind11_init_, name)(m); \ - return m.ptr(); \ - } catch (pybind11::error_already_set &e) { \ - PyErr_SetString(PyExc_ImportError, e.what()); \ - return nullptr; \ - } catch (const std::exception &e) { \ - PyErr_SetString(PyExc_ImportError, e.what()); \ - return nullptr; \ - } \ - } \ - PYBIND11_EMBEDDED_MODULE_IMPL(name) \ - pybind11::detail::embedded_module name(PYBIND11_TOSTRING(name), \ - PYBIND11_CONCAT(pybind11_init_impl_, name)); \ - void PYBIND11_CONCAT(pybind11_init_, name)(pybind11::module &variable) - - -NAMESPACE_BEGIN(PYBIND11_NAMESPACE) -NAMESPACE_BEGIN(detail) - -/// Python 2.7/3.x compatible version of `PyImport_AppendInittab` and error checks. -struct embedded_module { -#if PY_MAJOR_VERSION >= 3 - using init_t = PyObject *(*)(); -#else - using init_t = void (*)(); -#endif - embedded_module(const char *name, init_t init) { - if (Py_IsInitialized()) - pybind11_fail("Can't add new modules after the interpreter has been initialized"); - - auto result = PyImport_AppendInittab(name, init); - if (result == -1) - pybind11_fail("Insufficient memory to add a new module"); - } -}; - -NAMESPACE_END(detail) - -/** \rst - Initialize the Python interpreter. No other pybind11 or CPython API functions can be - called before this is done; with the exception of `PYBIND11_EMBEDDED_MODULE`. The - optional parameter can be used to skip the registration of signal handlers (see the - `Python documentation`_ for details). Calling this function again after the interpreter - has already been initialized is a fatal error. - - If initializing the Python interpreter fails, then the program is terminated. (This - is controlled by the CPython runtime and is an exception to pybind11's normal behavior - of throwing exceptions on errors.) - - .. _Python documentation: https://docs.python.org/3/c-api/init.html#c.Py_InitializeEx - \endrst */ -inline void initialize_interpreter(bool init_signal_handlers = true) { - if (Py_IsInitialized()) - pybind11_fail("The interpreter is already running"); - - Py_InitializeEx(init_signal_handlers ? 1 : 0); - - // Make .py files in the working directory available by default - module::import("sys").attr("path").cast().append("."); -} - -/** \rst - Shut down the Python interpreter. No pybind11 or CPython API functions can be called - after this. In addition, pybind11 objects must not outlive the interpreter: - - .. code-block:: cpp - - { // BAD - py::initialize_interpreter(); - auto hello = py::str("Hello, World!"); - py::finalize_interpreter(); - } // <-- BOOM, hello's destructor is called after interpreter shutdown - - { // GOOD - py::initialize_interpreter(); - { // scoped - auto hello = py::str("Hello, World!"); - } // <-- OK, hello is cleaned up properly - py::finalize_interpreter(); - } - - { // BETTER - py::scoped_interpreter guard{}; - auto hello = py::str("Hello, World!"); - } - - .. warning:: - - The interpreter can be restarted by calling `initialize_interpreter` again. - Modules created using pybind11 can be safely re-initialized. However, Python - itself cannot completely unload binary extension modules and there are several - caveats with regard to interpreter restarting. All the details can be found - in the CPython documentation. In short, not all interpreter memory may be - freed, either due to reference cycles or user-created global data. - - \endrst */ -inline void finalize_interpreter() { - handle builtins(PyEval_GetBuiltins()); - const char *id = PYBIND11_INTERNALS_ID; - - // Get the internals pointer (without creating it if it doesn't exist). It's possible for the - // internals to be created during Py_Finalize() (e.g. if a py::capsule calls `get_internals()` - // during destruction), so we get the pointer-pointer here and check it after Py_Finalize(). - detail::internals **internals_ptr_ptr = detail::get_internals_pp(); - // It could also be stashed in builtins, so look there too: - if (builtins.contains(id) && isinstance(builtins[id])) - internals_ptr_ptr = capsule(builtins[id]); - - Py_Finalize(); - - if (internals_ptr_ptr) { - delete *internals_ptr_ptr; - *internals_ptr_ptr = nullptr; - } -} - -/** \rst - Scope guard version of `initialize_interpreter` and `finalize_interpreter`. - This a move-only guard and only a single instance can exist. - - .. code-block:: cpp - - #include - - int main() { - py::scoped_interpreter guard{}; - py::print(Hello, World!); - } // <-- interpreter shutdown - \endrst */ -class scoped_interpreter { -public: - scoped_interpreter(bool init_signal_handlers = true) { - initialize_interpreter(init_signal_handlers); - } - - scoped_interpreter(const scoped_interpreter &) = delete; - scoped_interpreter(scoped_interpreter &&other) noexcept { other.is_valid = false; } - scoped_interpreter &operator=(const scoped_interpreter &) = delete; - scoped_interpreter &operator=(scoped_interpreter &&) = delete; - - ~scoped_interpreter() { - if (is_valid) - finalize_interpreter(); - } - -private: - bool is_valid = true; -}; - -NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/ptocr/postprocess/piexlmerge/include/pybind11/eval.h b/ptocr/postprocess/piexlmerge/include/pybind11/eval.h deleted file mode 100644 index ea85ba1..0000000 --- a/ptocr/postprocess/piexlmerge/include/pybind11/eval.h +++ /dev/null @@ -1,117 +0,0 @@ -/* - pybind11/exec.h: Support for evaluating Python expressions and statements - from strings and files - - Copyright (c) 2016 Klemens Morgenstern and - Wenzel Jakob - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#include "pybind11.h" - -NAMESPACE_BEGIN(PYBIND11_NAMESPACE) - -enum eval_mode { - /// Evaluate a string containing an isolated expression - eval_expr, - - /// Evaluate a string containing a single statement. Returns \c none - eval_single_statement, - - /// Evaluate a string containing a sequence of statement. Returns \c none - eval_statements -}; - -template -object eval(str expr, object global = globals(), object local = object()) { - if (!local) - local = global; - - /* PyRun_String does not accept a PyObject / encoding specifier, - this seems to be the only alternative */ - std::string buffer = "# -*- coding: utf-8 -*-\n" + (std::string) expr; - - int start; - switch (mode) { - case eval_expr: start = Py_eval_input; break; - case eval_single_statement: start = Py_single_input; break; - case eval_statements: start = Py_file_input; break; - default: pybind11_fail("invalid evaluation mode"); - } - - PyObject *result = PyRun_String(buffer.c_str(), start, global.ptr(), local.ptr()); - if (!result) - throw error_already_set(); - return reinterpret_steal(result); -} - -template -object eval(const char (&s)[N], object global = globals(), object local = object()) { - /* Support raw string literals by removing common leading whitespace */ - auto expr = (s[0] == '\n') ? str(module::import("textwrap").attr("dedent")(s)) - : str(s); - return eval(expr, global, local); -} - -inline void exec(str expr, object global = globals(), object local = object()) { - eval(expr, global, local); -} - -template -void exec(const char (&s)[N], object global = globals(), object local = object()) { - eval(s, global, local); -} - -template -object eval_file(str fname, object global = globals(), object local = object()) { - if (!local) - local = global; - - int start; - switch (mode) { - case eval_expr: start = Py_eval_input; break; - case eval_single_statement: start = Py_single_input; break; - case eval_statements: start = Py_file_input; break; - default: pybind11_fail("invalid evaluation mode"); - } - - int closeFile = 1; - std::string fname_str = (std::string) fname; -#if PY_VERSION_HEX >= 0x03040000 - FILE *f = _Py_fopen_obj(fname.ptr(), "r"); -#elif PY_VERSION_HEX >= 0x03000000 - FILE *f = _Py_fopen(fname.ptr(), "r"); -#else - /* No unicode support in open() :( */ - auto fobj = reinterpret_steal(PyFile_FromString( - const_cast(fname_str.c_str()), - const_cast("r"))); - FILE *f = nullptr; - if (fobj) - f = PyFile_AsFile(fobj.ptr()); - closeFile = 0; -#endif - if (!f) { - PyErr_Clear(); - pybind11_fail("File \"" + fname_str + "\" could not be opened!"); - } - -#if PY_VERSION_HEX < 0x03000000 && defined(PYPY_VERSION) - PyObject *result = PyRun_File(f, fname_str.c_str(), start, global.ptr(), - local.ptr()); - (void) closeFile; -#else - PyObject *result = PyRun_FileEx(f, fname_str.c_str(), start, global.ptr(), - local.ptr(), closeFile); -#endif - - if (!result) - throw error_already_set(); - return reinterpret_steal(result); -} - -NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/ptocr/postprocess/piexlmerge/include/pybind11/functional.h b/ptocr/postprocess/piexlmerge/include/pybind11/functional.h deleted file mode 100644 index 9cdf21f..0000000 --- a/ptocr/postprocess/piexlmerge/include/pybind11/functional.h +++ /dev/null @@ -1,83 +0,0 @@ -/* - pybind11/functional.h: std::function<> support - - Copyright (c) 2016 Wenzel Jakob - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#include "pybind11.h" -#include - -NAMESPACE_BEGIN(PYBIND11_NAMESPACE) -NAMESPACE_BEGIN(detail) - -template -struct type_caster> { - using type = std::function; - using retval_type = conditional_t::value, void_type, Return>; - using function_type = Return (*) (Args...); - -public: - bool load(handle src, bool convert) { - if (src.is_none()) { - // Defer accepting None to other overloads (if we aren't in convert mode): - if (!convert) return false; - return true; - } - - if (!isinstance(src)) - return false; - - auto func = reinterpret_borrow(src); - - /* - When passing a C++ function as an argument to another C++ - function via Python, every function call would normally involve - a full C++ -> Python -> C++ roundtrip, which can be prohibitive. - Here, we try to at least detect the case where the function is - stateless (i.e. function pointer or lambda function without - captured variables), in which case the roundtrip can be avoided. - */ - if (auto cfunc = func.cpp_function()) { - auto c = reinterpret_borrow(PyCFunction_GET_SELF(cfunc.ptr())); - auto rec = (function_record *) c; - - if (rec && rec->is_stateless && - same_type(typeid(function_type), *reinterpret_cast(rec->data[1]))) { - struct capture { function_type f; }; - value = ((capture *) &rec->data)->f; - return true; - } - } - - value = [func](Args... args) -> Return { - gil_scoped_acquire acq; - object retval(func(std::forward(args)...)); - /* Visual studio 2015 parser issue: need parentheses around this expression */ - return (retval.template cast()); - }; - return true; - } - - template - static handle cast(Func &&f_, return_value_policy policy, handle /* parent */) { - if (!f_) - return none().inc_ref(); - - auto result = f_.template target(); - if (result) - return cpp_function(*result, policy).release(); - else - return cpp_function(std::forward(f_), policy).release(); - } - - PYBIND11_TYPE_CASTER(type, _("Callable[[") + concat(make_caster::name...) + _("], ") - + make_caster::name + _("]")); -}; - -NAMESPACE_END(detail) -NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/ptocr/postprocess/piexlmerge/include/pybind11/iostream.h b/ptocr/postprocess/piexlmerge/include/pybind11/iostream.h deleted file mode 100644 index 182e8ee..0000000 --- a/ptocr/postprocess/piexlmerge/include/pybind11/iostream.h +++ /dev/null @@ -1,200 +0,0 @@ -/* - pybind11/iostream.h -- Tools to assist with redirecting cout and cerr to Python - - Copyright (c) 2017 Henry F. Schreiner - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#include "pybind11.h" - -#include -#include -#include -#include -#include - -NAMESPACE_BEGIN(PYBIND11_NAMESPACE) -NAMESPACE_BEGIN(detail) - -// Buffer that writes to Python instead of C++ -class pythonbuf : public std::streambuf { -private: - using traits_type = std::streambuf::traits_type; - - char d_buffer[1024]; - object pywrite; - object pyflush; - - int overflow(int c) { - if (!traits_type::eq_int_type(c, traits_type::eof())) { - *pptr() = traits_type::to_char_type(c); - pbump(1); - } - return sync() == 0 ? traits_type::not_eof(c) : traits_type::eof(); - } - - int sync() { - if (pbase() != pptr()) { - // This subtraction cannot be negative, so dropping the sign - str line(pbase(), static_cast(pptr() - pbase())); - - pywrite(line); - pyflush(); - - setp(pbase(), epptr()); - } - return 0; - } - -public: - pythonbuf(object pyostream) - : pywrite(pyostream.attr("write")), - pyflush(pyostream.attr("flush")) { - setp(d_buffer, d_buffer + sizeof(d_buffer) - 1); - } - - /// Sync before destroy - ~pythonbuf() { - sync(); - } -}; - -NAMESPACE_END(detail) - - -/** \rst - This a move-only guard that redirects output. - - .. code-block:: cpp - - #include - - ... - - { - py::scoped_ostream_redirect output; - std::cout << "Hello, World!"; // Python stdout - } // <-- return std::cout to normal - - You can explicitly pass the c++ stream and the python object, - for example to guard stderr instead. - - .. code-block:: cpp - - { - py::scoped_ostream_redirect output{std::cerr, py::module::import("sys").attr("stderr")}; - std::cerr << "Hello, World!"; - } - \endrst */ -class scoped_ostream_redirect { -protected: - std::streambuf *old; - std::ostream &costream; - detail::pythonbuf buffer; - -public: - scoped_ostream_redirect( - std::ostream &costream = std::cout, - object pyostream = module::import("sys").attr("stdout")) - : costream(costream), buffer(pyostream) { - old = costream.rdbuf(&buffer); - } - - ~scoped_ostream_redirect() { - costream.rdbuf(old); - } - - scoped_ostream_redirect(const scoped_ostream_redirect &) = delete; - scoped_ostream_redirect(scoped_ostream_redirect &&other) = default; - scoped_ostream_redirect &operator=(const scoped_ostream_redirect &) = delete; - scoped_ostream_redirect &operator=(scoped_ostream_redirect &&) = delete; -}; - - -/** \rst - Like `scoped_ostream_redirect`, but redirects cerr by default. This class - is provided primary to make ``py::call_guard`` easier to make. - - .. code-block:: cpp - - m.def("noisy_func", &noisy_func, - py::call_guard()); - -\endrst */ -class scoped_estream_redirect : public scoped_ostream_redirect { -public: - scoped_estream_redirect( - std::ostream &costream = std::cerr, - object pyostream = module::import("sys").attr("stderr")) - : scoped_ostream_redirect(costream,pyostream) {} -}; - - -NAMESPACE_BEGIN(detail) - -// Class to redirect output as a context manager. C++ backend. -class OstreamRedirect { - bool do_stdout_; - bool do_stderr_; - std::unique_ptr redirect_stdout; - std::unique_ptr redirect_stderr; - -public: - OstreamRedirect(bool do_stdout = true, bool do_stderr = true) - : do_stdout_(do_stdout), do_stderr_(do_stderr) {} - - void enter() { - if (do_stdout_) - redirect_stdout.reset(new scoped_ostream_redirect()); - if (do_stderr_) - redirect_stderr.reset(new scoped_estream_redirect()); - } - - void exit() { - redirect_stdout.reset(); - redirect_stderr.reset(); - } -}; - -NAMESPACE_END(detail) - -/** \rst - This is a helper function to add a C++ redirect context manager to Python - instead of using a C++ guard. To use it, add the following to your binding code: - - .. code-block:: cpp - - #include - - ... - - py::add_ostream_redirect(m, "ostream_redirect"); - - You now have a Python context manager that redirects your output: - - .. code-block:: python - - with m.ostream_redirect(): - m.print_to_cout_function() - - This manager can optionally be told which streams to operate on: - - .. code-block:: python - - with m.ostream_redirect(stdout=true, stderr=true): - m.noisy_function_with_error_printing() - - \endrst */ -inline class_ add_ostream_redirect(module m, std::string name = "ostream_redirect") { - return class_(m, name.c_str(), module_local()) - .def(init(), arg("stdout")=true, arg("stderr")=true) - .def("__enter__", &detail::OstreamRedirect::enter) - .def("__exit__", [](detail::OstreamRedirect &self_, args) { self_.exit(); }); -} - -NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/ptocr/postprocess/piexlmerge/include/pybind11/numpy.h b/ptocr/postprocess/piexlmerge/include/pybind11/numpy.h deleted file mode 100644 index 37471d8..0000000 --- a/ptocr/postprocess/piexlmerge/include/pybind11/numpy.h +++ /dev/null @@ -1,1610 +0,0 @@ -/* - pybind11/numpy.h: Basic NumPy support, vectorize() wrapper - - Copyright (c) 2016 Wenzel Jakob - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#include "pybind11.h" -#include "complex.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if defined(_MSC_VER) -# pragma warning(push) -# pragma warning(disable: 4127) // warning C4127: Conditional expression is constant -#endif - -/* This will be true on all flat address space platforms and allows us to reduce the - whole npy_intp / ssize_t / Py_intptr_t business down to just ssize_t for all size - and dimension types (e.g. shape, strides, indexing), instead of inflicting this - upon the library user. */ -static_assert(sizeof(ssize_t) == sizeof(Py_intptr_t), "ssize_t != Py_intptr_t"); - -NAMESPACE_BEGIN(PYBIND11_NAMESPACE) - -class array; // Forward declaration - -NAMESPACE_BEGIN(detail) -template struct npy_format_descriptor; - -struct PyArrayDescr_Proxy { - PyObject_HEAD - PyObject *typeobj; - char kind; - char type; - char byteorder; - char flags; - int type_num; - int elsize; - int alignment; - char *subarray; - PyObject *fields; - PyObject *names; -}; - -struct PyArray_Proxy { - PyObject_HEAD - char *data; - int nd; - ssize_t *dimensions; - ssize_t *strides; - PyObject *base; - PyObject *descr; - int flags; -}; - -struct PyVoidScalarObject_Proxy { - PyObject_VAR_HEAD - char *obval; - PyArrayDescr_Proxy *descr; - int flags; - PyObject *base; -}; - -struct numpy_type_info { - PyObject* dtype_ptr; - std::string format_str; -}; - -struct numpy_internals { - std::unordered_map registered_dtypes; - - numpy_type_info *get_type_info(const std::type_info& tinfo, bool throw_if_missing = true) { - auto it = registered_dtypes.find(std::type_index(tinfo)); - if (it != registered_dtypes.end()) - return &(it->second); - if (throw_if_missing) - pybind11_fail(std::string("NumPy type info missing for ") + tinfo.name()); - return nullptr; - } - - template numpy_type_info *get_type_info(bool throw_if_missing = true) { - return get_type_info(typeid(typename std::remove_cv::type), throw_if_missing); - } -}; - -inline PYBIND11_NOINLINE void load_numpy_internals(numpy_internals* &ptr) { - ptr = &get_or_create_shared_data("_numpy_internals"); -} - -inline numpy_internals& get_numpy_internals() { - static numpy_internals* ptr = nullptr; - if (!ptr) - load_numpy_internals(ptr); - return *ptr; -} - -struct npy_api { - enum constants { - NPY_ARRAY_C_CONTIGUOUS_ = 0x0001, - NPY_ARRAY_F_CONTIGUOUS_ = 0x0002, - NPY_ARRAY_OWNDATA_ = 0x0004, - NPY_ARRAY_FORCECAST_ = 0x0010, - NPY_ARRAY_ENSUREARRAY_ = 0x0040, - NPY_ARRAY_ALIGNED_ = 0x0100, - NPY_ARRAY_WRITEABLE_ = 0x0400, - NPY_BOOL_ = 0, - NPY_BYTE_, NPY_UBYTE_, - NPY_SHORT_, NPY_USHORT_, - NPY_INT_, NPY_UINT_, - NPY_LONG_, NPY_ULONG_, - NPY_LONGLONG_, NPY_ULONGLONG_, - NPY_FLOAT_, NPY_DOUBLE_, NPY_LONGDOUBLE_, - NPY_CFLOAT_, NPY_CDOUBLE_, NPY_CLONGDOUBLE_, - NPY_OBJECT_ = 17, - NPY_STRING_, NPY_UNICODE_, NPY_VOID_ - }; - - typedef struct { - Py_intptr_t *ptr; - int len; - } PyArray_Dims; - - static npy_api& get() { - static npy_api api = lookup(); - return api; - } - - bool PyArray_Check_(PyObject *obj) const { - return (bool) PyObject_TypeCheck(obj, PyArray_Type_); - } - bool PyArrayDescr_Check_(PyObject *obj) const { - return (bool) PyObject_TypeCheck(obj, PyArrayDescr_Type_); - } - - unsigned int (*PyArray_GetNDArrayCFeatureVersion_)(); - PyObject *(*PyArray_DescrFromType_)(int); - PyObject *(*PyArray_NewFromDescr_) - (PyTypeObject *, PyObject *, int, Py_intptr_t *, - Py_intptr_t *, void *, int, PyObject *); - PyObject *(*PyArray_DescrNewFromType_)(int); - int (*PyArray_CopyInto_)(PyObject *, PyObject *); - PyObject *(*PyArray_NewCopy_)(PyObject *, int); - PyTypeObject *PyArray_Type_; - PyTypeObject *PyVoidArrType_Type_; - PyTypeObject *PyArrayDescr_Type_; - PyObject *(*PyArray_DescrFromScalar_)(PyObject *); - PyObject *(*PyArray_FromAny_) (PyObject *, PyObject *, int, int, int, PyObject *); - int (*PyArray_DescrConverter_) (PyObject *, PyObject **); - bool (*PyArray_EquivTypes_) (PyObject *, PyObject *); - int (*PyArray_GetArrayParamsFromObject_)(PyObject *, PyObject *, char, PyObject **, int *, - Py_ssize_t *, PyObject **, PyObject *); - PyObject *(*PyArray_Squeeze_)(PyObject *); - int (*PyArray_SetBaseObject_)(PyObject *, PyObject *); - PyObject* (*PyArray_Resize_)(PyObject*, PyArray_Dims*, int, int); -private: - enum functions { - API_PyArray_GetNDArrayCFeatureVersion = 211, - API_PyArray_Type = 2, - API_PyArrayDescr_Type = 3, - API_PyVoidArrType_Type = 39, - API_PyArray_DescrFromType = 45, - API_PyArray_DescrFromScalar = 57, - API_PyArray_FromAny = 69, - API_PyArray_Resize = 80, - API_PyArray_CopyInto = 82, - API_PyArray_NewCopy = 85, - API_PyArray_NewFromDescr = 94, - API_PyArray_DescrNewFromType = 9, - API_PyArray_DescrConverter = 174, - API_PyArray_EquivTypes = 182, - API_PyArray_GetArrayParamsFromObject = 278, - API_PyArray_Squeeze = 136, - API_PyArray_SetBaseObject = 282 - }; - - static npy_api lookup() { - module m = module::import("numpy.core.multiarray"); - auto c = m.attr("_ARRAY_API"); -#if PY_MAJOR_VERSION >= 3 - void **api_ptr = (void **) PyCapsule_GetPointer(c.ptr(), NULL); -#else - void **api_ptr = (void **) PyCObject_AsVoidPtr(c.ptr()); -#endif - npy_api api; -#define DECL_NPY_API(Func) api.Func##_ = (decltype(api.Func##_)) api_ptr[API_##Func]; - DECL_NPY_API(PyArray_GetNDArrayCFeatureVersion); - if (api.PyArray_GetNDArrayCFeatureVersion_() < 0x7) - pybind11_fail("pybind11 numpy support requires numpy >= 1.7.0"); - DECL_NPY_API(PyArray_Type); - DECL_NPY_API(PyVoidArrType_Type); - DECL_NPY_API(PyArrayDescr_Type); - DECL_NPY_API(PyArray_DescrFromType); - DECL_NPY_API(PyArray_DescrFromScalar); - DECL_NPY_API(PyArray_FromAny); - DECL_NPY_API(PyArray_Resize); - DECL_NPY_API(PyArray_CopyInto); - DECL_NPY_API(PyArray_NewCopy); - DECL_NPY_API(PyArray_NewFromDescr); - DECL_NPY_API(PyArray_DescrNewFromType); - DECL_NPY_API(PyArray_DescrConverter); - DECL_NPY_API(PyArray_EquivTypes); - DECL_NPY_API(PyArray_GetArrayParamsFromObject); - DECL_NPY_API(PyArray_Squeeze); - DECL_NPY_API(PyArray_SetBaseObject); -#undef DECL_NPY_API - return api; - } -}; - -inline PyArray_Proxy* array_proxy(void* ptr) { - return reinterpret_cast(ptr); -} - -inline const PyArray_Proxy* array_proxy(const void* ptr) { - return reinterpret_cast(ptr); -} - -inline PyArrayDescr_Proxy* array_descriptor_proxy(PyObject* ptr) { - return reinterpret_cast(ptr); -} - -inline const PyArrayDescr_Proxy* array_descriptor_proxy(const PyObject* ptr) { - return reinterpret_cast(ptr); -} - -inline bool check_flags(const void* ptr, int flag) { - return (flag == (array_proxy(ptr)->flags & flag)); -} - -template struct is_std_array : std::false_type { }; -template struct is_std_array> : std::true_type { }; -template struct is_complex : std::false_type { }; -template struct is_complex> : std::true_type { }; - -template struct array_info_scalar { - typedef T type; - static constexpr bool is_array = false; - static constexpr bool is_empty = false; - static constexpr auto extents = _(""); - static void append_extents(list& /* shape */) { } -}; -// Computes underlying type and a comma-separated list of extents for array -// types (any mix of std::array and built-in arrays). An array of char is -// treated as scalar because it gets special handling. -template struct array_info : array_info_scalar { }; -template struct array_info> { - using type = typename array_info::type; - static constexpr bool is_array = true; - static constexpr bool is_empty = (N == 0) || array_info::is_empty; - static constexpr size_t extent = N; - - // appends the extents to shape - static void append_extents(list& shape) { - shape.append(N); - array_info::append_extents(shape); - } - - static constexpr auto extents = _::is_array>( - concat(_(), array_info::extents), _() - ); -}; -// For numpy we have special handling for arrays of characters, so we don't include -// the size in the array extents. -template struct array_info : array_info_scalar { }; -template struct array_info> : array_info_scalar> { }; -template struct array_info : array_info> { }; -template using remove_all_extents_t = typename array_info::type; - -template using is_pod_struct = all_of< - std::is_standard_layout, // since we're accessing directly in memory we need a standard layout type -#if !defined(__GNUG__) || defined(_LIBCPP_VERSION) || defined(_GLIBCXX_USE_CXX11_ABI) - // _GLIBCXX_USE_CXX11_ABI indicates that we're using libstdc++ from GCC 5 or newer, independent - // of the actual compiler (Clang can also use libstdc++, but it always defines __GNUC__ == 4). - std::is_trivially_copyable, -#else - // GCC 4 doesn't implement is_trivially_copyable, so approximate it - std::is_trivially_destructible, - satisfies_any_of, -#endif - satisfies_none_of ->; - -template ssize_t byte_offset_unsafe(const Strides &) { return 0; } -template -ssize_t byte_offset_unsafe(const Strides &strides, ssize_t i, Ix... index) { - return i * strides[Dim] + byte_offset_unsafe(strides, index...); -} - -/** - * Proxy class providing unsafe, unchecked const access to array data. This is constructed through - * the `unchecked()` method of `array` or the `unchecked()` method of `array_t`. `Dims` - * will be -1 for dimensions determined at runtime. - */ -template -class unchecked_reference { -protected: - static constexpr bool Dynamic = Dims < 0; - const unsigned char *data_; - // Storing the shape & strides in local variables (i.e. these arrays) allows the compiler to - // make large performance gains on big, nested loops, but requires compile-time dimensions - conditional_t> - shape_, strides_; - const ssize_t dims_; - - friend class pybind11::array; - // Constructor for compile-time dimensions: - template - unchecked_reference(const void *data, const ssize_t *shape, const ssize_t *strides, enable_if_t) - : data_{reinterpret_cast(data)}, dims_{Dims} { - for (size_t i = 0; i < (size_t) dims_; i++) { - shape_[i] = shape[i]; - strides_[i] = strides[i]; - } - } - // Constructor for runtime dimensions: - template - unchecked_reference(const void *data, const ssize_t *shape, const ssize_t *strides, enable_if_t dims) - : data_{reinterpret_cast(data)}, shape_{shape}, strides_{strides}, dims_{dims} {} - -public: - /** - * Unchecked const reference access to data at the given indices. For a compile-time known - * number of dimensions, this requires the correct number of arguments; for run-time - * dimensionality, this is not checked (and so is up to the caller to use safely). - */ - template const T &operator()(Ix... index) const { - static_assert(ssize_t{sizeof...(Ix)} == Dims || Dynamic, - "Invalid number of indices for unchecked array reference"); - return *reinterpret_cast(data_ + byte_offset_unsafe(strides_, ssize_t(index)...)); - } - /** - * Unchecked const reference access to data; this operator only participates if the reference - * is to a 1-dimensional array. When present, this is exactly equivalent to `obj(index)`. - */ - template > - const T &operator[](ssize_t index) const { return operator()(index); } - - /// Pointer access to the data at the given indices. - template const T *data(Ix... ix) const { return &operator()(ssize_t(ix)...); } - - /// Returns the item size, i.e. sizeof(T) - constexpr static ssize_t itemsize() { return sizeof(T); } - - /// Returns the shape (i.e. size) of dimension `dim` - ssize_t shape(ssize_t dim) const { return shape_[(size_t) dim]; } - - /// Returns the number of dimensions of the array - ssize_t ndim() const { return dims_; } - - /// Returns the total number of elements in the referenced array, i.e. the product of the shapes - template - enable_if_t size() const { - return std::accumulate(shape_.begin(), shape_.end(), (ssize_t) 1, std::multiplies()); - } - template - enable_if_t size() const { - return std::accumulate(shape_, shape_ + ndim(), (ssize_t) 1, std::multiplies()); - } - - /// Returns the total number of bytes used by the referenced data. Note that the actual span in - /// memory may be larger if the referenced array has non-contiguous strides (e.g. for a slice). - ssize_t nbytes() const { - return size() * itemsize(); - } -}; - -template -class unchecked_mutable_reference : public unchecked_reference { - friend class pybind11::array; - using ConstBase = unchecked_reference; - using ConstBase::ConstBase; - using ConstBase::Dynamic; -public: - /// Mutable, unchecked access to data at the given indices. - template T& operator()(Ix... index) { - static_assert(ssize_t{sizeof...(Ix)} == Dims || Dynamic, - "Invalid number of indices for unchecked array reference"); - return const_cast(ConstBase::operator()(index...)); - } - /** - * Mutable, unchecked access data at the given index; this operator only participates if the - * reference is to a 1-dimensional array (or has runtime dimensions). When present, this is - * exactly equivalent to `obj(index)`. - */ - template > - T &operator[](ssize_t index) { return operator()(index); } - - /// Mutable pointer access to the data at the given indices. - template T *mutable_data(Ix... ix) { return &operator()(ssize_t(ix)...); } -}; - -template -struct type_caster> { - static_assert(Dim == 0 && Dim > 0 /* always fail */, "unchecked array proxy object is not castable"); -}; -template -struct type_caster> : type_caster> {}; - -NAMESPACE_END(detail) - -class dtype : public object { -public: - PYBIND11_OBJECT_DEFAULT(dtype, object, detail::npy_api::get().PyArrayDescr_Check_); - - explicit dtype(const buffer_info &info) { - dtype descr(_dtype_from_pep3118()(PYBIND11_STR_TYPE(info.format))); - // If info.itemsize == 0, use the value calculated from the format string - m_ptr = descr.strip_padding(info.itemsize ? info.itemsize : descr.itemsize()).release().ptr(); - } - - explicit dtype(const std::string &format) { - m_ptr = from_args(pybind11::str(format)).release().ptr(); - } - - dtype(const char *format) : dtype(std::string(format)) { } - - dtype(list names, list formats, list offsets, ssize_t itemsize) { - dict args; - args["names"] = names; - args["formats"] = formats; - args["offsets"] = offsets; - args["itemsize"] = pybind11::int_(itemsize); - m_ptr = from_args(args).release().ptr(); - } - - /// This is essentially the same as calling numpy.dtype(args) in Python. - static dtype from_args(object args) { - PyObject *ptr = nullptr; - if (!detail::npy_api::get().PyArray_DescrConverter_(args.ptr(), &ptr) || !ptr) - throw error_already_set(); - return reinterpret_steal(ptr); - } - - /// Return dtype associated with a C++ type. - template static dtype of() { - return detail::npy_format_descriptor::type>::dtype(); - } - - /// Size of the data type in bytes. - ssize_t itemsize() const { - return detail::array_descriptor_proxy(m_ptr)->elsize; - } - - /// Returns true for structured data types. - bool has_fields() const { - return detail::array_descriptor_proxy(m_ptr)->names != nullptr; - } - - /// Single-character type code. - char kind() const { - return detail::array_descriptor_proxy(m_ptr)->kind; - } - -private: - static object _dtype_from_pep3118() { - static PyObject *obj = module::import("numpy.core._internal") - .attr("_dtype_from_pep3118").cast().release().ptr(); - return reinterpret_borrow(obj); - } - - dtype strip_padding(ssize_t itemsize) { - // Recursively strip all void fields with empty names that are generated for - // padding fields (as of NumPy v1.11). - if (!has_fields()) - return *this; - - struct field_descr { PYBIND11_STR_TYPE name; object format; pybind11::int_ offset; }; - std::vector field_descriptors; - - for (auto field : attr("fields").attr("items")()) { - auto spec = field.cast(); - auto name = spec[0].cast(); - auto format = spec[1].cast()[0].cast(); - auto offset = spec[1].cast()[1].cast(); - if (!len(name) && format.kind() == 'V') - continue; - field_descriptors.push_back({(PYBIND11_STR_TYPE) name, format.strip_padding(format.itemsize()), offset}); - } - - std::sort(field_descriptors.begin(), field_descriptors.end(), - [](const field_descr& a, const field_descr& b) { - return a.offset.cast() < b.offset.cast(); - }); - - list names, formats, offsets; - for (auto& descr : field_descriptors) { - names.append(descr.name); - formats.append(descr.format); - offsets.append(descr.offset); - } - return dtype(names, formats, offsets, itemsize); - } -}; - -class array : public buffer { -public: - PYBIND11_OBJECT_CVT(array, buffer, detail::npy_api::get().PyArray_Check_, raw_array) - - enum { - c_style = detail::npy_api::NPY_ARRAY_C_CONTIGUOUS_, - f_style = detail::npy_api::NPY_ARRAY_F_CONTIGUOUS_, - forcecast = detail::npy_api::NPY_ARRAY_FORCECAST_ - }; - - array() : array({{0}}, static_cast(nullptr)) {} - - using ShapeContainer = detail::any_container; - using StridesContainer = detail::any_container; - - // Constructs an array taking shape/strides from arbitrary container types - array(const pybind11::dtype &dt, ShapeContainer shape, StridesContainer strides, - const void *ptr = nullptr, handle base = handle()) { - - if (strides->empty()) - *strides = c_strides(*shape, dt.itemsize()); - - auto ndim = shape->size(); - if (ndim != strides->size()) - pybind11_fail("NumPy: shape ndim doesn't match strides ndim"); - auto descr = dt; - - int flags = 0; - if (base && ptr) { - if (isinstance(base)) - /* Copy flags from base (except ownership bit) */ - flags = reinterpret_borrow(base).flags() & ~detail::npy_api::NPY_ARRAY_OWNDATA_; - else - /* Writable by default, easy to downgrade later on if needed */ - flags = detail::npy_api::NPY_ARRAY_WRITEABLE_; - } - - auto &api = detail::npy_api::get(); - auto tmp = reinterpret_steal(api.PyArray_NewFromDescr_( - api.PyArray_Type_, descr.release().ptr(), (int) ndim, shape->data(), strides->data(), - const_cast(ptr), flags, nullptr)); - if (!tmp) - throw error_already_set(); - if (ptr) { - if (base) { - api.PyArray_SetBaseObject_(tmp.ptr(), base.inc_ref().ptr()); - } else { - tmp = reinterpret_steal(api.PyArray_NewCopy_(tmp.ptr(), -1 /* any order */)); - } - } - m_ptr = tmp.release().ptr(); - } - - array(const pybind11::dtype &dt, ShapeContainer shape, const void *ptr = nullptr, handle base = handle()) - : array(dt, std::move(shape), {}, ptr, base) { } - - template ::value && !std::is_same::value>> - array(const pybind11::dtype &dt, T count, const void *ptr = nullptr, handle base = handle()) - : array(dt, {{count}}, ptr, base) { } - - template - array(ShapeContainer shape, StridesContainer strides, const T *ptr, handle base = handle()) - : array(pybind11::dtype::of(), std::move(shape), std::move(strides), ptr, base) { } - - template - array(ShapeContainer shape, const T *ptr, handle base = handle()) - : array(std::move(shape), {}, ptr, base) { } - - template - explicit array(ssize_t count, const T *ptr, handle base = handle()) : array({count}, {}, ptr, base) { } - - explicit array(const buffer_info &info) - : array(pybind11::dtype(info), info.shape, info.strides, info.ptr) { } - - /// Array descriptor (dtype) - pybind11::dtype dtype() const { - return reinterpret_borrow(detail::array_proxy(m_ptr)->descr); - } - - /// Total number of elements - ssize_t size() const { - return std::accumulate(shape(), shape() + ndim(), (ssize_t) 1, std::multiplies()); - } - - /// Byte size of a single element - ssize_t itemsize() const { - return detail::array_descriptor_proxy(detail::array_proxy(m_ptr)->descr)->elsize; - } - - /// Total number of bytes - ssize_t nbytes() const { - return size() * itemsize(); - } - - /// Number of dimensions - ssize_t ndim() const { - return detail::array_proxy(m_ptr)->nd; - } - - /// Base object - object base() const { - return reinterpret_borrow(detail::array_proxy(m_ptr)->base); - } - - /// Dimensions of the array - const ssize_t* shape() const { - return detail::array_proxy(m_ptr)->dimensions; - } - - /// Dimension along a given axis - ssize_t shape(ssize_t dim) const { - if (dim >= ndim()) - fail_dim_check(dim, "invalid axis"); - return shape()[dim]; - } - - /// Strides of the array - const ssize_t* strides() const { - return detail::array_proxy(m_ptr)->strides; - } - - /// Stride along a given axis - ssize_t strides(ssize_t dim) const { - if (dim >= ndim()) - fail_dim_check(dim, "invalid axis"); - return strides()[dim]; - } - - /// Return the NumPy array flags - int flags() const { - return detail::array_proxy(m_ptr)->flags; - } - - /// If set, the array is writeable (otherwise the buffer is read-only) - bool writeable() const { - return detail::check_flags(m_ptr, detail::npy_api::NPY_ARRAY_WRITEABLE_); - } - - /// If set, the array owns the data (will be freed when the array is deleted) - bool owndata() const { - return detail::check_flags(m_ptr, detail::npy_api::NPY_ARRAY_OWNDATA_); - } - - /// Pointer to the contained data. If index is not provided, points to the - /// beginning of the buffer. May throw if the index would lead to out of bounds access. - template const void* data(Ix... index) const { - return static_cast(detail::array_proxy(m_ptr)->data + offset_at(index...)); - } - - /// Mutable pointer to the contained data. If index is not provided, points to the - /// beginning of the buffer. May throw if the index would lead to out of bounds access. - /// May throw if the array is not writeable. - template void* mutable_data(Ix... index) { - check_writeable(); - return static_cast(detail::array_proxy(m_ptr)->data + offset_at(index...)); - } - - /// Byte offset from beginning of the array to a given index (full or partial). - /// May throw if the index would lead to out of bounds access. - template ssize_t offset_at(Ix... index) const { - if ((ssize_t) sizeof...(index) > ndim()) - fail_dim_check(sizeof...(index), "too many indices for an array"); - return byte_offset(ssize_t(index)...); - } - - ssize_t offset_at() const { return 0; } - - /// Item count from beginning of the array to a given index (full or partial). - /// May throw if the index would lead to out of bounds access. - template ssize_t index_at(Ix... index) const { - return offset_at(index...) / itemsize(); - } - - /** - * Returns a proxy object that provides access to the array's data without bounds or - * dimensionality checking. Will throw if the array is missing the `writeable` flag. Use with - * care: the array must not be destroyed or reshaped for the duration of the returned object, - * and the caller must take care not to access invalid dimensions or dimension indices. - */ - template detail::unchecked_mutable_reference mutable_unchecked() & { - if (Dims >= 0 && ndim() != Dims) - throw std::domain_error("array has incorrect number of dimensions: " + std::to_string(ndim()) + - "; expected " + std::to_string(Dims)); - return detail::unchecked_mutable_reference(mutable_data(), shape(), strides(), ndim()); - } - - /** - * Returns a proxy object that provides const access to the array's data without bounds or - * dimensionality checking. Unlike `mutable_unchecked()`, this does not require that the - * underlying array have the `writable` flag. Use with care: the array must not be destroyed or - * reshaped for the duration of the returned object, and the caller must take care not to access - * invalid dimensions or dimension indices. - */ - template detail::unchecked_reference unchecked() const & { - if (Dims >= 0 && ndim() != Dims) - throw std::domain_error("array has incorrect number of dimensions: " + std::to_string(ndim()) + - "; expected " + std::to_string(Dims)); - return detail::unchecked_reference(data(), shape(), strides(), ndim()); - } - - /// Return a new view with all of the dimensions of length 1 removed - array squeeze() { - auto& api = detail::npy_api::get(); - return reinterpret_steal(api.PyArray_Squeeze_(m_ptr)); - } - - /// Resize array to given shape - /// If refcheck is true and more that one reference exist to this array - /// then resize will succeed only if it makes a reshape, i.e. original size doesn't change - void resize(ShapeContainer new_shape, bool refcheck = true) { - detail::npy_api::PyArray_Dims d = { - new_shape->data(), int(new_shape->size()) - }; - // try to resize, set ordering param to -1 cause it's not used anyway - object new_array = reinterpret_steal( - detail::npy_api::get().PyArray_Resize_(m_ptr, &d, int(refcheck), -1) - ); - if (!new_array) throw error_already_set(); - if (isinstance(new_array)) { *this = std::move(new_array); } - } - - /// Ensure that the argument is a NumPy array - /// In case of an error, nullptr is returned and the Python error is cleared. - static array ensure(handle h, int ExtraFlags = 0) { - auto result = reinterpret_steal(raw_array(h.ptr(), ExtraFlags)); - if (!result) - PyErr_Clear(); - return result; - } - -protected: - template friend struct detail::npy_format_descriptor; - - void fail_dim_check(ssize_t dim, const std::string& msg) const { - throw index_error(msg + ": " + std::to_string(dim) + - " (ndim = " + std::to_string(ndim()) + ")"); - } - - template ssize_t byte_offset(Ix... index) const { - check_dimensions(index...); - return detail::byte_offset_unsafe(strides(), ssize_t(index)...); - } - - void check_writeable() const { - if (!writeable()) - throw std::domain_error("array is not writeable"); - } - - // Default, C-style strides - static std::vector c_strides(const std::vector &shape, ssize_t itemsize) { - auto ndim = shape.size(); - std::vector strides(ndim, itemsize); - if (ndim > 0) - for (size_t i = ndim - 1; i > 0; --i) - strides[i - 1] = strides[i] * shape[i]; - return strides; - } - - // F-style strides; default when constructing an array_t with `ExtraFlags & f_style` - static std::vector f_strides(const std::vector &shape, ssize_t itemsize) { - auto ndim = shape.size(); - std::vector strides(ndim, itemsize); - for (size_t i = 1; i < ndim; ++i) - strides[i] = strides[i - 1] * shape[i - 1]; - return strides; - } - - template void check_dimensions(Ix... index) const { - check_dimensions_impl(ssize_t(0), shape(), ssize_t(index)...); - } - - void check_dimensions_impl(ssize_t, const ssize_t*) const { } - - template void check_dimensions_impl(ssize_t axis, const ssize_t* shape, ssize_t i, Ix... index) const { - if (i >= *shape) { - throw index_error(std::string("index ") + std::to_string(i) + - " is out of bounds for axis " + std::to_string(axis) + - " with size " + std::to_string(*shape)); - } - check_dimensions_impl(axis + 1, shape + 1, index...); - } - - /// Create array from any object -- always returns a new reference - static PyObject *raw_array(PyObject *ptr, int ExtraFlags = 0) { - if (ptr == nullptr) { - PyErr_SetString(PyExc_ValueError, "cannot create a pybind11::array from a nullptr"); - return nullptr; - } - return detail::npy_api::get().PyArray_FromAny_( - ptr, nullptr, 0, 0, detail::npy_api::NPY_ARRAY_ENSUREARRAY_ | ExtraFlags, nullptr); - } -}; - -template class array_t : public array { -private: - struct private_ctor {}; - // Delegating constructor needed when both moving and accessing in the same constructor - array_t(private_ctor, ShapeContainer &&shape, StridesContainer &&strides, const T *ptr, handle base) - : array(std::move(shape), std::move(strides), ptr, base) {} -public: - static_assert(!detail::array_info::is_array, "Array types cannot be used with array_t"); - - using value_type = T; - - array_t() : array(0, static_cast(nullptr)) {} - array_t(handle h, borrowed_t) : array(h, borrowed_t{}) { } - array_t(handle h, stolen_t) : array(h, stolen_t{}) { } - - PYBIND11_DEPRECATED("Use array_t::ensure() instead") - array_t(handle h, bool is_borrowed) : array(raw_array_t(h.ptr()), stolen_t{}) { - if (!m_ptr) PyErr_Clear(); - if (!is_borrowed) Py_XDECREF(h.ptr()); - } - - array_t(const object &o) : array(raw_array_t(o.ptr()), stolen_t{}) { - if (!m_ptr) throw error_already_set(); - } - - explicit array_t(const buffer_info& info) : array(info) { } - - array_t(ShapeContainer shape, StridesContainer strides, const T *ptr = nullptr, handle base = handle()) - : array(std::move(shape), std::move(strides), ptr, base) { } - - explicit array_t(ShapeContainer shape, const T *ptr = nullptr, handle base = handle()) - : array_t(private_ctor{}, std::move(shape), - ExtraFlags & f_style ? f_strides(*shape, itemsize()) : c_strides(*shape, itemsize()), - ptr, base) { } - - explicit array_t(size_t count, const T *ptr = nullptr, handle base = handle()) - : array({count}, {}, ptr, base) { } - - constexpr ssize_t itemsize() const { - return sizeof(T); - } - - template ssize_t index_at(Ix... index) const { - return offset_at(index...) / itemsize(); - } - - template const T* data(Ix... index) const { - return static_cast(array::data(index...)); - } - - template T* mutable_data(Ix... index) { - return static_cast(array::mutable_data(index...)); - } - - // Reference to element at a given index - template const T& at(Ix... index) const { - if (sizeof...(index) != ndim()) - fail_dim_check(sizeof...(index), "index dimension mismatch"); - return *(static_cast(array::data()) + byte_offset(ssize_t(index)...) / itemsize()); - } - - // Mutable reference to element at a given index - template T& mutable_at(Ix... index) { - if (sizeof...(index) != ndim()) - fail_dim_check(sizeof...(index), "index dimension mismatch"); - return *(static_cast(array::mutable_data()) + byte_offset(ssize_t(index)...) / itemsize()); - } - - /** - * Returns a proxy object that provides access to the array's data without bounds or - * dimensionality checking. Will throw if the array is missing the `writeable` flag. Use with - * care: the array must not be destroyed or reshaped for the duration of the returned object, - * and the caller must take care not to access invalid dimensions or dimension indices. - */ - template detail::unchecked_mutable_reference mutable_unchecked() & { - return array::mutable_unchecked(); - } - - /** - * Returns a proxy object that provides const access to the array's data without bounds or - * dimensionality checking. Unlike `unchecked()`, this does not require that the underlying - * array have the `writable` flag. Use with care: the array must not be destroyed or reshaped - * for the duration of the returned object, and the caller must take care not to access invalid - * dimensions or dimension indices. - */ - template detail::unchecked_reference unchecked() const & { - return array::unchecked(); - } - - /// Ensure that the argument is a NumPy array of the correct dtype (and if not, try to convert - /// it). In case of an error, nullptr is returned and the Python error is cleared. - static array_t ensure(handle h) { - auto result = reinterpret_steal(raw_array_t(h.ptr())); - if (!result) - PyErr_Clear(); - return result; - } - - static bool check_(handle h) { - const auto &api = detail::npy_api::get(); - return api.PyArray_Check_(h.ptr()) - && api.PyArray_EquivTypes_(detail::array_proxy(h.ptr())->descr, dtype::of().ptr()); - } - -protected: - /// Create array from any object -- always returns a new reference - static PyObject *raw_array_t(PyObject *ptr) { - if (ptr == nullptr) { - PyErr_SetString(PyExc_ValueError, "cannot create a pybind11::array_t from a nullptr"); - return nullptr; - } - return detail::npy_api::get().PyArray_FromAny_( - ptr, dtype::of().release().ptr(), 0, 0, - detail::npy_api::NPY_ARRAY_ENSUREARRAY_ | ExtraFlags, nullptr); - } -}; - -template -struct format_descriptor::value>> { - static std::string format() { - return detail::npy_format_descriptor::type>::format(); - } -}; - -template struct format_descriptor { - static std::string format() { return std::to_string(N) + "s"; } -}; -template struct format_descriptor> { - static std::string format() { return std::to_string(N) + "s"; } -}; - -template -struct format_descriptor::value>> { - static std::string format() { - return format_descriptor< - typename std::remove_cv::type>::type>::format(); - } -}; - -template -struct format_descriptor::is_array>> { - static std::string format() { - using namespace detail; - static constexpr auto extents = _("(") + array_info::extents + _(")"); - return extents.text + format_descriptor>::format(); - } -}; - -NAMESPACE_BEGIN(detail) -template -struct pyobject_caster> { - using type = array_t; - - bool load(handle src, bool convert) { - if (!convert && !type::check_(src)) - return false; - value = type::ensure(src); - return static_cast(value); - } - - static handle cast(const handle &src, return_value_policy /* policy */, handle /* parent */) { - return src.inc_ref(); - } - PYBIND11_TYPE_CASTER(type, handle_type_name::name); -}; - -template -struct compare_buffer_info::value>> { - static bool compare(const buffer_info& b) { - return npy_api::get().PyArray_EquivTypes_(dtype::of().ptr(), dtype(b).ptr()); - } -}; - -template -struct npy_format_descriptor_name; - -template -struct npy_format_descriptor_name::value>> { - static constexpr auto name = _::value>( - _("bool"), _::value>("int", "uint") + _() - ); -}; - -template -struct npy_format_descriptor_name::value>> { - static constexpr auto name = _::value || std::is_same::value>( - _("float") + _(), _("longdouble") - ); -}; - -template -struct npy_format_descriptor_name::value>> { - static constexpr auto name = _::value - || std::is_same::value>( - _("complex") + _(), _("longcomplex") - ); -}; - -template -struct npy_format_descriptor::value>> - : npy_format_descriptor_name { -private: - // NB: the order here must match the one in common.h - constexpr static const int values[15] = { - npy_api::NPY_BOOL_, - npy_api::NPY_BYTE_, npy_api::NPY_UBYTE_, npy_api::NPY_SHORT_, npy_api::NPY_USHORT_, - npy_api::NPY_INT_, npy_api::NPY_UINT_, npy_api::NPY_LONGLONG_, npy_api::NPY_ULONGLONG_, - npy_api::NPY_FLOAT_, npy_api::NPY_DOUBLE_, npy_api::NPY_LONGDOUBLE_, - npy_api::NPY_CFLOAT_, npy_api::NPY_CDOUBLE_, npy_api::NPY_CLONGDOUBLE_ - }; - -public: - static constexpr int value = values[detail::is_fmt_numeric::index]; - - static pybind11::dtype dtype() { - if (auto ptr = npy_api::get().PyArray_DescrFromType_(value)) - return reinterpret_borrow(ptr); - pybind11_fail("Unsupported buffer format!"); - } -}; - -#define PYBIND11_DECL_CHAR_FMT \ - static constexpr auto name = _("S") + _(); \ - static pybind11::dtype dtype() { return pybind11::dtype(std::string("S") + std::to_string(N)); } -template struct npy_format_descriptor { PYBIND11_DECL_CHAR_FMT }; -template struct npy_format_descriptor> { PYBIND11_DECL_CHAR_FMT }; -#undef PYBIND11_DECL_CHAR_FMT - -template struct npy_format_descriptor::is_array>> { -private: - using base_descr = npy_format_descriptor::type>; -public: - static_assert(!array_info::is_empty, "Zero-sized arrays are not supported"); - - static constexpr auto name = _("(") + array_info::extents + _(")") + base_descr::name; - static pybind11::dtype dtype() { - list shape; - array_info::append_extents(shape); - return pybind11::dtype::from_args(pybind11::make_tuple(base_descr::dtype(), shape)); - } -}; - -template struct npy_format_descriptor::value>> { -private: - using base_descr = npy_format_descriptor::type>; -public: - static constexpr auto name = base_descr::name; - static pybind11::dtype dtype() { return base_descr::dtype(); } -}; - -struct field_descriptor { - const char *name; - ssize_t offset; - ssize_t size; - std::string format; - dtype descr; -}; - -inline PYBIND11_NOINLINE void register_structured_dtype( - any_container fields, - const std::type_info& tinfo, ssize_t itemsize, - bool (*direct_converter)(PyObject *, void *&)) { - - auto& numpy_internals = get_numpy_internals(); - if (numpy_internals.get_type_info(tinfo, false)) - pybind11_fail("NumPy: dtype is already registered"); - - list names, formats, offsets; - for (auto field : *fields) { - if (!field.descr) - pybind11_fail(std::string("NumPy: unsupported field dtype: `") + - field.name + "` @ " + tinfo.name()); - names.append(PYBIND11_STR_TYPE(field.name)); - formats.append(field.descr); - offsets.append(pybind11::int_(field.offset)); - } - auto dtype_ptr = pybind11::dtype(names, formats, offsets, itemsize).release().ptr(); - - // There is an existing bug in NumPy (as of v1.11): trailing bytes are - // not encoded explicitly into the format string. This will supposedly - // get fixed in v1.12; for further details, see these: - // - https://github.com/numpy/numpy/issues/7797 - // - https://github.com/numpy/numpy/pull/7798 - // Because of this, we won't use numpy's logic to generate buffer format - // strings and will just do it ourselves. - std::vector ordered_fields(std::move(fields)); - std::sort(ordered_fields.begin(), ordered_fields.end(), - [](const field_descriptor &a, const field_descriptor &b) { return a.offset < b.offset; }); - ssize_t offset = 0; - std::ostringstream oss; - // mark the structure as unaligned with '^', because numpy and C++ don't - // always agree about alignment (particularly for complex), and we're - // explicitly listing all our padding. This depends on none of the fields - // overriding the endianness. Putting the ^ in front of individual fields - // isn't guaranteed to work due to https://github.com/numpy/numpy/issues/9049 - oss << "^T{"; - for (auto& field : ordered_fields) { - if (field.offset > offset) - oss << (field.offset - offset) << 'x'; - oss << field.format << ':' << field.name << ':'; - offset = field.offset + field.size; - } - if (itemsize > offset) - oss << (itemsize - offset) << 'x'; - oss << '}'; - auto format_str = oss.str(); - - // Sanity check: verify that NumPy properly parses our buffer format string - auto& api = npy_api::get(); - auto arr = array(buffer_info(nullptr, itemsize, format_str, 1)); - if (!api.PyArray_EquivTypes_(dtype_ptr, arr.dtype().ptr())) - pybind11_fail("NumPy: invalid buffer descriptor!"); - - auto tindex = std::type_index(tinfo); - numpy_internals.registered_dtypes[tindex] = { dtype_ptr, format_str }; - get_internals().direct_conversions[tindex].push_back(direct_converter); -} - -template struct npy_format_descriptor { - static_assert(is_pod_struct::value, "Attempt to use a non-POD or unimplemented POD type as a numpy dtype"); - - static constexpr auto name = make_caster::name; - - static pybind11::dtype dtype() { - return reinterpret_borrow(dtype_ptr()); - } - - static std::string format() { - static auto format_str = get_numpy_internals().get_type_info(true)->format_str; - return format_str; - } - - static void register_dtype(any_container fields) { - register_structured_dtype(std::move(fields), typeid(typename std::remove_cv::type), - sizeof(T), &direct_converter); - } - -private: - static PyObject* dtype_ptr() { - static PyObject* ptr = get_numpy_internals().get_type_info(true)->dtype_ptr; - return ptr; - } - - static bool direct_converter(PyObject *obj, void*& value) { - auto& api = npy_api::get(); - if (!PyObject_TypeCheck(obj, api.PyVoidArrType_Type_)) - return false; - if (auto descr = reinterpret_steal(api.PyArray_DescrFromScalar_(obj))) { - if (api.PyArray_EquivTypes_(dtype_ptr(), descr.ptr())) { - value = ((PyVoidScalarObject_Proxy *) obj)->obval; - return true; - } - } - return false; - } -}; - -#ifdef __CLION_IDE__ // replace heavy macro with dummy code for the IDE (doesn't affect code) -# define PYBIND11_NUMPY_DTYPE(Type, ...) ((void)0) -# define PYBIND11_NUMPY_DTYPE_EX(Type, ...) ((void)0) -#else - -#define PYBIND11_FIELD_DESCRIPTOR_EX(T, Field, Name) \ - ::pybind11::detail::field_descriptor { \ - Name, offsetof(T, Field), sizeof(decltype(std::declval().Field)), \ - ::pybind11::format_descriptor().Field)>::format(), \ - ::pybind11::detail::npy_format_descriptor().Field)>::dtype() \ - } - -// Extract name, offset and format descriptor for a struct field -#define PYBIND11_FIELD_DESCRIPTOR(T, Field) PYBIND11_FIELD_DESCRIPTOR_EX(T, Field, #Field) - -// The main idea of this macro is borrowed from https://github.com/swansontec/map-macro -// (C) William Swanson, Paul Fultz -#define PYBIND11_EVAL0(...) __VA_ARGS__ -#define PYBIND11_EVAL1(...) PYBIND11_EVAL0 (PYBIND11_EVAL0 (PYBIND11_EVAL0 (__VA_ARGS__))) -#define PYBIND11_EVAL2(...) PYBIND11_EVAL1 (PYBIND11_EVAL1 (PYBIND11_EVAL1 (__VA_ARGS__))) -#define PYBIND11_EVAL3(...) PYBIND11_EVAL2 (PYBIND11_EVAL2 (PYBIND11_EVAL2 (__VA_ARGS__))) -#define PYBIND11_EVAL4(...) PYBIND11_EVAL3 (PYBIND11_EVAL3 (PYBIND11_EVAL3 (__VA_ARGS__))) -#define PYBIND11_EVAL(...) PYBIND11_EVAL4 (PYBIND11_EVAL4 (PYBIND11_EVAL4 (__VA_ARGS__))) -#define PYBIND11_MAP_END(...) -#define PYBIND11_MAP_OUT -#define PYBIND11_MAP_COMMA , -#define PYBIND11_MAP_GET_END() 0, PYBIND11_MAP_END -#define PYBIND11_MAP_NEXT0(test, next, ...) next PYBIND11_MAP_OUT -#define PYBIND11_MAP_NEXT1(test, next) PYBIND11_MAP_NEXT0 (test, next, 0) -#define PYBIND11_MAP_NEXT(test, next) PYBIND11_MAP_NEXT1 (PYBIND11_MAP_GET_END test, next) -#ifdef _MSC_VER // MSVC is not as eager to expand macros, hence this workaround -#define PYBIND11_MAP_LIST_NEXT1(test, next) \ - PYBIND11_EVAL0 (PYBIND11_MAP_NEXT0 (test, PYBIND11_MAP_COMMA next, 0)) -#else -#define PYBIND11_MAP_LIST_NEXT1(test, next) \ - PYBIND11_MAP_NEXT0 (test, PYBIND11_MAP_COMMA next, 0) -#endif -#define PYBIND11_MAP_LIST_NEXT(test, next) \ - PYBIND11_MAP_LIST_NEXT1 (PYBIND11_MAP_GET_END test, next) -#define PYBIND11_MAP_LIST0(f, t, x, peek, ...) \ - f(t, x) PYBIND11_MAP_LIST_NEXT (peek, PYBIND11_MAP_LIST1) (f, t, peek, __VA_ARGS__) -#define PYBIND11_MAP_LIST1(f, t, x, peek, ...) \ - f(t, x) PYBIND11_MAP_LIST_NEXT (peek, PYBIND11_MAP_LIST0) (f, t, peek, __VA_ARGS__) -// PYBIND11_MAP_LIST(f, t, a1, a2, ...) expands to f(t, a1), f(t, a2), ... -#define PYBIND11_MAP_LIST(f, t, ...) \ - PYBIND11_EVAL (PYBIND11_MAP_LIST1 (f, t, __VA_ARGS__, (), 0)) - -#define PYBIND11_NUMPY_DTYPE(Type, ...) \ - ::pybind11::detail::npy_format_descriptor::register_dtype \ - (::std::vector<::pybind11::detail::field_descriptor> \ - {PYBIND11_MAP_LIST (PYBIND11_FIELD_DESCRIPTOR, Type, __VA_ARGS__)}) - -#ifdef _MSC_VER -#define PYBIND11_MAP2_LIST_NEXT1(test, next) \ - PYBIND11_EVAL0 (PYBIND11_MAP_NEXT0 (test, PYBIND11_MAP_COMMA next, 0)) -#else -#define PYBIND11_MAP2_LIST_NEXT1(test, next) \ - PYBIND11_MAP_NEXT0 (test, PYBIND11_MAP_COMMA next, 0) -#endif -#define PYBIND11_MAP2_LIST_NEXT(test, next) \ - PYBIND11_MAP2_LIST_NEXT1 (PYBIND11_MAP_GET_END test, next) -#define PYBIND11_MAP2_LIST0(f, t, x1, x2, peek, ...) \ - f(t, x1, x2) PYBIND11_MAP2_LIST_NEXT (peek, PYBIND11_MAP2_LIST1) (f, t, peek, __VA_ARGS__) -#define PYBIND11_MAP2_LIST1(f, t, x1, x2, peek, ...) \ - f(t, x1, x2) PYBIND11_MAP2_LIST_NEXT (peek, PYBIND11_MAP2_LIST0) (f, t, peek, __VA_ARGS__) -// PYBIND11_MAP2_LIST(f, t, a1, a2, ...) expands to f(t, a1, a2), f(t, a3, a4), ... -#define PYBIND11_MAP2_LIST(f, t, ...) \ - PYBIND11_EVAL (PYBIND11_MAP2_LIST1 (f, t, __VA_ARGS__, (), 0)) - -#define PYBIND11_NUMPY_DTYPE_EX(Type, ...) \ - ::pybind11::detail::npy_format_descriptor::register_dtype \ - (::std::vector<::pybind11::detail::field_descriptor> \ - {PYBIND11_MAP2_LIST (PYBIND11_FIELD_DESCRIPTOR_EX, Type, __VA_ARGS__)}) - -#endif // __CLION_IDE__ - -template -using array_iterator = typename std::add_pointer::type; - -template -array_iterator array_begin(const buffer_info& buffer) { - return array_iterator(reinterpret_cast(buffer.ptr)); -} - -template -array_iterator array_end(const buffer_info& buffer) { - return array_iterator(reinterpret_cast(buffer.ptr) + buffer.size); -} - -class common_iterator { -public: - using container_type = std::vector; - using value_type = container_type::value_type; - using size_type = container_type::size_type; - - common_iterator() : p_ptr(0), m_strides() {} - - common_iterator(void* ptr, const container_type& strides, const container_type& shape) - : p_ptr(reinterpret_cast(ptr)), m_strides(strides.size()) { - m_strides.back() = static_cast(strides.back()); - for (size_type i = m_strides.size() - 1; i != 0; --i) { - size_type j = i - 1; - value_type s = static_cast(shape[i]); - m_strides[j] = strides[j] + m_strides[i] - strides[i] * s; - } - } - - void increment(size_type dim) { - p_ptr += m_strides[dim]; - } - - void* data() const { - return p_ptr; - } - -private: - char* p_ptr; - container_type m_strides; -}; - -template class multi_array_iterator { -public: - using container_type = std::vector; - - multi_array_iterator(const std::array &buffers, - const container_type &shape) - : m_shape(shape.size()), m_index(shape.size(), 0), - m_common_iterator() { - - // Manual copy to avoid conversion warning if using std::copy - for (size_t i = 0; i < shape.size(); ++i) - m_shape[i] = shape[i]; - - container_type strides(shape.size()); - for (size_t i = 0; i < N; ++i) - init_common_iterator(buffers[i], shape, m_common_iterator[i], strides); - } - - multi_array_iterator& operator++() { - for (size_t j = m_index.size(); j != 0; --j) { - size_t i = j - 1; - if (++m_index[i] != m_shape[i]) { - increment_common_iterator(i); - break; - } else { - m_index[i] = 0; - } - } - return *this; - } - - template T* data() const { - return reinterpret_cast(m_common_iterator[K].data()); - } - -private: - - using common_iter = common_iterator; - - void init_common_iterator(const buffer_info &buffer, - const container_type &shape, - common_iter &iterator, - container_type &strides) { - auto buffer_shape_iter = buffer.shape.rbegin(); - auto buffer_strides_iter = buffer.strides.rbegin(); - auto shape_iter = shape.rbegin(); - auto strides_iter = strides.rbegin(); - - while (buffer_shape_iter != buffer.shape.rend()) { - if (*shape_iter == *buffer_shape_iter) - *strides_iter = *buffer_strides_iter; - else - *strides_iter = 0; - - ++buffer_shape_iter; - ++buffer_strides_iter; - ++shape_iter; - ++strides_iter; - } - - std::fill(strides_iter, strides.rend(), 0); - iterator = common_iter(buffer.ptr, strides, shape); - } - - void increment_common_iterator(size_t dim) { - for (auto &iter : m_common_iterator) - iter.increment(dim); - } - - container_type m_shape; - container_type m_index; - std::array m_common_iterator; -}; - -enum class broadcast_trivial { non_trivial, c_trivial, f_trivial }; - -// Populates the shape and number of dimensions for the set of buffers. Returns a broadcast_trivial -// enum value indicating whether the broadcast is "trivial"--that is, has each buffer being either a -// singleton or a full-size, C-contiguous (`c_trivial`) or Fortran-contiguous (`f_trivial`) storage -// buffer; returns `non_trivial` otherwise. -template -broadcast_trivial broadcast(const std::array &buffers, ssize_t &ndim, std::vector &shape) { - ndim = std::accumulate(buffers.begin(), buffers.end(), ssize_t(0), [](ssize_t res, const buffer_info &buf) { - return std::max(res, buf.ndim); - }); - - shape.clear(); - shape.resize((size_t) ndim, 1); - - // Figure out the output size, and make sure all input arrays conform (i.e. are either size 1 or - // the full size). - for (size_t i = 0; i < N; ++i) { - auto res_iter = shape.rbegin(); - auto end = buffers[i].shape.rend(); - for (auto shape_iter = buffers[i].shape.rbegin(); shape_iter != end; ++shape_iter, ++res_iter) { - const auto &dim_size_in = *shape_iter; - auto &dim_size_out = *res_iter; - - // Each input dimension can either be 1 or `n`, but `n` values must match across buffers - if (dim_size_out == 1) - dim_size_out = dim_size_in; - else if (dim_size_in != 1 && dim_size_in != dim_size_out) - pybind11_fail("pybind11::vectorize: incompatible size/dimension of inputs!"); - } - } - - bool trivial_broadcast_c = true; - bool trivial_broadcast_f = true; - for (size_t i = 0; i < N && (trivial_broadcast_c || trivial_broadcast_f); ++i) { - if (buffers[i].size == 1) - continue; - - // Require the same number of dimensions: - if (buffers[i].ndim != ndim) - return broadcast_trivial::non_trivial; - - // Require all dimensions be full-size: - if (!std::equal(buffers[i].shape.cbegin(), buffers[i].shape.cend(), shape.cbegin())) - return broadcast_trivial::non_trivial; - - // Check for C contiguity (but only if previous inputs were also C contiguous) - if (trivial_broadcast_c) { - ssize_t expect_stride = buffers[i].itemsize; - auto end = buffers[i].shape.crend(); - for (auto shape_iter = buffers[i].shape.crbegin(), stride_iter = buffers[i].strides.crbegin(); - trivial_broadcast_c && shape_iter != end; ++shape_iter, ++stride_iter) { - if (expect_stride == *stride_iter) - expect_stride *= *shape_iter; - else - trivial_broadcast_c = false; - } - } - - // Check for Fortran contiguity (if previous inputs were also F contiguous) - if (trivial_broadcast_f) { - ssize_t expect_stride = buffers[i].itemsize; - auto end = buffers[i].shape.cend(); - for (auto shape_iter = buffers[i].shape.cbegin(), stride_iter = buffers[i].strides.cbegin(); - trivial_broadcast_f && shape_iter != end; ++shape_iter, ++stride_iter) { - if (expect_stride == *stride_iter) - expect_stride *= *shape_iter; - else - trivial_broadcast_f = false; - } - } - } - - return - trivial_broadcast_c ? broadcast_trivial::c_trivial : - trivial_broadcast_f ? broadcast_trivial::f_trivial : - broadcast_trivial::non_trivial; -} - -template -struct vectorize_arg { - static_assert(!std::is_rvalue_reference::value, "Functions with rvalue reference arguments cannot be vectorized"); - // The wrapped function gets called with this type: - using call_type = remove_reference_t; - // Is this a vectorized argument? - static constexpr bool vectorize = - satisfies_any_of::value && - satisfies_none_of::value && - (!std::is_reference::value || - (std::is_lvalue_reference::value && std::is_const::value)); - // Accept this type: an array for vectorized types, otherwise the type as-is: - using type = conditional_t, array::forcecast>, T>; -}; - -template -struct vectorize_helper { -private: - static constexpr size_t N = sizeof...(Args); - static constexpr size_t NVectorized = constexpr_sum(vectorize_arg::vectorize...); - static_assert(NVectorized >= 1, - "pybind11::vectorize(...) requires a function with at least one vectorizable argument"); - -public: - template - explicit vectorize_helper(T &&f) : f(std::forward(f)) { } - - object operator()(typename vectorize_arg::type... args) { - return run(args..., - make_index_sequence(), - select_indices::vectorize...>(), - make_index_sequence()); - } - -private: - remove_reference_t f; - - // Internal compiler error in MSVC 19.16.27025.1 (Visual Studio 2017 15.9.4), when compiling with "/permissive-" flag - // when arg_call_types is manually inlined. - using arg_call_types = std::tuple::call_type...>; - template using param_n_t = typename std::tuple_element::type; - - // Runs a vectorized function given arguments tuple and three index sequences: - // - Index is the full set of 0 ... (N-1) argument indices; - // - VIndex is the subset of argument indices with vectorized parameters, letting us access - // vectorized arguments (anything not in this sequence is passed through) - // - BIndex is a incremental sequence (beginning at 0) of the same size as VIndex, so that - // we can store vectorized buffer_infos in an array (argument VIndex has its buffer at - // index BIndex in the array). - template object run( - typename vectorize_arg::type &...args, - index_sequence i_seq, index_sequence vi_seq, index_sequence bi_seq) { - - // Pointers to values the function was called with; the vectorized ones set here will start - // out as array_t pointers, but they will be changed them to T pointers before we make - // call the wrapped function. Non-vectorized pointers are left as-is. - std::array params{{ &args... }}; - - // The array of `buffer_info`s of vectorized arguments: - std::array buffers{{ reinterpret_cast(params[VIndex])->request()... }}; - - /* Determine dimensions parameters of output array */ - ssize_t nd = 0; - std::vector shape(0); - auto trivial = broadcast(buffers, nd, shape); - size_t ndim = (size_t) nd; - - size_t size = std::accumulate(shape.begin(), shape.end(), (size_t) 1, std::multiplies()); - - // If all arguments are 0-dimension arrays (i.e. single values) return a plain value (i.e. - // not wrapped in an array). - if (size == 1 && ndim == 0) { - PYBIND11_EXPAND_SIDE_EFFECTS(params[VIndex] = buffers[BIndex].ptr); - return cast(f(*reinterpret_cast *>(params[Index])...)); - } - - array_t result; - if (trivial == broadcast_trivial::f_trivial) result = array_t(shape); - else result = array_t(shape); - - if (size == 0) return result; - - /* Call the function */ - if (trivial == broadcast_trivial::non_trivial) - apply_broadcast(buffers, params, result, i_seq, vi_seq, bi_seq); - else - apply_trivial(buffers, params, result.mutable_data(), size, i_seq, vi_seq, bi_seq); - - return result; - } - - template - void apply_trivial(std::array &buffers, - std::array ¶ms, - Return *out, - size_t size, - index_sequence, index_sequence, index_sequence) { - - // Initialize an array of mutable byte references and sizes with references set to the - // appropriate pointer in `params`; as we iterate, we'll increment each pointer by its size - // (except for singletons, which get an increment of 0). - std::array, NVectorized> vecparams{{ - std::pair( - reinterpret_cast(params[VIndex] = buffers[BIndex].ptr), - buffers[BIndex].size == 1 ? 0 : sizeof(param_n_t) - )... - }}; - - for (size_t i = 0; i < size; ++i) { - out[i] = f(*reinterpret_cast *>(params[Index])...); - for (auto &x : vecparams) x.first += x.second; - } - } - - template - void apply_broadcast(std::array &buffers, - std::array ¶ms, - array_t &output_array, - index_sequence, index_sequence, index_sequence) { - - buffer_info output = output_array.request(); - multi_array_iterator input_iter(buffers, output.shape); - - for (array_iterator iter = array_begin(output), end = array_end(output); - iter != end; - ++iter, ++input_iter) { - PYBIND11_EXPAND_SIDE_EFFECTS(( - params[VIndex] = input_iter.template data() - )); - *iter = f(*reinterpret_cast *>(std::get(params))...); - } - } -}; - -template -vectorize_helper -vectorize_extractor(const Func &f, Return (*) (Args ...)) { - return detail::vectorize_helper(f); -} - -template struct handle_type_name> { - static constexpr auto name = _("numpy.ndarray[") + npy_format_descriptor::name + _("]"); -}; - -NAMESPACE_END(detail) - -// Vanilla pointer vectorizer: -template -detail::vectorize_helper -vectorize(Return (*f) (Args ...)) { - return detail::vectorize_helper(f); -} - -// lambda vectorizer: -template ::value, int> = 0> -auto vectorize(Func &&f) -> decltype( - detail::vectorize_extractor(std::forward(f), (detail::function_signature_t *) nullptr)) { - return detail::vectorize_extractor(std::forward(f), (detail::function_signature_t *) nullptr); -} - -// Vectorize a class method (non-const): -template ())), Return, Class *, Args...>> -Helper vectorize(Return (Class::*f)(Args...)) { - return Helper(std::mem_fn(f)); -} - -// Vectorize a class method (const): -template ())), Return, const Class *, Args...>> -Helper vectorize(Return (Class::*f)(Args...) const) { - return Helper(std::mem_fn(f)); -} - -NAMESPACE_END(PYBIND11_NAMESPACE) - -#if defined(_MSC_VER) -#pragma warning(pop) -#endif diff --git a/ptocr/postprocess/piexlmerge/include/pybind11/operators.h b/ptocr/postprocess/piexlmerge/include/pybind11/operators.h deleted file mode 100644 index b3dd62c..0000000 --- a/ptocr/postprocess/piexlmerge/include/pybind11/operators.h +++ /dev/null @@ -1,168 +0,0 @@ -/* - pybind11/operator.h: Metatemplates for operator overloading - - Copyright (c) 2016 Wenzel Jakob - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#include "pybind11.h" - -#if defined(__clang__) && !defined(__INTEL_COMPILER) -# pragma clang diagnostic ignored "-Wunsequenced" // multiple unsequenced modifications to 'self' (when using def(py::self OP Type())) -#elif defined(_MSC_VER) -# pragma warning(push) -# pragma warning(disable: 4127) // warning C4127: Conditional expression is constant -#endif - -NAMESPACE_BEGIN(PYBIND11_NAMESPACE) -NAMESPACE_BEGIN(detail) - -/// Enumeration with all supported operator types -enum op_id : int { - op_add, op_sub, op_mul, op_div, op_mod, op_divmod, op_pow, op_lshift, - op_rshift, op_and, op_xor, op_or, op_neg, op_pos, op_abs, op_invert, - op_int, op_long, op_float, op_str, op_cmp, op_gt, op_ge, op_lt, op_le, - op_eq, op_ne, op_iadd, op_isub, op_imul, op_idiv, op_imod, op_ilshift, - op_irshift, op_iand, op_ixor, op_ior, op_complex, op_bool, op_nonzero, - op_repr, op_truediv, op_itruediv, op_hash -}; - -enum op_type : int { - op_l, /* base type on left */ - op_r, /* base type on right */ - op_u /* unary operator */ -}; - -struct self_t { }; -static const self_t self = self_t(); - -/// Type for an unused type slot -struct undefined_t { }; - -/// Don't warn about an unused variable -inline self_t __self() { return self; } - -/// base template of operator implementations -template struct op_impl { }; - -/// Operator implementation generator -template struct op_ { - template void execute(Class &cl, const Extra&... extra) const { - using Base = typename Class::type; - using L_type = conditional_t::value, Base, L>; - using R_type = conditional_t::value, Base, R>; - using op = op_impl; - cl.def(op::name(), &op::execute, is_operator(), extra...); - #if PY_MAJOR_VERSION < 3 - if (id == op_truediv || id == op_itruediv) - cl.def(id == op_itruediv ? "__idiv__" : ot == op_l ? "__div__" : "__rdiv__", - &op::execute, is_operator(), extra...); - #endif - } - template void execute_cast(Class &cl, const Extra&... extra) const { - using Base = typename Class::type; - using L_type = conditional_t::value, Base, L>; - using R_type = conditional_t::value, Base, R>; - using op = op_impl; - cl.def(op::name(), &op::execute_cast, is_operator(), extra...); - #if PY_MAJOR_VERSION < 3 - if (id == op_truediv || id == op_itruediv) - cl.def(id == op_itruediv ? "__idiv__" : ot == op_l ? "__div__" : "__rdiv__", - &op::execute, is_operator(), extra...); - #endif - } -}; - -#define PYBIND11_BINARY_OPERATOR(id, rid, op, expr) \ -template struct op_impl { \ - static char const* name() { return "__" #id "__"; } \ - static auto execute(const L &l, const R &r) -> decltype(expr) { return (expr); } \ - static B execute_cast(const L &l, const R &r) { return B(expr); } \ -}; \ -template struct op_impl { \ - static char const* name() { return "__" #rid "__"; } \ - static auto execute(const R &r, const L &l) -> decltype(expr) { return (expr); } \ - static B execute_cast(const R &r, const L &l) { return B(expr); } \ -}; \ -inline op_ op(const self_t &, const self_t &) { \ - return op_(); \ -} \ -template op_ op(const self_t &, const T &) { \ - return op_(); \ -} \ -template op_ op(const T &, const self_t &) { \ - return op_(); \ -} - -#define PYBIND11_INPLACE_OPERATOR(id, op, expr) \ -template struct op_impl { \ - static char const* name() { return "__" #id "__"; } \ - static auto execute(L &l, const R &r) -> decltype(expr) { return expr; } \ - static B execute_cast(L &l, const R &r) { return B(expr); } \ -}; \ -template op_ op(const self_t &, const T &) { \ - return op_(); \ -} - -#define PYBIND11_UNARY_OPERATOR(id, op, expr) \ -template struct op_impl { \ - static char const* name() { return "__" #id "__"; } \ - static auto execute(const L &l) -> decltype(expr) { return expr; } \ - static B execute_cast(const L &l) { return B(expr); } \ -}; \ -inline op_ op(const self_t &) { \ - return op_(); \ -} - -PYBIND11_BINARY_OPERATOR(sub, rsub, operator-, l - r) -PYBIND11_BINARY_OPERATOR(add, radd, operator+, l + r) -PYBIND11_BINARY_OPERATOR(mul, rmul, operator*, l * r) -PYBIND11_BINARY_OPERATOR(truediv, rtruediv, operator/, l / r) -PYBIND11_BINARY_OPERATOR(mod, rmod, operator%, l % r) -PYBIND11_BINARY_OPERATOR(lshift, rlshift, operator<<, l << r) -PYBIND11_BINARY_OPERATOR(rshift, rrshift, operator>>, l >> r) -PYBIND11_BINARY_OPERATOR(and, rand, operator&, l & r) -PYBIND11_BINARY_OPERATOR(xor, rxor, operator^, l ^ r) -PYBIND11_BINARY_OPERATOR(eq, eq, operator==, l == r) -PYBIND11_BINARY_OPERATOR(ne, ne, operator!=, l != r) -PYBIND11_BINARY_OPERATOR(or, ror, operator|, l | r) -PYBIND11_BINARY_OPERATOR(gt, lt, operator>, l > r) -PYBIND11_BINARY_OPERATOR(ge, le, operator>=, l >= r) -PYBIND11_BINARY_OPERATOR(lt, gt, operator<, l < r) -PYBIND11_BINARY_OPERATOR(le, ge, operator<=, l <= r) -//PYBIND11_BINARY_OPERATOR(pow, rpow, pow, std::pow(l, r)) -PYBIND11_INPLACE_OPERATOR(iadd, operator+=, l += r) -PYBIND11_INPLACE_OPERATOR(isub, operator-=, l -= r) -PYBIND11_INPLACE_OPERATOR(imul, operator*=, l *= r) -PYBIND11_INPLACE_OPERATOR(itruediv, operator/=, l /= r) -PYBIND11_INPLACE_OPERATOR(imod, operator%=, l %= r) -PYBIND11_INPLACE_OPERATOR(ilshift, operator<<=, l <<= r) -PYBIND11_INPLACE_OPERATOR(irshift, operator>>=, l >>= r) -PYBIND11_INPLACE_OPERATOR(iand, operator&=, l &= r) -PYBIND11_INPLACE_OPERATOR(ixor, operator^=, l ^= r) -PYBIND11_INPLACE_OPERATOR(ior, operator|=, l |= r) -PYBIND11_UNARY_OPERATOR(neg, operator-, -l) -PYBIND11_UNARY_OPERATOR(pos, operator+, +l) -PYBIND11_UNARY_OPERATOR(abs, abs, std::abs(l)) -PYBIND11_UNARY_OPERATOR(hash, hash, std::hash()(l)) -PYBIND11_UNARY_OPERATOR(invert, operator~, (~l)) -PYBIND11_UNARY_OPERATOR(bool, operator!, !!l) -PYBIND11_UNARY_OPERATOR(int, int_, (int) l) -PYBIND11_UNARY_OPERATOR(float, float_, (double) l) - -#undef PYBIND11_BINARY_OPERATOR -#undef PYBIND11_INPLACE_OPERATOR -#undef PYBIND11_UNARY_OPERATOR -NAMESPACE_END(detail) - -using detail::self; - -NAMESPACE_END(PYBIND11_NAMESPACE) - -#if defined(_MSC_VER) -# pragma warning(pop) -#endif diff --git a/ptocr/postprocess/piexlmerge/include/pybind11/options.h b/ptocr/postprocess/piexlmerge/include/pybind11/options.h deleted file mode 100644 index cc1e1f6..0000000 --- a/ptocr/postprocess/piexlmerge/include/pybind11/options.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - pybind11/options.h: global settings that are configurable at runtime. - - Copyright (c) 2016 Wenzel Jakob - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#include "detail/common.h" - -NAMESPACE_BEGIN(PYBIND11_NAMESPACE) - -class options { -public: - - // Default RAII constructor, which leaves settings as they currently are. - options() : previous_state(global_state()) {} - - // Class is non-copyable. - options(const options&) = delete; - options& operator=(const options&) = delete; - - // Destructor, which restores settings that were in effect before. - ~options() { - global_state() = previous_state; - } - - // Setter methods (affect the global state): - - options& disable_user_defined_docstrings() & { global_state().show_user_defined_docstrings = false; return *this; } - - options& enable_user_defined_docstrings() & { global_state().show_user_defined_docstrings = true; return *this; } - - options& disable_function_signatures() & { global_state().show_function_signatures = false; return *this; } - - options& enable_function_signatures() & { global_state().show_function_signatures = true; return *this; } - - // Getter methods (return the global state): - - static bool show_user_defined_docstrings() { return global_state().show_user_defined_docstrings; } - - static bool show_function_signatures() { return global_state().show_function_signatures; } - - // This type is not meant to be allocated on the heap. - void* operator new(size_t) = delete; - -private: - - struct state { - bool show_user_defined_docstrings = true; //< Include user-supplied texts in docstrings. - bool show_function_signatures = true; //< Include auto-generated function signatures in docstrings. - }; - - static state &global_state() { - static state instance; - return instance; - } - - state previous_state; -}; - -NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/ptocr/postprocess/piexlmerge/include/pybind11/pybind11.h b/ptocr/postprocess/piexlmerge/include/pybind11/pybind11.h deleted file mode 100644 index 7fa0f0e..0000000 --- a/ptocr/postprocess/piexlmerge/include/pybind11/pybind11.h +++ /dev/null @@ -1,2094 +0,0 @@ -/* - pybind11/pybind11.h: Main header file of the C++11 python - binding generator library - - Copyright (c) 2016 Wenzel Jakob - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#if defined(__INTEL_COMPILER) -# pragma warning push -# pragma warning disable 68 // integer conversion resulted in a change of sign -# pragma warning disable 186 // pointless comparison of unsigned integer with zero -# pragma warning disable 878 // incompatible exception specifications -# pragma warning disable 1334 // the "template" keyword used for syntactic disambiguation may only be used within a template -# pragma warning disable 1682 // implicit conversion of a 64-bit integral type to a smaller integral type (potential portability problem) -# pragma warning disable 1786 // function "strdup" was declared deprecated -# pragma warning disable 1875 // offsetof applied to non-POD (Plain Old Data) types is nonstandard -# pragma warning disable 2196 // warning #2196: routine is both "inline" and "noinline" -#elif defined(_MSC_VER) -# pragma warning(push) -# pragma warning(disable: 4100) // warning C4100: Unreferenced formal parameter -# pragma warning(disable: 4127) // warning C4127: Conditional expression is constant -# pragma warning(disable: 4512) // warning C4512: Assignment operator was implicitly defined as deleted -# pragma warning(disable: 4800) // warning C4800: 'int': forcing value to bool 'true' or 'false' (performance warning) -# pragma warning(disable: 4996) // warning C4996: The POSIX name for this item is deprecated. Instead, use the ISO C and C++ conformant name -# pragma warning(disable: 4702) // warning C4702: unreachable code -# pragma warning(disable: 4522) // warning C4522: multiple assignment operators specified -#elif defined(__GNUG__) && !defined(__clang__) -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wunused-but-set-parameter" -# pragma GCC diagnostic ignored "-Wunused-but-set-variable" -# pragma GCC diagnostic ignored "-Wmissing-field-initializers" -# pragma GCC diagnostic ignored "-Wstrict-aliasing" -# pragma GCC diagnostic ignored "-Wattributes" -# if __GNUC__ >= 7 -# pragma GCC diagnostic ignored "-Wnoexcept-type" -# endif -#endif - -#include "attr.h" -#include "options.h" -#include "detail/class.h" -#include "detail/init.h" - -NAMESPACE_BEGIN(PYBIND11_NAMESPACE) - -/// Wraps an arbitrary C++ function/method/lambda function/.. into a callable Python object -class cpp_function : public function { -public: - cpp_function() { } - cpp_function(std::nullptr_t) { } - - /// Construct a cpp_function from a vanilla function pointer - template - cpp_function(Return (*f)(Args...), const Extra&... extra) { - initialize(f, f, extra...); - } - - /// Construct a cpp_function from a lambda function (possibly with internal state) - template ::value>> - cpp_function(Func &&f, const Extra&... extra) { - initialize(std::forward(f), - (detail::function_signature_t *) nullptr, extra...); - } - - /// Construct a cpp_function from a class method (non-const) - template - cpp_function(Return (Class::*f)(Arg...), const Extra&... extra) { - initialize([f](Class *c, Arg... args) -> Return { return (c->*f)(args...); }, - (Return (*) (Class *, Arg...)) nullptr, extra...); - } - - /// Construct a cpp_function from a class method (const) - template - cpp_function(Return (Class::*f)(Arg...) const, const Extra&... extra) { - initialize([f](const Class *c, Arg... args) -> Return { return (c->*f)(args...); }, - (Return (*)(const Class *, Arg ...)) nullptr, extra...); - } - - /// Return the function name - object name() const { return attr("__name__"); } - -protected: - /// Space optimization: don't inline this frequently instantiated fragment - PYBIND11_NOINLINE detail::function_record *make_function_record() { - return new detail::function_record(); - } - - /// Special internal constructor for functors, lambda functions, etc. - template - void initialize(Func &&f, Return (*)(Args...), const Extra&... extra) { - using namespace detail; - struct capture { remove_reference_t f; }; - - /* Store the function including any extra state it might have (e.g. a lambda capture object) */ - auto rec = make_function_record(); - - /* Store the capture object directly in the function record if there is enough space */ - if (sizeof(capture) <= sizeof(rec->data)) { - /* Without these pragmas, GCC warns that there might not be - enough space to use the placement new operator. However, the - 'if' statement above ensures that this is the case. */ -#if defined(__GNUG__) && !defined(__clang__) && __GNUC__ >= 6 -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wplacement-new" -#endif - new ((capture *) &rec->data) capture { std::forward(f) }; -#if defined(__GNUG__) && !defined(__clang__) && __GNUC__ >= 6 -# pragma GCC diagnostic pop -#endif - if (!std::is_trivially_destructible::value) - rec->free_data = [](function_record *r) { ((capture *) &r->data)->~capture(); }; - } else { - rec->data[0] = new capture { std::forward(f) }; - rec->free_data = [](function_record *r) { delete ((capture *) r->data[0]); }; - } - - /* Type casters for the function arguments and return value */ - using cast_in = argument_loader; - using cast_out = make_caster< - conditional_t::value, void_type, Return> - >; - - static_assert(expected_num_args(sizeof...(Args), cast_in::has_args, cast_in::has_kwargs), - "The number of argument annotations does not match the number of function arguments"); - - /* Dispatch code which converts function arguments and performs the actual function call */ - rec->impl = [](function_call &call) -> handle { - cast_in args_converter; - - /* Try to cast the function arguments into the C++ domain */ - if (!args_converter.load_args(call)) - return PYBIND11_TRY_NEXT_OVERLOAD; - - /* Invoke call policy pre-call hook */ - process_attributes::precall(call); - - /* Get a pointer to the capture object */ - auto data = (sizeof(capture) <= sizeof(call.func.data) - ? &call.func.data : call.func.data[0]); - capture *cap = const_cast(reinterpret_cast(data)); - - /* Override policy for rvalues -- usually to enforce rvp::move on an rvalue */ - return_value_policy policy = return_value_policy_override::policy(call.func.policy); - - /* Function scope guard -- defaults to the compile-to-nothing `void_type` */ - using Guard = extract_guard_t; - - /* Perform the function call */ - handle result = cast_out::cast( - std::move(args_converter).template call(cap->f), policy, call.parent); - - /* Invoke call policy post-call hook */ - process_attributes::postcall(call, result); - - return result; - }; - - /* Process any user-provided function attributes */ - process_attributes::init(extra..., rec); - - /* Generate a readable signature describing the function's arguments and return value types */ - static constexpr auto signature = _("(") + cast_in::arg_names + _(") -> ") + cast_out::name; - PYBIND11_DESCR_CONSTEXPR auto types = decltype(signature)::types(); - - /* Register the function with Python from generic (non-templated) code */ - initialize_generic(rec, signature.text, types.data(), sizeof...(Args)); - - if (cast_in::has_args) rec->has_args = true; - if (cast_in::has_kwargs) rec->has_kwargs = true; - - /* Stash some additional information used by an important optimization in 'functional.h' */ - using FunctionType = Return (*)(Args...); - constexpr bool is_function_ptr = - std::is_convertible::value && - sizeof(capture) == sizeof(void *); - if (is_function_ptr) { - rec->is_stateless = true; - rec->data[1] = const_cast(reinterpret_cast(&typeid(FunctionType))); - } - } - - /// Register a function call with Python (generic non-templated code goes here) - void initialize_generic(detail::function_record *rec, const char *text, - const std::type_info *const *types, size_t args) { - - /* Create copies of all referenced C-style strings */ - rec->name = strdup(rec->name ? rec->name : ""); - if (rec->doc) rec->doc = strdup(rec->doc); - for (auto &a: rec->args) { - if (a.name) - a.name = strdup(a.name); - if (a.descr) - a.descr = strdup(a.descr); - else if (a.value) - a.descr = strdup(a.value.attr("__repr__")().cast().c_str()); - } - - rec->is_constructor = !strcmp(rec->name, "__init__") || !strcmp(rec->name, "__setstate__"); - -#if !defined(NDEBUG) && !defined(PYBIND11_DISABLE_NEW_STYLE_INIT_WARNING) - if (rec->is_constructor && !rec->is_new_style_constructor) { - const auto class_name = std::string(((PyTypeObject *) rec->scope.ptr())->tp_name); - const auto func_name = std::string(rec->name); - PyErr_WarnEx( - PyExc_FutureWarning, - ("pybind11-bound class '" + class_name + "' is using an old-style " - "placement-new '" + func_name + "' which has been deprecated. See " - "the upgrade guide in pybind11's docs. This message is only visible " - "when compiled in debug mode.").c_str(), 0 - ); - } -#endif - - /* Generate a proper function signature */ - std::string signature; - size_t type_index = 0, arg_index = 0; - for (auto *pc = text; *pc != '\0'; ++pc) { - const auto c = *pc; - - if (c == '{') { - // Write arg name for everything except *args and **kwargs. - if (*(pc + 1) == '*') - continue; - - if (arg_index < rec->args.size() && rec->args[arg_index].name) { - signature += rec->args[arg_index].name; - } else if (arg_index == 0 && rec->is_method) { - signature += "self"; - } else { - signature += "arg" + std::to_string(arg_index - (rec->is_method ? 1 : 0)); - } - signature += ": "; - } else if (c == '}') { - // Write default value if available. - if (arg_index < rec->args.size() && rec->args[arg_index].descr) { - signature += " = "; - signature += rec->args[arg_index].descr; - } - arg_index++; - } else if (c == '%') { - const std::type_info *t = types[type_index++]; - if (!t) - pybind11_fail("Internal error while parsing type signature (1)"); - if (auto tinfo = detail::get_type_info(*t)) { - handle th((PyObject *) tinfo->type); - signature += - th.attr("__module__").cast() + "." + - th.attr("__qualname__").cast(); // Python 3.3+, but we backport it to earlier versions - } else if (rec->is_new_style_constructor && arg_index == 0) { - // A new-style `__init__` takes `self` as `value_and_holder`. - // Rewrite it to the proper class type. - signature += - rec->scope.attr("__module__").cast() + "." + - rec->scope.attr("__qualname__").cast(); - } else { - std::string tname(t->name()); - detail::clean_type_id(tname); - signature += tname; - } - } else { - signature += c; - } - } - if (arg_index != args || types[type_index] != nullptr) - pybind11_fail("Internal error while parsing type signature (2)"); - -#if PY_MAJOR_VERSION < 3 - if (strcmp(rec->name, "__next__") == 0) { - std::free(rec->name); - rec->name = strdup("next"); - } else if (strcmp(rec->name, "__bool__") == 0) { - std::free(rec->name); - rec->name = strdup("__nonzero__"); - } -#endif - rec->signature = strdup(signature.c_str()); - rec->args.shrink_to_fit(); - rec->nargs = (std::uint16_t) args; - - if (rec->sibling && PYBIND11_INSTANCE_METHOD_CHECK(rec->sibling.ptr())) - rec->sibling = PYBIND11_INSTANCE_METHOD_GET_FUNCTION(rec->sibling.ptr()); - - detail::function_record *chain = nullptr, *chain_start = rec; - if (rec->sibling) { - if (PyCFunction_Check(rec->sibling.ptr())) { - auto rec_capsule = reinterpret_borrow(PyCFunction_GET_SELF(rec->sibling.ptr())); - chain = (detail::function_record *) rec_capsule; - /* Never append a method to an overload chain of a parent class; - instead, hide the parent's overloads in this case */ - if (!chain->scope.is(rec->scope)) - chain = nullptr; - } - // Don't trigger for things like the default __init__, which are wrapper_descriptors that we are intentionally replacing - else if (!rec->sibling.is_none() && rec->name[0] != '_') - pybind11_fail("Cannot overload existing non-function object \"" + std::string(rec->name) + - "\" with a function of the same name"); - } - - if (!chain) { - /* No existing overload was found, create a new function object */ - rec->def = new PyMethodDef(); - std::memset(rec->def, 0, sizeof(PyMethodDef)); - rec->def->ml_name = rec->name; - rec->def->ml_meth = reinterpret_cast(reinterpret_cast(*dispatcher)); - rec->def->ml_flags = METH_VARARGS | METH_KEYWORDS; - - capsule rec_capsule(rec, [](void *ptr) { - destruct((detail::function_record *) ptr); - }); - - object scope_module; - if (rec->scope) { - if (hasattr(rec->scope, "__module__")) { - scope_module = rec->scope.attr("__module__"); - } else if (hasattr(rec->scope, "__name__")) { - scope_module = rec->scope.attr("__name__"); - } - } - - m_ptr = PyCFunction_NewEx(rec->def, rec_capsule.ptr(), scope_module.ptr()); - if (!m_ptr) - pybind11_fail("cpp_function::cpp_function(): Could not allocate function object"); - } else { - /* Append at the end of the overload chain */ - m_ptr = rec->sibling.ptr(); - inc_ref(); - chain_start = chain; - if (chain->is_method != rec->is_method) - pybind11_fail("overloading a method with both static and instance methods is not supported; " - #if defined(NDEBUG) - "compile in debug mode for more details" - #else - "error while attempting to bind " + std::string(rec->is_method ? "instance" : "static") + " method " + - std::string(pybind11::str(rec->scope.attr("__name__"))) + "." + std::string(rec->name) + signature - #endif - ); - while (chain->next) - chain = chain->next; - chain->next = rec; - } - - std::string signatures; - int index = 0; - /* Create a nice pydoc rec including all signatures and - docstrings of the functions in the overload chain */ - if (chain && options::show_function_signatures()) { - // First a generic signature - signatures += rec->name; - signatures += "(*args, **kwargs)\n"; - signatures += "Overloaded function.\n\n"; - } - // Then specific overload signatures - bool first_user_def = true; - for (auto it = chain_start; it != nullptr; it = it->next) { - if (options::show_function_signatures()) { - if (index > 0) signatures += "\n"; - if (chain) - signatures += std::to_string(++index) + ". "; - signatures += rec->name; - signatures += it->signature; - signatures += "\n"; - } - if (it->doc && strlen(it->doc) > 0 && options::show_user_defined_docstrings()) { - // If we're appending another docstring, and aren't printing function signatures, we - // need to append a newline first: - if (!options::show_function_signatures()) { - if (first_user_def) first_user_def = false; - else signatures += "\n"; - } - if (options::show_function_signatures()) signatures += "\n"; - signatures += it->doc; - if (options::show_function_signatures()) signatures += "\n"; - } - } - - /* Install docstring */ - PyCFunctionObject *func = (PyCFunctionObject *) m_ptr; - if (func->m_ml->ml_doc) - std::free(const_cast(func->m_ml->ml_doc)); - func->m_ml->ml_doc = strdup(signatures.c_str()); - - if (rec->is_method) { - m_ptr = PYBIND11_INSTANCE_METHOD_NEW(m_ptr, rec->scope.ptr()); - if (!m_ptr) - pybind11_fail("cpp_function::cpp_function(): Could not allocate instance method object"); - Py_DECREF(func); - } - } - - /// When a cpp_function is GCed, release any memory allocated by pybind11 - static void destruct(detail::function_record *rec) { - while (rec) { - detail::function_record *next = rec->next; - if (rec->free_data) - rec->free_data(rec); - std::free((char *) rec->name); - std::free((char *) rec->doc); - std::free((char *) rec->signature); - for (auto &arg: rec->args) { - std::free(const_cast(arg.name)); - std::free(const_cast(arg.descr)); - arg.value.dec_ref(); - } - if (rec->def) { - std::free(const_cast(rec->def->ml_doc)); - delete rec->def; - } - delete rec; - rec = next; - } - } - - /// Main dispatch logic for calls to functions bound using pybind11 - static PyObject *dispatcher(PyObject *self, PyObject *args_in, PyObject *kwargs_in) { - using namespace detail; - - /* Iterator over the list of potentially admissible overloads */ - const function_record *overloads = (function_record *) PyCapsule_GetPointer(self, nullptr), - *it = overloads; - - /* Need to know how many arguments + keyword arguments there are to pick the right overload */ - const size_t n_args_in = (size_t) PyTuple_GET_SIZE(args_in); - - handle parent = n_args_in > 0 ? PyTuple_GET_ITEM(args_in, 0) : nullptr, - result = PYBIND11_TRY_NEXT_OVERLOAD; - - auto self_value_and_holder = value_and_holder(); - if (overloads->is_constructor) { - const auto tinfo = get_type_info((PyTypeObject *) overloads->scope.ptr()); - const auto pi = reinterpret_cast(parent.ptr()); - self_value_and_holder = pi->get_value_and_holder(tinfo, false); - - if (!self_value_and_holder.type || !self_value_and_holder.inst) { - PyErr_SetString(PyExc_TypeError, "__init__(self, ...) called with invalid `self` argument"); - return nullptr; - } - - // If this value is already registered it must mean __init__ is invoked multiple times; - // we really can't support that in C++, so just ignore the second __init__. - if (self_value_and_holder.instance_registered()) - return none().release().ptr(); - } - - try { - // We do this in two passes: in the first pass, we load arguments with `convert=false`; - // in the second, we allow conversion (except for arguments with an explicit - // py::arg().noconvert()). This lets us prefer calls without conversion, with - // conversion as a fallback. - std::vector second_pass; - - // However, if there are no overloads, we can just skip the no-convert pass entirely - const bool overloaded = it != nullptr && it->next != nullptr; - - for (; it != nullptr; it = it->next) { - - /* For each overload: - 1. Copy all positional arguments we were given, also checking to make sure that - named positional arguments weren't *also* specified via kwarg. - 2. If we weren't given enough, try to make up the omitted ones by checking - whether they were provided by a kwarg matching the `py::arg("name")` name. If - so, use it (and remove it from kwargs; if not, see if the function binding - provided a default that we can use. - 3. Ensure that either all keyword arguments were "consumed", or that the function - takes a kwargs argument to accept unconsumed kwargs. - 4. Any positional arguments still left get put into a tuple (for args), and any - leftover kwargs get put into a dict. - 5. Pack everything into a vector; if we have py::args or py::kwargs, they are an - extra tuple or dict at the end of the positional arguments. - 6. Call the function call dispatcher (function_record::impl) - - If one of these fail, move on to the next overload and keep trying until we get a - result other than PYBIND11_TRY_NEXT_OVERLOAD. - */ - - const function_record &func = *it; - size_t pos_args = func.nargs; // Number of positional arguments that we need - if (func.has_args) --pos_args; // (but don't count py::args - if (func.has_kwargs) --pos_args; // or py::kwargs) - - if (!func.has_args && n_args_in > pos_args) - continue; // Too many arguments for this overload - - if (n_args_in < pos_args && func.args.size() < pos_args) - continue; // Not enough arguments given, and not enough defaults to fill in the blanks - - function_call call(func, parent); - - size_t args_to_copy = std::min(pos_args, n_args_in); - size_t args_copied = 0; - - // 0. Inject new-style `self` argument - if (func.is_new_style_constructor) { - // The `value` may have been preallocated by an old-style `__init__` - // if it was a preceding candidate for overload resolution. - if (self_value_and_holder) - self_value_and_holder.type->dealloc(self_value_and_holder); - - call.init_self = PyTuple_GET_ITEM(args_in, 0); - call.args.push_back(reinterpret_cast(&self_value_and_holder)); - call.args_convert.push_back(false); - ++args_copied; - } - - // 1. Copy any position arguments given. - bool bad_arg = false; - for (; args_copied < args_to_copy; ++args_copied) { - const argument_record *arg_rec = args_copied < func.args.size() ? &func.args[args_copied] : nullptr; - if (kwargs_in && arg_rec && arg_rec->name && PyDict_GetItemString(kwargs_in, arg_rec->name)) { - bad_arg = true; - break; - } - - handle arg(PyTuple_GET_ITEM(args_in, args_copied)); - if (arg_rec && !arg_rec->none && arg.is_none()) { - bad_arg = true; - break; - } - call.args.push_back(arg); - call.args_convert.push_back(arg_rec ? arg_rec->convert : true); - } - if (bad_arg) - continue; // Maybe it was meant for another overload (issue #688) - - // We'll need to copy this if we steal some kwargs for defaults - dict kwargs = reinterpret_borrow(kwargs_in); - - // 2. Check kwargs and, failing that, defaults that may help complete the list - if (args_copied < pos_args) { - bool copied_kwargs = false; - - for (; args_copied < pos_args; ++args_copied) { - const auto &arg = func.args[args_copied]; - - handle value; - if (kwargs_in && arg.name) - value = PyDict_GetItemString(kwargs.ptr(), arg.name); - - if (value) { - // Consume a kwargs value - if (!copied_kwargs) { - kwargs = reinterpret_steal(PyDict_Copy(kwargs.ptr())); - copied_kwargs = true; - } - PyDict_DelItemString(kwargs.ptr(), arg.name); - } else if (arg.value) { - value = arg.value; - } - - if (value) { - call.args.push_back(value); - call.args_convert.push_back(arg.convert); - } - else - break; - } - - if (args_copied < pos_args) - continue; // Not enough arguments, defaults, or kwargs to fill the positional arguments - } - - // 3. Check everything was consumed (unless we have a kwargs arg) - if (kwargs && kwargs.size() > 0 && !func.has_kwargs) - continue; // Unconsumed kwargs, but no py::kwargs argument to accept them - - // 4a. If we have a py::args argument, create a new tuple with leftovers - if (func.has_args) { - tuple extra_args; - if (args_to_copy == 0) { - // We didn't copy out any position arguments from the args_in tuple, so we - // can reuse it directly without copying: - extra_args = reinterpret_borrow(args_in); - } else if (args_copied >= n_args_in) { - extra_args = tuple(0); - } else { - size_t args_size = n_args_in - args_copied; - extra_args = tuple(args_size); - for (size_t i = 0; i < args_size; ++i) { - extra_args[i] = PyTuple_GET_ITEM(args_in, args_copied + i); - } - } - call.args.push_back(extra_args); - call.args_convert.push_back(false); - call.args_ref = std::move(extra_args); - } - - // 4b. If we have a py::kwargs, pass on any remaining kwargs - if (func.has_kwargs) { - if (!kwargs.ptr()) - kwargs = dict(); // If we didn't get one, send an empty one - call.args.push_back(kwargs); - call.args_convert.push_back(false); - call.kwargs_ref = std::move(kwargs); - } - - // 5. Put everything in a vector. Not technically step 5, we've been building it - // in `call.args` all along. - #if !defined(NDEBUG) - if (call.args.size() != func.nargs || call.args_convert.size() != func.nargs) - pybind11_fail("Internal error: function call dispatcher inserted wrong number of arguments!"); - #endif - - std::vector second_pass_convert; - if (overloaded) { - // We're in the first no-convert pass, so swap out the conversion flags for a - // set of all-false flags. If the call fails, we'll swap the flags back in for - // the conversion-allowed call below. - second_pass_convert.resize(func.nargs, false); - call.args_convert.swap(second_pass_convert); - } - - // 6. Call the function. - try { - loader_life_support guard{}; - result = func.impl(call); - } catch (reference_cast_error &) { - result = PYBIND11_TRY_NEXT_OVERLOAD; - } - - if (result.ptr() != PYBIND11_TRY_NEXT_OVERLOAD) - break; - - if (overloaded) { - // The (overloaded) call failed; if the call has at least one argument that - // permits conversion (i.e. it hasn't been explicitly specified `.noconvert()`) - // then add this call to the list of second pass overloads to try. - for (size_t i = func.is_method ? 1 : 0; i < pos_args; i++) { - if (second_pass_convert[i]) { - // Found one: swap the converting flags back in and store the call for - // the second pass. - call.args_convert.swap(second_pass_convert); - second_pass.push_back(std::move(call)); - break; - } - } - } - } - - if (overloaded && !second_pass.empty() && result.ptr() == PYBIND11_TRY_NEXT_OVERLOAD) { - // The no-conversion pass finished without success, try again with conversion allowed - for (auto &call : second_pass) { - try { - loader_life_support guard{}; - result = call.func.impl(call); - } catch (reference_cast_error &) { - result = PYBIND11_TRY_NEXT_OVERLOAD; - } - - if (result.ptr() != PYBIND11_TRY_NEXT_OVERLOAD) { - // The error reporting logic below expects 'it' to be valid, as it would be - // if we'd encountered this failure in the first-pass loop. - if (!result) - it = &call.func; - break; - } - } - } - } catch (error_already_set &e) { - e.restore(); - return nullptr; - } catch (...) { - /* When an exception is caught, give each registered exception - translator a chance to translate it to a Python exception - in reverse order of registration. - - A translator may choose to do one of the following: - - - catch the exception and call PyErr_SetString or PyErr_SetObject - to set a standard (or custom) Python exception, or - - do nothing and let the exception fall through to the next translator, or - - delegate translation to the next translator by throwing a new type of exception. */ - - auto last_exception = std::current_exception(); - auto ®istered_exception_translators = get_internals().registered_exception_translators; - for (auto& translator : registered_exception_translators) { - try { - translator(last_exception); - } catch (...) { - last_exception = std::current_exception(); - continue; - } - return nullptr; - } - PyErr_SetString(PyExc_SystemError, "Exception escaped from default exception translator!"); - return nullptr; - } - - auto append_note_if_missing_header_is_suspected = [](std::string &msg) { - if (msg.find("std::") != std::string::npos) { - msg += "\n\n" - "Did you forget to `#include `? Or ,\n" - ", , etc. Some automatic\n" - "conversions are optional and require extra headers to be included\n" - "when compiling your pybind11 module."; - } - }; - - if (result.ptr() == PYBIND11_TRY_NEXT_OVERLOAD) { - if (overloads->is_operator) - return handle(Py_NotImplemented).inc_ref().ptr(); - - std::string msg = std::string(overloads->name) + "(): incompatible " + - std::string(overloads->is_constructor ? "constructor" : "function") + - " arguments. The following argument types are supported:\n"; - - int ctr = 0; - for (const function_record *it2 = overloads; it2 != nullptr; it2 = it2->next) { - msg += " "+ std::to_string(++ctr) + ". "; - - bool wrote_sig = false; - if (overloads->is_constructor) { - // For a constructor, rewrite `(self: Object, arg0, ...) -> NoneType` as `Object(arg0, ...)` - std::string sig = it2->signature; - size_t start = sig.find('(') + 7; // skip "(self: " - if (start < sig.size()) { - // End at the , for the next argument - size_t end = sig.find(", "), next = end + 2; - size_t ret = sig.rfind(" -> "); - // Or the ), if there is no comma: - if (end >= sig.size()) next = end = sig.find(')'); - if (start < end && next < sig.size()) { - msg.append(sig, start, end - start); - msg += '('; - msg.append(sig, next, ret - next); - wrote_sig = true; - } - } - } - if (!wrote_sig) msg += it2->signature; - - msg += "\n"; - } - msg += "\nInvoked with: "; - auto args_ = reinterpret_borrow(args_in); - bool some_args = false; - for (size_t ti = overloads->is_constructor ? 1 : 0; ti < args_.size(); ++ti) { - if (!some_args) some_args = true; - else msg += ", "; - msg += pybind11::repr(args_[ti]); - } - if (kwargs_in) { - auto kwargs = reinterpret_borrow(kwargs_in); - if (kwargs.size() > 0) { - if (some_args) msg += "; "; - msg += "kwargs: "; - bool first = true; - for (auto kwarg : kwargs) { - if (first) first = false; - else msg += ", "; - msg += pybind11::str("{}={!r}").format(kwarg.first, kwarg.second); - } - } - } - - append_note_if_missing_header_is_suspected(msg); - PyErr_SetString(PyExc_TypeError, msg.c_str()); - return nullptr; - } else if (!result) { - std::string msg = "Unable to convert function return value to a " - "Python type! The signature was\n\t"; - msg += it->signature; - append_note_if_missing_header_is_suspected(msg); - PyErr_SetString(PyExc_TypeError, msg.c_str()); - return nullptr; - } else { - if (overloads->is_constructor && !self_value_and_holder.holder_constructed()) { - auto *pi = reinterpret_cast(parent.ptr()); - self_value_and_holder.type->init_instance(pi, nullptr); - } - return result.ptr(); - } - } -}; - -/// Wrapper for Python extension modules -class module : public object { -public: - PYBIND11_OBJECT_DEFAULT(module, object, PyModule_Check) - - /// Create a new top-level Python module with the given name and docstring - explicit module(const char *name, const char *doc = nullptr) { - if (!options::show_user_defined_docstrings()) doc = nullptr; -#if PY_MAJOR_VERSION >= 3 - PyModuleDef *def = new PyModuleDef(); - std::memset(def, 0, sizeof(PyModuleDef)); - def->m_name = name; - def->m_doc = doc; - def->m_size = -1; - Py_INCREF(def); - m_ptr = PyModule_Create(def); -#else - m_ptr = Py_InitModule3(name, nullptr, doc); -#endif - if (m_ptr == nullptr) - pybind11_fail("Internal error in module::module()"); - inc_ref(); - } - - /** \rst - Create Python binding for a new function within the module scope. ``Func`` - can be a plain C++ function, a function pointer, or a lambda function. For - details on the ``Extra&& ... extra`` argument, see section :ref:`extras`. - \endrst */ - template - module &def(const char *name_, Func &&f, const Extra& ... extra) { - cpp_function func(std::forward(f), name(name_), scope(*this), - sibling(getattr(*this, name_, none())), extra...); - // NB: allow overwriting here because cpp_function sets up a chain with the intention of - // overwriting (and has already checked internally that it isn't overwriting non-functions). - add_object(name_, func, true /* overwrite */); - return *this; - } - - /** \rst - Create and return a new Python submodule with the given name and docstring. - This also works recursively, i.e. - - .. code-block:: cpp - - py::module m("example", "pybind11 example plugin"); - py::module m2 = m.def_submodule("sub", "A submodule of 'example'"); - py::module m3 = m2.def_submodule("subsub", "A submodule of 'example.sub'"); - \endrst */ - module def_submodule(const char *name, const char *doc = nullptr) { - std::string full_name = std::string(PyModule_GetName(m_ptr)) - + std::string(".") + std::string(name); - auto result = reinterpret_borrow(PyImport_AddModule(full_name.c_str())); - if (doc && options::show_user_defined_docstrings()) - result.attr("__doc__") = pybind11::str(doc); - attr(name) = result; - return result; - } - - /// Import and return a module or throws `error_already_set`. - static module import(const char *name) { - PyObject *obj = PyImport_ImportModule(name); - if (!obj) - throw error_already_set(); - return reinterpret_steal(obj); - } - - /// Reload the module or throws `error_already_set`. - void reload() { - PyObject *obj = PyImport_ReloadModule(ptr()); - if (!obj) - throw error_already_set(); - *this = reinterpret_steal(obj); - } - - // Adds an object to the module using the given name. Throws if an object with the given name - // already exists. - // - // overwrite should almost always be false: attempting to overwrite objects that pybind11 has - // established will, in most cases, break things. - PYBIND11_NOINLINE void add_object(const char *name, handle obj, bool overwrite = false) { - if (!overwrite && hasattr(*this, name)) - pybind11_fail("Error during initialization: multiple incompatible definitions with name \"" + - std::string(name) + "\""); - - PyModule_AddObject(ptr(), name, obj.inc_ref().ptr() /* steals a reference */); - } -}; - -/// \ingroup python_builtins -/// Return a dictionary representing the global variables in the current execution frame, -/// or ``__main__.__dict__`` if there is no frame (usually when the interpreter is embedded). -inline dict globals() { - PyObject *p = PyEval_GetGlobals(); - return reinterpret_borrow(p ? p : module::import("__main__").attr("__dict__").ptr()); -} - -NAMESPACE_BEGIN(detail) -/// Generic support for creating new Python heap types -class generic_type : public object { - template friend class class_; -public: - PYBIND11_OBJECT_DEFAULT(generic_type, object, PyType_Check) -protected: - void initialize(const type_record &rec) { - if (rec.scope && hasattr(rec.scope, rec.name)) - pybind11_fail("generic_type: cannot initialize type \"" + std::string(rec.name) + - "\": an object with that name is already defined"); - - if (rec.module_local ? get_local_type_info(*rec.type) : get_global_type_info(*rec.type)) - pybind11_fail("generic_type: type \"" + std::string(rec.name) + - "\" is already registered!"); - - m_ptr = make_new_python_type(rec); - - /* Register supplemental type information in C++ dict */ - auto *tinfo = new detail::type_info(); - tinfo->type = (PyTypeObject *) m_ptr; - tinfo->cpptype = rec.type; - tinfo->type_size = rec.type_size; - tinfo->type_align = rec.type_align; - tinfo->operator_new = rec.operator_new; - tinfo->holder_size_in_ptrs = size_in_ptrs(rec.holder_size); - tinfo->init_instance = rec.init_instance; - tinfo->dealloc = rec.dealloc; - tinfo->simple_type = true; - tinfo->simple_ancestors = true; - tinfo->default_holder = rec.default_holder; - tinfo->module_local = rec.module_local; - - auto &internals = get_internals(); - auto tindex = std::type_index(*rec.type); - tinfo->direct_conversions = &internals.direct_conversions[tindex]; - if (rec.module_local) - registered_local_types_cpp()[tindex] = tinfo; - else - internals.registered_types_cpp[tindex] = tinfo; - internals.registered_types_py[(PyTypeObject *) m_ptr] = { tinfo }; - - if (rec.bases.size() > 1 || rec.multiple_inheritance) { - mark_parents_nonsimple(tinfo->type); - tinfo->simple_ancestors = false; - } - else if (rec.bases.size() == 1) { - auto parent_tinfo = get_type_info((PyTypeObject *) rec.bases[0].ptr()); - tinfo->simple_ancestors = parent_tinfo->simple_ancestors; - } - - if (rec.module_local) { - // Stash the local typeinfo and loader so that external modules can access it. - tinfo->module_local_load = &type_caster_generic::local_load; - setattr(m_ptr, PYBIND11_MODULE_LOCAL_ID, capsule(tinfo)); - } - } - - /// Helper function which tags all parents of a type using mult. inheritance - void mark_parents_nonsimple(PyTypeObject *value) { - auto t = reinterpret_borrow(value->tp_bases); - for (handle h : t) { - auto tinfo2 = get_type_info((PyTypeObject *) h.ptr()); - if (tinfo2) - tinfo2->simple_type = false; - mark_parents_nonsimple((PyTypeObject *) h.ptr()); - } - } - - void install_buffer_funcs( - buffer_info *(*get_buffer)(PyObject *, void *), - void *get_buffer_data) { - PyHeapTypeObject *type = (PyHeapTypeObject*) m_ptr; - auto tinfo = detail::get_type_info(&type->ht_type); - - if (!type->ht_type.tp_as_buffer) - pybind11_fail( - "To be able to register buffer protocol support for the type '" + - std::string(tinfo->type->tp_name) + - "' the associated class<>(..) invocation must " - "include the pybind11::buffer_protocol() annotation!"); - - tinfo->get_buffer = get_buffer; - tinfo->get_buffer_data = get_buffer_data; - } - - // rec_func must be set for either fget or fset. - void def_property_static_impl(const char *name, - handle fget, handle fset, - detail::function_record *rec_func) { - const auto is_static = rec_func && !(rec_func->is_method && rec_func->scope); - const auto has_doc = rec_func && rec_func->doc && pybind11::options::show_user_defined_docstrings(); - auto property = handle((PyObject *) (is_static ? get_internals().static_property_type - : &PyProperty_Type)); - attr(name) = property(fget.ptr() ? fget : none(), - fset.ptr() ? fset : none(), - /*deleter*/none(), - pybind11::str(has_doc ? rec_func->doc : "")); - } -}; - -/// Set the pointer to operator new if it exists. The cast is needed because it can be overloaded. -template (T::operator new))>> -void set_operator_new(type_record *r) { r->operator_new = &T::operator new; } - -template void set_operator_new(...) { } - -template struct has_operator_delete : std::false_type { }; -template struct has_operator_delete(T::operator delete))>> - : std::true_type { }; -template struct has_operator_delete_size : std::false_type { }; -template struct has_operator_delete_size(T::operator delete))>> - : std::true_type { }; -/// Call class-specific delete if it exists or global otherwise. Can also be an overload set. -template ::value, int> = 0> -void call_operator_delete(T *p, size_t, size_t) { T::operator delete(p); } -template ::value && has_operator_delete_size::value, int> = 0> -void call_operator_delete(T *p, size_t s, size_t) { T::operator delete(p, s); } - -inline void call_operator_delete(void *p, size_t s, size_t a) { - (void)s; (void)a; -#if defined(PYBIND11_CPP17) - if (a > __STDCPP_DEFAULT_NEW_ALIGNMENT__) - ::operator delete(p, s, std::align_val_t(a)); - else - ::operator delete(p, s); -#else - ::operator delete(p); -#endif -} - -NAMESPACE_END(detail) - -/// Given a pointer to a member function, cast it to its `Derived` version. -/// Forward everything else unchanged. -template -auto method_adaptor(F &&f) -> decltype(std::forward(f)) { return std::forward(f); } - -template -auto method_adaptor(Return (Class::*pmf)(Args...)) -> Return (Derived::*)(Args...) { - static_assert(detail::is_accessible_base_of::value, - "Cannot bind an inaccessible base class method; use a lambda definition instead"); - return pmf; -} - -template -auto method_adaptor(Return (Class::*pmf)(Args...) const) -> Return (Derived::*)(Args...) const { - static_assert(detail::is_accessible_base_of::value, - "Cannot bind an inaccessible base class method; use a lambda definition instead"); - return pmf; -} - -template -class class_ : public detail::generic_type { - template using is_holder = detail::is_holder_type; - template using is_subtype = detail::is_strict_base_of; - template using is_base = detail::is_strict_base_of; - // struct instead of using here to help MSVC: - template struct is_valid_class_option : - detail::any_of, is_subtype, is_base> {}; - -public: - using type = type_; - using type_alias = detail::exactly_one_t; - constexpr static bool has_alias = !std::is_void::value; - using holder_type = detail::exactly_one_t, options...>; - - static_assert(detail::all_of...>::value, - "Unknown/invalid class_ template parameters provided"); - - static_assert(!has_alias || std::is_polymorphic::value, - "Cannot use an alias class with a non-polymorphic type"); - - PYBIND11_OBJECT(class_, generic_type, PyType_Check) - - template - class_(handle scope, const char *name, const Extra &... extra) { - using namespace detail; - - // MI can only be specified via class_ template options, not constructor parameters - static_assert( - none_of...>::value || // no base class arguments, or: - ( constexpr_sum(is_pyobject::value...) == 1 && // Exactly one base - constexpr_sum(is_base::value...) == 0 && // no template option bases - none_of...>::value), // no multiple_inheritance attr - "Error: multiple inheritance bases must be specified via class_ template options"); - - type_record record; - record.scope = scope; - record.name = name; - record.type = &typeid(type); - record.type_size = sizeof(conditional_t); - record.type_align = alignof(conditional_t&); - record.holder_size = sizeof(holder_type); - record.init_instance = init_instance; - record.dealloc = dealloc; - record.default_holder = detail::is_instantiation::value; - - set_operator_new(&record); - - /* Register base classes specified via template arguments to class_, if any */ - PYBIND11_EXPAND_SIDE_EFFECTS(add_base(record)); - - /* Process optional arguments, if any */ - process_attributes::init(extra..., &record); - - generic_type::initialize(record); - - if (has_alias) { - auto &instances = record.module_local ? registered_local_types_cpp() : get_internals().registered_types_cpp; - instances[std::type_index(typeid(type_alias))] = instances[std::type_index(typeid(type))]; - } - } - - template ::value, int> = 0> - static void add_base(detail::type_record &rec) { - rec.add_base(typeid(Base), [](void *src) -> void * { - return static_cast(reinterpret_cast(src)); - }); - } - - template ::value, int> = 0> - static void add_base(detail::type_record &) { } - - template - class_ &def(const char *name_, Func&& f, const Extra&... extra) { - cpp_function cf(method_adaptor(std::forward(f)), name(name_), is_method(*this), - sibling(getattr(*this, name_, none())), extra...); - attr(cf.name()) = cf; - return *this; - } - - template class_ & - def_static(const char *name_, Func &&f, const Extra&... extra) { - static_assert(!std::is_member_function_pointer::value, - "def_static(...) called with a non-static member function pointer"); - cpp_function cf(std::forward(f), name(name_), scope(*this), - sibling(getattr(*this, name_, none())), extra...); - attr(cf.name()) = cf; - return *this; - } - - template - class_ &def(const detail::op_ &op, const Extra&... extra) { - op.execute(*this, extra...); - return *this; - } - - template - class_ & def_cast(const detail::op_ &op, const Extra&... extra) { - op.execute_cast(*this, extra...); - return *this; - } - - template - class_ &def(const detail::initimpl::constructor &init, const Extra&... extra) { - init.execute(*this, extra...); - return *this; - } - - template - class_ &def(const detail::initimpl::alias_constructor &init, const Extra&... extra) { - init.execute(*this, extra...); - return *this; - } - - template - class_ &def(detail::initimpl::factory &&init, const Extra&... extra) { - std::move(init).execute(*this, extra...); - return *this; - } - - template - class_ &def(detail::initimpl::pickle_factory &&pf, const Extra &...extra) { - std::move(pf).execute(*this, extra...); - return *this; - } - - template class_& def_buffer(Func &&func) { - struct capture { Func func; }; - capture *ptr = new capture { std::forward(func) }; - install_buffer_funcs([](PyObject *obj, void *ptr) -> buffer_info* { - detail::make_caster caster; - if (!caster.load(obj, false)) - return nullptr; - return new buffer_info(((capture *) ptr)->func(caster)); - }, ptr); - return *this; - } - - template - class_ &def_buffer(Return (Class::*func)(Args...)) { - return def_buffer([func] (type &obj) { return (obj.*func)(); }); - } - - template - class_ &def_buffer(Return (Class::*func)(Args...) const) { - return def_buffer([func] (const type &obj) { return (obj.*func)(); }); - } - - template - class_ &def_readwrite(const char *name, D C::*pm, const Extra&... extra) { - static_assert(std::is_base_of::value, "def_readwrite() requires a class member (or base class member)"); - cpp_function fget([pm](const type &c) -> const D &{ return c.*pm; }, is_method(*this)), - fset([pm](type &c, const D &value) { c.*pm = value; }, is_method(*this)); - def_property(name, fget, fset, return_value_policy::reference_internal, extra...); - return *this; - } - - template - class_ &def_readonly(const char *name, const D C::*pm, const Extra& ...extra) { - static_assert(std::is_base_of::value, "def_readonly() requires a class member (or base class member)"); - cpp_function fget([pm](const type &c) -> const D &{ return c.*pm; }, is_method(*this)); - def_property_readonly(name, fget, return_value_policy::reference_internal, extra...); - return *this; - } - - template - class_ &def_readwrite_static(const char *name, D *pm, const Extra& ...extra) { - cpp_function fget([pm](object) -> const D &{ return *pm; }, scope(*this)), - fset([pm](object, const D &value) { *pm = value; }, scope(*this)); - def_property_static(name, fget, fset, return_value_policy::reference, extra...); - return *this; - } - - template - class_ &def_readonly_static(const char *name, const D *pm, const Extra& ...extra) { - cpp_function fget([pm](object) -> const D &{ return *pm; }, scope(*this)); - def_property_readonly_static(name, fget, return_value_policy::reference, extra...); - return *this; - } - - /// Uses return_value_policy::reference_internal by default - template - class_ &def_property_readonly(const char *name, const Getter &fget, const Extra& ...extra) { - return def_property_readonly(name, cpp_function(method_adaptor(fget)), - return_value_policy::reference_internal, extra...); - } - - /// Uses cpp_function's return_value_policy by default - template - class_ &def_property_readonly(const char *name, const cpp_function &fget, const Extra& ...extra) { - return def_property(name, fget, nullptr, extra...); - } - - /// Uses return_value_policy::reference by default - template - class_ &def_property_readonly_static(const char *name, const Getter &fget, const Extra& ...extra) { - return def_property_readonly_static(name, cpp_function(fget), return_value_policy::reference, extra...); - } - - /// Uses cpp_function's return_value_policy by default - template - class_ &def_property_readonly_static(const char *name, const cpp_function &fget, const Extra& ...extra) { - return def_property_static(name, fget, nullptr, extra...); - } - - /// Uses return_value_policy::reference_internal by default - template - class_ &def_property(const char *name, const Getter &fget, const Setter &fset, const Extra& ...extra) { - return def_property(name, fget, cpp_function(method_adaptor(fset)), extra...); - } - template - class_ &def_property(const char *name, const Getter &fget, const cpp_function &fset, const Extra& ...extra) { - return def_property(name, cpp_function(method_adaptor(fget)), fset, - return_value_policy::reference_internal, extra...); - } - - /// Uses cpp_function's return_value_policy by default - template - class_ &def_property(const char *name, const cpp_function &fget, const cpp_function &fset, const Extra& ...extra) { - return def_property_static(name, fget, fset, is_method(*this), extra...); - } - - /// Uses return_value_policy::reference by default - template - class_ &def_property_static(const char *name, const Getter &fget, const cpp_function &fset, const Extra& ...extra) { - return def_property_static(name, cpp_function(fget), fset, return_value_policy::reference, extra...); - } - - /// Uses cpp_function's return_value_policy by default - template - class_ &def_property_static(const char *name, const cpp_function &fget, const cpp_function &fset, const Extra& ...extra) { - auto rec_fget = get_function_record(fget), rec_fset = get_function_record(fset); - auto *rec_active = rec_fget; - if (rec_fget) { - char *doc_prev = rec_fget->doc; /* 'extra' field may include a property-specific documentation string */ - detail::process_attributes::init(extra..., rec_fget); - if (rec_fget->doc && rec_fget->doc != doc_prev) { - free(doc_prev); - rec_fget->doc = strdup(rec_fget->doc); - } - } - if (rec_fset) { - char *doc_prev = rec_fset->doc; - detail::process_attributes::init(extra..., rec_fset); - if (rec_fset->doc && rec_fset->doc != doc_prev) { - free(doc_prev); - rec_fset->doc = strdup(rec_fset->doc); - } - if (! rec_active) rec_active = rec_fset; - } - def_property_static_impl(name, fget, fset, rec_active); - return *this; - } - -private: - /// Initialize holder object, variant 1: object derives from enable_shared_from_this - template - static void init_holder(detail::instance *inst, detail::value_and_holder &v_h, - const holder_type * /* unused */, const std::enable_shared_from_this * /* dummy */) { - try { - auto sh = std::dynamic_pointer_cast( - v_h.value_ptr()->shared_from_this()); - if (sh) { - new (std::addressof(v_h.holder())) holder_type(std::move(sh)); - v_h.set_holder_constructed(); - } - } catch (const std::bad_weak_ptr &) {} - - if (!v_h.holder_constructed() && inst->owned) { - new (std::addressof(v_h.holder())) holder_type(v_h.value_ptr()); - v_h.set_holder_constructed(); - } - } - - static void init_holder_from_existing(const detail::value_and_holder &v_h, - const holder_type *holder_ptr, std::true_type /*is_copy_constructible*/) { - new (std::addressof(v_h.holder())) holder_type(*reinterpret_cast(holder_ptr)); - } - - static void init_holder_from_existing(const detail::value_and_holder &v_h, - const holder_type *holder_ptr, std::false_type /*is_copy_constructible*/) { - new (std::addressof(v_h.holder())) holder_type(std::move(*const_cast(holder_ptr))); - } - - /// Initialize holder object, variant 2: try to construct from existing holder object, if possible - static void init_holder(detail::instance *inst, detail::value_and_holder &v_h, - const holder_type *holder_ptr, const void * /* dummy -- not enable_shared_from_this) */) { - if (holder_ptr) { - init_holder_from_existing(v_h, holder_ptr, std::is_copy_constructible()); - v_h.set_holder_constructed(); - } else if (inst->owned || detail::always_construct_holder::value) { - new (std::addressof(v_h.holder())) holder_type(v_h.value_ptr()); - v_h.set_holder_constructed(); - } - } - - /// Performs instance initialization including constructing a holder and registering the known - /// instance. Should be called as soon as the `type` value_ptr is set for an instance. Takes an - /// optional pointer to an existing holder to use; if not specified and the instance is - /// `.owned`, a new holder will be constructed to manage the value pointer. - static void init_instance(detail::instance *inst, const void *holder_ptr) { - auto v_h = inst->get_value_and_holder(detail::get_type_info(typeid(type))); - if (!v_h.instance_registered()) { - register_instance(inst, v_h.value_ptr(), v_h.type); - v_h.set_instance_registered(); - } - init_holder(inst, v_h, (const holder_type *) holder_ptr, v_h.value_ptr()); - } - - /// Deallocates an instance; via holder, if constructed; otherwise via operator delete. - static void dealloc(detail::value_and_holder &v_h) { - if (v_h.holder_constructed()) { - v_h.holder().~holder_type(); - v_h.set_holder_constructed(false); - } - else { - detail::call_operator_delete(v_h.value_ptr(), - v_h.type->type_size, - v_h.type->type_align - ); - } - v_h.value_ptr() = nullptr; - } - - static detail::function_record *get_function_record(handle h) { - h = detail::get_function(h); - return h ? (detail::function_record *) reinterpret_borrow(PyCFunction_GET_SELF(h.ptr())) - : nullptr; - } -}; - -/// Binds an existing constructor taking arguments Args... -template detail::initimpl::constructor init() { return {}; } -/// Like `init()`, but the instance is always constructed through the alias class (even -/// when not inheriting on the Python side). -template detail::initimpl::alias_constructor init_alias() { return {}; } - -/// Binds a factory function as a constructor -template > -Ret init(Func &&f) { return {std::forward(f)}; } - -/// Dual-argument factory function: the first function is called when no alias is needed, the second -/// when an alias is needed (i.e. due to python-side inheritance). Arguments must be identical. -template > -Ret init(CFunc &&c, AFunc &&a) { - return {std::forward(c), std::forward(a)}; -} - -/// Binds pickling functions `__getstate__` and `__setstate__` and ensures that the type -/// returned by `__getstate__` is the same as the argument accepted by `__setstate__`. -template -detail::initimpl::pickle_factory pickle(GetState &&g, SetState &&s) { - return {std::forward(g), std::forward(s)}; -} - -NAMESPACE_BEGIN(detail) -struct enum_base { - enum_base(handle base, handle parent) : m_base(base), m_parent(parent) { } - - PYBIND11_NOINLINE void init(bool is_arithmetic, bool is_convertible) { - m_base.attr("__entries") = dict(); - auto property = handle((PyObject *) &PyProperty_Type); - auto static_property = handle((PyObject *) get_internals().static_property_type); - - m_base.attr("__repr__") = cpp_function( - [](handle arg) -> str { - handle type = arg.get_type(); - object type_name = type.attr("__name__"); - dict entries = type.attr("__entries"); - for (const auto &kv : entries) { - object other = kv.second[int_(0)]; - if (other.equal(arg)) - return pybind11::str("{}.{}").format(type_name, kv.first); - } - return pybind11::str("{}.???").format(type_name); - }, is_method(m_base) - ); - - m_base.attr("name") = property(cpp_function( - [](handle arg) -> str { - dict entries = arg.get_type().attr("__entries"); - for (const auto &kv : entries) { - if (handle(kv.second[int_(0)]).equal(arg)) - return pybind11::str(kv.first); - } - return "???"; - }, is_method(m_base) - )); - - m_base.attr("__doc__") = static_property(cpp_function( - [](handle arg) -> std::string { - std::string docstring; - dict entries = arg.attr("__entries"); - if (((PyTypeObject *) arg.ptr())->tp_doc) - docstring += std::string(((PyTypeObject *) arg.ptr())->tp_doc) + "\n\n"; - docstring += "Members:"; - for (const auto &kv : entries) { - auto key = std::string(pybind11::str(kv.first)); - auto comment = kv.second[int_(1)]; - docstring += "\n\n " + key; - if (!comment.is_none()) - docstring += " : " + (std::string) pybind11::str(comment); - } - return docstring; - } - ), none(), none(), ""); - - m_base.attr("__members__") = static_property(cpp_function( - [](handle arg) -> dict { - dict entries = arg.attr("__entries"), m; - for (const auto &kv : entries) - m[kv.first] = kv.second[int_(0)]; - return m; - }), none(), none(), "" - ); - - #define PYBIND11_ENUM_OP_STRICT(op, expr, strict_behavior) \ - m_base.attr(op) = cpp_function( \ - [](object a, object b) { \ - if (!a.get_type().is(b.get_type())) \ - strict_behavior; \ - return expr; \ - }, \ - is_method(m_base)) - - #define PYBIND11_ENUM_OP_CONV(op, expr) \ - m_base.attr(op) = cpp_function( \ - [](object a_, object b_) { \ - int_ a(a_), b(b_); \ - return expr; \ - }, \ - is_method(m_base)) - - if (is_convertible) { - PYBIND11_ENUM_OP_CONV("__eq__", !b.is_none() && a.equal(b)); - PYBIND11_ENUM_OP_CONV("__ne__", b.is_none() || !a.equal(b)); - - if (is_arithmetic) { - PYBIND11_ENUM_OP_CONV("__lt__", a < b); - PYBIND11_ENUM_OP_CONV("__gt__", a > b); - PYBIND11_ENUM_OP_CONV("__le__", a <= b); - PYBIND11_ENUM_OP_CONV("__ge__", a >= b); - PYBIND11_ENUM_OP_CONV("__and__", a & b); - PYBIND11_ENUM_OP_CONV("__rand__", a & b); - PYBIND11_ENUM_OP_CONV("__or__", a | b); - PYBIND11_ENUM_OP_CONV("__ror__", a | b); - PYBIND11_ENUM_OP_CONV("__xor__", a ^ b); - PYBIND11_ENUM_OP_CONV("__rxor__", a ^ b); - } - } else { - PYBIND11_ENUM_OP_STRICT("__eq__", int_(a).equal(int_(b)), return false); - PYBIND11_ENUM_OP_STRICT("__ne__", !int_(a).equal(int_(b)), return true); - - if (is_arithmetic) { - #define PYBIND11_THROW throw type_error("Expected an enumeration of matching type!"); - PYBIND11_ENUM_OP_STRICT("__lt__", int_(a) < int_(b), PYBIND11_THROW); - PYBIND11_ENUM_OP_STRICT("__gt__", int_(a) > int_(b), PYBIND11_THROW); - PYBIND11_ENUM_OP_STRICT("__le__", int_(a) <= int_(b), PYBIND11_THROW); - PYBIND11_ENUM_OP_STRICT("__ge__", int_(a) >= int_(b), PYBIND11_THROW); - #undef PYBIND11_THROW - } - } - - #undef PYBIND11_ENUM_OP_CONV - #undef PYBIND11_ENUM_OP_STRICT - - object getstate = cpp_function( - [](object arg) { return int_(arg); }, is_method(m_base)); - - m_base.attr("__getstate__") = getstate; - m_base.attr("__hash__") = getstate; - } - - PYBIND11_NOINLINE void value(char const* name_, object value, const char *doc = nullptr) { - dict entries = m_base.attr("__entries"); - str name(name_); - if (entries.contains(name)) { - std::string type_name = (std::string) str(m_base.attr("__name__")); - throw value_error(type_name + ": element \"" + std::string(name_) + "\" already exists!"); - } - - entries[name] = std::make_pair(value, doc); - m_base.attr(name) = value; - } - - PYBIND11_NOINLINE void export_values() { - dict entries = m_base.attr("__entries"); - for (const auto &kv : entries) - m_parent.attr(kv.first) = kv.second[int_(0)]; - } - - handle m_base; - handle m_parent; -}; - -NAMESPACE_END(detail) - -/// Binds C++ enumerations and enumeration classes to Python -template class enum_ : public class_ { -public: - using Base = class_; - using Base::def; - using Base::attr; - using Base::def_property_readonly; - using Base::def_property_readonly_static; - using Scalar = typename std::underlying_type::type; - - template - enum_(const handle &scope, const char *name, const Extra&... extra) - : class_(scope, name, extra...), m_base(*this, scope) { - constexpr bool is_arithmetic = detail::any_of...>::value; - constexpr bool is_convertible = std::is_convertible::value; - m_base.init(is_arithmetic, is_convertible); - - def(init([](Scalar i) { return static_cast(i); })); - def("__int__", [](Type value) { return (Scalar) value; }); - #if PY_MAJOR_VERSION < 3 - def("__long__", [](Type value) { return (Scalar) value; }); - #endif - cpp_function setstate( - [](Type &value, Scalar arg) { value = static_cast(arg); }, - is_method(*this)); - attr("__setstate__") = setstate; - } - - /// Export enumeration entries into the parent scope - enum_& export_values() { - m_base.export_values(); - return *this; - } - - /// Add an enumeration entry - enum_& value(char const* name, Type value, const char *doc = nullptr) { - m_base.value(name, pybind11::cast(value, return_value_policy::copy), doc); - return *this; - } - -private: - detail::enum_base m_base; -}; - -NAMESPACE_BEGIN(detail) - - -inline void keep_alive_impl(handle nurse, handle patient) { - if (!nurse || !patient) - pybind11_fail("Could not activate keep_alive!"); - - if (patient.is_none() || nurse.is_none()) - return; /* Nothing to keep alive or nothing to be kept alive by */ - - auto tinfo = all_type_info(Py_TYPE(nurse.ptr())); - if (!tinfo.empty()) { - /* It's a pybind-registered type, so we can store the patient in the - * internal list. */ - add_patient(nurse.ptr(), patient.ptr()); - } - else { - /* Fall back to clever approach based on weak references taken from - * Boost.Python. This is not used for pybind-registered types because - * the objects can be destroyed out-of-order in a GC pass. */ - cpp_function disable_lifesupport( - [patient](handle weakref) { patient.dec_ref(); weakref.dec_ref(); }); - - weakref wr(nurse, disable_lifesupport); - - patient.inc_ref(); /* reference patient and leak the weak reference */ - (void) wr.release(); - } -} - -PYBIND11_NOINLINE inline void keep_alive_impl(size_t Nurse, size_t Patient, function_call &call, handle ret) { - auto get_arg = [&](size_t n) { - if (n == 0) - return ret; - else if (n == 1 && call.init_self) - return call.init_self; - else if (n <= call.args.size()) - return call.args[n - 1]; - return handle(); - }; - - keep_alive_impl(get_arg(Nurse), get_arg(Patient)); -} - -inline std::pair all_type_info_get_cache(PyTypeObject *type) { - auto res = get_internals().registered_types_py -#ifdef __cpp_lib_unordered_map_try_emplace - .try_emplace(type); -#else - .emplace(type, std::vector()); -#endif - if (res.second) { - // New cache entry created; set up a weak reference to automatically remove it if the type - // gets destroyed: - weakref((PyObject *) type, cpp_function([type](handle wr) { - get_internals().registered_types_py.erase(type); - wr.dec_ref(); - })).release(); - } - - return res; -} - -template -struct iterator_state { - Iterator it; - Sentinel end; - bool first_or_done; -}; - -NAMESPACE_END(detail) - -/// Makes a python iterator from a first and past-the-end C++ InputIterator. -template ()), - typename... Extra> -iterator make_iterator(Iterator first, Sentinel last, Extra &&... extra) { - typedef detail::iterator_state state; - - if (!detail::get_type_info(typeid(state), false)) { - class_(handle(), "iterator", pybind11::module_local()) - .def("__iter__", [](state &s) -> state& { return s; }) - .def("__next__", [](state &s) -> ValueType { - if (!s.first_or_done) - ++s.it; - else - s.first_or_done = false; - if (s.it == s.end) { - s.first_or_done = true; - throw stop_iteration(); - } - return *s.it; - }, std::forward(extra)..., Policy); - } - - return cast(state{first, last, true}); -} - -/// Makes an python iterator over the keys (`.first`) of a iterator over pairs from a -/// first and past-the-end InputIterator. -template ()).first), - typename... Extra> -iterator make_key_iterator(Iterator first, Sentinel last, Extra &&... extra) { - typedef detail::iterator_state state; - - if (!detail::get_type_info(typeid(state), false)) { - class_(handle(), "iterator", pybind11::module_local()) - .def("__iter__", [](state &s) -> state& { return s; }) - .def("__next__", [](state &s) -> KeyType { - if (!s.first_or_done) - ++s.it; - else - s.first_or_done = false; - if (s.it == s.end) { - s.first_or_done = true; - throw stop_iteration(); - } - return (*s.it).first; - }, std::forward(extra)..., Policy); - } - - return cast(state{first, last, true}); -} - -/// Makes an iterator over values of an stl container or other container supporting -/// `std::begin()`/`std::end()` -template iterator make_iterator(Type &value, Extra&&... extra) { - return make_iterator(std::begin(value), std::end(value), extra...); -} - -/// Makes an iterator over the keys (`.first`) of a stl map-like container supporting -/// `std::begin()`/`std::end()` -template iterator make_key_iterator(Type &value, Extra&&... extra) { - return make_key_iterator(std::begin(value), std::end(value), extra...); -} - -template void implicitly_convertible() { - struct set_flag { - bool &flag; - set_flag(bool &flag) : flag(flag) { flag = true; } - ~set_flag() { flag = false; } - }; - auto implicit_caster = [](PyObject *obj, PyTypeObject *type) -> PyObject * { - static bool currently_used = false; - if (currently_used) // implicit conversions are non-reentrant - return nullptr; - set_flag flag_helper(currently_used); - if (!detail::make_caster().load(obj, false)) - return nullptr; - tuple args(1); - args[0] = obj; - PyObject *result = PyObject_Call((PyObject *) type, args.ptr(), nullptr); - if (result == nullptr) - PyErr_Clear(); - return result; - }; - - if (auto tinfo = detail::get_type_info(typeid(OutputType))) - tinfo->implicit_conversions.push_back(implicit_caster); - else - pybind11_fail("implicitly_convertible: Unable to find type " + type_id()); -} - -template -void register_exception_translator(ExceptionTranslator&& translator) { - detail::get_internals().registered_exception_translators.push_front( - std::forward(translator)); -} - -/** - * Wrapper to generate a new Python exception type. - * - * This should only be used with PyErr_SetString for now. - * It is not (yet) possible to use as a py::base. - * Template type argument is reserved for future use. - */ -template -class exception : public object { -public: - exception() = default; - exception(handle scope, const char *name, PyObject *base = PyExc_Exception) { - std::string full_name = scope.attr("__name__").cast() + - std::string(".") + name; - m_ptr = PyErr_NewException(const_cast(full_name.c_str()), base, NULL); - if (hasattr(scope, name)) - pybind11_fail("Error during initialization: multiple incompatible " - "definitions with name \"" + std::string(name) + "\""); - scope.attr(name) = *this; - } - - // Sets the current python exception to this exception object with the given message - void operator()(const char *message) { - PyErr_SetString(m_ptr, message); - } -}; - -NAMESPACE_BEGIN(detail) -// Returns a reference to a function-local static exception object used in the simple -// register_exception approach below. (It would be simpler to have the static local variable -// directly in register_exception, but that makes clang <3.5 segfault - issue #1349). -template -exception &get_exception_object() { static exception ex; return ex; } -NAMESPACE_END(detail) - -/** - * Registers a Python exception in `m` of the given `name` and installs an exception translator to - * translate the C++ exception to the created Python exception using the exceptions what() method. - * This is intended for simple exception translations; for more complex translation, register the - * exception object and translator directly. - */ -template -exception ®ister_exception(handle scope, - const char *name, - PyObject *base = PyExc_Exception) { - auto &ex = detail::get_exception_object(); - if (!ex) ex = exception(scope, name, base); - - register_exception_translator([](std::exception_ptr p) { - if (!p) return; - try { - std::rethrow_exception(p); - } catch (const CppException &e) { - detail::get_exception_object()(e.what()); - } - }); - return ex; -} - -NAMESPACE_BEGIN(detail) -PYBIND11_NOINLINE inline void print(tuple args, dict kwargs) { - auto strings = tuple(args.size()); - for (size_t i = 0; i < args.size(); ++i) { - strings[i] = str(args[i]); - } - auto sep = kwargs.contains("sep") ? kwargs["sep"] : cast(" "); - auto line = sep.attr("join")(strings); - - object file; - if (kwargs.contains("file")) { - file = kwargs["file"].cast(); - } else { - try { - file = module::import("sys").attr("stdout"); - } catch (const error_already_set &) { - /* If print() is called from code that is executed as - part of garbage collection during interpreter shutdown, - importing 'sys' can fail. Give up rather than crashing the - interpreter in this case. */ - return; - } - } - - auto write = file.attr("write"); - write(line); - write(kwargs.contains("end") ? kwargs["end"] : cast("\n")); - - if (kwargs.contains("flush") && kwargs["flush"].cast()) - file.attr("flush")(); -} -NAMESPACE_END(detail) - -template -void print(Args &&...args) { - auto c = detail::collect_arguments(std::forward(args)...); - detail::print(c.args(), c.kwargs()); -} - -#if defined(WITH_THREAD) && !defined(PYPY_VERSION) - -/* The functions below essentially reproduce the PyGILState_* API using a RAII - * pattern, but there are a few important differences: - * - * 1. When acquiring the GIL from an non-main thread during the finalization - * phase, the GILState API blindly terminates the calling thread, which - * is often not what is wanted. This API does not do this. - * - * 2. The gil_scoped_release function can optionally cut the relationship - * of a PyThreadState and its associated thread, which allows moving it to - * another thread (this is a fairly rare/advanced use case). - * - * 3. The reference count of an acquired thread state can be controlled. This - * can be handy to prevent cases where callbacks issued from an external - * thread would otherwise constantly construct and destroy thread state data - * structures. - * - * See the Python bindings of NanoGUI (http://github.com/wjakob/nanogui) for an - * example which uses features 2 and 3 to migrate the Python thread of - * execution to another thread (to run the event loop on the original thread, - * in this case). - */ - -class gil_scoped_acquire { -public: - PYBIND11_NOINLINE gil_scoped_acquire() { - auto const &internals = detail::get_internals(); - tstate = (PyThreadState *) PYBIND11_TLS_GET_VALUE(internals.tstate); - - if (!tstate) { - /* Check if the GIL was acquired using the PyGILState_* API instead (e.g. if - calling from a Python thread). Since we use a different key, this ensures - we don't create a new thread state and deadlock in PyEval_AcquireThread - below. Note we don't save this state with internals.tstate, since we don't - create it we would fail to clear it (its reference count should be > 0). */ - tstate = PyGILState_GetThisThreadState(); - } - - if (!tstate) { - tstate = PyThreadState_New(internals.istate); - #if !defined(NDEBUG) - if (!tstate) - pybind11_fail("scoped_acquire: could not create thread state!"); - #endif - tstate->gilstate_counter = 0; - PYBIND11_TLS_REPLACE_VALUE(internals.tstate, tstate); - } else { - release = detail::get_thread_state_unchecked() != tstate; - } - - if (release) { - /* Work around an annoying assertion in PyThreadState_Swap */ - #if defined(Py_DEBUG) - PyInterpreterState *interp = tstate->interp; - tstate->interp = nullptr; - #endif - PyEval_AcquireThread(tstate); - #if defined(Py_DEBUG) - tstate->interp = interp; - #endif - } - - inc_ref(); - } - - void inc_ref() { - ++tstate->gilstate_counter; - } - - PYBIND11_NOINLINE void dec_ref() { - --tstate->gilstate_counter; - #if !defined(NDEBUG) - if (detail::get_thread_state_unchecked() != tstate) - pybind11_fail("scoped_acquire::dec_ref(): thread state must be current!"); - if (tstate->gilstate_counter < 0) - pybind11_fail("scoped_acquire::dec_ref(): reference count underflow!"); - #endif - if (tstate->gilstate_counter == 0) { - #if !defined(NDEBUG) - if (!release) - pybind11_fail("scoped_acquire::dec_ref(): internal error!"); - #endif - PyThreadState_Clear(tstate); - PyThreadState_DeleteCurrent(); - PYBIND11_TLS_DELETE_VALUE(detail::get_internals().tstate); - release = false; - } - } - - PYBIND11_NOINLINE ~gil_scoped_acquire() { - dec_ref(); - if (release) - PyEval_SaveThread(); - } -private: - PyThreadState *tstate = nullptr; - bool release = true; -}; - -class gil_scoped_release { -public: - explicit gil_scoped_release(bool disassoc = false) : disassoc(disassoc) { - // `get_internals()` must be called here unconditionally in order to initialize - // `internals.tstate` for subsequent `gil_scoped_acquire` calls. Otherwise, an - // initialization race could occur as multiple threads try `gil_scoped_acquire`. - const auto &internals = detail::get_internals(); - tstate = PyEval_SaveThread(); - if (disassoc) { - auto key = internals.tstate; - PYBIND11_TLS_DELETE_VALUE(key); - } - } - ~gil_scoped_release() { - if (!tstate) - return; - PyEval_RestoreThread(tstate); - if (disassoc) { - auto key = detail::get_internals().tstate; - PYBIND11_TLS_REPLACE_VALUE(key, tstate); - } - } -private: - PyThreadState *tstate; - bool disassoc; -}; -#elif defined(PYPY_VERSION) -class gil_scoped_acquire { - PyGILState_STATE state; -public: - gil_scoped_acquire() { state = PyGILState_Ensure(); } - ~gil_scoped_acquire() { PyGILState_Release(state); } -}; - -class gil_scoped_release { - PyThreadState *state; -public: - gil_scoped_release() { state = PyEval_SaveThread(); } - ~gil_scoped_release() { PyEval_RestoreThread(state); } -}; -#else -class gil_scoped_acquire { }; -class gil_scoped_release { }; -#endif - -error_already_set::~error_already_set() { - if (type) { - error_scope scope; - gil_scoped_acquire gil; - type.release().dec_ref(); - value.release().dec_ref(); - trace.release().dec_ref(); - } -} - -inline function get_type_overload(const void *this_ptr, const detail::type_info *this_type, const char *name) { - handle self = detail::get_object_handle(this_ptr, this_type); - if (!self) - return function(); - handle type = self.get_type(); - auto key = std::make_pair(type.ptr(), name); - - /* Cache functions that aren't overloaded in Python to avoid - many costly Python dictionary lookups below */ - auto &cache = detail::get_internals().inactive_overload_cache; - if (cache.find(key) != cache.end()) - return function(); - - function overload = getattr(self, name, function()); - if (overload.is_cpp_function()) { - cache.insert(key); - return function(); - } - - /* Don't call dispatch code if invoked from overridden function. - Unfortunately this doesn't work on PyPy. */ -#if !defined(PYPY_VERSION) - PyFrameObject *frame = PyThreadState_Get()->frame; - if (frame && (std::string) str(frame->f_code->co_name) == name && - frame->f_code->co_argcount > 0) { - PyFrame_FastToLocals(frame); - PyObject *self_caller = PyDict_GetItem( - frame->f_locals, PyTuple_GET_ITEM(frame->f_code->co_varnames, 0)); - if (self_caller == self.ptr()) - return function(); - } -#else - /* PyPy currently doesn't provide a detailed cpyext emulation of - frame objects, so we have to emulate this using Python. This - is going to be slow..*/ - dict d; d["self"] = self; d["name"] = pybind11::str(name); - PyObject *result = PyRun_String( - "import inspect\n" - "frame = inspect.currentframe()\n" - "if frame is not None:\n" - " frame = frame.f_back\n" - " if frame is not None and str(frame.f_code.co_name) == name and " - "frame.f_code.co_argcount > 0:\n" - " self_caller = frame.f_locals[frame.f_code.co_varnames[0]]\n" - " if self_caller == self:\n" - " self = None\n", - Py_file_input, d.ptr(), d.ptr()); - if (result == nullptr) - throw error_already_set(); - if (d["self"].is_none()) - return function(); - Py_DECREF(result); -#endif - - return overload; -} - -template function get_overload(const T *this_ptr, const char *name) { - auto tinfo = detail::get_type_info(typeid(T)); - return tinfo ? get_type_overload(this_ptr, tinfo, name) : function(); -} - -#define PYBIND11_OVERLOAD_INT(ret_type, cname, name, ...) { \ - pybind11::gil_scoped_acquire gil; \ - pybind11::function overload = pybind11::get_overload(static_cast(this), name); \ - if (overload) { \ - auto o = overload(__VA_ARGS__); \ - if (pybind11::detail::cast_is_temporary_value_reference::value) { \ - static pybind11::detail::overload_caster_t caster; \ - return pybind11::detail::cast_ref(std::move(o), caster); \ - } \ - else return pybind11::detail::cast_safe(std::move(o)); \ - } \ - } - -#define PYBIND11_OVERLOAD_NAME(ret_type, cname, name, fn, ...) \ - PYBIND11_OVERLOAD_INT(PYBIND11_TYPE(ret_type), PYBIND11_TYPE(cname), name, __VA_ARGS__) \ - return cname::fn(__VA_ARGS__) - -#define PYBIND11_OVERLOAD_PURE_NAME(ret_type, cname, name, fn, ...) \ - PYBIND11_OVERLOAD_INT(PYBIND11_TYPE(ret_type), PYBIND11_TYPE(cname), name, __VA_ARGS__) \ - pybind11::pybind11_fail("Tried to call pure virtual function \"" PYBIND11_STRINGIFY(cname) "::" name "\""); - -#define PYBIND11_OVERLOAD(ret_type, cname, fn, ...) \ - PYBIND11_OVERLOAD_NAME(PYBIND11_TYPE(ret_type), PYBIND11_TYPE(cname), #fn, fn, __VA_ARGS__) - -#define PYBIND11_OVERLOAD_PURE(ret_type, cname, fn, ...) \ - PYBIND11_OVERLOAD_PURE_NAME(PYBIND11_TYPE(ret_type), PYBIND11_TYPE(cname), #fn, fn, __VA_ARGS__) - -NAMESPACE_END(PYBIND11_NAMESPACE) - -#if defined(_MSC_VER) && !defined(__INTEL_COMPILER) -# pragma warning(pop) -#elif defined(__GNUG__) && !defined(__clang__) -# pragma GCC diagnostic pop -#endif diff --git a/ptocr/postprocess/piexlmerge/include/pybind11/pytypes.h b/ptocr/postprocess/piexlmerge/include/pybind11/pytypes.h deleted file mode 100644 index 3329fda..0000000 --- a/ptocr/postprocess/piexlmerge/include/pybind11/pytypes.h +++ /dev/null @@ -1,1438 +0,0 @@ -/* - pybind11/pytypes.h: Convenience wrapper classes for basic Python types - - Copyright (c) 2016 Wenzel Jakob - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#include "detail/common.h" -#include "buffer_info.h" -#include -#include - -NAMESPACE_BEGIN(PYBIND11_NAMESPACE) - -/* A few forward declarations */ -class handle; class object; -class str; class iterator; -struct arg; struct arg_v; - -NAMESPACE_BEGIN(detail) -class args_proxy; -inline bool isinstance_generic(handle obj, const std::type_info &tp); - -// Accessor forward declarations -template class accessor; -namespace accessor_policies { - struct obj_attr; - struct str_attr; - struct generic_item; - struct sequence_item; - struct list_item; - struct tuple_item; -} -using obj_attr_accessor = accessor; -using str_attr_accessor = accessor; -using item_accessor = accessor; -using sequence_accessor = accessor; -using list_accessor = accessor; -using tuple_accessor = accessor; - -/// Tag and check to identify a class which implements the Python object API -class pyobject_tag { }; -template using is_pyobject = std::is_base_of>; - -/** \rst - A mixin class which adds common functions to `handle`, `object` and various accessors. - The only requirement for `Derived` is to implement ``PyObject *Derived::ptr() const``. -\endrst */ -template -class object_api : public pyobject_tag { - const Derived &derived() const { return static_cast(*this); } - -public: - /** \rst - Return an iterator equivalent to calling ``iter()`` in Python. The object - must be a collection which supports the iteration protocol. - \endrst */ - iterator begin() const; - /// Return a sentinel which ends iteration. - iterator end() const; - - /** \rst - Return an internal functor to invoke the object's sequence protocol. Casting - the returned ``detail::item_accessor`` instance to a `handle` or `object` - subclass causes a corresponding call to ``__getitem__``. Assigning a `handle` - or `object` subclass causes a call to ``__setitem__``. - \endrst */ - item_accessor operator[](handle key) const; - /// See above (the only difference is that they key is provided as a string literal) - item_accessor operator[](const char *key) const; - - /** \rst - Return an internal functor to access the object's attributes. Casting the - returned ``detail::obj_attr_accessor`` instance to a `handle` or `object` - subclass causes a corresponding call to ``getattr``. Assigning a `handle` - or `object` subclass causes a call to ``setattr``. - \endrst */ - obj_attr_accessor attr(handle key) const; - /// See above (the only difference is that they key is provided as a string literal) - str_attr_accessor attr(const char *key) const; - - /** \rst - Matches * unpacking in Python, e.g. to unpack arguments out of a ``tuple`` - or ``list`` for a function call. Applying another * to the result yields - ** unpacking, e.g. to unpack a dict as function keyword arguments. - See :ref:`calling_python_functions`. - \endrst */ - args_proxy operator*() const; - - /// Check if the given item is contained within this object, i.e. ``item in obj``. - template bool contains(T &&item) const; - - /** \rst - Assuming the Python object is a function or implements the ``__call__`` - protocol, ``operator()`` invokes the underlying function, passing an - arbitrary set of parameters. The result is returned as a `object` and - may need to be converted back into a Python object using `handle::cast()`. - - When some of the arguments cannot be converted to Python objects, the - function will throw a `cast_error` exception. When the Python function - call fails, a `error_already_set` exception is thrown. - \endrst */ - template - object operator()(Args &&...args) const; - template - PYBIND11_DEPRECATED("call(...) was deprecated in favor of operator()(...)") - object call(Args&&... args) const; - - /// Equivalent to ``obj is other`` in Python. - bool is(object_api const& other) const { return derived().ptr() == other.derived().ptr(); } - /// Equivalent to ``obj is None`` in Python. - bool is_none() const { return derived().ptr() == Py_None; } - /// Equivalent to obj == other in Python - bool equal(object_api const &other) const { return rich_compare(other, Py_EQ); } - bool not_equal(object_api const &other) const { return rich_compare(other, Py_NE); } - bool operator<(object_api const &other) const { return rich_compare(other, Py_LT); } - bool operator<=(object_api const &other) const { return rich_compare(other, Py_LE); } - bool operator>(object_api const &other) const { return rich_compare(other, Py_GT); } - bool operator>=(object_api const &other) const { return rich_compare(other, Py_GE); } - - object operator-() const; - object operator~() const; - object operator+(object_api const &other) const; - object operator+=(object_api const &other) const; - object operator-(object_api const &other) const; - object operator-=(object_api const &other) const; - object operator*(object_api const &other) const; - object operator*=(object_api const &other) const; - object operator/(object_api const &other) const; - object operator/=(object_api const &other) const; - object operator|(object_api const &other) const; - object operator|=(object_api const &other) const; - object operator&(object_api const &other) const; - object operator&=(object_api const &other) const; - object operator^(object_api const &other) const; - object operator^=(object_api const &other) const; - object operator<<(object_api const &other) const; - object operator<<=(object_api const &other) const; - object operator>>(object_api const &other) const; - object operator>>=(object_api const &other) const; - - PYBIND11_DEPRECATED("Use py::str(obj) instead") - pybind11::str str() const; - - /// Get or set the object's docstring, i.e. ``obj.__doc__``. - str_attr_accessor doc() const; - - /// Return the object's current reference count - int ref_count() const { return static_cast(Py_REFCNT(derived().ptr())); } - /// Return a handle to the Python type object underlying the instance - handle get_type() const; - -private: - bool rich_compare(object_api const &other, int value) const; -}; - -NAMESPACE_END(detail) - -/** \rst - Holds a reference to a Python object (no reference counting) - - The `handle` class is a thin wrapper around an arbitrary Python object (i.e. a - ``PyObject *`` in Python's C API). It does not perform any automatic reference - counting and merely provides a basic C++ interface to various Python API functions. - - .. seealso:: - The `object` class inherits from `handle` and adds automatic reference - counting features. -\endrst */ -class handle : public detail::object_api { -public: - /// The default constructor creates a handle with a ``nullptr``-valued pointer - handle() = default; - /// Creates a ``handle`` from the given raw Python object pointer - handle(PyObject *ptr) : m_ptr(ptr) { } // Allow implicit conversion from PyObject* - - /// Return the underlying ``PyObject *`` pointer - PyObject *ptr() const { return m_ptr; } - PyObject *&ptr() { return m_ptr; } - - /** \rst - Manually increase the reference count of the Python object. Usually, it is - preferable to use the `object` class which derives from `handle` and calls - this function automatically. Returns a reference to itself. - \endrst */ - const handle& inc_ref() const & { Py_XINCREF(m_ptr); return *this; } - - /** \rst - Manually decrease the reference count of the Python object. Usually, it is - preferable to use the `object` class which derives from `handle` and calls - this function automatically. Returns a reference to itself. - \endrst */ - const handle& dec_ref() const & { Py_XDECREF(m_ptr); return *this; } - - /** \rst - Attempt to cast the Python object into the given C++ type. A `cast_error` - will be throw upon failure. - \endrst */ - template T cast() const; - /// Return ``true`` when the `handle` wraps a valid Python object - explicit operator bool() const { return m_ptr != nullptr; } - /** \rst - Deprecated: Check that the underlying pointers are the same. - Equivalent to ``obj1 is obj2`` in Python. - \endrst */ - PYBIND11_DEPRECATED("Use obj1.is(obj2) instead") - bool operator==(const handle &h) const { return m_ptr == h.m_ptr; } - PYBIND11_DEPRECATED("Use !obj1.is(obj2) instead") - bool operator!=(const handle &h) const { return m_ptr != h.m_ptr; } - PYBIND11_DEPRECATED("Use handle::operator bool() instead") - bool check() const { return m_ptr != nullptr; } -protected: - PyObject *m_ptr = nullptr; -}; - -/** \rst - Holds a reference to a Python object (with reference counting) - - Like `handle`, the `object` class is a thin wrapper around an arbitrary Python - object (i.e. a ``PyObject *`` in Python's C API). In contrast to `handle`, it - optionally increases the object's reference count upon construction, and it - *always* decreases the reference count when the `object` instance goes out of - scope and is destructed. When using `object` instances consistently, it is much - easier to get reference counting right at the first attempt. -\endrst */ -class object : public handle { -public: - object() = default; - PYBIND11_DEPRECATED("Use reinterpret_borrow() or reinterpret_steal()") - object(handle h, bool is_borrowed) : handle(h) { if (is_borrowed) inc_ref(); } - /// Copy constructor; always increases the reference count - object(const object &o) : handle(o) { inc_ref(); } - /// Move constructor; steals the object from ``other`` and preserves its reference count - object(object &&other) noexcept { m_ptr = other.m_ptr; other.m_ptr = nullptr; } - /// Destructor; automatically calls `handle::dec_ref()` - ~object() { dec_ref(); } - - /** \rst - Resets the internal pointer to ``nullptr`` without without decreasing the - object's reference count. The function returns a raw handle to the original - Python object. - \endrst */ - handle release() { - PyObject *tmp = m_ptr; - m_ptr = nullptr; - return handle(tmp); - } - - object& operator=(const object &other) { - other.inc_ref(); - dec_ref(); - m_ptr = other.m_ptr; - return *this; - } - - object& operator=(object &&other) noexcept { - if (this != &other) { - handle temp(m_ptr); - m_ptr = other.m_ptr; - other.m_ptr = nullptr; - temp.dec_ref(); - } - return *this; - } - - // Calling cast() on an object lvalue just copies (via handle::cast) - template T cast() const &; - // Calling on an object rvalue does a move, if needed and/or possible - template T cast() &&; - -protected: - // Tags for choosing constructors from raw PyObject * - struct borrowed_t { }; - struct stolen_t { }; - - template friend T reinterpret_borrow(handle); - template friend T reinterpret_steal(handle); - -public: - // Only accessible from derived classes and the reinterpret_* functions - object(handle h, borrowed_t) : handle(h) { inc_ref(); } - object(handle h, stolen_t) : handle(h) { } -}; - -/** \rst - Declare that a `handle` or ``PyObject *`` is a certain type and borrow the reference. - The target type ``T`` must be `object` or one of its derived classes. The function - doesn't do any conversions or checks. It's up to the user to make sure that the - target type is correct. - - .. code-block:: cpp - - PyObject *p = PyList_GetItem(obj, index); - py::object o = reinterpret_borrow(p); - // or - py::tuple t = reinterpret_borrow(p); // <-- `p` must be already be a `tuple` -\endrst */ -template T reinterpret_borrow(handle h) { return {h, object::borrowed_t{}}; } - -/** \rst - Like `reinterpret_borrow`, but steals the reference. - - .. code-block:: cpp - - PyObject *p = PyObject_Str(obj); - py::str s = reinterpret_steal(p); // <-- `p` must be already be a `str` -\endrst */ -template T reinterpret_steal(handle h) { return {h, object::stolen_t{}}; } - -NAMESPACE_BEGIN(detail) -inline std::string error_string(); -NAMESPACE_END(detail) - -/// Fetch and hold an error which was already set in Python. An instance of this is typically -/// thrown to propagate python-side errors back through C++ which can either be caught manually or -/// else falls back to the function dispatcher (which then raises the captured error back to -/// python). -class error_already_set : public std::runtime_error { -public: - /// Constructs a new exception from the current Python error indicator, if any. The current - /// Python error indicator will be cleared. - error_already_set() : std::runtime_error(detail::error_string()) { - PyErr_Fetch(&type.ptr(), &value.ptr(), &trace.ptr()); - } - - error_already_set(const error_already_set &) = default; - error_already_set(error_already_set &&) = default; - - inline ~error_already_set(); - - /// Give the currently-held error back to Python, if any. If there is currently a Python error - /// already set it is cleared first. After this call, the current object no longer stores the - /// error variables (but the `.what()` string is still available). - void restore() { PyErr_Restore(type.release().ptr(), value.release().ptr(), trace.release().ptr()); } - - // Does nothing; provided for backwards compatibility. - PYBIND11_DEPRECATED("Use of error_already_set.clear() is deprecated") - void clear() {} - - /// Check if the currently trapped error type matches the given Python exception class (or a - /// subclass thereof). May also be passed a tuple to search for any exception class matches in - /// the given tuple. - bool matches(handle ex) const { return PyErr_GivenExceptionMatches(ex.ptr(), type.ptr()); } - -private: - object type, value, trace; -}; - -/** \defgroup python_builtins _ - Unless stated otherwise, the following C++ functions behave the same - as their Python counterparts. - */ - -/** \ingroup python_builtins - \rst - Return true if ``obj`` is an instance of ``T``. Type ``T`` must be a subclass of - `object` or a class which was exposed to Python as ``py::class_``. -\endrst */ -template ::value, int> = 0> -bool isinstance(handle obj) { return T::check_(obj); } - -template ::value, int> = 0> -bool isinstance(handle obj) { return detail::isinstance_generic(obj, typeid(T)); } - -template <> inline bool isinstance(handle obj) = delete; -template <> inline bool isinstance(handle obj) { return obj.ptr() != nullptr; } - -/// \ingroup python_builtins -/// Return true if ``obj`` is an instance of the ``type``. -inline bool isinstance(handle obj, handle type) { - const auto result = PyObject_IsInstance(obj.ptr(), type.ptr()); - if (result == -1) - throw error_already_set(); - return result != 0; -} - -/// \addtogroup python_builtins -/// @{ -inline bool hasattr(handle obj, handle name) { - return PyObject_HasAttr(obj.ptr(), name.ptr()) == 1; -} - -inline bool hasattr(handle obj, const char *name) { - return PyObject_HasAttrString(obj.ptr(), name) == 1; -} - -inline void delattr(handle obj, handle name) { - if (PyObject_DelAttr(obj.ptr(), name.ptr()) != 0) { throw error_already_set(); } -} - -inline void delattr(handle obj, const char *name) { - if (PyObject_DelAttrString(obj.ptr(), name) != 0) { throw error_already_set(); } -} - -inline object getattr(handle obj, handle name) { - PyObject *result = PyObject_GetAttr(obj.ptr(), name.ptr()); - if (!result) { throw error_already_set(); } - return reinterpret_steal(result); -} - -inline object getattr(handle obj, const char *name) { - PyObject *result = PyObject_GetAttrString(obj.ptr(), name); - if (!result) { throw error_already_set(); } - return reinterpret_steal(result); -} - -inline object getattr(handle obj, handle name, handle default_) { - if (PyObject *result = PyObject_GetAttr(obj.ptr(), name.ptr())) { - return reinterpret_steal(result); - } else { - PyErr_Clear(); - return reinterpret_borrow(default_); - } -} - -inline object getattr(handle obj, const char *name, handle default_) { - if (PyObject *result = PyObject_GetAttrString(obj.ptr(), name)) { - return reinterpret_steal(result); - } else { - PyErr_Clear(); - return reinterpret_borrow(default_); - } -} - -inline void setattr(handle obj, handle name, handle value) { - if (PyObject_SetAttr(obj.ptr(), name.ptr(), value.ptr()) != 0) { throw error_already_set(); } -} - -inline void setattr(handle obj, const char *name, handle value) { - if (PyObject_SetAttrString(obj.ptr(), name, value.ptr()) != 0) { throw error_already_set(); } -} - -inline ssize_t hash(handle obj) { - auto h = PyObject_Hash(obj.ptr()); - if (h == -1) { throw error_already_set(); } - return h; -} - -/// @} python_builtins - -NAMESPACE_BEGIN(detail) -inline handle get_function(handle value) { - if (value) { -#if PY_MAJOR_VERSION >= 3 - if (PyInstanceMethod_Check(value.ptr())) - value = PyInstanceMethod_GET_FUNCTION(value.ptr()); - else -#endif - if (PyMethod_Check(value.ptr())) - value = PyMethod_GET_FUNCTION(value.ptr()); - } - return value; -} - -// Helper aliases/functions to support implicit casting of values given to python accessors/methods. -// When given a pyobject, this simply returns the pyobject as-is; for other C++ type, the value goes -// through pybind11::cast(obj) to convert it to an `object`. -template ::value, int> = 0> -auto object_or_cast(T &&o) -> decltype(std::forward(o)) { return std::forward(o); } -// The following casting version is implemented in cast.h: -template ::value, int> = 0> -object object_or_cast(T &&o); -// Match a PyObject*, which we want to convert directly to handle via its converting constructor -inline handle object_or_cast(PyObject *ptr) { return ptr; } - -template -class accessor : public object_api> { - using key_type = typename Policy::key_type; - -public: - accessor(handle obj, key_type key) : obj(obj), key(std::move(key)) { } - accessor(const accessor &) = default; - accessor(accessor &&) = default; - - // accessor overload required to override default assignment operator (templates are not allowed - // to replace default compiler-generated assignments). - void operator=(const accessor &a) && { std::move(*this).operator=(handle(a)); } - void operator=(const accessor &a) & { operator=(handle(a)); } - - template void operator=(T &&value) && { - Policy::set(obj, key, object_or_cast(std::forward(value))); - } - template void operator=(T &&value) & { - get_cache() = reinterpret_borrow(object_or_cast(std::forward(value))); - } - - template - PYBIND11_DEPRECATED("Use of obj.attr(...) as bool is deprecated in favor of pybind11::hasattr(obj, ...)") - explicit operator enable_if_t::value || - std::is_same::value, bool>() const { - return hasattr(obj, key); - } - template - PYBIND11_DEPRECATED("Use of obj[key] as bool is deprecated in favor of obj.contains(key)") - explicit operator enable_if_t::value, bool>() const { - return obj.contains(key); - } - - operator object() const { return get_cache(); } - PyObject *ptr() const { return get_cache().ptr(); } - template T cast() const { return get_cache().template cast(); } - -private: - object &get_cache() const { - if (!cache) { cache = Policy::get(obj, key); } - return cache; - } - -private: - handle obj; - key_type key; - mutable object cache; -}; - -NAMESPACE_BEGIN(accessor_policies) -struct obj_attr { - using key_type = object; - static object get(handle obj, handle key) { return getattr(obj, key); } - static void set(handle obj, handle key, handle val) { setattr(obj, key, val); } -}; - -struct str_attr { - using key_type = const char *; - static object get(handle obj, const char *key) { return getattr(obj, key); } - static void set(handle obj, const char *key, handle val) { setattr(obj, key, val); } -}; - -struct generic_item { - using key_type = object; - - static object get(handle obj, handle key) { - PyObject *result = PyObject_GetItem(obj.ptr(), key.ptr()); - if (!result) { throw error_already_set(); } - return reinterpret_steal(result); - } - - static void set(handle obj, handle key, handle val) { - if (PyObject_SetItem(obj.ptr(), key.ptr(), val.ptr()) != 0) { throw error_already_set(); } - } -}; - -struct sequence_item { - using key_type = size_t; - - static object get(handle obj, size_t index) { - PyObject *result = PySequence_GetItem(obj.ptr(), static_cast(index)); - if (!result) { throw error_already_set(); } - return reinterpret_steal(result); - } - - static void set(handle obj, size_t index, handle val) { - // PySequence_SetItem does not steal a reference to 'val' - if (PySequence_SetItem(obj.ptr(), static_cast(index), val.ptr()) != 0) { - throw error_already_set(); - } - } -}; - -struct list_item { - using key_type = size_t; - - static object get(handle obj, size_t index) { - PyObject *result = PyList_GetItem(obj.ptr(), static_cast(index)); - if (!result) { throw error_already_set(); } - return reinterpret_borrow(result); - } - - static void set(handle obj, size_t index, handle val) { - // PyList_SetItem steals a reference to 'val' - if (PyList_SetItem(obj.ptr(), static_cast(index), val.inc_ref().ptr()) != 0) { - throw error_already_set(); - } - } -}; - -struct tuple_item { - using key_type = size_t; - - static object get(handle obj, size_t index) { - PyObject *result = PyTuple_GetItem(obj.ptr(), static_cast(index)); - if (!result) { throw error_already_set(); } - return reinterpret_borrow(result); - } - - static void set(handle obj, size_t index, handle val) { - // PyTuple_SetItem steals a reference to 'val' - if (PyTuple_SetItem(obj.ptr(), static_cast(index), val.inc_ref().ptr()) != 0) { - throw error_already_set(); - } - } -}; -NAMESPACE_END(accessor_policies) - -/// STL iterator template used for tuple, list, sequence and dict -template -class generic_iterator : public Policy { - using It = generic_iterator; - -public: - using difference_type = ssize_t; - using iterator_category = typename Policy::iterator_category; - using value_type = typename Policy::value_type; - using reference = typename Policy::reference; - using pointer = typename Policy::pointer; - - generic_iterator() = default; - generic_iterator(handle seq, ssize_t index) : Policy(seq, index) { } - - reference operator*() const { return Policy::dereference(); } - reference operator[](difference_type n) const { return *(*this + n); } - pointer operator->() const { return **this; } - - It &operator++() { Policy::increment(); return *this; } - It operator++(int) { auto copy = *this; Policy::increment(); return copy; } - It &operator--() { Policy::decrement(); return *this; } - It operator--(int) { auto copy = *this; Policy::decrement(); return copy; } - It &operator+=(difference_type n) { Policy::advance(n); return *this; } - It &operator-=(difference_type n) { Policy::advance(-n); return *this; } - - friend It operator+(const It &a, difference_type n) { auto copy = a; return copy += n; } - friend It operator+(difference_type n, const It &b) { return b + n; } - friend It operator-(const It &a, difference_type n) { auto copy = a; return copy -= n; } - friend difference_type operator-(const It &a, const It &b) { return a.distance_to(b); } - - friend bool operator==(const It &a, const It &b) { return a.equal(b); } - friend bool operator!=(const It &a, const It &b) { return !(a == b); } - friend bool operator< (const It &a, const It &b) { return b - a > 0; } - friend bool operator> (const It &a, const It &b) { return b < a; } - friend bool operator>=(const It &a, const It &b) { return !(a < b); } - friend bool operator<=(const It &a, const It &b) { return !(a > b); } -}; - -NAMESPACE_BEGIN(iterator_policies) -/// Quick proxy class needed to implement ``operator->`` for iterators which can't return pointers -template -struct arrow_proxy { - T value; - - arrow_proxy(T &&value) : value(std::move(value)) { } - T *operator->() const { return &value; } -}; - -/// Lightweight iterator policy using just a simple pointer: see ``PySequence_Fast_ITEMS`` -class sequence_fast_readonly { -protected: - using iterator_category = std::random_access_iterator_tag; - using value_type = handle; - using reference = const handle; - using pointer = arrow_proxy; - - sequence_fast_readonly(handle obj, ssize_t n) : ptr(PySequence_Fast_ITEMS(obj.ptr()) + n) { } - - reference dereference() const { return *ptr; } - void increment() { ++ptr; } - void decrement() { --ptr; } - void advance(ssize_t n) { ptr += n; } - bool equal(const sequence_fast_readonly &b) const { return ptr == b.ptr; } - ssize_t distance_to(const sequence_fast_readonly &b) const { return ptr - b.ptr; } - -private: - PyObject **ptr; -}; - -/// Full read and write access using the sequence protocol: see ``detail::sequence_accessor`` -class sequence_slow_readwrite { -protected: - using iterator_category = std::random_access_iterator_tag; - using value_type = object; - using reference = sequence_accessor; - using pointer = arrow_proxy; - - sequence_slow_readwrite(handle obj, ssize_t index) : obj(obj), index(index) { } - - reference dereference() const { return {obj, static_cast(index)}; } - void increment() { ++index; } - void decrement() { --index; } - void advance(ssize_t n) { index += n; } - bool equal(const sequence_slow_readwrite &b) const { return index == b.index; } - ssize_t distance_to(const sequence_slow_readwrite &b) const { return index - b.index; } - -private: - handle obj; - ssize_t index; -}; - -/// Python's dictionary protocol permits this to be a forward iterator -class dict_readonly { -protected: - using iterator_category = std::forward_iterator_tag; - using value_type = std::pair; - using reference = const value_type; - using pointer = arrow_proxy; - - dict_readonly() = default; - dict_readonly(handle obj, ssize_t pos) : obj(obj), pos(pos) { increment(); } - - reference dereference() const { return {key, value}; } - void increment() { if (!PyDict_Next(obj.ptr(), &pos, &key, &value)) { pos = -1; } } - bool equal(const dict_readonly &b) const { return pos == b.pos; } - -private: - handle obj; - PyObject *key, *value; - ssize_t pos = -1; -}; -NAMESPACE_END(iterator_policies) - -#if !defined(PYPY_VERSION) -using tuple_iterator = generic_iterator; -using list_iterator = generic_iterator; -#else -using tuple_iterator = generic_iterator; -using list_iterator = generic_iterator; -#endif - -using sequence_iterator = generic_iterator; -using dict_iterator = generic_iterator; - -inline bool PyIterable_Check(PyObject *obj) { - PyObject *iter = PyObject_GetIter(obj); - if (iter) { - Py_DECREF(iter); - return true; - } else { - PyErr_Clear(); - return false; - } -} - -inline bool PyNone_Check(PyObject *o) { return o == Py_None; } -#if PY_MAJOR_VERSION >= 3 -inline bool PyEllipsis_Check(PyObject *o) { return o == Py_Ellipsis; } -#endif - -inline bool PyUnicode_Check_Permissive(PyObject *o) { return PyUnicode_Check(o) || PYBIND11_BYTES_CHECK(o); } - -class kwargs_proxy : public handle { -public: - explicit kwargs_proxy(handle h) : handle(h) { } -}; - -class args_proxy : public handle { -public: - explicit args_proxy(handle h) : handle(h) { } - kwargs_proxy operator*() const { return kwargs_proxy(*this); } -}; - -/// Python argument categories (using PEP 448 terms) -template using is_keyword = std::is_base_of; -template using is_s_unpacking = std::is_same; // * unpacking -template using is_ds_unpacking = std::is_same; // ** unpacking -template using is_positional = satisfies_none_of; -template using is_keyword_or_ds = satisfies_any_of; - -// Call argument collector forward declarations -template -class simple_collector; -template -class unpacking_collector; - -NAMESPACE_END(detail) - -// TODO: After the deprecated constructors are removed, this macro can be simplified by -// inheriting ctors: `using Parent::Parent`. It's not an option right now because -// the `using` statement triggers the parent deprecation warning even if the ctor -// isn't even used. -#define PYBIND11_OBJECT_COMMON(Name, Parent, CheckFun) \ - public: \ - PYBIND11_DEPRECATED("Use reinterpret_borrow<"#Name">() or reinterpret_steal<"#Name">()") \ - Name(handle h, bool is_borrowed) : Parent(is_borrowed ? Parent(h, borrowed_t{}) : Parent(h, stolen_t{})) { } \ - Name(handle h, borrowed_t) : Parent(h, borrowed_t{}) { } \ - Name(handle h, stolen_t) : Parent(h, stolen_t{}) { } \ - PYBIND11_DEPRECATED("Use py::isinstance(obj) instead") \ - bool check() const { return m_ptr != nullptr && (bool) CheckFun(m_ptr); } \ - static bool check_(handle h) { return h.ptr() != nullptr && CheckFun(h.ptr()); } - -#define PYBIND11_OBJECT_CVT(Name, Parent, CheckFun, ConvertFun) \ - PYBIND11_OBJECT_COMMON(Name, Parent, CheckFun) \ - /* This is deliberately not 'explicit' to allow implicit conversion from object: */ \ - Name(const object &o) \ - : Parent(check_(o) ? o.inc_ref().ptr() : ConvertFun(o.ptr()), stolen_t{}) \ - { if (!m_ptr) throw error_already_set(); } \ - Name(object &&o) \ - : Parent(check_(o) ? o.release().ptr() : ConvertFun(o.ptr()), stolen_t{}) \ - { if (!m_ptr) throw error_already_set(); } \ - template \ - Name(const ::pybind11::detail::accessor &a) : Name(object(a)) { } - -#define PYBIND11_OBJECT(Name, Parent, CheckFun) \ - PYBIND11_OBJECT_COMMON(Name, Parent, CheckFun) \ - /* This is deliberately not 'explicit' to allow implicit conversion from object: */ \ - Name(const object &o) : Parent(o) { } \ - Name(object &&o) : Parent(std::move(o)) { } - -#define PYBIND11_OBJECT_DEFAULT(Name, Parent, CheckFun) \ - PYBIND11_OBJECT(Name, Parent, CheckFun) \ - Name() : Parent() { } - -/// \addtogroup pytypes -/// @{ - -/** \rst - Wraps a Python iterator so that it can also be used as a C++ input iterator - - Caveat: copying an iterator does not (and cannot) clone the internal - state of the Python iterable. This also applies to the post-increment - operator. This iterator should only be used to retrieve the current - value using ``operator*()``. -\endrst */ -class iterator : public object { -public: - using iterator_category = std::input_iterator_tag; - using difference_type = ssize_t; - using value_type = handle; - using reference = const handle; - using pointer = const handle *; - - PYBIND11_OBJECT_DEFAULT(iterator, object, PyIter_Check) - - iterator& operator++() { - advance(); - return *this; - } - - iterator operator++(int) { - auto rv = *this; - advance(); - return rv; - } - - reference operator*() const { - if (m_ptr && !value.ptr()) { - auto& self = const_cast(*this); - self.advance(); - } - return value; - } - - pointer operator->() const { operator*(); return &value; } - - /** \rst - The value which marks the end of the iteration. ``it == iterator::sentinel()`` - is equivalent to catching ``StopIteration`` in Python. - - .. code-block:: cpp - - void foo(py::iterator it) { - while (it != py::iterator::sentinel()) { - // use `*it` - ++it; - } - } - \endrst */ - static iterator sentinel() { return {}; } - - friend bool operator==(const iterator &a, const iterator &b) { return a->ptr() == b->ptr(); } - friend bool operator!=(const iterator &a, const iterator &b) { return a->ptr() != b->ptr(); } - -private: - void advance() { - value = reinterpret_steal(PyIter_Next(m_ptr)); - if (PyErr_Occurred()) { throw error_already_set(); } - } - -private: - object value = {}; -}; - -class iterable : public object { -public: - PYBIND11_OBJECT_DEFAULT(iterable, object, detail::PyIterable_Check) -}; - -class bytes; - -class str : public object { -public: - PYBIND11_OBJECT_CVT(str, object, detail::PyUnicode_Check_Permissive, raw_str) - - str(const char *c, size_t n) - : object(PyUnicode_FromStringAndSize(c, (ssize_t) n), stolen_t{}) { - if (!m_ptr) pybind11_fail("Could not allocate string object!"); - } - - // 'explicit' is explicitly omitted from the following constructors to allow implicit conversion to py::str from C++ string-like objects - str(const char *c = "") - : object(PyUnicode_FromString(c), stolen_t{}) { - if (!m_ptr) pybind11_fail("Could not allocate string object!"); - } - - str(const std::string &s) : str(s.data(), s.size()) { } - - explicit str(const bytes &b); - - /** \rst - Return a string representation of the object. This is analogous to - the ``str()`` function in Python. - \endrst */ - explicit str(handle h) : object(raw_str(h.ptr()), stolen_t{}) { } - - operator std::string() const { - object temp = *this; - if (PyUnicode_Check(m_ptr)) { - temp = reinterpret_steal(PyUnicode_AsUTF8String(m_ptr)); - if (!temp) - pybind11_fail("Unable to extract string contents! (encoding issue)"); - } - char *buffer; - ssize_t length; - if (PYBIND11_BYTES_AS_STRING_AND_SIZE(temp.ptr(), &buffer, &length)) - pybind11_fail("Unable to extract string contents! (invalid type)"); - return std::string(buffer, (size_t) length); - } - - template - str format(Args &&...args) const { - return attr("format")(std::forward(args)...); - } - -private: - /// Return string representation -- always returns a new reference, even if already a str - static PyObject *raw_str(PyObject *op) { - PyObject *str_value = PyObject_Str(op); -#if PY_MAJOR_VERSION < 3 - if (!str_value) throw error_already_set(); - PyObject *unicode = PyUnicode_FromEncodedObject(str_value, "utf-8", nullptr); - Py_XDECREF(str_value); str_value = unicode; -#endif - return str_value; - } -}; -/// @} pytypes - -inline namespace literals { -/** \rst - String literal version of `str` - \endrst */ -inline str operator"" _s(const char *s, size_t size) { return {s, size}; } -} - -/// \addtogroup pytypes -/// @{ -class bytes : public object { -public: - PYBIND11_OBJECT(bytes, object, PYBIND11_BYTES_CHECK) - - // Allow implicit conversion: - bytes(const char *c = "") - : object(PYBIND11_BYTES_FROM_STRING(c), stolen_t{}) { - if (!m_ptr) pybind11_fail("Could not allocate bytes object!"); - } - - bytes(const char *c, size_t n) - : object(PYBIND11_BYTES_FROM_STRING_AND_SIZE(c, (ssize_t) n), stolen_t{}) { - if (!m_ptr) pybind11_fail("Could not allocate bytes object!"); - } - - // Allow implicit conversion: - bytes(const std::string &s) : bytes(s.data(), s.size()) { } - - explicit bytes(const pybind11::str &s); - - operator std::string() const { - char *buffer; - ssize_t length; - if (PYBIND11_BYTES_AS_STRING_AND_SIZE(m_ptr, &buffer, &length)) - pybind11_fail("Unable to extract bytes contents!"); - return std::string(buffer, (size_t) length); - } -}; - -inline bytes::bytes(const pybind11::str &s) { - object temp = s; - if (PyUnicode_Check(s.ptr())) { - temp = reinterpret_steal(PyUnicode_AsUTF8String(s.ptr())); - if (!temp) - pybind11_fail("Unable to extract string contents! (encoding issue)"); - } - char *buffer; - ssize_t length; - if (PYBIND11_BYTES_AS_STRING_AND_SIZE(temp.ptr(), &buffer, &length)) - pybind11_fail("Unable to extract string contents! (invalid type)"); - auto obj = reinterpret_steal(PYBIND11_BYTES_FROM_STRING_AND_SIZE(buffer, length)); - if (!obj) - pybind11_fail("Could not allocate bytes object!"); - m_ptr = obj.release().ptr(); -} - -inline str::str(const bytes& b) { - char *buffer; - ssize_t length; - if (PYBIND11_BYTES_AS_STRING_AND_SIZE(b.ptr(), &buffer, &length)) - pybind11_fail("Unable to extract bytes contents!"); - auto obj = reinterpret_steal(PyUnicode_FromStringAndSize(buffer, (ssize_t) length)); - if (!obj) - pybind11_fail("Could not allocate string object!"); - m_ptr = obj.release().ptr(); -} - -class none : public object { -public: - PYBIND11_OBJECT(none, object, detail::PyNone_Check) - none() : object(Py_None, borrowed_t{}) { } -}; - -#if PY_MAJOR_VERSION >= 3 -class ellipsis : public object { -public: - PYBIND11_OBJECT(ellipsis, object, detail::PyEllipsis_Check) - ellipsis() : object(Py_Ellipsis, borrowed_t{}) { } -}; -#endif - -class bool_ : public object { -public: - PYBIND11_OBJECT_CVT(bool_, object, PyBool_Check, raw_bool) - bool_() : object(Py_False, borrowed_t{}) { } - // Allow implicit conversion from and to `bool`: - bool_(bool value) : object(value ? Py_True : Py_False, borrowed_t{}) { } - operator bool() const { return m_ptr && PyLong_AsLong(m_ptr) != 0; } - -private: - /// Return the truth value of an object -- always returns a new reference - static PyObject *raw_bool(PyObject *op) { - const auto value = PyObject_IsTrue(op); - if (value == -1) return nullptr; - return handle(value ? Py_True : Py_False).inc_ref().ptr(); - } -}; - -NAMESPACE_BEGIN(detail) -// Converts a value to the given unsigned type. If an error occurs, you get back (Unsigned) -1; -// otherwise you get back the unsigned long or unsigned long long value cast to (Unsigned). -// (The distinction is critically important when casting a returned -1 error value to some other -// unsigned type: (A)-1 != (B)-1 when A and B are unsigned types of different sizes). -template -Unsigned as_unsigned(PyObject *o) { - if (sizeof(Unsigned) <= sizeof(unsigned long) -#if PY_VERSION_HEX < 0x03000000 - || PyInt_Check(o) -#endif - ) { - unsigned long v = PyLong_AsUnsignedLong(o); - return v == (unsigned long) -1 && PyErr_Occurred() ? (Unsigned) -1 : (Unsigned) v; - } - else { - unsigned long long v = PyLong_AsUnsignedLongLong(o); - return v == (unsigned long long) -1 && PyErr_Occurred() ? (Unsigned) -1 : (Unsigned) v; - } -} -NAMESPACE_END(detail) - -class int_ : public object { -public: - PYBIND11_OBJECT_CVT(int_, object, PYBIND11_LONG_CHECK, PyNumber_Long) - int_() : object(PyLong_FromLong(0), stolen_t{}) { } - // Allow implicit conversion from C++ integral types: - template ::value, int> = 0> - int_(T value) { - if (sizeof(T) <= sizeof(long)) { - if (std::is_signed::value) - m_ptr = PyLong_FromLong((long) value); - else - m_ptr = PyLong_FromUnsignedLong((unsigned long) value); - } else { - if (std::is_signed::value) - m_ptr = PyLong_FromLongLong((long long) value); - else - m_ptr = PyLong_FromUnsignedLongLong((unsigned long long) value); - } - if (!m_ptr) pybind11_fail("Could not allocate int object!"); - } - - template ::value, int> = 0> - operator T() const { - return std::is_unsigned::value - ? detail::as_unsigned(m_ptr) - : sizeof(T) <= sizeof(long) - ? (T) PyLong_AsLong(m_ptr) - : (T) PYBIND11_LONG_AS_LONGLONG(m_ptr); - } -}; - -class float_ : public object { -public: - PYBIND11_OBJECT_CVT(float_, object, PyFloat_Check, PyNumber_Float) - // Allow implicit conversion from float/double: - float_(float value) : object(PyFloat_FromDouble((double) value), stolen_t{}) { - if (!m_ptr) pybind11_fail("Could not allocate float object!"); - } - float_(double value = .0) : object(PyFloat_FromDouble((double) value), stolen_t{}) { - if (!m_ptr) pybind11_fail("Could not allocate float object!"); - } - operator float() const { return (float) PyFloat_AsDouble(m_ptr); } - operator double() const { return (double) PyFloat_AsDouble(m_ptr); } -}; - -class weakref : public object { -public: - PYBIND11_OBJECT_DEFAULT(weakref, object, PyWeakref_Check) - explicit weakref(handle obj, handle callback = {}) - : object(PyWeakref_NewRef(obj.ptr(), callback.ptr()), stolen_t{}) { - if (!m_ptr) pybind11_fail("Could not allocate weak reference!"); - } -}; - -class slice : public object { -public: - PYBIND11_OBJECT_DEFAULT(slice, object, PySlice_Check) - slice(ssize_t start_, ssize_t stop_, ssize_t step_) { - int_ start(start_), stop(stop_), step(step_); - m_ptr = PySlice_New(start.ptr(), stop.ptr(), step.ptr()); - if (!m_ptr) pybind11_fail("Could not allocate slice object!"); - } - bool compute(size_t length, size_t *start, size_t *stop, size_t *step, - size_t *slicelength) const { - return PySlice_GetIndicesEx((PYBIND11_SLICE_OBJECT *) m_ptr, - (ssize_t) length, (ssize_t *) start, - (ssize_t *) stop, (ssize_t *) step, - (ssize_t *) slicelength) == 0; - } -}; - -class capsule : public object { -public: - PYBIND11_OBJECT_DEFAULT(capsule, object, PyCapsule_CheckExact) - PYBIND11_DEPRECATED("Use reinterpret_borrow() or reinterpret_steal()") - capsule(PyObject *ptr, bool is_borrowed) : object(is_borrowed ? object(ptr, borrowed_t{}) : object(ptr, stolen_t{})) { } - - explicit capsule(const void *value, const char *name = nullptr, void (*destructor)(PyObject *) = nullptr) - : object(PyCapsule_New(const_cast(value), name, destructor), stolen_t{}) { - if (!m_ptr) - pybind11_fail("Could not allocate capsule object!"); - } - - PYBIND11_DEPRECATED("Please pass a destructor that takes a void pointer as input") - capsule(const void *value, void (*destruct)(PyObject *)) - : object(PyCapsule_New(const_cast(value), nullptr, destruct), stolen_t{}) { - if (!m_ptr) - pybind11_fail("Could not allocate capsule object!"); - } - - capsule(const void *value, void (*destructor)(void *)) { - m_ptr = PyCapsule_New(const_cast(value), nullptr, [](PyObject *o) { - auto destructor = reinterpret_cast(PyCapsule_GetContext(o)); - void *ptr = PyCapsule_GetPointer(o, nullptr); - destructor(ptr); - }); - - if (!m_ptr) - pybind11_fail("Could not allocate capsule object!"); - - if (PyCapsule_SetContext(m_ptr, (void *) destructor) != 0) - pybind11_fail("Could not set capsule context!"); - } - - capsule(void (*destructor)()) { - m_ptr = PyCapsule_New(reinterpret_cast(destructor), nullptr, [](PyObject *o) { - auto destructor = reinterpret_cast(PyCapsule_GetPointer(o, nullptr)); - destructor(); - }); - - if (!m_ptr) - pybind11_fail("Could not allocate capsule object!"); - } - - template operator T *() const { - auto name = this->name(); - T * result = static_cast(PyCapsule_GetPointer(m_ptr, name)); - if (!result) pybind11_fail("Unable to extract capsule contents!"); - return result; - } - - const char *name() const { return PyCapsule_GetName(m_ptr); } -}; - -class tuple : public object { -public: - PYBIND11_OBJECT_CVT(tuple, object, PyTuple_Check, PySequence_Tuple) - explicit tuple(size_t size = 0) : object(PyTuple_New((ssize_t) size), stolen_t{}) { - if (!m_ptr) pybind11_fail("Could not allocate tuple object!"); - } - size_t size() const { return (size_t) PyTuple_Size(m_ptr); } - detail::tuple_accessor operator[](size_t index) const { return {*this, index}; } - detail::item_accessor operator[](handle h) const { return object::operator[](h); } - detail::tuple_iterator begin() const { return {*this, 0}; } - detail::tuple_iterator end() const { return {*this, PyTuple_GET_SIZE(m_ptr)}; } -}; - -class dict : public object { -public: - PYBIND11_OBJECT_CVT(dict, object, PyDict_Check, raw_dict) - dict() : object(PyDict_New(), stolen_t{}) { - if (!m_ptr) pybind11_fail("Could not allocate dict object!"); - } - template ...>::value>, - // MSVC workaround: it can't compile an out-of-line definition, so defer the collector - typename collector = detail::deferred_t, Args...>> - explicit dict(Args &&...args) : dict(collector(std::forward(args)...).kwargs()) { } - - size_t size() const { return (size_t) PyDict_Size(m_ptr); } - detail::dict_iterator begin() const { return {*this, 0}; } - detail::dict_iterator end() const { return {}; } - void clear() const { PyDict_Clear(ptr()); } - bool contains(handle key) const { return PyDict_Contains(ptr(), key.ptr()) == 1; } - bool contains(const char *key) const { return PyDict_Contains(ptr(), pybind11::str(key).ptr()) == 1; } - -private: - /// Call the `dict` Python type -- always returns a new reference - static PyObject *raw_dict(PyObject *op) { - if (PyDict_Check(op)) - return handle(op).inc_ref().ptr(); - return PyObject_CallFunctionObjArgs((PyObject *) &PyDict_Type, op, nullptr); - } -}; - -class sequence : public object { -public: - PYBIND11_OBJECT_DEFAULT(sequence, object, PySequence_Check) - size_t size() const { return (size_t) PySequence_Size(m_ptr); } - detail::sequence_accessor operator[](size_t index) const { return {*this, index}; } - detail::item_accessor operator[](handle h) const { return object::operator[](h); } - detail::sequence_iterator begin() const { return {*this, 0}; } - detail::sequence_iterator end() const { return {*this, PySequence_Size(m_ptr)}; } -}; - -class list : public object { -public: - PYBIND11_OBJECT_CVT(list, object, PyList_Check, PySequence_List) - explicit list(size_t size = 0) : object(PyList_New((ssize_t) size), stolen_t{}) { - if (!m_ptr) pybind11_fail("Could not allocate list object!"); - } - size_t size() const { return (size_t) PyList_Size(m_ptr); } - detail::list_accessor operator[](size_t index) const { return {*this, index}; } - detail::item_accessor operator[](handle h) const { return object::operator[](h); } - detail::list_iterator begin() const { return {*this, 0}; } - detail::list_iterator end() const { return {*this, PyList_GET_SIZE(m_ptr)}; } - template void append(T &&val) const { - PyList_Append(m_ptr, detail::object_or_cast(std::forward(val)).ptr()); - } -}; - -class args : public tuple { PYBIND11_OBJECT_DEFAULT(args, tuple, PyTuple_Check) }; -class kwargs : public dict { PYBIND11_OBJECT_DEFAULT(kwargs, dict, PyDict_Check) }; - -class set : public object { -public: - PYBIND11_OBJECT_CVT(set, object, PySet_Check, PySet_New) - set() : object(PySet_New(nullptr), stolen_t{}) { - if (!m_ptr) pybind11_fail("Could not allocate set object!"); - } - size_t size() const { return (size_t) PySet_Size(m_ptr); } - template bool add(T &&val) const { - return PySet_Add(m_ptr, detail::object_or_cast(std::forward(val)).ptr()) == 0; - } - void clear() const { PySet_Clear(m_ptr); } -}; - -class function : public object { -public: - PYBIND11_OBJECT_DEFAULT(function, object, PyCallable_Check) - handle cpp_function() const { - handle fun = detail::get_function(m_ptr); - if (fun && PyCFunction_Check(fun.ptr())) - return fun; - return handle(); - } - bool is_cpp_function() const { return (bool) cpp_function(); } -}; - -class buffer : public object { -public: - PYBIND11_OBJECT_DEFAULT(buffer, object, PyObject_CheckBuffer) - - buffer_info request(bool writable = false) { - int flags = PyBUF_STRIDES | PyBUF_FORMAT; - if (writable) flags |= PyBUF_WRITABLE; - Py_buffer *view = new Py_buffer(); - if (PyObject_GetBuffer(m_ptr, view, flags) != 0) { - delete view; - throw error_already_set(); - } - return buffer_info(view); - } -}; - -class memoryview : public object { -public: - explicit memoryview(const buffer_info& info) { - static Py_buffer buf { }; - // Py_buffer uses signed sizes, strides and shape!.. - static std::vector py_strides { }; - static std::vector py_shape { }; - buf.buf = info.ptr; - buf.itemsize = info.itemsize; - buf.format = const_cast(info.format.c_str()); - buf.ndim = (int) info.ndim; - buf.len = info.size; - py_strides.clear(); - py_shape.clear(); - for (size_t i = 0; i < (size_t) info.ndim; ++i) { - py_strides.push_back(info.strides[i]); - py_shape.push_back(info.shape[i]); - } - buf.strides = py_strides.data(); - buf.shape = py_shape.data(); - buf.suboffsets = nullptr; - buf.readonly = false; - buf.internal = nullptr; - - m_ptr = PyMemoryView_FromBuffer(&buf); - if (!m_ptr) - pybind11_fail("Unable to create memoryview from buffer descriptor"); - } - - PYBIND11_OBJECT_CVT(memoryview, object, PyMemoryView_Check, PyMemoryView_FromObject) -}; -/// @} pytypes - -/// \addtogroup python_builtins -/// @{ -inline size_t len(handle h) { - ssize_t result = PyObject_Length(h.ptr()); - if (result < 0) - pybind11_fail("Unable to compute length of object"); - return (size_t) result; -} - -inline str repr(handle h) { - PyObject *str_value = PyObject_Repr(h.ptr()); - if (!str_value) throw error_already_set(); -#if PY_MAJOR_VERSION < 3 - PyObject *unicode = PyUnicode_FromEncodedObject(str_value, "utf-8", nullptr); - Py_XDECREF(str_value); str_value = unicode; - if (!str_value) throw error_already_set(); -#endif - return reinterpret_steal(str_value); -} - -inline iterator iter(handle obj) { - PyObject *result = PyObject_GetIter(obj.ptr()); - if (!result) { throw error_already_set(); } - return reinterpret_steal(result); -} -/// @} python_builtins - -NAMESPACE_BEGIN(detail) -template iterator object_api::begin() const { return iter(derived()); } -template iterator object_api::end() const { return iterator::sentinel(); } -template item_accessor object_api::operator[](handle key) const { - return {derived(), reinterpret_borrow(key)}; -} -template item_accessor object_api::operator[](const char *key) const { - return {derived(), pybind11::str(key)}; -} -template obj_attr_accessor object_api::attr(handle key) const { - return {derived(), reinterpret_borrow(key)}; -} -template str_attr_accessor object_api::attr(const char *key) const { - return {derived(), key}; -} -template args_proxy object_api::operator*() const { - return args_proxy(derived().ptr()); -} -template template bool object_api::contains(T &&item) const { - return attr("__contains__")(std::forward(item)).template cast(); -} - -template -pybind11::str object_api::str() const { return pybind11::str(derived()); } - -template -str_attr_accessor object_api::doc() const { return attr("__doc__"); } - -template -handle object_api::get_type() const { return (PyObject *) Py_TYPE(derived().ptr()); } - -template -bool object_api::rich_compare(object_api const &other, int value) const { - int rv = PyObject_RichCompareBool(derived().ptr(), other.derived().ptr(), value); - if (rv == -1) - throw error_already_set(); - return rv == 1; -} - -#define PYBIND11_MATH_OPERATOR_UNARY(op, fn) \ - template object object_api::op() const { \ - object result = reinterpret_steal(fn(derived().ptr())); \ - if (!result.ptr()) \ - throw error_already_set(); \ - return result; \ - } - -#define PYBIND11_MATH_OPERATOR_BINARY(op, fn) \ - template \ - object object_api::op(object_api const &other) const { \ - object result = reinterpret_steal( \ - fn(derived().ptr(), other.derived().ptr())); \ - if (!result.ptr()) \ - throw error_already_set(); \ - return result; \ - } - -PYBIND11_MATH_OPERATOR_UNARY (operator~, PyNumber_Invert) -PYBIND11_MATH_OPERATOR_UNARY (operator-, PyNumber_Negative) -PYBIND11_MATH_OPERATOR_BINARY(operator+, PyNumber_Add) -PYBIND11_MATH_OPERATOR_BINARY(operator+=, PyNumber_InPlaceAdd) -PYBIND11_MATH_OPERATOR_BINARY(operator-, PyNumber_Subtract) -PYBIND11_MATH_OPERATOR_BINARY(operator-=, PyNumber_InPlaceSubtract) -PYBIND11_MATH_OPERATOR_BINARY(operator*, PyNumber_Multiply) -PYBIND11_MATH_OPERATOR_BINARY(operator*=, PyNumber_InPlaceMultiply) -PYBIND11_MATH_OPERATOR_BINARY(operator/, PyNumber_TrueDivide) -PYBIND11_MATH_OPERATOR_BINARY(operator/=, PyNumber_InPlaceTrueDivide) -PYBIND11_MATH_OPERATOR_BINARY(operator|, PyNumber_Or) -PYBIND11_MATH_OPERATOR_BINARY(operator|=, PyNumber_InPlaceOr) -PYBIND11_MATH_OPERATOR_BINARY(operator&, PyNumber_And) -PYBIND11_MATH_OPERATOR_BINARY(operator&=, PyNumber_InPlaceAnd) -PYBIND11_MATH_OPERATOR_BINARY(operator^, PyNumber_Xor) -PYBIND11_MATH_OPERATOR_BINARY(operator^=, PyNumber_InPlaceXor) -PYBIND11_MATH_OPERATOR_BINARY(operator<<, PyNumber_Lshift) -PYBIND11_MATH_OPERATOR_BINARY(operator<<=, PyNumber_InPlaceLshift) -PYBIND11_MATH_OPERATOR_BINARY(operator>>, PyNumber_Rshift) -PYBIND11_MATH_OPERATOR_BINARY(operator>>=, PyNumber_InPlaceRshift) - -#undef PYBIND11_MATH_OPERATOR_UNARY -#undef PYBIND11_MATH_OPERATOR_BINARY - -NAMESPACE_END(detail) -NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/ptocr/postprocess/piexlmerge/include/pybind11/stl.h b/ptocr/postprocess/piexlmerge/include/pybind11/stl.h deleted file mode 100644 index 32f8d29..0000000 --- a/ptocr/postprocess/piexlmerge/include/pybind11/stl.h +++ /dev/null @@ -1,386 +0,0 @@ -/* - pybind11/stl.h: Transparent conversion for STL data types - - Copyright (c) 2016 Wenzel Jakob - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#include "pybind11.h" -#include -#include -#include -#include -#include -#include -#include -#include - -#if defined(_MSC_VER) -#pragma warning(push) -#pragma warning(disable: 4127) // warning C4127: Conditional expression is constant -#endif - -#ifdef __has_include -// std::optional (but including it in c++14 mode isn't allowed) -# if defined(PYBIND11_CPP17) && __has_include() -# include -# define PYBIND11_HAS_OPTIONAL 1 -# endif -// std::experimental::optional (but not allowed in c++11 mode) -# if defined(PYBIND11_CPP14) && (__has_include() && \ - !__has_include()) -# include -# define PYBIND11_HAS_EXP_OPTIONAL 1 -# endif -// std::variant -# if defined(PYBIND11_CPP17) && __has_include() -# include -# define PYBIND11_HAS_VARIANT 1 -# endif -#elif defined(_MSC_VER) && defined(PYBIND11_CPP17) -# include -# include -# define PYBIND11_HAS_OPTIONAL 1 -# define PYBIND11_HAS_VARIANT 1 -#endif - -NAMESPACE_BEGIN(PYBIND11_NAMESPACE) -NAMESPACE_BEGIN(detail) - -/// Extracts an const lvalue reference or rvalue reference for U based on the type of T (e.g. for -/// forwarding a container element). Typically used indirect via forwarded_type(), below. -template -using forwarded_type = conditional_t< - std::is_lvalue_reference::value, remove_reference_t &, remove_reference_t &&>; - -/// Forwards a value U as rvalue or lvalue according to whether T is rvalue or lvalue; typically -/// used for forwarding a container's elements. -template -forwarded_type forward_like(U &&u) { - return std::forward>(std::forward(u)); -} - -template struct set_caster { - using type = Type; - using key_conv = make_caster; - - bool load(handle src, bool convert) { - if (!isinstance(src)) - return false; - auto s = reinterpret_borrow(src); - value.clear(); - for (auto entry : s) { - key_conv conv; - if (!conv.load(entry, convert)) - return false; - value.insert(cast_op(std::move(conv))); - } - return true; - } - - template - static handle cast(T &&src, return_value_policy policy, handle parent) { - if (!std::is_lvalue_reference::value) - policy = return_value_policy_override::policy(policy); - pybind11::set s; - for (auto &&value : src) { - auto value_ = reinterpret_steal(key_conv::cast(forward_like(value), policy, parent)); - if (!value_ || !s.add(value_)) - return handle(); - } - return s.release(); - } - - PYBIND11_TYPE_CASTER(type, _("Set[") + key_conv::name + _("]")); -}; - -template struct map_caster { - using key_conv = make_caster; - using value_conv = make_caster; - - bool load(handle src, bool convert) { - if (!isinstance(src)) - return false; - auto d = reinterpret_borrow(src); - value.clear(); - for (auto it : d) { - key_conv kconv; - value_conv vconv; - if (!kconv.load(it.first.ptr(), convert) || - !vconv.load(it.second.ptr(), convert)) - return false; - value.emplace(cast_op(std::move(kconv)), cast_op(std::move(vconv))); - } - return true; - } - - template - static handle cast(T &&src, return_value_policy policy, handle parent) { - dict d; - return_value_policy policy_key = policy; - return_value_policy policy_value = policy; - if (!std::is_lvalue_reference::value) { - policy_key = return_value_policy_override::policy(policy_key); - policy_value = return_value_policy_override::policy(policy_value); - } - for (auto &&kv : src) { - auto key = reinterpret_steal(key_conv::cast(forward_like(kv.first), policy_key, parent)); - auto value = reinterpret_steal(value_conv::cast(forward_like(kv.second), policy_value, parent)); - if (!key || !value) - return handle(); - d[key] = value; - } - return d.release(); - } - - PYBIND11_TYPE_CASTER(Type, _("Dict[") + key_conv::name + _(", ") + value_conv::name + _("]")); -}; - -template struct list_caster { - using value_conv = make_caster; - - bool load(handle src, bool convert) { - if (!isinstance(src) || isinstance(src)) - return false; - auto s = reinterpret_borrow(src); - value.clear(); - reserve_maybe(s, &value); - for (auto it : s) { - value_conv conv; - if (!conv.load(it, convert)) - return false; - value.push_back(cast_op(std::move(conv))); - } - return true; - } - -private: - template ().reserve(0)), void>::value, int> = 0> - void reserve_maybe(sequence s, Type *) { value.reserve(s.size()); } - void reserve_maybe(sequence, void *) { } - -public: - template - static handle cast(T &&src, return_value_policy policy, handle parent) { - if (!std::is_lvalue_reference::value) - policy = return_value_policy_override::policy(policy); - list l(src.size()); - size_t index = 0; - for (auto &&value : src) { - auto value_ = reinterpret_steal(value_conv::cast(forward_like(value), policy, parent)); - if (!value_) - return handle(); - PyList_SET_ITEM(l.ptr(), (ssize_t) index++, value_.release().ptr()); // steals a reference - } - return l.release(); - } - - PYBIND11_TYPE_CASTER(Type, _("List[") + value_conv::name + _("]")); -}; - -template struct type_caster> - : list_caster, Type> { }; - -template struct type_caster> - : list_caster, Type> { }; - -template struct type_caster> - : list_caster, Type> { }; - -template struct array_caster { - using value_conv = make_caster; - -private: - template - bool require_size(enable_if_t size) { - if (value.size() != size) - value.resize(size); - return true; - } - template - bool require_size(enable_if_t size) { - return size == Size; - } - -public: - bool load(handle src, bool convert) { - if (!isinstance(src)) - return false; - auto l = reinterpret_borrow(src); - if (!require_size(l.size())) - return false; - size_t ctr = 0; - for (auto it : l) { - value_conv conv; - if (!conv.load(it, convert)) - return false; - value[ctr++] = cast_op(std::move(conv)); - } - return true; - } - - template - static handle cast(T &&src, return_value_policy policy, handle parent) { - list l(src.size()); - size_t index = 0; - for (auto &&value : src) { - auto value_ = reinterpret_steal(value_conv::cast(forward_like(value), policy, parent)); - if (!value_) - return handle(); - PyList_SET_ITEM(l.ptr(), (ssize_t) index++, value_.release().ptr()); // steals a reference - } - return l.release(); - } - - PYBIND11_TYPE_CASTER(ArrayType, _("List[") + value_conv::name + _(_(""), _("[") + _() + _("]")) + _("]")); -}; - -template struct type_caster> - : array_caster, Type, false, Size> { }; - -template struct type_caster> - : array_caster, Type, true> { }; - -template struct type_caster> - : set_caster, Key> { }; - -template struct type_caster> - : set_caster, Key> { }; - -template struct type_caster> - : map_caster, Key, Value> { }; - -template struct type_caster> - : map_caster, Key, Value> { }; - -// This type caster is intended to be used for std::optional and std::experimental::optional -template struct optional_caster { - using value_conv = make_caster; - - template - static handle cast(T_ &&src, return_value_policy policy, handle parent) { - if (!src) - return none().inc_ref(); - policy = return_value_policy_override::policy(policy); - return value_conv::cast(*std::forward(src), policy, parent); - } - - bool load(handle src, bool convert) { - if (!src) { - return false; - } else if (src.is_none()) { - return true; // default-constructed value is already empty - } - value_conv inner_caster; - if (!inner_caster.load(src, convert)) - return false; - - value.emplace(cast_op(std::move(inner_caster))); - return true; - } - - PYBIND11_TYPE_CASTER(T, _("Optional[") + value_conv::name + _("]")); -}; - -#if PYBIND11_HAS_OPTIONAL -template struct type_caster> - : public optional_caster> {}; - -template<> struct type_caster - : public void_caster {}; -#endif - -#if PYBIND11_HAS_EXP_OPTIONAL -template struct type_caster> - : public optional_caster> {}; - -template<> struct type_caster - : public void_caster {}; -#endif - -/// Visit a variant and cast any found type to Python -struct variant_caster_visitor { - return_value_policy policy; - handle parent; - - using result_type = handle; // required by boost::variant in C++11 - - template - result_type operator()(T &&src) const { - return make_caster::cast(std::forward(src), policy, parent); - } -}; - -/// Helper class which abstracts away variant's `visit` function. `std::variant` and similar -/// `namespace::variant` types which provide a `namespace::visit()` function are handled here -/// automatically using argument-dependent lookup. Users can provide specializations for other -/// variant-like classes, e.g. `boost::variant` and `boost::apply_visitor`. -template class Variant> -struct visit_helper { - template - static auto call(Args &&...args) -> decltype(visit(std::forward(args)...)) { - return visit(std::forward(args)...); - } -}; - -/// Generic variant caster -template struct variant_caster; - -template class V, typename... Ts> -struct variant_caster> { - static_assert(sizeof...(Ts) > 0, "Variant must consist of at least one alternative."); - - template - bool load_alternative(handle src, bool convert, type_list) { - auto caster = make_caster(); - if (caster.load(src, convert)) { - value = cast_op(caster); - return true; - } - return load_alternative(src, convert, type_list{}); - } - - bool load_alternative(handle, bool, type_list<>) { return false; } - - bool load(handle src, bool convert) { - // Do a first pass without conversions to improve constructor resolution. - // E.g. `py::int_(1).cast>()` needs to fill the `int` - // slot of the variant. Without two-pass loading `double` would be filled - // because it appears first and a conversion is possible. - if (convert && load_alternative(src, false, type_list{})) - return true; - return load_alternative(src, convert, type_list{}); - } - - template - static handle cast(Variant &&src, return_value_policy policy, handle parent) { - return visit_helper::call(variant_caster_visitor{policy, parent}, - std::forward(src)); - } - - using Type = V; - PYBIND11_TYPE_CASTER(Type, _("Union[") + detail::concat(make_caster::name...) + _("]")); -}; - -#if PYBIND11_HAS_VARIANT -template -struct type_caster> : variant_caster> { }; -#endif - -NAMESPACE_END(detail) - -inline std::ostream &operator<<(std::ostream &os, const handle &obj) { - os << (std::string) str(obj); - return os; -} - -NAMESPACE_END(PYBIND11_NAMESPACE) - -#if defined(_MSC_VER) -#pragma warning(pop) -#endif diff --git a/ptocr/postprocess/piexlmerge/include/pybind11/stl_bind.h b/ptocr/postprocess/piexlmerge/include/pybind11/stl_bind.h deleted file mode 100644 index 38dd68f..0000000 --- a/ptocr/postprocess/piexlmerge/include/pybind11/stl_bind.h +++ /dev/null @@ -1,599 +0,0 @@ -/* - pybind11/std_bind.h: Binding generators for STL data types - - Copyright (c) 2016 Sergey Lyskov and Wenzel Jakob - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#include "detail/common.h" -#include "operators.h" - -#include -#include - -NAMESPACE_BEGIN(PYBIND11_NAMESPACE) -NAMESPACE_BEGIN(detail) - -/* SFINAE helper class used by 'is_comparable */ -template struct container_traits { - template static std::true_type test_comparable(decltype(std::declval() == std::declval())*); - template static std::false_type test_comparable(...); - template static std::true_type test_value(typename T2::value_type *); - template static std::false_type test_value(...); - template static std::true_type test_pair(typename T2::first_type *, typename T2::second_type *); - template static std::false_type test_pair(...); - - static constexpr const bool is_comparable = std::is_same(nullptr))>::value; - static constexpr const bool is_pair = std::is_same(nullptr, nullptr))>::value; - static constexpr const bool is_vector = std::is_same(nullptr))>::value; - static constexpr const bool is_element = !is_pair && !is_vector; -}; - -/* Default: is_comparable -> std::false_type */ -template -struct is_comparable : std::false_type { }; - -/* For non-map data structures, check whether operator== can be instantiated */ -template -struct is_comparable< - T, enable_if_t::is_element && - container_traits::is_comparable>> - : std::true_type { }; - -/* For a vector/map data structure, recursively check the value type (which is std::pair for maps) */ -template -struct is_comparable::is_vector>> { - static constexpr const bool value = - is_comparable::value; -}; - -/* For pairs, recursively check the two data types */ -template -struct is_comparable::is_pair>> { - static constexpr const bool value = - is_comparable::value && - is_comparable::value; -}; - -/* Fallback functions */ -template void vector_if_copy_constructible(const Args &...) { } -template void vector_if_equal_operator(const Args &...) { } -template void vector_if_insertion_operator(const Args &...) { } -template void vector_modifiers(const Args &...) { } - -template -void vector_if_copy_constructible(enable_if_t::value, Class_> &cl) { - cl.def(init(), "Copy constructor"); -} - -template -void vector_if_equal_operator(enable_if_t::value, Class_> &cl) { - using T = typename Vector::value_type; - - cl.def(self == self); - cl.def(self != self); - - cl.def("count", - [](const Vector &v, const T &x) { - return std::count(v.begin(), v.end(), x); - }, - arg("x"), - "Return the number of times ``x`` appears in the list" - ); - - cl.def("remove", [](Vector &v, const T &x) { - auto p = std::find(v.begin(), v.end(), x); - if (p != v.end()) - v.erase(p); - else - throw value_error(); - }, - arg("x"), - "Remove the first item from the list whose value is x. " - "It is an error if there is no such item." - ); - - cl.def("__contains__", - [](const Vector &v, const T &x) { - return std::find(v.begin(), v.end(), x) != v.end(); - }, - arg("x"), - "Return true the container contains ``x``" - ); -} - -// Vector modifiers -- requires a copyable vector_type: -// (Technically, some of these (pop and __delitem__) don't actually require copyability, but it seems -// silly to allow deletion but not insertion, so include them here too.) -template -void vector_modifiers(enable_if_t::value, Class_> &cl) { - using T = typename Vector::value_type; - using SizeType = typename Vector::size_type; - using DiffType = typename Vector::difference_type; - - cl.def("append", - [](Vector &v, const T &value) { v.push_back(value); }, - arg("x"), - "Add an item to the end of the list"); - - cl.def(init([](iterable it) { - auto v = std::unique_ptr(new Vector()); - v->reserve(len(it)); - for (handle h : it) - v->push_back(h.cast()); - return v.release(); - })); - - cl.def("extend", - [](Vector &v, const Vector &src) { - v.insert(v.end(), src.begin(), src.end()); - }, - arg("L"), - "Extend the list by appending all the items in the given list" - ); - - cl.def("insert", - [](Vector &v, SizeType i, const T &x) { - if (i > v.size()) - throw index_error(); - v.insert(v.begin() + (DiffType) i, x); - }, - arg("i") , arg("x"), - "Insert an item at a given position." - ); - - cl.def("pop", - [](Vector &v) { - if (v.empty()) - throw index_error(); - T t = v.back(); - v.pop_back(); - return t; - }, - "Remove and return the last item" - ); - - cl.def("pop", - [](Vector &v, SizeType i) { - if (i >= v.size()) - throw index_error(); - T t = v[i]; - v.erase(v.begin() + (DiffType) i); - return t; - }, - arg("i"), - "Remove and return the item at index ``i``" - ); - - cl.def("__setitem__", - [](Vector &v, SizeType i, const T &t) { - if (i >= v.size()) - throw index_error(); - v[i] = t; - } - ); - - /// Slicing protocol - cl.def("__getitem__", - [](const Vector &v, slice slice) -> Vector * { - size_t start, stop, step, slicelength; - - if (!slice.compute(v.size(), &start, &stop, &step, &slicelength)) - throw error_already_set(); - - Vector *seq = new Vector(); - seq->reserve((size_t) slicelength); - - for (size_t i=0; ipush_back(v[start]); - start += step; - } - return seq; - }, - arg("s"), - "Retrieve list elements using a slice object" - ); - - cl.def("__setitem__", - [](Vector &v, slice slice, const Vector &value) { - size_t start, stop, step, slicelength; - if (!slice.compute(v.size(), &start, &stop, &step, &slicelength)) - throw error_already_set(); - - if (slicelength != value.size()) - throw std::runtime_error("Left and right hand size of slice assignment have different sizes!"); - - for (size_t i=0; i= v.size()) - throw index_error(); - v.erase(v.begin() + DiffType(i)); - }, - "Delete the list elements at index ``i``" - ); - - cl.def("__delitem__", - [](Vector &v, slice slice) { - size_t start, stop, step, slicelength; - - if (!slice.compute(v.size(), &start, &stop, &step, &slicelength)) - throw error_already_set(); - - if (step == 1 && false) { - v.erase(v.begin() + (DiffType) start, v.begin() + DiffType(start + slicelength)); - } else { - for (size_t i = 0; i < slicelength; ++i) { - v.erase(v.begin() + DiffType(start)); - start += step - 1; - } - } - }, - "Delete list elements using a slice object" - ); - -} - -// If the type has an operator[] that doesn't return a reference (most notably std::vector), -// we have to access by copying; otherwise we return by reference. -template using vector_needs_copy = negation< - std::is_same()[typename Vector::size_type()]), typename Vector::value_type &>>; - -// The usual case: access and iterate by reference -template -void vector_accessor(enable_if_t::value, Class_> &cl) { - using T = typename Vector::value_type; - using SizeType = typename Vector::size_type; - using ItType = typename Vector::iterator; - - cl.def("__getitem__", - [](Vector &v, SizeType i) -> T & { - if (i >= v.size()) - throw index_error(); - return v[i]; - }, - return_value_policy::reference_internal // ref + keepalive - ); - - cl.def("__iter__", - [](Vector &v) { - return make_iterator< - return_value_policy::reference_internal, ItType, ItType, T&>( - v.begin(), v.end()); - }, - keep_alive<0, 1>() /* Essential: keep list alive while iterator exists */ - ); -} - -// The case for special objects, like std::vector, that have to be returned-by-copy: -template -void vector_accessor(enable_if_t::value, Class_> &cl) { - using T = typename Vector::value_type; - using SizeType = typename Vector::size_type; - using ItType = typename Vector::iterator; - cl.def("__getitem__", - [](const Vector &v, SizeType i) -> T { - if (i >= v.size()) - throw index_error(); - return v[i]; - } - ); - - cl.def("__iter__", - [](Vector &v) { - return make_iterator< - return_value_policy::copy, ItType, ItType, T>( - v.begin(), v.end()); - }, - keep_alive<0, 1>() /* Essential: keep list alive while iterator exists */ - ); -} - -template auto vector_if_insertion_operator(Class_ &cl, std::string const &name) - -> decltype(std::declval() << std::declval(), void()) { - using size_type = typename Vector::size_type; - - cl.def("__repr__", - [name](Vector &v) { - std::ostringstream s; - s << name << '['; - for (size_type i=0; i < v.size(); ++i) { - s << v[i]; - if (i != v.size() - 1) - s << ", "; - } - s << ']'; - return s.str(); - }, - "Return the canonical string representation of this list." - ); -} - -// Provide the buffer interface for vectors if we have data() and we have a format for it -// GCC seems to have "void std::vector::data()" - doing SFINAE on the existence of data() is insufficient, we need to check it returns an appropriate pointer -template -struct vector_has_data_and_format : std::false_type {}; -template -struct vector_has_data_and_format::format(), std::declval().data()), typename Vector::value_type*>::value>> : std::true_type {}; - -// Add the buffer interface to a vector -template -enable_if_t...>::value> -vector_buffer(Class_& cl) { - using T = typename Vector::value_type; - - static_assert(vector_has_data_and_format::value, "There is not an appropriate format descriptor for this vector"); - - // numpy.h declares this for arbitrary types, but it may raise an exception and crash hard at runtime if PYBIND11_NUMPY_DTYPE hasn't been called, so check here - format_descriptor::format(); - - cl.def_buffer([](Vector& v) -> buffer_info { - return buffer_info(v.data(), static_cast(sizeof(T)), format_descriptor::format(), 1, {v.size()}, {sizeof(T)}); - }); - - cl.def(init([](buffer buf) { - auto info = buf.request(); - if (info.ndim != 1 || info.strides[0] % static_cast(sizeof(T))) - throw type_error("Only valid 1D buffers can be copied to a vector"); - if (!detail::compare_buffer_info::compare(info) || (ssize_t) sizeof(T) != info.itemsize) - throw type_error("Format mismatch (Python: " + info.format + " C++: " + format_descriptor::format() + ")"); - - auto vec = std::unique_ptr(new Vector()); - vec->reserve((size_t) info.shape[0]); - T *p = static_cast(info.ptr); - ssize_t step = info.strides[0] / static_cast(sizeof(T)); - T *end = p + info.shape[0] * step; - for (; p != end; p += step) - vec->push_back(*p); - return vec.release(); - })); - - return; -} - -template -enable_if_t...>::value> vector_buffer(Class_&) {} - -NAMESPACE_END(detail) - -// -// std::vector -// -template , typename... Args> -class_ bind_vector(handle scope, std::string const &name, Args&&... args) { - using Class_ = class_; - - // If the value_type is unregistered (e.g. a converting type) or is itself registered - // module-local then make the vector binding module-local as well: - using vtype = typename Vector::value_type; - auto vtype_info = detail::get_type_info(typeid(vtype)); - bool local = !vtype_info || vtype_info->module_local; - - Class_ cl(scope, name.c_str(), pybind11::module_local(local), std::forward(args)...); - - // Declare the buffer interface if a buffer_protocol() is passed in - detail::vector_buffer(cl); - - cl.def(init<>()); - - // Register copy constructor (if possible) - detail::vector_if_copy_constructible(cl); - - // Register comparison-related operators and functions (if possible) - detail::vector_if_equal_operator(cl); - - // Register stream insertion operator (if possible) - detail::vector_if_insertion_operator(cl, name); - - // Modifiers require copyable vector value type - detail::vector_modifiers(cl); - - // Accessor and iterator; return by value if copyable, otherwise we return by ref + keep-alive - detail::vector_accessor(cl); - - cl.def("__bool__", - [](const Vector &v) -> bool { - return !v.empty(); - }, - "Check whether the list is nonempty" - ); - - cl.def("__len__", &Vector::size); - - - - -#if 0 - // C++ style functions deprecated, leaving it here as an example - cl.def(init()); - - cl.def("resize", - (void (Vector::*) (size_type count)) & Vector::resize, - "changes the number of elements stored"); - - cl.def("erase", - [](Vector &v, SizeType i) { - if (i >= v.size()) - throw index_error(); - v.erase(v.begin() + i); - }, "erases element at index ``i``"); - - cl.def("empty", &Vector::empty, "checks whether the container is empty"); - cl.def("size", &Vector::size, "returns the number of elements"); - cl.def("push_back", (void (Vector::*)(const T&)) &Vector::push_back, "adds an element to the end"); - cl.def("pop_back", &Vector::pop_back, "removes the last element"); - - cl.def("max_size", &Vector::max_size, "returns the maximum possible number of elements"); - cl.def("reserve", &Vector::reserve, "reserves storage"); - cl.def("capacity", &Vector::capacity, "returns the number of elements that can be held in currently allocated storage"); - cl.def("shrink_to_fit", &Vector::shrink_to_fit, "reduces memory usage by freeing unused memory"); - - cl.def("clear", &Vector::clear, "clears the contents"); - cl.def("swap", &Vector::swap, "swaps the contents"); - - cl.def("front", [](Vector &v) { - if (v.size()) return v.front(); - else throw index_error(); - }, "access the first element"); - - cl.def("back", [](Vector &v) { - if (v.size()) return v.back(); - else throw index_error(); - }, "access the last element "); - -#endif - - return cl; -} - - - -// -// std::map, std::unordered_map -// - -NAMESPACE_BEGIN(detail) - -/* Fallback functions */ -template void map_if_insertion_operator(const Args &...) { } -template void map_assignment(const Args &...) { } - -// Map assignment when copy-assignable: just copy the value -template -void map_assignment(enable_if_t::value, Class_> &cl) { - using KeyType = typename Map::key_type; - using MappedType = typename Map::mapped_type; - - cl.def("__setitem__", - [](Map &m, const KeyType &k, const MappedType &v) { - auto it = m.find(k); - if (it != m.end()) it->second = v; - else m.emplace(k, v); - } - ); -} - -// Not copy-assignable, but still copy-constructible: we can update the value by erasing and reinserting -template -void map_assignment(enable_if_t< - !std::is_copy_assignable::value && - is_copy_constructible::value, - Class_> &cl) { - using KeyType = typename Map::key_type; - using MappedType = typename Map::mapped_type; - - cl.def("__setitem__", - [](Map &m, const KeyType &k, const MappedType &v) { - // We can't use m[k] = v; because value type might not be default constructable - auto r = m.emplace(k, v); - if (!r.second) { - // value type is not copy assignable so the only way to insert it is to erase it first... - m.erase(r.first); - m.emplace(k, v); - } - } - ); -} - - -template auto map_if_insertion_operator(Class_ &cl, std::string const &name) --> decltype(std::declval() << std::declval() << std::declval(), void()) { - - cl.def("__repr__", - [name](Map &m) { - std::ostringstream s; - s << name << '{'; - bool f = false; - for (auto const &kv : m) { - if (f) - s << ", "; - s << kv.first << ": " << kv.second; - f = true; - } - s << '}'; - return s.str(); - }, - "Return the canonical string representation of this map." - ); -} - - -NAMESPACE_END(detail) - -template , typename... Args> -class_ bind_map(handle scope, const std::string &name, Args&&... args) { - using KeyType = typename Map::key_type; - using MappedType = typename Map::mapped_type; - using Class_ = class_; - - // If either type is a non-module-local bound type then make the map binding non-local as well; - // otherwise (e.g. both types are either module-local or converting) the map will be - // module-local. - auto tinfo = detail::get_type_info(typeid(MappedType)); - bool local = !tinfo || tinfo->module_local; - if (local) { - tinfo = detail::get_type_info(typeid(KeyType)); - local = !tinfo || tinfo->module_local; - } - - Class_ cl(scope, name.c_str(), pybind11::module_local(local), std::forward(args)...); - - cl.def(init<>()); - - // Register stream insertion operator (if possible) - detail::map_if_insertion_operator(cl, name); - - cl.def("__bool__", - [](const Map &m) -> bool { return !m.empty(); }, - "Check whether the map is nonempty" - ); - - cl.def("__iter__", - [](Map &m) { return make_key_iterator(m.begin(), m.end()); }, - keep_alive<0, 1>() /* Essential: keep list alive while iterator exists */ - ); - - cl.def("items", - [](Map &m) { return make_iterator(m.begin(), m.end()); }, - keep_alive<0, 1>() /* Essential: keep list alive while iterator exists */ - ); - - cl.def("__getitem__", - [](Map &m, const KeyType &k) -> MappedType & { - auto it = m.find(k); - if (it == m.end()) - throw key_error(); - return it->second; - }, - return_value_policy::reference_internal // ref + keepalive - ); - - // Assignment provided only if the type is copyable - detail::map_assignment(cl); - - cl.def("__delitem__", - [](Map &m, const KeyType &k) { - auto it = m.find(k); - if (it == m.end()) - throw key_error(); - m.erase(it); - } - ); - - cl.def("__len__", &Map::size); - - return cl; -} - -NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/ptocr/postprocess/piexlmerge/include/pybind11/typeid.h b/ptocr/postprocess/piexlmerge/include/pybind11/typeid.h deleted file mode 100644 index c903fb1..0000000 --- a/ptocr/postprocess/piexlmerge/include/pybind11/typeid.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - pybind11/typeid.h: Compiler-independent access to type identifiers - - Copyright (c) 2016 Wenzel Jakob - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#include -#include - -#if defined(__GNUG__) -#include -#endif - -NAMESPACE_BEGIN(pybind11) -NAMESPACE_BEGIN(detail) -/// Erase all occurrences of a substring -inline void erase_all(std::string &string, const std::string &search) { - for (size_t pos = 0;;) { - pos = string.find(search, pos); - if (pos == std::string::npos) break; - string.erase(pos, search.length()); - } -} - -PYBIND11_NOINLINE inline void clean_type_id(std::string &name) { -#if defined(__GNUG__) - int status = 0; - std::unique_ptr res { - abi::__cxa_demangle(name.c_str(), nullptr, nullptr, &status), std::free }; - if (status == 0) - name = res.get(); -#else - detail::erase_all(name, "class "); - detail::erase_all(name, "struct "); - detail::erase_all(name, "enum "); -#endif - detail::erase_all(name, "pybind11::"); -} -NAMESPACE_END(detail) - -/// Return a string representation of a C++ type -template static std::string type_id() { - std::string name(typeid(T).name()); - detail::clean_type_id(name); - return name; -} - -NAMESPACE_END(pybind11) diff --git a/ptocr/postprocess/piexlmerge/lanms.h b/ptocr/postprocess/piexlmerge/lanms.h deleted file mode 100644 index 679666c..0000000 --- a/ptocr/postprocess/piexlmerge/lanms.h +++ /dev/null @@ -1,234 +0,0 @@ -#pragma once - -#include "clipper/clipper.hpp" - -// locality-aware NMS -namespace lanms { - - namespace cl = ClipperLib; - - struct Polygon { - cl::Path poly; - float score; - }; - - float paths_area(const ClipperLib::Paths &ps) { - float area = 0; - for (auto &&p: ps) - area += cl::Area(p); - return area; - } - - float poly_iou(const Polygon &a, const Polygon &b) { - cl::Clipper clpr; - clpr.AddPath(a.poly, cl::ptSubject, true); - clpr.AddPath(b.poly, cl::ptClip, true); - - cl::Paths inter, uni; - clpr.Execute(cl::ctIntersection, inter, cl::pftEvenOdd); - clpr.Execute(cl::ctUnion, uni, cl::pftEvenOdd); - - auto inter_area = paths_area(inter), - uni_area = paths_area(uni); - return std::abs(inter_area) / std::max(std::abs(uni_area), 1.0f); - } - - bool should_merge(const Polygon &a, const Polygon &b, float iou_threshold) { - return poly_iou(a, b) > iou_threshold; - } - - /** - * Incrementally merge polygons - */ - class PolyMerger { - public: - PolyMerger(): score(0), nr_polys(0) { - memset(data, 0, sizeof(data)); - } - - /** - * Add a new polygon to be merged. - */ - void add(const Polygon &p_given) { - Polygon p; - if (nr_polys > 0) { - // vertices of two polygons to merge may not in the same order; - // we match their vertices by choosing the ordering that - // minimizes the total squared distance. - // see function normalize_poly for details. - p = normalize_poly(get(), p_given); - } else { - p = p_given; - } - assert(p.poly.size() == 4); - auto &poly = p.poly; - auto s = p.score; - data[0] += poly[0].X * s; - data[1] += poly[0].Y * s; - - data[2] += poly[1].X * s; - data[3] += poly[1].Y * s; - - data[4] += poly[2].X * s; - data[5] += poly[2].Y * s; - - data[6] += poly[3].X * s; - data[7] += poly[3].Y * s; - - score += p.score; - - nr_polys += 1; - } - - inline std::int64_t sqr(std::int64_t x) { return x * x; } - - Polygon normalize_poly( - const Polygon &ref, - const Polygon &p) { - - std::int64_t min_d = std::numeric_limits::max(); - size_t best_start = 0, best_order = 0; - - for (size_t start = 0; start < 4; start ++) { - size_t j = start; - std::int64_t d = ( - sqr(ref.poly[(j + 0) % 4].X - p.poly[(j + 0) % 4].X) - + sqr(ref.poly[(j + 0) % 4].Y - p.poly[(j + 0) % 4].Y) - + sqr(ref.poly[(j + 1) % 4].X - p.poly[(j + 1) % 4].X) - + sqr(ref.poly[(j + 1) % 4].Y - p.poly[(j + 1) % 4].Y) - + sqr(ref.poly[(j + 2) % 4].X - p.poly[(j + 2) % 4].X) - + sqr(ref.poly[(j + 2) % 4].Y - p.poly[(j + 2) % 4].Y) - + sqr(ref.poly[(j + 3) % 4].X - p.poly[(j + 3) % 4].X) - + sqr(ref.poly[(j + 3) % 4].Y - p.poly[(j + 3) % 4].Y) - ); - if (d < min_d) { - min_d = d; - best_start = start; - best_order = 0; - } - - d = ( - sqr(ref.poly[(j + 0) % 4].X - p.poly[(j + 3) % 4].X) - + sqr(ref.poly[(j + 0) % 4].Y - p.poly[(j + 3) % 4].Y) - + sqr(ref.poly[(j + 1) % 4].X - p.poly[(j + 2) % 4].X) - + sqr(ref.poly[(j + 1) % 4].Y - p.poly[(j + 2) % 4].Y) - + sqr(ref.poly[(j + 2) % 4].X - p.poly[(j + 1) % 4].X) - + sqr(ref.poly[(j + 2) % 4].Y - p.poly[(j + 1) % 4].Y) - + sqr(ref.poly[(j + 3) % 4].X - p.poly[(j + 0) % 4].X) - + sqr(ref.poly[(j + 3) % 4].Y - p.poly[(j + 0) % 4].Y) - ); - if (d < min_d) { - min_d = d; - best_start = start; - best_order = 1; - } - } - - Polygon r; - r.poly.resize(4); - auto j = best_start; - if (best_order == 0) { - for (size_t i = 0; i < 4; i ++) - r.poly[i] = p.poly[(j + i) % 4]; - } else { - for (size_t i = 0; i < 4; i ++) - r.poly[i] = p.poly[(j + 4 - i - 1) % 4]; - } - r.score = p.score; - return r; - } - - Polygon get() const { - Polygon p; - - auto &poly = p.poly; - poly.resize(4); - auto score_inv = 1.0f / std::max(1e-8f, score); - poly[0].X = data[0] * score_inv; - poly[0].Y = data[1] * score_inv; - poly[1].X = data[2] * score_inv; - poly[1].Y = data[3] * score_inv; - poly[2].X = data[4] * score_inv; - poly[2].Y = data[5] * score_inv; - poly[3].X = data[6] * score_inv; - poly[3].Y = data[7] * score_inv; - - assert(score > 0); - p.score = score; - - return p; - } - - private: - std::int64_t data[8]; - float score; - std::int32_t nr_polys; - }; - - - /** - * The standard NMS algorithm. - */ - std::vector standard_nms(std::vector &polys, float iou_threshold) { - size_t n = polys.size(); - if (n == 0) - return {}; - std::vector indices(n); - std::iota(std::begin(indices), std::end(indices), 0); - std::sort(std::begin(indices), std::end(indices), [&](size_t i, size_t j) { return polys[i].score > polys[j].score; }); - - std::vector keep; - while (indices.size()) { - size_t p = 0, cur = indices[0]; - keep.emplace_back(cur); - for (size_t i = 1; i < indices.size(); i ++) { - if (!should_merge(polys[cur], polys[indices[i]], iou_threshold)) { - indices[p ++] = indices[i]; - } - } - indices.resize(p); - } - - std::vector ret; - for (auto &&i: keep) { - ret.emplace_back(polys[i]); - } - return ret; - } - - std::vector - merge_quadrangle_n9(const float *data, size_t n, float iou_threshold) { - using cInt = cl::cInt; - - // first pass - std::vector polys; - for (size_t i = 0; i < n; i ++) { - auto p = data + i * 9; - Polygon poly{ - { - {cInt(p[0]), cInt(p[1])}, - {cInt(p[2]), cInt(p[3])}, - {cInt(p[4]), cInt(p[5])}, - {cInt(p[6]), cInt(p[7])}, - }, - p[8], - }; - - if (polys.size()) { - // merge with the last one - auto &bpoly = polys.back(); - if (should_merge(poly, bpoly, iou_threshold)) { - PolyMerger merger; - merger.add(bpoly); - merger.add(poly); - bpoly = merger.get(); - } else { - polys.emplace_back(poly); - } - } else { - polys.emplace_back(poly); - } - } - return standard_nms(polys, iou_threshold); - } -} diff --git a/ptocr/postprocess/piexlmerge/pixelmerge.cpp b/ptocr/postprocess/piexlmerge/pixelmerge.cpp deleted file mode 100644 index 70b62a2..0000000 --- a/ptocr/postprocess/piexlmerge/pixelmerge.cpp +++ /dev/null @@ -1,310 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include "include/pybind11/pybind11.h" -#include "include/pybind11/numpy.h" -#include "include/pybind11/stl.h" -#include "include/pybind11/stl_bind.h" - -#include -#include -#include -#include - -using namespace std; -using namespace cv; - -namespace py = pybind11; - - - -namespace pixelmerge{ - - - void get_kernals(const int *data, vector data_shape, vector &kernals) { - for (int i = 0; i < data_shape[0]; ++i) { - Mat kernal = Mat::zeros(data_shape[1], data_shape[2], CV_8UC1); - for (int x = 0; x < kernal.rows; ++x) { - for (int y = 0; y < kernal.cols; ++y) { - kernal.at(x, y) = data[i * data_shape[1] * data_shape[2] + x * data_shape[2] + y]; - } - } - kernals.emplace_back(kernal); - } - } - - void growing_text_line(vector &kernals, vector> &text_line, float min_area) { - - Mat label_mat; - int label_num = connectedComponents(kernals[kernals.size() - 1], label_mat, 4); - - // cout << "label num: " << label_num << endl; - - int area[label_num + 1]; - memset(area, 0, sizeof(area)); - for (int x = 0; x < label_mat.rows; ++x) { - for (int y = 0; y < label_mat.cols; ++y) { - int label = label_mat.at(x, y); - if (label == 0) continue; - area[label] += 1; - } - } - - queue queue, next_queue; - for (int x = 0; x < label_mat.rows; ++x) { - vector row(label_mat.cols); - for (int y = 0; y < label_mat.cols; ++y) { - int label = label_mat.at(x, y); - - if (label == 0) continue; - if (area[label] < min_area) continue; - - Point point(x, y); - queue.push(point); - row[y] = label; - } - text_line.emplace_back(row); - } - - // cout << "ok" << endl; - - int dx[] = {-1, 1, 0, 0}; - int dy[] = {0, 0, -1, 1}; - - for (int kernal_id = kernals.size() - 2; kernal_id >= 0; --kernal_id) { - while (!queue.empty()) { - Point point = queue.front(); queue.pop(); - int x = point.x; - int y = point.y; - int label = text_line[x][y]; - // cout << text_line.size() << ' ' << text_line[0].size() << ' ' << x << ' ' << y << endl; - - bool is_edge = true; - for (int d = 0; d < 4; ++d) { - int tmp_x = x + dx[d]; - int tmp_y = y + dy[d]; - - if (tmp_x < 0 || tmp_x >= (int)text_line.size()) continue; - if (tmp_y < 0 || tmp_y >= (int)text_line[1].size()) continue; - if (kernals[kernal_id].at(tmp_x, tmp_y) == 0) continue; - if (text_line[tmp_x][tmp_y] > 0) continue; - - Point point(tmp_x, tmp_y); - queue.push(point); - text_line[tmp_x][tmp_y] = label; - is_edge = false; - } - - if (is_edge) { - next_queue.push(point); - } - } - swap(queue, next_queue); - } - } - - vector> pse(py::array_t quad_n9, float min_area) { - auto buf = quad_n9.request(); - auto data = static_cast(buf.ptr); - vector kernals; - get_kernals(data, buf.shape, kernals); - - // cout << "min_area: " << min_area << endl; - // for (int i = 0; i < kernals.size(); ++i) { - // cout << "kernal" << i <<" shape: " << kernals[i].rows << ' ' << kernals[i].cols << endl; - // } - - vector> text_line; - growing_text_line(kernals, text_line, min_area); - - return text_line; - } - - - py::array_t pan( - py::array_t text, - py::array_t similarity_vectors, - py::array_t label_map, - int label_num, - float dis_threshold = 0.8) - { - auto pbuf_text = text.request(); - auto pbuf_similarity_vectors = similarity_vectors.request(); - auto pbuf_label_map = label_map.request(); - if (pbuf_label_map.ndim != 2 || pbuf_label_map.shape[0]==0 || pbuf_label_map.shape[1]==0) - throw std::runtime_error("label map must have a shape of (h>0, w>0)"); - int h = pbuf_label_map.shape[0]; - int w = pbuf_label_map.shape[1]; - if (pbuf_similarity_vectors.ndim != 3 || pbuf_similarity_vectors.shape[0]!=h || pbuf_similarity_vectors.shape[1]!=w || pbuf_similarity_vectors.shape[2]!=4 || - pbuf_text.shape[0]!=h || pbuf_text.shape[1]!=w) - throw std::runtime_error("similarity_vectors must have a shape of (h,w,4) and text must have a shape of (h,w,4)"); - //初始化结果 - auto res = py::array_t(pbuf_text.size); - auto pbuf_res = res.request(); - // 获取 text similarity_vectors 和 label_map的指针 - auto ptr_label_map = static_cast(pbuf_label_map.ptr); - auto ptr_text = static_cast(pbuf_text.ptr); - auto ptr_similarity_vectors = static_cast(pbuf_similarity_vectors.ptr); - auto ptr_res = static_cast(pbuf_res.ptr); - - std::queue> q; - // 计算各个kernel的similarity_vectors - float kernel_vector[label_num][5] = {0}; - - // 文本像素入队列 - for (int i = 0; i0) - { - kernel_vector[label][0] += p_similarity_vectors[k]; - kernel_vector[label][1] += p_similarity_vectors[k+1]; - kernel_vector[label][2] += p_similarity_vectors[k+2]; - kernel_vector[label][3] += p_similarity_vectors[k+3]; - kernel_vector[label][4] += 1; - q.push(std::make_tuple(i, j, label)); - } - p_res[j] = label; - } - } - - for(int i=0;i(q_n); - int x = std::get<1>(q_n); - int32_t l = std::get<2>(q_n); - //store the edge pixel after one expansion - auto kernel_cv = kernel_vector[l]; - for (int idx=0; idx<4; idx++) - { - int tmpy = y + dy[idx]; - int tmpx = x + dx[idx]; - auto p_res = ptr_res + tmpy*w; - if (tmpy<0 || tmpy>=h || tmpx<0 || tmpx>=w) - continue; - if (!ptr_text[tmpy*w+tmpx] || p_res[tmpx]>0) - continue; - // 计算距离 - float dis = 0; - auto p_similarity_vectors = ptr_similarity_vectors + tmpy * w*4; - for(size_t i=0;i<4;i++) - { - dis += pow(kernel_cv[i] - p_similarity_vectors[tmpx*4 + i],2); - } - dis = sqrt(dis); - if(dis >= dis_threshold) - continue; - q.push(std::make_tuple(tmpy, tmpx, l)); - p_res[tmpx]=l; - } - } - return res; - } - - std::map> get_points( - py::array_t label_map, - py::array_t score_map, - int label_num) - { - auto pbuf_label_map = label_map.request(); - auto pbuf_score_map = score_map.request(); - auto ptr_label_map = static_cast(pbuf_label_map.ptr); - auto ptr_score_map = static_cast(pbuf_score_map.ptr); - int h = pbuf_label_map.shape[0]; - int w = pbuf_label_map.shape[1]; - - std::map> point_dict; - std::vector> point_vector; - for(int i=0;i point; - point.push_back(0); - point.push_back(0); - point_vector.push_back(point); - } - for (int i = 0; i 2) - { - point_vector[i][0] /= point_vector[i][1]; - point_dict[i] = point_vector[i]; - } - } - return point_dict; - } - std::vector get_num( - py::array_t label_map, - int label_num) - { - auto pbuf_label_map = label_map.request(); - auto ptr_label_map = static_cast(pbuf_label_map.ptr); - int h = pbuf_label_map.shape[0]; - int w = pbuf_label_map.shape[1]; - - std::vector point_vector; - for(int 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
50VUAsY>~DXSho06cT7~WYEz~mR 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

OXcwn$an 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 diff --git a/ptocr/utils/__init__.py b/ptocr/utils/__init__.py deleted file mode 100644 index 35de2ad..0000000 --- a/ptocr/utils/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -#-*- coding:utf-8 _*- -""" -@author:fxw -@file: __init__.py.py -@time: 2020/08/07 -""" diff --git a/ptocr/utils/cal_iou_acc.py b/ptocr/utils/cal_iou_acc.py deleted file mode 100644 index 8c731a3..0000000 --- a/ptocr/utils/cal_iou_acc.py +++ /dev/null @@ -1,64 +0,0 @@ -#-*- coding:utf-8 _*- -""" -@author:fxw -@file: cal_iou_acc.py -@time: 2020/08/13 -""" -import torch -import numpy as np - -def cal_binary_score(binarys, gt_binarys, training_masks, running_metric_binary, thresh=0.5): - training_masks = training_masks.data.cpu().numpy() - pred_binary = binarys.data.cpu().numpy() * training_masks - pred_binary[pred_binary <= thresh] = 0 - pred_binary[pred_binary > thresh] = 1 - pred_binary = pred_binary.astype(np.int32) - gt_binary = gt_binarys.data.cpu().numpy() * training_masks - gt_binary = gt_binary.astype(np.int32) - running_metric_binary.update(gt_binary, pred_binary) - score_binary, _ = running_metric_binary.get_scores() - return score_binary - -def cal_text_score(texts, gt_texts, training_masks, running_metric_text, thresh=0.5): - training_masks = training_masks.data.cpu().numpy() - pred_text = torch.sigmoid(texts).data.cpu().numpy() * training_masks - pred_text[pred_text <= thresh] = 0 - pred_text[pred_text > thresh] = 1 - pred_text = pred_text.astype(np.int32) - gt_text = gt_texts.data.cpu().numpy() * training_masks - gt_text = gt_text.astype(np.int32) - running_metric_text.update(gt_text, pred_text) - score_text, _ = running_metric_text.get_scores() - return score_text - -def cal_kernel_score(kernels, gt_kernels, gt_texts, training_masks, running_metric_kernel, thresh=0.5): - mask = (gt_texts * training_masks).data.cpu().numpy() - kernel = kernels[:, -1, :, :] - gt_kernel = gt_kernels[:, -1, :, :] - pred_kernel = torch.sigmoid(kernel).data.cpu().numpy() - pred_kernel[pred_kernel <= thresh] = 0 - pred_kernel[pred_kernel > thresh] = 1 - pred_kernel = (pred_kernel * mask).astype(np.int32) - gt_kernel = gt_kernel.data.cpu().numpy() - gt_kernel = (gt_kernel * mask).astype(np.int32) - running_metric_kernel.update(gt_kernel, pred_kernel) - score_kernel, _ = running_metric_kernel.get_scores() - return score_kernel - -def cal_PAN_PSE(kernels, gt_kernels,texts ,gt_texts, training_masks, running_metric_text,running_metric_kernel): - if(len(kernels.shape)==3): - kernels = kernels.unsqueeze(1) - gt_kernels = gt_kernels.unsqueeze(1) - score_kernel = cal_kernel_score(kernels, gt_kernels, gt_texts, training_masks, running_metric_kernel) - score_text = cal_text_score(texts, gt_texts, training_masks, running_metric_text) - acc = (score_text['Mean Acc'] + score_kernel['Mean Acc'])/2 - iou = (score_text['Mean IoU'] + score_kernel['Mean IoU'])/2 - return iou,acc - -def cal_DB(texts ,gt_texts, training_masks, running_metric_text): - score_text = cal_binary_score(texts.squeeze(1), gt_texts.squeeze(1), training_masks.squeeze(1), running_metric_text) - acc = score_text['Mean Acc'] - iou = score_text['Mean IoU'] - return iou,acc - - diff --git a/ptocr/utils/gen_teacher_model.py b/ptocr/utils/gen_teacher_model.py deleted file mode 100644 index c3f9e74..0000000 --- a/ptocr/utils/gen_teacher_model.py +++ /dev/null @@ -1,58 +0,0 @@ -#-*- coding:utf-8 _*- -""" -@author:fxw -@file: gen_teacher_model.py -@time: 2020/10/15 -""" -import torch -import torch.nn as nn -import torch.nn.functional as F -import yaml -from ptocr.utils.util_function import create_module,load_model - - -class DiceLoss(nn.Module): - def __init__(self,eps=1e-6): - super(DiceLoss,self).__init__() - self.eps = eps - def forward(self,pre_score,gt_score,train_mask): - pre_score = pre_score.contiguous().view(pre_score.size()[0], -1) - gt_score = gt_score.contiguous().view(gt_score.size()[0], -1) - train_mask = train_mask.contiguous().view(train_mask.size()[0], -1) - - pre_score = pre_score * train_mask - gt_score = gt_score * train_mask - - a = torch.sum(pre_score * gt_score, 1) - b = torch.sum(pre_score * pre_score, 1) + self.eps - c = torch.sum(gt_score * gt_score, 1) + self.eps - d = (2 * a) / (b + c) - dice_loss = torch.mean(d) - return 1 - dice_loss - - -def GetTeacherModel(args): - config = yaml.load(open(args.t_config, 'r', encoding='utf-8'), Loader=yaml.FullLoader) - model = create_module(config['architectures']['model_function'])(config) - model = load_model(model,args.t_model_path) - if torch.cuda.is_available(): - model = model.cuda() - return model - -class DistilLoss(nn.Module): - def __init__(self): - super(DistilLoss, self).__init__() - self.mse = nn.MSELoss() - self.diceloss = DiceLoss() - self.ignore = ['thresh'] - def forward(self, s_map, t_map): - loss = 0 -# import pdb -# pdb.set_trace() - for key in s_map.keys(): - if(key in self.ignore): - continue - loss+=self.diceloss(s_map[key],t_map[key],torch.ones(t_map[key].shape).cuda()) - return loss - - diff --git a/ptocr/utils/logger.py b/ptocr/utils/logger.py deleted file mode 100644 index 4ca3794..0000000 --- a/ptocr/utils/logger.py +++ /dev/null @@ -1,66 +0,0 @@ -#-*- coding:utf-8 _*- -# A simple torch style logger -# (C) Wei YANG 2017 -from __future__ import absolute_import - -class Logger(object): - def __init__(self, fpath, title=None, resume=False): - self.file = None - self.resume = resume - self.title = '' if title == None else title - if fpath is not None: - if resume: - self.file = open(fpath, 'r') - name = self.file.readline() - self.names = name.rstrip().split('\t') - self.numbers = {} - for _, name in enumerate(self.names): - self.numbers[name] = [] - - for numbers in self.file: - numbers = numbers.rstrip().split('\t') - for i in range(0, len(numbers)): - self.numbers[self.names[i]].append(numbers[i]) - self.file.close() - self.file = open(fpath, 'a') - else: - self.file = open(fpath, 'w') - - def set_names(self, names): - if self.resume: - pass - self.numbers = {} - self.names = names - for _, name in enumerate(self.names): - self.file.write(name) - self.file.write('\t') - self.numbers[name] = [] - self.file.write('\n') - self.file.flush() - - def set_split(self, names): - if self.resume: - pass - self.numbers = {} - self.names = names - for _, name in enumerate(self.names): - self.file.write(name) - self.numbers[name] = [] - self.file.write('\n') - self.file.flush() - - def append(self, numbers): - assert len(self.names) == len(numbers), 'Numbers do not match names' - for index, num in enumerate(numbers): - self.file.write("{0:.6f}".format(num)) - self.file.write('\t') - self.numbers[self.names[index]].append(num) - self.file.write('\n') - self.file.flush() - - def close(self): - if self.file is not None: - self.file.close() - - - diff --git a/ptocr/utils/metrics.py b/ptocr/utils/metrics.py deleted file mode 100644 index 59d45e8..0000000 --- a/ptocr/utils/metrics.py +++ /dev/null @@ -1,50 +0,0 @@ -# Adapted from score written by wkentaro -# https://github.com/wkentaro/pytorch-fcn/blob/master/torchfcn/utils.py - -import numpy as np - -class runningScore(object): - - def __init__(self, n_classes): - self.n_classes = n_classes - self.confusion_matrix = np.zeros((n_classes, n_classes)) - - def _fast_hist(self, label_true, label_pred, n_class): - mask = (label_true >= 0) & (label_true < n_class) - - if np.sum((label_pred[mask] < 0)) > 0: - print (label_pred[label_pred < 0]) - hist = np.bincount( - n_class * label_true[mask].astype(int) + - label_pred[mask], minlength=n_class**2).reshape(n_class, n_class) - return hist - - def update(self, label_trues, label_preds): - # print label_trues.dtype, label_preds.dtype - for lt, lp in zip(label_trues, label_preds): - self.confusion_matrix += self._fast_hist(lt.flatten(), lp.flatten(), self.n_classes) - - def get_scores(self): - """Returns accuracy score evaluation result. - - overall accuracy - - mean accuracy - - mean IU - - fwavacc - """ - hist = self.confusion_matrix - acc = np.diag(hist).sum() / (hist.sum() + 0.0001) - acc_cls = np.diag(hist) / (hist.sum(axis=1) + 0.0001) - acc_cls = np.nanmean(acc_cls) - iu = np.diag(hist) / (hist.sum(axis=1) + hist.sum(axis=0) - np.diag(hist) + 0.0001) - mean_iu = np.nanmean(iu) - freq = hist.sum(axis=1) / (hist.sum() + 0.0001) - fwavacc = (freq[freq > 0] * iu[freq > 0]).sum() - cls_iu = dict(zip(range(self.n_classes), iu)) - - return {'Overall Acc': acc, - 'Mean Acc': acc_cls, - 'FreqW Acc': fwavacc, - 'Mean IoU': mean_iu,}, cls_iu - - def reset(self): - self.confusion_matrix = np.zeros((self.n_classes, self.n_classes)) \ No newline at end of file diff --git a/ptocr/utils/prune_script.py b/ptocr/utils/prune_script.py deleted file mode 100644 index 918c827..0000000 --- a/ptocr/utils/prune_script.py +++ /dev/null @@ -1,572 +0,0 @@ -import torch -import torch.nn as nn -import numpy as np -import copy -from .util_function import load_model -import ptocr - -def updateBN(model,args): - for indedx,m in enumerate(model.modules()): -# if(indedx>187): -# break - if isinstance(m, nn.BatchNorm2d): - if hasattr(m.weight, 'data'): - m.weight.grad.data.add_(args.sr_lr*torch.sign(m.weight.data)) #L1正则 - - -def get_pruned_model_backbone(model,new_model,prued_mask,bn_index): - keys = {} - tag = 0 - for k, m in enumerate(new_model.modules()): - if (isinstance(m, ptocr.model.backbone.det_mobilev3.Block)): - keys[tag] = k - tag += 1 - - #### step 1 - mg_1 = np.array([-3, 7, 16]) - block_idx = keys[0] - tag = 0 - for idx in mg_1 + block_idx: - if (tag == 0): - msk = prued_mask[bn_index.index(idx)] - else: - msk = msk | prued_mask[bn_index.index(idx)] - tag += 1 - - for idx in mg_1 + block_idx: - prued_mask[bn_index.index(idx)] = msk - msk_1 = msk.clone() - - #### step 2 - block_idx2 = np.array([keys[1], keys[2]]) - mg_2 = 7 - tag = 0 - for idx in mg_2 + block_idx2: - if (tag == 0): - msk = prued_mask[bn_index.index(idx)] - else: - msk = msk | prued_mask[bn_index.index(idx)] - tag += 1 - for idx in mg_2 + block_idx2: - prued_mask[bn_index.index(idx)] = msk - msk_2 = msk.clone() - - ####step 3 - block_idx3s = [keys[3], keys[4], keys[5]] - mg_3 = np.array([7, 16]) - tag = 0 - for block_idx3 in block_idx3s: - for idx in block_idx3 + mg_3: - if (tag == 0): - msk = prued_mask[bn_index.index(idx)] - else: - msk = msk | prued_mask[bn_index.index(idx)] - tag += 1 - for block_idx3 in block_idx3s: - for idx in block_idx3 + mg_3: - prued_mask[bn_index.index(idx)] = msk - msk_3 = msk.clone() - - ####step 4_1 - block_idx4_all = [] - - block_idx4 = keys[6] - - mg_4 = np.array([7, 16]) - block_idx4_all.extend((block_idx4 + mg_4).tolist()) - - ####step 4_2 - block_idx4 = keys[7] - mg_4 = np.array([7, 16]) - block_idx4_all.extend((block_idx4 + mg_4).tolist()) - tag = 0 - - for idx in block_idx4_all: - if (tag == 0): - msk = prued_mask[bn_index.index(idx)] - else: - msk = msk | prued_mask[bn_index.index(idx)] - tag += 1 - - for idx in block_idx4_all: - prued_mask[bn_index.index(idx)] = msk - msk_4 = msk.clone() - - ####step 5 - block_idx5s = [keys[8], keys[9], keys[10]] - mg_5 = np.array([7, 16]) - tag = 0 - for block_idx5 in block_idx5s: - for idx in block_idx5 + mg_5: - if (tag == 0): - msk = prued_mask[bn_index.index(idx)] - else: - msk = msk | prued_mask[bn_index.index(idx)] - tag += 1 - - for block_idx5 in block_idx5s: - for idx in block_idx5 + mg_5: - prued_mask[bn_index.index(idx)] = msk - msk_5 = msk.clone() - - group_index = [] - spl_index = [] - for i in range(11): - block_idx6 = keys[i] - tag = 0 - mg_6 = np.array([2, 5]) - for idx in mg_6 + block_idx6: - if (tag == 0): - msk = prued_mask[bn_index.index(idx)] - else: - msk = msk | prued_mask[bn_index.index(idx)] - tag += 1 - for idx in mg_6 + block_idx6: - prued_mask[bn_index.index(idx)] = msk - if (i == 6): - spl_index.extend([block_idx6 + 9, block_idx6 - 2]) - group_index.append(block_idx6 + 4) - - count_conv = 0 - count_bn = 0 - conv_in_mask = [torch.ones(3)] - conv_out_mask = [] - bn_mask = [] - tag = 0 - for k, m in enumerate(new_model.modules()): - if (tag > 187): - break - if isinstance(m, nn.Conv2d): - - if (tag in group_index): - m.groups = int(prued_mask[bn_index.index(tag + 1)].sum()) - m.out_channels = int(prued_mask[count_conv].sum()) - conv_out_mask.append(prued_mask[count_conv]) - if (count_conv > 0): - if (tag == spl_index[0]): - m.in_channels = int(prued_mask[bn_index.index(spl_index[1])].sum()) - conv_in_mask.append(prued_mask[bn_index.index(spl_index[1])]) - else: - m.in_channels = int(prued_mask[count_conv - 1].sum()) - conv_in_mask.append(prued_mask[count_conv - 1]) - - count_conv += 1 - elif isinstance(m, nn.BatchNorm2d): - m.num_features = prued_mask[count_bn].sum() - bn_mask.append(prued_mask[count_bn]) - count_bn += 1 - tag += 1 - - bn_i = 0 - conv_i = 0 - model_i = 0 - scale = [188, 192, 196, 200] - scale_mask = [msk_5, msk_4, msk_3, msk_2] - for [m0, m1] in zip(model.modules(), new_model.modules()): - if (model_i > 187): - if isinstance(m0, nn.Conv2d): - if (model_i in scale): - index = scale.index(model_i) - m1.in_channels = int(scale_mask[index].sum()) - idx0 = np.squeeze(np.argwhere(np.asarray(scale_mask[index].cpu().numpy()))) - idx1 = np.squeeze(np.argwhere(np.asarray(torch.ones(96).cpu().numpy()))) - if idx0.size == 1: - idx0 = np.resize(idx0, (1,)) - if idx1.size == 1: - idx1 = np.resize(idx1, (1,)) - w = m0.weight.data[:, idx0, :, :].clone() - m1.weight.data = w[idx1, :, :, :].clone() - if m1.bias is not None: - m1.bias.data = m0.bias.data[idx1].clone() - - else: - m1.weight.data = m0.weight.data.clone() - if m1.bias is not None: - m1.bias.data = m0.bias.data.clone() - - elif isinstance(m0, nn.BatchNorm2d): - m1.weight.data = m0.weight.data.clone() - if m1.bias is not None: - m1.bias.data = m0.bias.data.clone() - m1.running_mean = m0.running_mean.clone() - m1.running_var = m0.running_var.clone() - else: - if isinstance(m0, nn.BatchNorm2d): - idx1 = np.squeeze(np.argwhere(np.asarray(bn_mask[bn_i].cpu().numpy()))) - if idx1.size == 1: - idx1 = np.resize(idx1, (1,)) - m1.weight.data = m0.weight.data[idx1].clone() - if m1.bias is not None: - m1.bias.data = m0.bias.data[idx1].clone() - m1.running_mean = m0.running_mean[idx1].clone() - m1.running_var = m0.running_var[idx1].clone() - bn_i += 1 - elif isinstance(m0, nn.Conv2d): - if (isinstance(conv_in_mask[conv_i], list)): - idx0 = np.squeeze(np.argwhere(np.asarray(torch.cat(conv_in_mask[conv_i], 0).cpu().numpy()))) - else: - idx0 = np.squeeze(np.argwhere(np.asarray(conv_in_mask[conv_i].cpu().numpy()))) - idx1 = np.squeeze(np.argwhere(np.asarray(conv_out_mask[conv_i].cpu().numpy()))) - if idx0.size == 1: - idx0 = np.resize(idx0, (1,)) - if idx1.size == 1: - idx1 = np.resize(idx1, (1,)) - if (model_i in group_index): - m1.weight.data = m0.weight.data[idx1, :, :, :].clone() - if m1.bias is not None: - m1.bias.data = m0.bias.clone() - else: - w = m0.weight.data[:, idx0, :, :].clone() - m1.weight.data = w[idx1, :, :, :].clone() - if m1.bias is not None: - m1.bias.data = m0.bias.data[idx1].clone() - conv_i += 1 - model_i += 1 - return new_model - - -def get_pruned_model_total(model,new_model,prued_mask,bn_index): - -# new_model = create_module(config['architectures']['model_function'])(config).cuda() - - keys = {} - tag = 0 - for k, m in enumerate(new_model.modules()): - if(isinstance(m,ptocr.model.backbone.det_mobilev3.Block)): - keys[tag]=k - tag+=1 -# print(keys) - #### step 1 - mg_1 = np.array([-3,7,16]) - block_idx = keys[0] - tag = 0 - for idx in mg_1+block_idx: - if (tag == 0): - msk = prued_mask[bn_index.index(idx)] - else: - msk = msk | prued_mask[bn_index.index(idx)] - tag+=1 -# print('step1',idx) -# print(msk.sum()) - for idx in mg_1+block_idx: - prued_mask[bn_index.index(idx)] = msk - msk_1 = msk.clone() - - #### step 2 - block_idx2 = np.array([keys[1],keys[2]]) - mg_2 = 7 - tag = 0 - for idx in mg_2+block_idx2: -# print('step2',idx) - if(tag==0): - msk = prued_mask[bn_index.index(idx)] - else: - msk = msk|prued_mask[bn_index.index(idx)] - tag += 1 - for idx in mg_2+block_idx2: - prued_mask[bn_index.index(idx)] = msk -# print(msk.sum()) - msk_2 = msk.clone() - - ####step 3 - block_idx3s = [keys[3],keys[4],keys[5]] - mg_3 = np.array([7,16]) - tag = 0 - for block_idx3 in block_idx3s: - for idx in block_idx3+mg_3: -# print('step3',idx) - if (tag == 0): - msk = prued_mask[bn_index.index(idx)] - else: - msk = msk | prued_mask[bn_index.index(idx)] - tag += 1 - for block_idx3 in block_idx3s: - for idx in block_idx3+mg_3: - prued_mask[bn_index.index(idx)] = msk -# print(msk.sum()) - msk_3 = msk.clone() - - ####step 4_1 - block_idx4_all = [] - - block_idx4 = keys[6] - - mg_4 = np.array([7,16]) - block_idx4_all.extend((block_idx4+mg_4).tolist()) - - ####step 4_2 - block_idx4 = keys[7] - mg_4 = np.array([7,16]) - block_idx4_all.extend((block_idx4+mg_4).tolist()) - tag = 0 - - for idx in block_idx4_all: -# print('step4',idx) - if(tag==0): - msk = prued_mask[bn_index.index(idx)] - else: - msk = msk | prued_mask[bn_index.index(idx)] - tag += 1 - - for idx in block_idx4_all: - prued_mask[bn_index.index(idx)] = msk -# print(msk.sum()) - msk_4 = msk.clone() - - ####step 5 - block_idx5s = [keys[8],keys[9],keys[10]] - mg_5 = np.array([7,16]) - tag = 0 - for block_idx5 in block_idx5s: - for idx in block_idx5+mg_5: - if (tag == 0): - msk = prued_mask[bn_index.index(idx)] - else: - msk = msk | prued_mask[bn_index.index(idx)] - tag += 1 - - for block_idx5 in block_idx5s: - for idx in block_idx5+mg_5: - prued_mask[bn_index.index(idx)] = msk -# print(msk.sum()) - msk_5 = msk.clone() - - group_index = [] - spl_index = [] - for i in range(11): - block_idx6 = keys[i] - tag = 0 - mg_6 = np.array([2,5]) - for idx in mg_6+block_idx6: - if(tag==0): - msk = prued_mask[bn_index.index(idx)] - else: - msk = msk | prued_mask[bn_index.index(idx)] - tag+=1 - for idx in mg_6 + block_idx6: - prued_mask[bn_index.index(idx)] = msk - if(i==6): - spl_index.extend([block_idx6+9,block_idx6-2]) - group_index.append(block_idx6+4) - - - count_conv = 0 - count_bn = 0 - conv_in_mask = [torch.ones(3)] - conv_out_mask = [] - bn_mask = [] - tag = 0 - for k, m in enumerate(new_model.modules()): - if(tag>187 ): - continue - else: - if isinstance(m,nn.Conv2d): - - if(tag in group_index): - m.groups = int(prued_mask[bn_index.index(tag+1)].sum()) - m.out_channels = int(prued_mask[count_conv].sum()) - conv_out_mask.append(prued_mask[count_conv]) - if(count_conv>0): - if (tag == spl_index[0]): - m.in_channels = int(prued_mask[bn_index.index(spl_index[1])].sum()) - conv_in_mask.append(prued_mask[bn_index.index(spl_index[1])]) - else: - m.in_channels = int(prued_mask[count_conv-1].sum()) - conv_in_mask.append(prued_mask[count_conv-1]) - - count_conv+=1 - elif isinstance(m,nn.BatchNorm2d): - m.num_features = int(prued_mask[count_bn].sum()) - bn_mask.append(prued_mask[count_bn]) - count_bn+=1 - tag+=1 - - - head_bn_merge_idx1 = np.array([189,193,197,201]) - tag = 0 - for idx in head_bn_merge_idx1: - if(tag==0): - _msk1 = prued_mask[bn_index.index(idx)] - else: - _msk1 = _msk1 | prued_mask[bn_index.index(idx)] - tag += 1 - - head_bn_merge_idx2 = np.array([205,209,213,217]) - num_ch = 0 - head_mask_1 = [] - for idx in head_bn_merge_idx2: - num_ch += int(prued_mask[bn_index.index(idx)].sum()) - head_mask_1.append(prued_mask[bn_index.index(idx)]) - - -# print(new_model) - - head_bn_merge_idx2 = head_bn_merge_idx2 - 1 - - bn_i = 0 - conv_i = 0 - model_i = 0 - scale = [188,192,196,200] - scale_mask = [msk_5,msk_4,msk_3,msk_2] - - prued_mask_bk = copy.deepcopy(prued_mask) - for [m0, m1] in zip(model.modules(), new_model.modules()): - if(model_i>187): - if isinstance(m0, nn.Conv2d) or isinstance(m0, nn.ConvTranspose2d): - if(model_i in scale): - index = scale.index(model_i) - m1.in_channels = int(scale_mask[index].sum()) - m1.out_channels = int(_msk1.sum()) - idx0 = np.squeeze(np.argwhere(np.asarray(scale_mask[index].cpu().numpy()))) - idx1 = np.squeeze(np.argwhere(np.asarray(_msk1.cpu().numpy()))) - if idx0.size == 1: - idx0 = np.resize(idx0, (1,)) - if idx1.size == 1: - idx1 = np.resize(idx1, (1,)) - w = m0.weight.data[:, idx0, :, :].clone() - m1.weight.data = w[idx1, :, :, :].clone() - if m1.bias is not None: - m1.bias.data = m0.bias.data[idx0].clone() - elif(model_i in head_bn_merge_idx2.tolist()): - - m1.in_channels = int(_msk1.sum()) - index = head_bn_merge_idx2.tolist().index(model_i) - m1.out_channels = int(head_mask_1[index].sum()) - - idx1 = np.squeeze(np.argwhere(np.asarray(head_mask_1[index].cpu().numpy()))) - idx0 = np.squeeze(np.argwhere(np.asarray(_msk1.cpu().numpy()))) - if idx0.size == 1: - idx0 = np.resize(idx0, (1,)) - if idx1.size == 1: - idx1 = np.resize(idx1, (1,)) - w = m0.weight.data[:, idx0, :, :].clone() - m1.weight.data = w[idx1, :, :, :].clone() -# merge_weight_mask.append(m1.weight.data) - if m1.bias is not None: - m1.bias.data = m0.bias.data[idx0].clone() - elif(model_i==221 or model_i==230): - - merge_mask = torch.cat(head_mask_1,0) - m1.in_channels = num_ch - index = bn_index.index(model_i+1) - m1.out_channels = int(prued_mask[index].sum()) - - idx1 = np.squeeze(np.argwhere(np.asarray(prued_mask[index].cpu().numpy()))) - idx0 = np.squeeze(np.argwhere(np.asarray(merge_mask.cpu().numpy()))) - if idx0.size == 1: - idx0 = np.resize(idx0, (1,)) - if idx1.size == 1: - idx1 = np.resize(idx1, (1,)) - w = m0.weight.data[:, idx0, :, :].clone() - m1.weight.data = w[idx1, :, :, :].clone() - - if m1.bias is not None: - m1.bias.data = m0.bias.data[idx0].clone() - - elif(model_i==224 or model_i==233): - - m1.in_channels = int(prued_mask[bn_index.index(model_i-2)].sum()) - m1.out_channels = int(prued_mask[bn_index.index(model_i+1)].sum()) - idx0 = np.squeeze(np.argwhere(np.asarray(prued_mask[bn_index.index(model_i+1)].cpu().numpy()))) - idx1 = np.squeeze(np.argwhere(np.asarray(prued_mask[bn_index.index(model_i-2)].cpu().numpy()))) - if idx0.size == 1: - idx0 = np.resize(idx0, (1,)) - if idx1.size == 1: - idx1 = np.resize(idx1, (1,)) - w = m0.weight.data[:, idx0, :, :].clone() - m1.weight.data = w[idx1, :, :, :].clone() - if m1.bias is not None: - m1.bias.data = m0.bias.data[idx0].clone() - elif(model_i==227 or model_i==236): -# import pdb -# pdb.set_trace() - m1.in_channels = int(prued_mask[bn_index.index(model_i-2)].sum()) - idx1 = np.squeeze(np.argwhere(np.asarray(prued_mask[bn_index.index(model_i-2)].cpu().numpy()))) - idx0 = np.squeeze(np.argwhere(np.asarray(torch.ones(1).cpu().numpy()))) - if idx0.size == 1: - idx0 = np.resize(idx0, (1,)) - if idx1.size == 1: - idx1 = np.resize(idx1, (1,)) - w = m0.weight.data[:, idx0, :, :].clone() - m1.weight.data = w[idx1, :, :, :].clone() - if m1.bias is not None: - m1.bias.data = m0.bias.data[idx0].clone() - else: - m1.weight.data = m0.weight.data.clone() - if m1.bias is not None: - m1.bias.data = m0.bias.data.clone() - - elif isinstance(m0, nn.BatchNorm2d): - - if(model_i in [189,193,197,201]): - m1.num_features = int(_msk1.sum()) - idx1 = np.squeeze(np.argwhere(np.asarray(_msk1.cpu().numpy()))) - if idx1.size == 1: - idx1 = np.resize(idx1, (1,)) - m1.weight.data = m0.weight.data[idx1].clone() - if m1.bias is not None: - m1.bias.data = m0.bias.data[idx1].clone() - m1.running_mean = m0.running_mean[idx1].clone() - m1.running_var = m0.running_var[idx1].clone() - - else: - - index = bn_index.index(model_i) - m1.num_features = prued_mask[index].sum() - idx1 = np.squeeze(np.argwhere(np.asarray(prued_mask[index].cpu().numpy()))) - - if idx1.size == 1: - idx1 = np.resize(idx1, (1,)) - m1.weight.data = m0.weight.data[idx1].clone() - if m1.bias is not None: - m1.bias.data = m0.bias.data[idx1].clone() - m1.running_mean = m0.running_mean[idx1].clone() - m1.running_var = m0.running_var[idx1].clone() - - - else: - if isinstance(m0, nn.BatchNorm2d): - idx1 = np.squeeze(np.argwhere(np.asarray(bn_mask[bn_i].cpu().numpy()))) - if idx1.size == 1: - idx1 = np.resize(idx1, (1,)) - m1.weight.data = m0.weight.data[idx1].clone() - if m1.bias is not None: - m1.bias.data = m0.bias.data[idx1].clone() - m1.running_mean = m0.running_mean[idx1].clone() - m1.running_var = m0.running_var[idx1].clone() - bn_i += 1 - elif isinstance(m0, nn.Conv2d): - if (isinstance(conv_in_mask[conv_i], list)): - idx0 = np.squeeze(np.argwhere(np.asarray(torch.cat(conv_in_mask[conv_i], 0).cpu().numpy()))) - else: - idx0 = np.squeeze(np.argwhere(np.asarray(conv_in_mask[conv_i].cpu().numpy()))) - idx1 = np.squeeze(np.argwhere(np.asarray(conv_out_mask[conv_i].cpu().numpy()))) - if idx0.size == 1: - idx0 = np.resize(idx0, (1,)) - if idx1.size == 1: - idx1 = np.resize(idx1, (1,)) - if(model_i in group_index): - m1.weight.data = m0.weight.data[idx1, :, :, :].clone() - if m1.bias is not None: - m1.bias.data = m0.bias.clone() - else: - w = m0.weight.data[:, idx0, :, :].clone() - m1.weight.data = w[idx1, :, :, :].clone() - if m1.bias is not None: - m1.bias.data = m0.bias.data[idx0].clone() - conv_i += 1 - model_i+=1 - - return new_model - -def load_prune_model(model,model_path,pruned_model_dict_path,d_type = 'total'): - _load = torch.load(pruned_model_dict_path) - prued_mask = _load['prued_mask'] - bn_index = _load['bn_index'] - if d_type=='total': - prune_model = get_pruned_model_total(model, model, prued_mask, bn_index) - else: - prune_model = get_pruned_model_backbone(model, model, prued_mask, bn_index) - prune_model = load_model(prune_model,model_path) - return prune_model - diff --git a/ptocr/utils/transform_label.py b/ptocr/utils/transform_label.py deleted file mode 100644 index a0d322e..0000000 --- a/ptocr/utils/transform_label.py +++ /dev/null @@ -1,128 +0,0 @@ -#-*- coding:utf-8 _*- -""" -@author:fxw -@file: transform_label.py -@time: 2020/07/24 -""" -import torch -import torch.nn as nn -from torch.autograd import Variable -import collections -# import chardet -import numpy as np -import sys - -def get_keys(key_path): - with open(key_path,'r',encoding='utf-8') as fid: - lines = fid.readlines()[0] - lines = lines.strip('\n') - return lines - - -class strLabelConverter(object): - """Convert between str and label. - - NOTE: - Insert `blank` to the alphabet for CTC. - - Args: - alphabet (str): set of the possible characters. - ignore_case (bool, default=True): whether or not to ignore all of the case. - """ - - def __init__(self, config): - alphabet = get_keys(config['trainload']['key_file']) - self.alphabet = alphabet + '-' # for `-1` index - self.dict = {} - for i, char in enumerate(alphabet): - # NOTE: 0 is reserved for 'blank' required by wrap_ctc - self.dict[char] = i + 1 - - def encode(self, text,t_step): - """Support batch or single str. - - Args: - text (str or list of str): texts to convert. - - Returns: - torch.IntTensor [length_0 + length_1 + ... length_{n - 1}]: encoded texts. - torch.IntTensor [n]: length of each text. - """ - length = [] - result = [] - - for i in range(len(text)): - length.append(len(text[i])) - for j in range(len(text[i])): - index = self.dict[text[i][j]] - result.append(index) - - text = result - return (torch.IntTensor(text), torch.IntTensor(length)) - - def decode(self, t, length, raw=False): - """Decode encoded texts back into strs. - - Args: - torch.IntTensor [length_0 + length_1 + ... length_{n - 1}]: encoded texts. - torch.IntTensor [n]: length of each text. - - Raises: - AssertionError: when the texts and its length does not match. - - Returns: - text (str or list of str): texts to convert. - """ - if length.numel() == 1: - length = length[0] - assert t.numel() == length, "text with length: {} does not match declared length: {}".format(t.numel(), - length) - if raw: - return ''.join([self.alphabet[i - 1] for i in t]) - else: - char_list = [] - for i in range(length): - if t[i] != 0 and (not (i > 0 and t[i - 1] == t[i])): - char_list.append(self.alphabet[t[i] - 1]) - return ''.join(char_list) - else: - # batch mode - assert t.numel() == length.sum(), "texts with length: {} does not match declared length: {}".format( - t.numel(), length.sum()) - texts = [] - index = 0 - for i in range(length.numel()): - l = length[i] - texts.append( - self.decode( - t[index:index + l], torch.IntTensor([l]), raw=raw)) - index += l - return texts - - -class averager(object): - """Compute average for `torch.Variable` and `torch.Tensor`. """ - - def __init__(self): - self.reset() - - def add(self, v): - if isinstance(v, Variable): - count = v.data.numel() - v = v.data.sum() - elif isinstance(v, torch.Tensor): - count = v.numel() - v = v.sum() - - self.n_count += count - self.sum += v - - def reset(self): - self.n_count = 0 - self.sum = 0 - - def val(self): - res = 0 - if self.n_count != 0: - res = self.sum / float(self.n_count) - return res \ No newline at end of file diff --git a/ptocr/utils/util_function.py b/ptocr/utils/util_function.py deleted file mode 100644 index c2b3dba..0000000 --- a/ptocr/utils/util_function.py +++ /dev/null @@ -1,185 +0,0 @@ -#-*- coding:utf-8 _*- -""" -@author:fxw -@file: util_function.py -@time: 2020/08/07 -""" -import os -import importlib -import math -import cv2 -import torch -import numpy as np - -def create_module(module_str): - tmpss = module_str.split(",") - assert len(tmpss) == 2, "Error formate\ - of the module path: {}".format(module_str) - module_name, function_name = tmpss[0], tmpss[1] - somemodule = importlib.import_module(module_name, __package__) - function = getattr(somemodule, function_name) - return function - -def resize_image_batch(img,algorithm,side_len=1536,add_padding=True): - - if(algorithm=='SAST'): - stride = 128 - else: - stride = 32 - height, width, _ = img.shape - flag = None - if height > width: - flag = True - new_height = side_len - new_width = int(math.ceil(new_height / height * width / stride) * stride) - else: - flag = False - new_width = side_len - new_height = int(math.ceil(new_width / width * height / stride) * stride) - resized_img = cv2.resize(img, (new_width, new_height)) - scale = (float(width)/new_width,float(height)/new_height) - if add_padding is True: - if flag: - padded_image = cv2.copyMakeBorder(resized_img, 0, 0, - 0, side_len-new_width, cv2.BORDER_CONSTANT, value=(0,0,0)) - else: - padded_image = cv2.copyMakeBorder(resized_img, 0, side_len-new_height, - 0, 0, cv2.BORDER_CONSTANT, value=(0,0,0)) - else: - return resized_img,scale - return padded_image,scale - - -def resize_image(img,algorithm,side_len=736,stride = 128): - if algorithm == 'DB' or algorithm == 'PAN' or algorithm == 'CRNN': - height, width, _ = img.shape - if height < width: - new_height = side_len - new_width = int(math.ceil(new_height / height * width / stride) * stride) - else: - new_width = side_len - new_height = int(math.ceil(new_width / width * height / stride) * stride) - resized_img = cv2.resize(img, (new_width, new_height)) - else: - height, width, _ = img.shape - if height > width: - new_height = side_len - new_width = int(math.ceil(new_height / height * width / stride) * stride) - else: - new_width = side_len - new_height = int(math.ceil(new_width / width * height / stride) * stride) - resized_img = cv2.resize(img, (new_width, new_height)) - return resized_img - - -def save_checkpoint(state, checkpoint='checkpoint', filename='model.pth.tar'): - filepath = os.path.join(checkpoint, filename) - torch.save(state, filepath) - -class LossAccumulator(): - def __init__(self): - super(LossAccumulator,self).__init__() - self.loss_items = [] - def loss_add(self,loss): - self.loss_items.append(loss) - def loss_sum(self): - return sum(self.loss_items) - def loss_mean(self): - return sum(self.loss_items)/len(self.loss_items) - def loss_clear(self): - self.loss_items = [] - -def create_process_obj(algorithm,pred): - if(algorithm=='DB'): - return pred.cpu().numpy() - elif(algorithm=='SAST'): - pred['f_score'] = pred['f_score'].cpu().numpy() - pred['f_border'] = pred['f_border'].cpu().numpy() - pred['f_tvo'] = pred['f_tvo'].cpu().numpy() - pred['f_tco'] = pred['f_tco'].cpu().numpy() - return pred - else: - return pred - - -def create_loss_bin(algorithm,use_distil=False,use_center=False): - bin_dict = {} - if(algorithm=='DB'): - keys = ['loss_total','loss_l1', 'loss_bce', 'loss_thresh'] - elif(algorithm=='PSE'): - keys = ['loss_total','loss_kernel', 'loss_text'] - elif(algorithm=='PAN'): - keys = ['loss_total','loss_text', 'loss_agg', 'loss_kernel', 'loss_dis'] - elif (algorithm == 'SAST'): - keys = ['loss_total', 'loss_score', 'loss_border', 'loss_tvo', 'loss_tco'] - elif (algorithm == 'CRNN'): - if use_center: - keys = ['loss_total','loss_ctc','loss_center'] - else: - keys = ['loss_ctc'] - else: - assert 1==2,'only support algorithm DB,SAST,PSE,PAN,CRNN !!!' - - for key in keys: - bin_dict[key] = LossAccumulator() - if(use_distil): - bin_dict['loss_distil'] = LossAccumulator() - return bin_dict - -def set_seed(seed): - import numpy as np - import random - import torch - random.seed(seed) - np.random.seed(seed) - torch.manual_seed(seed) - torch.cuda.manual_seed(seed) - torch.cuda.manual_seed_all(seed) - -def create_dir(path): - if not (os.path.exists(path)): - os.mkdir(path) - -def load_model(model,model_path): - if torch.cuda.is_available(): - model_dict = torch.load(model_path) - else: - model_dict = torch.load(model_path,map_location='cpu') - - if('state_dict' in model_dict.keys()): - model_dict = model_dict['state_dict'] - - try: - model.load_state_dict(model_dict) - except: - state = model.state_dict() - for key in state.keys(): - state[key] = model_dict['module.' + key] - model.load_state_dict(state) - return model - -def merge_config(config,args): - for key_1 in config.keys(): - if(isinstance(config[key_1],dict)): - for key_2 in config[key_1].keys(): - if(key_2) in dir(args): - config[key_1][key_2] = getattr(args,key_2) - return config - - -class AverageMeter(object): - """Computes and stores the average and current value""" - def __init__(self): - self.reset() - - def reset(self): - self.val = 0 - self.avg = 0 - self.sum = 0 - self.count = 0 - - def update(self, val, n=1): - self.val = val - self.sum += val * n - self.count += n - self.avg = self.sum / self.count \ No newline at end of file diff --git a/script/__init__.py b/script/__init__.py deleted file mode 100644 index eab50c3..0000000 --- a/script/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -#-*- coding:utf-8 _*- -""" -@author:fxw -@file: __init__.py.py -@time: 2020/08/07 -""" \ No newline at end of file diff --git a/script/create_lmdb.py b/script/create_lmdb.py deleted file mode 100644 index 51833ee..0000000 --- a/script/create_lmdb.py +++ /dev/null @@ -1,138 +0,0 @@ -import os -import lmdb -import cv2 -import numpy as np -import argparse -import shutil -import sys - -def checkImageIsValid(imageBin): - if imageBin is None: - return False - - try: - imageBuf = np.fromstring(imageBin, dtype=np.uint8) - img = cv2.imdecode(imageBuf, cv2.IMREAD_GRAYSCALE) - imgH, imgW = img.shape[0], img.shape[1] - except: - return False - else: - if imgH * imgW == 0: - return False - - return True - - -def writeCache(env, cache): - with env.begin(write=True) as txn: - for k, v in cache.items(): - if type(k) == str: - k = k.encode() - if type(v) == str: - v = v.encode() - txn.put(k,v) - -def createDataset(outputPath, imagePathList, labelList, lexiconList=None, checkValid=True): - """ - Create LMDB dataset for CRNN training. - ARGS: - outputPath : LMDB output path - imagePathList : list of image path - labelList : list of corresponding groundtruth texts - lexiconList : (optional) list of lexicon lists - checkValid : if true, check the validity of every image - """ - # If lmdb file already exists, remove it. Or the new data will add to it. - if os.path.exists(outputPath): - shutil.rmtree(outputPath) - os.makedirs(outputPath) - else: - os.makedirs(outputPath) - - assert (len(imagePathList) == len(labelList)) - nSamples = len(imagePathList) - env = lmdb.open(outputPath, map_size=1099511627776) - cache = {} - cnt = 1 - for i in range(nSamples): - imagePath = imagePathList[i] - label = labelList[i] - - if not os.path.exists(imagePath): - print('%s does not exist' % imagePath) - continue - with open(imagePath, 'rb') as f: - imageBin = f.read() - if checkValid: - if not checkImageIsValid(imageBin): - print('%s is not a valid image' % imagePath) - continue - - imageKey = 'image-%09d' % cnt - labelKey = 'label-%09d' % cnt - cache[imageKey] = imageBin - cache[labelKey] = label - if lexiconList: - lexiconKey = 'lexicon-%09d' % cnt - cache[lexiconKey] = ' '.join(lexiconList[i]) - if cnt % 1000 == 0: - writeCache(env, cache) - cache = {} - print('Written %d / %d' % (cnt, nSamples)) - cnt += 1 - nSamples = cnt-1 - cache['num-samples'] = str(nSamples) - writeCache(env, cache) - env.close() - print('Created dataset with %d samples' % nSamples) - -def read_data_from_folder(folder_path): - image_path_list = [] - label_list = [] - pics = os.listdir(folder_path) - pics.sort(key = lambda i: len(i)) - for pic in pics: - image_path_list.append(folder_path + '/' + pic) - label_list.append(pic.split('_')[0]) - return image_path_list, label_list - -def read_data_from_file(file_path): - image_path_list = [] - label_list = [] - f = open(file_path) - while True: - line1 = f.readline() - line2 = f.readline() - if not line1 or not line2: - break - line1 = line1.replace('\r', '').replace('\n', '') - line2 = line2.replace('\r', '').replace('\n', '') - image_path_list.append(line1) - label_list.append(line2) - - return image_path_list, label_list - -def show_demo(demo_number, image_path_list, label_list): - print ('\nShow some demo to prevent creating wrong lmdb data') - print ('The first line is the path to image and the second line is the image label') - for i in range(demo_number): - print ('image: %s\nlabel: %s\n' % (image_path_list[i], label_list[i])) - -if __name__ == '__main__': - parser = argparse.ArgumentParser() - parser.add_argument('--out', type = str, required = True, help = 'lmdb data output path') - parser.add_argument('--folder', type = str, help = 'path to folder which contains the images') - parser.add_argument('--file', type = str, help = 'path to file which contains the image path and label') - args = parser.parse_args() - - if args.file is not None: - image_path_list, label_list = read_data_from_file(args.file) - createDataset(args.out, image_path_list, label_list) - show_demo(2, image_path_list, label_list) - elif args.folder is not None: - image_path_list, label_list = read_data_from_folder(args.folder) - createDataset(args.out, image_path_list, label_list) - show_demo(2, image_path_list, label_list) - else: - print ('Please use --floder or --file to assign the input. Use -h to see more.') - sys.exit() \ No newline at end of file diff --git a/script/get_key_label.py b/script/get_key_label.py deleted file mode 100644 index 99f276b..0000000 --- a/script/get_key_label.py +++ /dev/null @@ -1,31 +0,0 @@ -""" -#!-*- coding=utf-8 -*- -@author: BADBADBADBADBOY -@contact: 2441124901@qq.com -@software: PyCharm Community Edition -@file: get_key_label.py -@time: 2020/11/9 20:33 - -""" - -train_list_file = './test_list.txt' -test_list_file = './train_list.txt' -keys_file = './key.txt' - - -fid_key = open(keys_file,'w+',encoding='utf-8') -keys = '' -with open(train_list_file,'r',encoding='utf-8') as fid_train: - lines = fid_train.readlines() - for line in lines: - line = line.strip().split('\t') - keys+=line[-1] - -with open(test_list_file,'r',encoding='utf-8') as fid_test: - lines = fid_test.readlines() - for line in lines: - line = line.strip().split('\t') - keys+=line[-1] - -key = ''.join(list(set(list(keys)))) -fid_key.write(key) \ No newline at end of file diff --git a/script/get_train_list.py b/script/get_train_list.py deleted file mode 100644 index 6e5872d..0000000 --- a/script/get_train_list.py +++ /dev/null @@ -1,29 +0,0 @@ -""" -#!-*- coding=utf-8 -*- -@author: BADBADBADBADBOY -@contact: 2441124901@qq.com -@software: PyCharm Community Edition -@file: test.py -@time: 2020/9/3 20:17 - -""" -import os -import argparse -def gen_train_file(args): - label_path = args.label_path - img_path = args.img_path - files = os.listdir(img_path) - with open(os.path.join(args.save_path,'train_list.txt'),'w+',encoding='utf-8') as fid: - for file in files: - label_str = os.path.join(img_path,file)+'\t'+os.path.join(label_path,os.path.splitext(file)[0]+'.txt')+'\n' - fid.write(label_str) - - - - -if __name__ == "__main__": - parser = argparse.ArgumentParser(description='Hyperparams') - parser.add_argument('--label_path', nargs='?', type=str, default=None) - parser.add_argument('--img_path', nargs='?', type=str, default=None) - parser.add_argument('--save_path', nargs='?', type=str, default=None) - args = parser.parse_args() \ No newline at end of file diff --git a/script/onnx_to_tensorrt.py b/script/onnx_to_tensorrt.py deleted file mode 100644 index 8d5126f..0000000 --- a/script/onnx_to_tensorrt.py +++ /dev/null @@ -1,188 +0,0 @@ -import os -import tensorrt as trt -import pycuda.driver as cuda -import pycuda.autoinit -import onnx -import cv2 -import numpy as np -import time -import argparse -import math - -def resize_image(img,algorithm,side_len=1536,add_padding=True): - - if algorithm == 'CRNN': - img = transform_img_shape(img,[32,crnn_max_length]) - return img - - if(algorithm=='SAST'): - stride = 128 - else: - stride = 32 - height, width, _ = img.shape - flag = None - if height > width: - flag = True - new_height = side_len - new_width = int(math.ceil(new_height / height * width / stride) * stride) - else: - flag = False - new_width = side_len - new_height = int(math.ceil(new_width / width * height / stride) * stride) - resized_img = cv2.resize(img, (new_width, new_height)) - if add_padding is True: - if flag: - padded_image = cv2.copyMakeBorder(resized_img, 0, 0, - 0, side_len-new_width, cv2.BORDER_CONSTANT, value=(0,0,0)) - else: - padded_image = cv2.copyMakeBorder(resized_img, 0, side_len-new_height, - 0, 0, cv2.BORDER_CONSTANT, value=(0,0,0)) - else: - return resized_img - return padded_image - - -def build_engine(onnx_file_path,engine_file_path,batch_size=1,mode='fp16'): - TRT_LOGGER = trt.Logger(trt.Logger.WARNING) - if os.path.exists(engine_file_path): - # If a serialized engine exists, load it instead of building a new one. - print("Reading engine from file {}".format(engine_file_path)) - with open(engine_file_path, "rb") as f, trt.Runtime(TRT_LOGGER) as runtime: - return runtime.deserialize_cuda_engine(f.read()) - - - EXPLICIT_BATCH = 1 << int(trt.NetworkDefinitionCreationFlag.EXPLICIT_BATCH) - with trt.Builder(TRT_LOGGER) as builder, \ - builder.create_network(EXPLICIT_BATCH) as network, \ - trt.OnnxParser(network, TRT_LOGGER) as parser: - builder.max_batch_size = int(batch_size) - builder.fp16_mode = True if mode == 'fp16' else False - builder.int8_mode = True if mode == 'int8' else False - builder.strict_type_constraints = True - - builder.max_workspace_size = 1 << 32 # 1GB:30 - - with open(onnx_file_path, 'rb') as model: - parser.parse(model.read()) - - print('network layers is',len(network)) # Printed output == 0. Something is wrong. - last_layer = network.get_layer(network.num_layers - 1) - # Check if last layer recognizes it's output - if not last_layer.get_output(0): - # If not, then mark the output using TensorRT API - network.mark_output(last_layer.get_output(0)) - engine = builder.build_cuda_engine(network) - with open(engine_file_path, "wb") as f: - f.write(engine.serialize()) - return engine - - -class HostDeviceMem(object): - def __init__(self, host_mem, device_mem): - self.host = host_mem - self.device = device_mem - - def __str__(self): - return "Host:\n" + str(self.host) + "\nDevice:\n" + str(self.device) - - def __repr__(self): - return self.__str__() - -def allocate_buffers(engine): - inputs = [] - outputs = [] - bindings = [] - stream = cuda.Stream() - for binding in engine: - dtype = trt.nptype(engine.get_binding_dtype(binding)) - # Allocate host and device buffers - host_mem = cuda.pagelocked_empty(trt.volume(engine.get_binding_shape(binding)), dtype) - device_mem = cuda.mem_alloc(host_mem.nbytes) - # Append the device buffer to device bindings. - bindings.append(int(device_mem)) - # Append to the appropriate list. - if engine.binding_is_input(binding): - inputs.append(HostDeviceMem(host_mem, device_mem)) - else: - outputs.append(HostDeviceMem(host_mem, device_mem)) - return inputs, outputs, bindings, stream - -def do_inference(context, bindings, inputs, outputs, stream, batch_size=1): - - # Transfer input data to the GPU. - [cuda.memcpy_htod_async(inp.device, inp.host, stream) for inp in inputs] - # Run inference. - context.execute_async(batch_size=batch_size, bindings=bindings, stream_handle=stream.handle) - # Transfer predictions back from the GPU. - [cuda.memcpy_dtoh_async(out.host, out.device, stream) for out in outputs] - # Synchronize the stream - stream.synchronize() - # Return only the host outputs. - return [out.host for out in outputs] - -def get_img(args): - img = cv2.imread(args.img_path) - img = resize_image(img,args.algorithm,side_len=args.max_size,add_padding=args.add_padding) - print(img.shape) - cv2.imwrite('./onnx/'+args.algorithm+'_ori_img.jpg',img) - image = img / 255.0 - mean = np.array([0.485, 0.456, 0.406]) - std = np.array([0.229, 0.224, 0.225]) - image = (image - mean) / std - image = np.transpose(image, [2, 0, 1]) - image = np.expand_dims(image, axis=0).astype(np.float32) - return image - -def gen_trt_engine(args): - print("start check onnx file") - test = onnx.load(args.onnx_path) - onnx.checker.check_model(test) - print("check onnx file Passed") - print("build trt engine......") - engine = build_engine(args.onnx_path,args.trt_engine_path,batch_size=int(args.batch_size)) - inputs, outputs, bindings, stream = allocate_buffers(engine) - context = engine.create_execution_context() - print("build trt engine done") - - img1 = get_img(args) - h,w = img1.shape[2:4] - img_numpy = np.array([img1,img1]) - - inputs[0].host = img_numpy - - s = time.time() - output = do_inference(context, bindings=bindings, inputs=inputs, outputs=outputs, stream=stream,batch_size=int(args.batch_size)) - print(time.time()-s) - if args.algorithm=='DB': - out = output[0].reshape(int(args.batch_size),h,w) - - cv2.imwrite('./onnx/'+args.algorithm+'_trt_img1.jpg',out[0]*255) - cv2.imwrite('./onnx/'+args.algorithm+'_trt_img2.jpg',out[1]*255) - elif args.algorithm=='PAN': - out = output[0].reshape(int(args.batch_size),6,h,w) - cv2.imwrite('./onnx/'+args.algorithm+'_trt_img_text.jpg',out[1,0]*255) - cv2.imwrite('./onnx/'+args.algorithm+'_trt_img_kernel.jpg',out[1,1]*255) - elif args.algorithm=='PSE': - out = output[0].reshape(int(args.batch_size),7,h,w) - cv2.imwrite('./onnx/'+args.algorithm+'_trt_img_text.jpg',out[0,0]*255) - cv2.imwrite('./onnx/'+args.algorithm+'_trt_img_kernel.jpg',out[0,6]*255) - elif args.algorithm=='SAST': - out = output[0].reshape(int(args.batch_size),h//4,w//4) - out = out[0] - cv2.imwrite('./onnx/'+args.algorithm+'_trt_img.jpg',out*255) - else: - print('not support this algorithm!!') - - - -if __name__ == "__main__": - parser = argparse.ArgumentParser(description='Hyperparams') - parser.add_argument('--onnx_path', help='config file path') - parser.add_argument('--trt_engine_path', nargs='?', type=str, default=None) - parser.add_argument('--img_path', nargs='?', type=str, default=None) - parser.add_argument('--batch_size', nargs='?', type=str, default=None) - parser.add_argument('--algorithm', nargs='?', type=str, default=None) - parser.add_argument('--max_size', nargs='?', type=int, default=None) - parser.add_argument('--add_padding', action='store_true', default=False) - args = parser.parse_args() - gen_trt_engine(args) \ No newline at end of file diff --git a/script/pytorch_to_onnx.py b/script/pytorch_to_onnx.py deleted file mode 100644 index e5c5d36..0000000 --- a/script/pytorch_to_onnx.py +++ /dev/null @@ -1,144 +0,0 @@ -import sys -sys.path.append('./') -import yaml -import math -import argparse -import torch.nn as nn -import torch -import cv2 -import numpy as np -import onnx -import time -import onnxruntime -from PIL import Image -import torchvision.transforms as transforms -from ptocr.utils.util_function import create_module,load_model - - -crnn_max_length = 280 - -def transform_img_shape(img,img_shape): - img= np.array(img) - H,W = img_shape - h,w = img.shape[:2] - new_w = int((h/H)*w) - if(new_w > W): - img = cv2.resize(img,(W,H)) - else: - img = cv2.resize(img,(new_w,H)) - img = cv2.copyMakeBorder(img, 0, 0, - 0, W-new_w, cv2.BORDER_CONSTANT, value=(0,0,0)) - return img - - -def resize_image(img,algorithm,side_len=1536,add_padding=True): - - if algorithm == 'CRNN': - img = transform_img_shape(img,[32,crnn_max_length]) - return img - - if(algorithm=='SAST'): - stride = 128 - else: - stride = 32 - height, width, _ = img.shape - flag = None - if height > width: - flag = True - new_height = side_len - new_width = int(math.ceil(new_height / height * width / stride) * stride) - else: - flag = False - new_width = side_len - new_height = int(math.ceil(new_width / width * height / stride) * stride) - resized_img = cv2.resize(img, (new_width, new_height)) - if add_padding is True: - if flag: - padded_image = cv2.copyMakeBorder(resized_img, 0, 0, - 0, side_len-new_width, cv2.BORDER_CONSTANT, value=(0,0,0)) - else: - padded_image = cv2.copyMakeBorder(resized_img, 0, side_len-new_height, - 0, 0, cv2.BORDER_CONSTANT, value=(0,0,0)) - else: - return resized_img - return padded_image - - -def gen_onnx(args): - stream = open(args.config, 'r', encoding='utf-8') - config = yaml.load(stream,Loader=yaml.FullLoader) - - model = create_module(config['architectures']['model_function'])(config) - - model = model.cuda() - model = load_model(model,args.model_path) - model.eval() - - print('load model ok.....') - - - img = cv2.imread(args.img_path) - img = resize_image(img,args.algorithm,side_len=args.max_size,add_padding=args.add_padding) - w,h = img.shape[:2] - print(w,h) - img1 = Image.fromarray(img).convert('RGB') - img1 = transforms.ToTensor()(img1) - img1 = transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])(img1).unsqueeze(0).cuda() - - s = time.time() - with torch.no_grad(): - out = model(img1) - print('cost time:',time.time()-s) - if isinstance(out,dict): - out = out['f_score'] - - cv2.imwrite('./onnx/ori_output.jpg',out[0,0].cpu().detach().numpy()*255) - - output_onnx = args.save_path - print("==> Exporting model to ONNX format at '{}'".format(output_onnx)) - input_names = ["input"] - # output_names = ["hm" , "wh" , "reg"] - output_names = ["out"] - inputs = torch.randn(args.batch_size, 3,w,h).cuda() - torch_out = torch.onnx._export(model, inputs, output_onnx, export_params=True, verbose=False,do_constant_folding=False,keep_initializers_as_inputs=True, - input_names=input_names, output_names=output_names) - - - onnx_path = args.save_path - session = onnxruntime.InferenceSession(onnx_path) - # session.get_modelmeta() - # input_name = session.get_inputs()[0].name - # output_name = session.get_outputs()[0].name - - image = img / 255.0 - mean = np.array([0.485, 0.456, 0.406]) - std = np.array([0.229, 0.224, 0.225]) - image = (image - mean) / std - image = np.transpose(image, [2, 0, 1]) - image = np.expand_dims(image, axis=0) - image = image.astype(np.float32) - - s = time.time() - preds = session.run(['out'], {'input': image}) - preds = preds[0] - print(time.time()-s) - if isinstance(preds,dict): - preds = preds['f_score'] - cv2.imwrite('./onnx/onnx_output.jpg',preds[0,0]*255) - - print('error_distance:',np.abs((out.cpu().detach().numpy()-preds)).mean()) - -if __name__ == "__main__": - parser = argparse.ArgumentParser(description='Hyperparams') - parser.add_argument('--config', help='config file path') - parser.add_argument('--model_path', nargs='?', type=str, default=None) - parser.add_argument('--img_path', nargs='?', type=str, default=None) - parser.add_argument('--save_path', nargs='?', type=str, default=None) - parser.add_argument('--batch_size', nargs='?', type=int, default=None) - parser.add_argument('--max_size', nargs='?', type=int, default=None) - parser.add_argument('--algorithm', nargs='?', type=str, default=None) - parser.add_argument('--add_padding', action='store_true', default=False) - - args = parser.parse_args() - gen_onnx(args) - \ No newline at end of file diff --git a/to_onnx.sh b/to_onnx.sh deleted file mode 100644 index 77b3924..0000000 --- a/to_onnx.sh +++ /dev/null @@ -1 +0,0 @@ -python ./script/pytorch_to_onnx.py --config ./config/det_PSE_resnet50.yaml --model_path ./checkpoint/ag_PSE_bb_resnet50_he_FPN_Head_bs_8_ep_600_PSE20201004/PSE_best.pth.tar --img_path /src/notebooks/detect_text/icdar2015/ch4_test_images/img_10.jpg --save_path ./onnx/PSEnet_20201012.onnx --batch_size 2 --max_size 1536 --algorithm PSE --add_padding \ No newline at end of file diff --git a/to_tensorrt.sh b/to_tensorrt.sh deleted file mode 100644 index b05f6c8..0000000 --- a/to_tensorrt.sh +++ /dev/null @@ -1 +0,0 @@ -CUDA_VISIBLE_DEVICES=2 python3 ./script/onnx_to_tensorrt.py --onnx_path ./onnx/PSEnet.onnx --trt_engine_path ./onnx/PSEnet_batch.engine --img_path /src/notebooks/detect_text/icdar2015/ch4_test_images/img_10.jpg --batch_size 2 --algorithm PSE --max_size 1536 --add_padding \ No newline at end of file diff --git a/tools/__init__.py b/tools/__init__.py deleted file mode 100644 index 35de2ad..0000000 --- a/tools/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -#-*- coding:utf-8 _*- -""" -@author:fxw -@file: __init__.py.py -@time: 2020/08/07 -""" diff --git a/tools/cal.py b/tools/cal.py deleted file mode 100644 index 363509b..0000000 --- a/tools/cal.py +++ /dev/null @@ -1,11 +0,0 @@ -import sys -sys.path.append('./') -from tools.cal_rescall.script import cal_recall_precison_f1 -from tools.cal_rescall.cal_det import cal_det_metrics - - -result = cal_recall_precison_f1('/src/notebooks/detect_text/icdar2015/ch4_test_gts/','/src/notebooks/detect_text/PytorchOCR3/result/result_txt') -print(result) - -out = cal_det_metrics('/src/notebooks/detect_text/icdar2015/ch4_test_gts/', '/src/notebooks/detect_text/PytorchOCR3/result/result_txt') -print(out) \ No newline at end of file diff --git a/tools/cal_rescall/__init__.py b/tools/cal_rescall/__init__.py deleted file mode 100644 index 4292570..0000000 --- a/tools/cal_rescall/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -#-*- coding:utf-8 _*- -""" -@author:fxw -@file: __init__.py.py -@time: 2020/08/13 -""" diff --git a/tools/cal_rescall/__pycache__/__init__.cpython-35.pyc b/tools/cal_rescall/__pycache__/__init__.cpython-35.pyc deleted file mode 100644 index f055973cfdb7d3cb9ddf5dee18bcba277a13d7c5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 225 zcmWgR<>flbVHzL7z`*brh~a<{$Z`PUVlE(&!oUy(BpDfkHJPeRxf~KpOEU6{tkNpV zxg63mb5gAo;^Q;(GE3s)^$IG1h|8fQGZ!doWME{VZ(yNsXw2oO$#{!BK0YNsIX-?R zLlG0uR50<&+u156v^ce>I3_JIFTJ9)JT)^WpfWilu_!m7C_gJTxuh7#FUc=T&hU2* oiYdv@&nb>cPRxlfN-YLbIWb69-{P>z%}*)KNws4GIUR@@0PMCuHvj+t diff --git a/tools/cal_rescall/__pycache__/__init__.cpython-36.pyc b/tools/cal_rescall/__pycache__/__init__.cpython-36.pyc deleted file mode 100644 index a36e979926e30ca991feb7fd6fbddf99c5bfaf01..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 179 zcmXr!<>m6KG>wm7U|@I*#Bjg}WH|tFF$a)HVTfW#VGL%_WU4ada!4#K$;dCVN~}zQ`1q9! xMNB|5!Nf0lJ^hmW{G4L_uWpXag{`K z+1$deWygPjpEO%mcf*2ZHgIN~CzfXB%$(<(^PTahqfzkZ@t4WUMd&Yd<1>MN2BZB1 zCV~hqQQWUMHi$vY(wtkdg%RRG~;<) zeO>?X#mltpQeLy&%urJBjbTh+d=8@>gAzvt7HEkVMvfNd$+<9g&`Z1F$qALo!+##dwK;Gu}p8 zVgjpOOoYG07#;8IqkTx(x9B_cBLYi*3tO1NDyC#g)p`*=?Ra`8BZiSv$i;FEJoX8^)GzUnl$QGx@F#Sk~3-54Xvcrr2I%)1sIS%qbVu# zinh{jIm;VZnlk6oQaaL{r;Rkhku)k`rHrMg(gpiRX+x#MYc1uZ4cH}$(UfUMqj5Yb z(*s&2hj~d8?an84(qBT_>a>mTVSO!9jhJi#UqSn|u`q$g08tJ8lV`#NXg>z9o5BIWy226G3zt2G*6=D%So+ixMqvn7cuNBs zz%I-SP<{k}GN5&IBZvT?Wu|M;Z$StjTsP~=l+%zO(Gc$3w`@44Q0c7IGM}+o`$SVp z17Oq_L)Gko0z@^7o`id#K4W!P5zab(bi{^_DgK2K+qeE^(zlGXDAz?llaD|s6Y;?b?V#G_lci~S2L0M_x9 zU=Q9gxlE|u>()^=WA76Dv3IEwdzZq}>0QeaRO+ZE-}Lp;nC_=+*G$FO4$fg9o7$YoQYq+NCM=9Ik)bnGf~dEH&-M@!%Es(OlP Y*WUKLCS6INs{cW&o-5^NXawv30j)F9lmGw# diff --git a/tools/cal_rescall/__pycache__/cal_iou.cpython-36.pyc b/tools/cal_rescall/__pycache__/cal_iou.cpython-36.pyc deleted file mode 100644 index bca5b297299ab1a0951bc562a86eee3e4a5d9356..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4979 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 z z(s-Ak)z^b|ULl^NOx|{u-33qDZNs+PY1NiL-yhDCvIPY9{|CR~zSyMP$2hazqLjJV z_5=7Qpx~B0c^FVg-2p}f>5(oOsQiH(q*vKncFSvVPG#{T)~(dhqv#=@rEVFd2Sd|| zt-8DBAz3IGB)r8I=Mh64h|>b{E0CmNaF($rr6lJ}oQ9GfAZHYlqnzV^3C{VXSuVbw7|G!r?ActEk>PGR}J3O8pMwUvtPK8j%eU$DI`nkkV0S%#bw#~S+- zdrLqDv}skiwR%AoIEsi^79Qwl;n*WZ6P)Hv++;w-w!{mf+`ky8rtPPgyb~r3^4>{5 zSNQqhO4&mkXD`2Z{`|$um)YL9?@{}06iAFcOm*T2k{nI)4iy_zY*Nvt;)f`Z@=gCi zAju|GzAiF|i)r~IYWOh~#4F@ID(LKPn@e&u2V?(?=0%A?TGr5KaOmiGlKMG{BU#WT zC7fW13Uo90Hs8Nz78mDL{yWd7m|k35%iHW$Xrve~E*7WN)Og~=L6w%ji)x$} z%&he}dW=wjGuB(s#B2U?Z1_z{Ruup11ZSpmZmv8=Z7))BkqSzhQT79Q^w_v(g_^ks z&u(aic;8u*x6a_-0oL)KB=m{kBnoocvg;OW)B7u=thD{O?bwf5W14PH+IhQaS=M8y JAHa3)e*xm=hZ6t* diff --git a/tools/cal_rescall/__pycache__/script.cpython-35.pyc b/tools/cal_rescall/__pycache__/script.cpython-35.pyc deleted file mode 100644 index 20d08d770ca75b7abe29800a475efdec7deef673..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8636 zcmcIq&2Jk?cCT)JNTNtdmSjn^WV>zoi(^^iuNlc2jcr+!y-I9Z5jCsbUK`vVc1xnn zFI6|kl+wJ!i|j5CEcTF7j(b_`DYyIqK?X>GJqJN&fdoj9z}EzG*&LGJd&O?j8qdsL zLaK^YuU=QZ?|N0OiA*N-SLu(6zgrOE-$c(RhW0}gw~orjpC&3oR5f90LbG*Y$3#UJ z)tE42=wm!CDsf>Z7#HoNF#Cj!Zc_Y22(w?<==O7aK-l<8aXTgKL1Cwboe@95+-S!| zAvg3Ei;A2Uq-s{xvAt%a;=I)qXk5xt(fPuvG%c@OuN8NjwUWD0q6fmy2pX#>?p;(M zT#-w4yhD;AMv*}^)3Q*V=HCgV>`Q6v*Pux*RZ5jb(f618Zx-=c#d=puWmG5 z$Jnh)!`pWZ&{(#aNW-exMwjM!o;w&?fBso<_4!6&duwHVV|!1L|E~{%?8?^KN^x^* zZFRk{{(PecCm4IOT`a6T+kCoK+*o2_iJH$rAW zYq$FS`s&h)&s>P$#jaJ`Yc*dx6XHM=t2qDkIaLsdSo|{u9OD^%g&-az3VkI!!d z%eAVAC&pzLlzB!2QUQx3qbJ*P5NI2@xQydB7^&1PyQs)4k|fQEj!!$qO1Va!g!lt^ zFtFAtISqlVCPqCZ5E0wMT#ty>OrJjp2OIBepAu;K zr$n|r%7|lJk8^#R>j`qW18|7$X|bmZcwKvvXPV+V%k>$qr@5Zt`YhMyxIWLd!Sw~6 z_M)&SM0=KrhnJ7Cmrkm1FvUFBS&9fR5J)bG_GQt&B3i!^UQ&2){!2s!{_-z>A=+1| z?_UKE*MxsXwQ|DG3HuC%jP?gSHzt+;tobS7UlV@;VO*ym<6mcX=0q*cVd+|@>raV; ze&NqiSM$$shsQMk10jE@`7;C}N~Q<}f|(-B!(VDU{;aU4g$Lb*qZpZ_&J3dti&t?W zZQY&;kr+-$_E|AmgHfFmlM4_H)Z(WFLZ2)o(O@mz2nBZIq`rdYyHpH zh4!B(ga*ST@%&!ziswTX&xb6Jo9~L}rtq^Yk()|9(>>xj$>*1P>oswne_0ppTO=<3 z78G?${0v5nKBj<%Zd2nRCEh?U*fQ>rg;22nB8El{q65MmG2I>)KK?+hA+jXJdollP zs0dg~R}q_s_hLJTU+S!l3!$JHCDg{*9&P+rtg8)%zegL9I*B%{{Z9#ks8xF*Y!J~9 z{H1-j<3avimgXdP;kKcDkbk9F4j=Ul@yy+!k44K470FA0#Qe)m(OwX(C*eTIVE*MD z(OzU}FF@M&h)#bIttH`OKSX_x*7GNYzd!?sOv{JAj_(})BEHl5?{K#6GXKKM{9`!K zUFL6jnU83iY)5B93IAhWtlP&LGvZaBkQf71XTx^S@|+D^R9YQMz@L1cbeUQ_-LgMH zZnn^#5dJyRxeW(d5H>u2QIt6xy0%n_TM-Ri_{v%2Lmg&LxRC!%TI@mYRLZp>wfi6RUSIk1$-bMD!F+?%tySMvj zF6<`=%Hv|(A435VK$vbrsnEm(w22>3vy2vghKP>^Y*yhazZG_lp0*LfF%v>IHlaB( z02}e|qOj+?a1!B6 zz<4NpAU%wv^<%~Yk1+NJkkmsIg)rS-VJSRhDLh~)00RE+i*j7F9|;?702wXPj%vf# z0fC42^XRJZSCQ_1V!b+%?sl>#r>HW&rSM4Q7@bs*j#<+2nzE+y!A`=<86oAZTPgvb zH}d5YC2N*EGG3RxeNG;c=T+*jDN9A()pXpY=&>8&{MV#&*epxO&Kbs&O8s-I5{`fX zSuVNr##(F1_*3KRCbH_XizTiu-JD;XySnAjIfpv;jCRk&Di?K)Yjw^ZtxC?=L~7ni zQQw{Qs)F9SYJB$+#+=bXzx{`xGespjVbg~)$2dv>#r=0w>IkLsNbC+OiF~7JZJcB{ zx5Gg{OU{YiEG?EhFgj}~SSEt?MX;d=M&}%bHypu|5o{!a4MZ>)eP@kS1RITDgAr^@ zEE7o}?f@wYlG|h_8ANW_)`gc42Yl4R@`c>^@o}V>t&R~{#%8^8v{$bg_0Q=T8y@i^ zqv;IaHcA!C#mP$6tA=G%%C2YBca24(Q7_~8v}Cky%^lsEYu%nZx;@vrGk0`nu61|r z=x(&2B$>aZ^GcAgy`zQ`B=F#!+mdwhmXz^Ur;5=M)S`3l+dfn|osqZ-+cQ0IdL)FO z?`fayX&XK5b3N?~)b0!PS|dm})rNNz#6VWi2glmCK%}-E#2S^opsyo`Tr5Z`mVoPLitM?I6`AuvBE*WU8AjH5Ff!&YR%FXvtjLtREB072 zpV7LO+|QOgK&dFUAglvR-e7Z*bPAMbsG6pVgdxc?B$yi6EZsJcqkHB_*zINepq4+wIds_r!uADD5uRq}8F zRdI@59rnbY`vi!}ptNai?A@E~KOjJ!5ZG?6K+xXslTS@JB>};wP=8 zwd0H5sNd+3hzsH-g~D8r6pzmFp)aKLXHY=co-XX*0Iq2ow#C8^Ynu3h4Qt5h8_Lf} z@=(P`_>mv0Hib61NENGv>7G8xQ?dSqBNZn<2C7S2g3w~nOiVkz`>nk6il%3ph&$Q{ zh(k8n571D&Jfxiv9ky05Ln)#`mqrDE$ew%H%IQI@;?(3_Je`YmxijO9_1ek!E{Wk? zlnxUqTvx@l`;h78pb^ict?f+M3^JDG|_r0yq=kXn18uQ7hr^f+@y{48fQb7Y#F!53m^tJ zGsq{<2Qp9qot$0(d55mxlUhg|-QeirEJn~Oo@yxH@o{3iH12)wY<_kHG_d#v--qah2sQ$or~N+U)eNMG=*cs zd+b1~1qpuQWOPWC>eWWmbBb02uX==Qpb~u3KB^7lJEEoV&7vexX7sd{(vN4qQJdY_ z*dv00tuQ(tk5SY&DBK$q?a@NWr(C9UV@tvaMDN9IY<70`IP+v12^B|mq*TYcaqT!` z*vPRxqXd?7sX$Bd4Xz~rKOVvxkRE(V79>y8)G=HnAE%I!l=gAyYs?-Gs$)Rj@r+rS z2Fe}^eI5n_xFA-ke2_#7S$vS#uR0c_*s#jdl`B9ClI406^Swn35W(P{w^^@ja_SeP zu~yVGaW|aDojPgA9K=Z2k>m7WrXx&*v8VMt-l5GDmMk?Z7Sw0wf)ymyfyYcW z5G>TOAU7<@x%uW=(rc3LR%>3SDC1T5(z>WlH!A0%%j;rz$uB>pr^o_>q}6CRu$7qW zNp|)iaqP&t8^jT07Ug}$@#>W4=JJwmr6t8o=0eU<>o!$)sJcg09+f!)Y72b%{!AU0 z%oH1<8YA<3Vfk#JGGMInj;aE6$}Wa zt8@o%Ce_Wn{3BXrovJ^krBYCY!nNfy0ss#x!-Gn9u6$0on>2K)t3X28XjaY99zAwo z@+sjCb>{`1%k(P#S9|Ck0=*rnkU^ULN}ns6@=s`zl*)_Qy|tn_>LCuO*JZF0m%#E7 zQAhi|nR#c_?1ytGTLVYrerAF>W5!q_!xTk2lB2nJgfWwfcUWm>4zOm9M0*T^3Djbl z>4u#zSDhN2bzO6?-t-Vc3UoLy2h^I7EK8VXr8Y<)snPuS<;_uTB&)>61LnEbtmYl2 zoZZVzD|^Qj@IP}{^`bMjIj8{NBeyg&s`IR4Vx)XJ?sK%mj@;dac>^9k5x*#fa_1<^ z>raoQhtrAC#OY)@nI2E-NqtzK(6Tz-J#xwD6bZSfpAMlh!s}oVo<4w$JI>`a$IcXz z5-P)ppBa3U+B9aptEcr;4{i)kj1fN8=;Dq7KZQt~)@BfmC$#~7@{&FU*fhS`&I%WF zYK>`^wKKq1Q9Gl}W3H^0#!9EO49Y1yJ%PE>+9k|1qFuykYgC^D7QUc{d1DSQDL%O# zk5lv?)h`17f;On1!&m+g@_HO?*A!?e0+T{r%7CylW0%F2i0u>ip4cssaL`LxG?09t z=1mcA0o1mL3x;eV7vBi9y+Cv27N$VPOZg>qZ{ej|H%{`)Uw#3`MC1}?AKr#H=-t~O zG*2(@sbdbh`}NoKz8R1}Yfvnw=E*&90tN;wGP9xz{PZsdw;sSCI$6vpvX}u)7Kx^G zFj&H^k;+D06@MRNKm}nswD9pINWh*OM=BuG%jzz@JWmf1-OR9mxmvfI73V%hb$;(X R0-Zx!G1%o~`hUf#{{z|uj&%S4 diff --git a/tools/cal_rescall/__pycache__/script.cpython-36.pyc b/tools/cal_rescall/__pycache__/script.cpython-36.pyc deleted file mode 100644 index 6eebef95884f9ec0c84550442c4bc3297d43001f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7714 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 Be`Ejv diff --git a/tools/cal_rescall/cal_det.py b/tools/cal_rescall/cal_det.py deleted file mode 100644 index 0c6d503..0000000 --- a/tools/cal_rescall/cal_det.py +++ /dev/null @@ -1,53 +0,0 @@ -import os -import numpy as np -from .cal_iou import DetectionIoUEvaluator - -def load_label_infor(label_file_path, do_ignore=False): - files = os.listdir(label_file_path) - img_name_label_dict = {} - for file in files: - bbox_infor = [] - with open(os.path.join(label_file_path,file), "r",encoding='utf-8') as fin: - lines = fin.readlines() - for line in lines: - txt_dict = {} - substr = line.strip("\n").split(",") - coord = list(map(int,substr[:8])) - text = substr[-1] - ignore = False - if text == "###" and do_ignore: - ignore = True - txt_dict['ignore'] = ignore - txt_dict['points'] = np.array(coord).reshape(4,2).tolist() - txt_dict['text'] = ignore - bbox_infor.append(txt_dict) - if do_ignore: - img_name_label_dict[file.replace('gt_','').replace('.txt','')] = bbox_infor - else: - img_name_label_dict[file.replace('res_','').replace('.txt','')] = bbox_infor - return img_name_label_dict - - -def cal_det_metrics(gt_label_path, save_res_path): - """ - calculate the detection metrics - Args: - gt_label_path(string): The groundtruth detection label file path - save_res_path(string): The saved predicted detection label path - return: - claculated metrics including Hmean, precision and recall - """ - evaluator = DetectionIoUEvaluator() - gt_label_infor = load_label_infor(gt_label_path, do_ignore=True) - dt_label_infor = load_label_infor(save_res_path) - results = [] - for img_name in gt_label_infor: - gt_label = gt_label_infor[img_name] - if img_name not in dt_label_infor: - dt_label = [] - else: - dt_label = dt_label_infor[img_name] - result = evaluator.evaluate_image(gt_label, dt_label) - results.append(result) - methodMetrics = evaluator.combine_results(results) - return methodMetrics diff --git a/tools/cal_rescall/cal_iou.py b/tools/cal_rescall/cal_iou.py deleted file mode 100644 index 6440598..0000000 --- a/tools/cal_rescall/cal_iou.py +++ /dev/null @@ -1,235 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -from collections import namedtuple -import numpy as np -from shapely.geometry import Polygon -""" -reference from : -https://github.com/MhLiao/DB/blob/3c32b808d4412680310d3d28eeb6a2d5bf1566c5/concern/icdar2015_eval/detection/iou.py#L8 -""" - - -class DetectionIoUEvaluator(object): - def __init__(self, iou_constraint=0.5, area_precision_constraint=0.5): - self.iou_constraint = iou_constraint - self.area_precision_constraint = area_precision_constraint - - def evaluate_image(self, gt, pred): - def get_union(pD, pG): - return Polygon(pD).union(Polygon(pG)).area - - def get_intersection_over_union(pD, pG): - return get_intersection(pD, pG) / get_union(pD, pG) - - def get_intersection(pD, pG): - return Polygon(pD).intersection(Polygon(pG)).area - - def compute_ap(confList, matchList, numGtCare): - correct = 0 - AP = 0 - if len(confList) > 0: - confList = np.array(confList) - matchList = np.array(matchList) - sorted_ind = np.argsort(-confList) - confList = confList[sorted_ind] - matchList = matchList[sorted_ind] - for n in range(len(confList)): - match = matchList[n] - if match: - correct += 1 - AP += float(correct) / (n + 1) - - if numGtCare > 0: - AP /= numGtCare - - return AP - - perSampleMetrics = {} - - matchedSum = 0 - - Rectangle = namedtuple('Rectangle', 'xmin ymin xmax ymax') - - numGlobalCareGt = 0 - numGlobalCareDet = 0 - - arrGlobalConfidences = [] - arrGlobalMatches = [] - - recall = 0 - precision = 0 - hmean = 0 - - detMatched = 0 - - iouMat = np.empty([1, 1]) - - gtPols = [] - detPols = [] - - gtPolPoints = [] - detPolPoints = [] - - # Array of Ground Truth Polygons' keys marked as don't Care - gtDontCarePolsNum = [] - # Array of Detected Polygons' matched with a don't Care GT - detDontCarePolsNum = [] - - pairs = [] - detMatchedNums = [] - - arrSampleConfidences = [] - arrSampleMatch = [] - - evaluationLog = "" - - # print(len(gt)) - for n in range(len(gt)): - points = gt[n]['points'] - # transcription = gt[n]['text'] - dontCare = gt[n]['ignore'] -# points = Polygon(points) -# points = points.buffer(0) - if not Polygon(points).is_valid or not Polygon(points).is_simple: - continue - - gtPol = points - gtPols.append(gtPol) - gtPolPoints.append(points) - if dontCare: - gtDontCarePolsNum.append(len(gtPols) - 1) - - evaluationLog += "GT polygons: " + str(len(gtPols)) + ( - " (" + str(len(gtDontCarePolsNum)) + " don't care)\n" - if len(gtDontCarePolsNum) > 0 else "\n") - - for n in range(len(pred)): - points = pred[n]['points'] -# points = Polygon(points) -# points = points.buffer(0) - if not Polygon(points).is_valid or not Polygon(points).is_simple: - continue - - detPol = points - detPols.append(detPol) - detPolPoints.append(points) - if len(gtDontCarePolsNum) > 0: - for dontCarePol in gtDontCarePolsNum: - dontCarePol = gtPols[dontCarePol] - intersected_area = get_intersection(dontCarePol, detPol) - pdDimensions = Polygon(detPol).area - precision = 0 if pdDimensions == 0 else intersected_area / pdDimensions - if (precision > self.area_precision_constraint): - detDontCarePolsNum.append(len(detPols) - 1) - break - - evaluationLog += "DET polygons: " + str(len(detPols)) + ( - " (" + str(len(detDontCarePolsNum)) + " don't care)\n" - if len(detDontCarePolsNum) > 0 else "\n") - - if len(gtPols) > 0 and len(detPols) > 0: - # Calculate IoU and precision matrixs - outputShape = [len(gtPols), len(detPols)] - iouMat = np.empty(outputShape) - gtRectMat = np.zeros(len(gtPols), np.int8) - detRectMat = np.zeros(len(detPols), np.int8) - for gtNum in range(len(gtPols)): - for detNum in range(len(detPols)): - pG = gtPols[gtNum] - pD = detPols[detNum] - iouMat[gtNum, detNum] = get_intersection_over_union(pD, pG) - - for gtNum in range(len(gtPols)): - for detNum in range(len(detPols)): - if gtRectMat[gtNum] == 0 and detRectMat[ - detNum] == 0 and gtNum not in gtDontCarePolsNum and detNum not in detDontCarePolsNum: - if iouMat[gtNum, detNum] > self.iou_constraint: - gtRectMat[gtNum] = 1 - detRectMat[detNum] = 1 - detMatched += 1 - pairs.append({'gt': gtNum, 'det': detNum}) - detMatchedNums.append(detNum) - evaluationLog += "Match GT #" + \ - str(gtNum) + " with Det #" + str(detNum) + "\n" - - numGtCare = (len(gtPols) - len(gtDontCarePolsNum)) - numDetCare = (len(detPols) - len(detDontCarePolsNum)) - if numGtCare == 0: - recall = float(1) - precision = float(0) if numDetCare > 0 else float(1) - else: - recall = float(detMatched) / numGtCare - precision = 0 if numDetCare == 0 else float(detMatched) / numDetCare - - hmean = 0 if (precision + recall) == 0 else 2.0 * \ - precision * recall / (precision + recall) - - matchedSum += detMatched - numGlobalCareGt += numGtCare - numGlobalCareDet += numDetCare - - perSampleMetrics = { - 'precision': precision, - 'recall': recall, - 'hmean': hmean, - 'pairs': pairs, - 'iouMat': [] if len(detPols) > 100 else iouMat.tolist(), - 'gtPolPoints': gtPolPoints, - 'detPolPoints': detPolPoints, - 'gtCare': numGtCare, - 'detCare': numDetCare, - 'gtDontCare': gtDontCarePolsNum, - 'detDontCare': detDontCarePolsNum, - 'detMatched': detMatched, - 'evaluationLog': evaluationLog - } - - return perSampleMetrics - - def combine_results(self, results): - numGlobalCareGt = 0 - numGlobalCareDet = 0 - matchedSum = 0 - for result in results: - numGlobalCareGt += result['gtCare'] - numGlobalCareDet += result['detCare'] - matchedSum += result['detMatched'] - - methodRecall = 0 if numGlobalCareGt == 0 else float( - matchedSum) / numGlobalCareGt - methodPrecision = 0 if numGlobalCareDet == 0 else float( - matchedSum) / numGlobalCareDet - methodHmean = 0 if methodRecall + methodPrecision == 0 else 2 * \ - methodRecall * methodPrecision / (methodRecall + methodPrecision) - # print(methodRecall, methodPrecision, methodHmean) - # sys.exit(-1) - methodMetrics = { - 'precision': methodPrecision, - 'recall': methodRecall, - 'hmean': methodHmean - } - - return methodMetrics - - -if __name__ == '__main__': - evaluator = DetectionIoUEvaluator() - gts = [[{ - 'points': [(0, 0), (1, 0), (1, 1), (0, 1)], - 'text': 1234, - 'ignore': False, - }, { - 'points': [(2, 2), (3, 2), (3, 3), (2, 3)], - 'text': 5678, - 'ignore': False, - }]] - preds = [[{ - 'points': [(0.1, 0.1), (1, 0), (1, 1), (0, 1)], - 'text': 123, - 'ignore': False, - }]] - results = [] - for gt, pred in zip(gts, preds): - results.append(evaluator.evaluate_image(gt, pred)) - metrics = evaluator.combine_results(results) - print(metrics) diff --git a/tools/cal_rescall/rrc_evaluation_funcs.py b/tools/cal_rescall/rrc_evaluation_funcs.py deleted file mode 100644 index b536b6a..0000000 --- a/tools/cal_rescall/rrc_evaluation_funcs.py +++ /dev/null @@ -1,414 +0,0 @@ -# encoding: UTF-8 -import json -import sys; - -sys.path.append('./') -import zipfile -import re -import sys -import os -import codecs -import traceback -import importlib -from io import StringIO - - -def print_help(): - sys.stdout.write('Usage: python %s.py -g= -s= [-o= -p=]' % sys.argv[0]) - sys.exit(2) - - -def load_zip_file_keys(file, fileNameRegExp=''): - """ - Returns an array with the entries of the ZIP file that match with the regular expression. - The key's are the names or the file or the capturing group definied in the fileNameRegExp - """ - try: - archive = zipfile.ZipFile(file, mode='r', allowZip64=True) - except: - raise Exception('Error loading the ZIP archive.') - - pairs = [] - - for name in archive.namelist(): - addFile = True - keyName = name - if fileNameRegExp != "": - m = re.match(fileNameRegExp, name) - if m == None: - addFile = False - else: - if len(m.groups()) > 0: - keyName = m.group(1) - - if addFile: - pairs.append(keyName) - - return pairs - - -def load_zip_file(file, fileNameRegExp='', allEntries=False): - """ - Returns an array with the contents (filtered by fileNameRegExp) of a ZIP file. - The key's are the names or the file or the capturing group definied in the fileNameRegExp - allEntries validates that all entries in the ZIP file pass the fileNameRegExp - """ - try: - archive = zipfile.ZipFile(file, mode='r', allowZip64=True) - except: - raise Exception('Error loading the ZIP archive') - - pairs = [] - for name in archive.namelist(): - addFile = True - keyName = name - if fileNameRegExp != "": - m = re.match(fileNameRegExp, name) - if m == None: - addFile = False - else: - if len(m.groups()) > 0: - keyName = m.group(1) - - if addFile: - pairs.append([keyName, archive.read(name)]) - else: - if allEntries: - raise Exception('ZIP entry not valid: %s' % name) - - return dict(pairs) - - -def load_folder_file(file, fileNameRegExp='', allEntries=False): - """ - Returns an array with the contents (filtered by fileNameRegExp) of a ZIP file. - The key's are the names or the file or the capturing group definied in the fileNameRegExp - allEntries validates that all entries in the ZIP file pass the fileNameRegExp - """ - pairs = [] - for name in os.listdir(file): - addFile = True - keyName = name - if fileNameRegExp != "": - m = re.match(fileNameRegExp, name) - if m == None: - addFile = False - else: - if len(m.groups()) > 0: - keyName = m.group(1) - - if addFile: - pairs.append([keyName, open(os.path.join(file, name)).read()]) - else: - if allEntries: - raise Exception('ZIP entry not valid: %s' % name) - - return dict(pairs) - - -def decode_utf8(raw): - """ - Returns a Unicode object on success, or None on failure - """ - try: - raw = codecs.decode(raw, 'utf-8', 'replace') - # extracts BOM if exists - raw = raw.encode('utf8') - if raw.startswith(codecs.BOM_UTF8): - raw = raw.replace(codecs.BOM_UTF8, '', 1) - return raw.decode('utf-8') - except: - return None - - -def validate_lines_in_file(fileName, file_contents, CRLF=True, LTRB=True, withTranscription=False, withConfidence=False, - imWidth=0, imHeight=0): - """ - This function validates that all lines of the file calling the Line validation function for each line - """ - utf8File = decode_utf8(file_contents) - if (utf8File is None): - raise Exception("The file %s is not UTF-8" % fileName) - - lines = utf8File.split("\r\n" if CRLF else "\n") - for line in lines: - line = line.replace("\r", "").replace("\n", "") - if (line != ""): - try: - validate_tl_line(line, LTRB, withTranscription, withConfidence, imWidth, imHeight) - except Exception as e: - raise Exception( - ("Line in sample not valid. Sample: %s Line: %s Error: %s" % (fileName, line, str(e))).encode( - 'utf-8', 'replace')) - - -def validate_tl_line(line, LTRB=True, withTranscription=True, withConfidence=True, imWidth=0, imHeight=0): - """ - Validate the format of the line. If the line is not valid an exception will be raised. - If maxWidth and maxHeight are specified, all points must be inside the imgage bounds. - Posible values are: - LTRB=True: xmin,ymin,xmax,ymax[,confidence][,transcription] - LTRB=False: x1,y1,x2,y2,x3,y3,x4,y4[,confidence][,transcription] - """ - get_tl_line_values(line, LTRB, withTranscription, withConfidence, imWidth, imHeight) - - -def get_tl_line_values(line, LTRB=True, withTranscription=False, withConfidence=False, imWidth=0, imHeight=0): - """ - Validate the format of the line. If the line is not valid an exception will be raised. - If maxWidth and maxHeight are specified, all points must be inside the imgage bounds. - Posible values are: - LTRB=True: xmin,ymin,xmax,ymax[,confidence][,transcription] - LTRB=False: x1,y1,x2,y2,x3,y3,x4,y4[,confidence][,transcription] - Returns values from a textline. Points , [Confidences], [Transcriptions] - """ - confidence = 0.0 - transcription = ""; - points = [] - - numPoints = 4; - - if LTRB: - - numPoints = 4; - - if withTranscription and withConfidence: - m = re.match( - r'^\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-1].?[0-9]*)\s*,(.*)$', line) - if m == None: - m = re.match( - r'^\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-1].?[0-9]*)\s*,(.*)$', - line) - raise Exception("Format incorrect. Should be: xmin,ymin,xmax,ymax,confidence,transcription") - elif withConfidence: - m = re.match(r'^\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-1].?[0-9]*)\s*$', - line) - if m == None: - raise Exception("Format incorrect. Should be: xmin,ymin,xmax,ymax,confidence") - elif withTranscription: - m = re.match(r'^\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*,(.*)$', line) - if m == None: - raise Exception("Format incorrect. Should be: xmin,ymin,xmax,ymax,transcription") - else: - m = re.match(r'^\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*,?\s*$', line) - if m == None: - raise Exception("Format incorrect. Should be: xmin,ymin,xmax,ymax") - - xmin = int(m.group(1)) - ymin = int(m.group(2)) - xmax = int(m.group(3)) - ymax = int(m.group(4)) - if (xmax < xmin): - raise Exception("Xmax value (%s) not valid (Xmax < Xmin)." % (xmax)) - if (ymax < ymin): - raise Exception("Ymax value (%s) not valid (Ymax < Ymin)." % (ymax)) - - points = [float(m.group(i)) for i in range(1, (numPoints + 1))] - - if (imWidth > 0 and imHeight > 0): - validate_point_inside_bounds(xmin, ymin, imWidth, imHeight); - validate_point_inside_bounds(xmax, ymax, imWidth, imHeight); - - else: - - numPoints = 8; - - if withTranscription and withConfidence: - m = re.match( - r'^\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*([0-1].?[0-9]*)\s*,(.*)$', - line) - if m == None: - raise Exception("Format incorrect. Should be: x1,y1,x2,y2,x3,y3,x4,y4,confidence,transcription") - elif withConfidence: - m = re.match( - r'^\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*([0-1].?[0-9]*)\s*$', - line) - if m == None: - raise Exception("Format incorrect. Should be: x1,y1,x2,y2,x3,y3,x4,y4,confidence") - elif withTranscription: - m = re.match( - r'^\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,(.*)$', - line) - if m == None: - raise Exception("Format incorrect. Should be: x1,y1,x2,y2,x3,y3,x4,y4,transcription") - else: - m = re.match( - r'^\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*$', - line) - if m == None: - raise Exception("Format incorrect. Should be: x1,y1,x2,y2,x3,y3,x4,y4") - - points = [float(m.group(i)) for i in range(1, (numPoints + 1))] - - validate_clockwise_points(points) - - if (imWidth > 0 and imHeight > 0): - validate_point_inside_bounds(points[0], points[1], imWidth, imHeight); - validate_point_inside_bounds(points[2], points[3], imWidth, imHeight); - validate_point_inside_bounds(points[4], points[5], imWidth, imHeight); - validate_point_inside_bounds(points[6], points[7], imWidth, imHeight); - - if withConfidence: - try: - confidence = float(m.group(numPoints + 1)) - except ValueError: - raise Exception("Confidence value must be a float") - - if withTranscription: - posTranscription = numPoints + (2 if withConfidence else 1) - transcription = m.group(posTranscription) - m2 = re.match(r'^\s*\"(.*)\"\s*$', transcription) - if m2 != None: # Transcription with double quotes, we extract the value and replace escaped characters - transcription = m2.group(1).replace("\\\\", "\\").replace("\\\"", "\"") - - return points, confidence, transcription - - -def validate_point_inside_bounds(x, y, imWidth, imHeight): - if (x < 0 or x > imWidth): - raise Exception("X value (%s) not valid. Image dimensions: (%s,%s)" % (xmin, imWidth, imHeight)) - if (y < 0 or y > imHeight): - raise Exception( - "Y value (%s) not valid. Image dimensions: (%s,%s) Sample: %s Line:%s" % (ymin, imWidth, imHeight)) - - -def validate_clockwise_points(points): - """ - Validates that the points that the 4 points that dlimite a polygon are in clockwise order. - """ - - if len(points) != 8: - raise Exception("Points list not valid." + str(len(points))) - - point = [ - [int(points[0]), int(points[1])], - [int(points[2]), int(points[3])], - [int(points[4]), int(points[5])], - [int(points[6]), int(points[7])] - ] - edge = [ - (point[1][0] - point[0][0]) * (point[1][1] + point[0][1]), - (point[2][0] - point[1][0]) * (point[2][1] + point[1][1]), - (point[3][0] - point[2][0]) * (point[3][1] + point[2][1]), - (point[0][0] - point[3][0]) * (point[0][1] + point[3][1]) - ] - - summatory = edge[0] + edge[1] + edge[2] + edge[3]; - if summatory > 0: - raise Exception( - "Points are not clockwise. The coordinates of bounding quadrilaterals have to be given in clockwise order. Regarding the correct interpretation of 'clockwise' remember that the image coordinate system used is the standard one, with the image origin at the upper left, the X axis extending to the right and Y axis extending downwards.") - - -def get_tl_line_values_from_file_contents(content, CRLF=True, LTRB=True, withTranscription=False, withConfidence=False, - imWidth=0, imHeight=0, sort_by_confidences=True): - """ - Returns all points, confindences and transcriptions of a file in lists. Valid line formats: - xmin,ymin,xmax,ymax,[confidence],[transcription] - x1,y1,x2,y2,x3,y3,x4,y4,[confidence],[transcription] - """ - pointsList = [] - transcriptionsList = [] - confidencesList = [] - - lines = content.split("\r\n" if CRLF else "\n") - for line in lines: - line = line.replace("\r", "").replace("\n", "") - if (line != ""): - points, confidence, transcription = get_tl_line_values(line, LTRB, withTranscription, withConfidence, - imWidth, imHeight); - pointsList.append(points) - transcriptionsList.append(transcription) - confidencesList.append(confidence) - - if withConfidence and len(confidencesList) > 0 and sort_by_confidences: - import numpy as np - sorted_ind = np.argsort(-np.array(confidencesList)) - confidencesList = [confidencesList[i] for i in sorted_ind] - pointsList = [pointsList[i] for i in sorted_ind] - transcriptionsList = [transcriptionsList[i] for i in sorted_ind] - - return pointsList, confidencesList, transcriptionsList - - -def main_evaluation(p, default_evaluation_params_fn, validate_data_fn, evaluate_method_fn, show_result=True, - per_sample=True): - """ - This process validates a method, evaluates it and if it succed generates a ZIP file with a JSON entry for each sample. - Params: - p: Dictionary of parmeters with the GT/submission locations. If None is passed, the parameters send by the system are used. - default_evaluation_params_fn: points to a function that returns a dictionary with the default parameters used for the evaluation - validate_data_fn: points to a method that validates the corrct format of the submission - evaluate_method_fn: points to a function that evaluated the submission and return a Dictionary with the results - """ - evalParams = default_evaluation_params_fn() - if 'p' in p.keys(): - evalParams.update(p['p'] if isinstance(p['p'], dict) else json.loads(p['p'][1:-1])) - - resDict = {'calculated': True, 'Message': '', 'method': '{}', 'per_sample': '{}'} - try: - # validate_data_fn(p['g'], p['s'], evalParams) - evalData = evaluate_method_fn(p['g'], p['s'], evalParams) - resDict.update(evalData) - - except Exception as e: - traceback.print_exc() - resDict['Message'] = str(e) - resDict['calculated'] = False - - if 'o' in p: - if not os.path.exists(p['o']): - os.makedirs(p['o']) - - resultsOutputname = p['o'] + '/results.zip' - outZip = zipfile.ZipFile(resultsOutputname, mode='w', allowZip64=True) - - del resDict['per_sample'] - if 'output_items' in resDict.keys(): - del resDict['output_items'] - - outZip.writestr('method.json', json.dumps(resDict)) - - if not resDict['calculated']: - if show_result: - sys.stderr.write('Error!\n' + resDict['Message'] + '\n\n') - if 'o' in p: - outZip.close() - return resDict - - if 'o' in p: - if per_sample == True: - for k, v in evalData['per_sample'].iteritems(): - outZip.writestr(k + '.json', json.dumps(v)) - - if 'output_items' in evalData.keys(): - for k, v in evalData['output_items'].iteritems(): - outZip.writestr(k, v) - - outZip.close() - - if show_result: - sys.stdout.write("Calculated!") - sys.stdout.write(json.dumps(resDict['method'])) - - return resDict - - -def main_validation(default_evaluation_params_fn, validate_data_fn): - """ - This process validates a method - Params: - default_evaluation_params_fn: points to a function that returns a dictionary with the default parameters used for the evaluation - validate_data_fn: points to a method that validates the corrct format of the submission - """ - try: - p = dict([s[1:].split('=') for s in sys.argv[1:]]) - evalParams = default_evaluation_params_fn() - if 'p' in p.keys(): - evalParams.update(p['p'] if isinstance(p['p'], dict) else json.loads(p['p'][1:-1])) - - validate_data_fn(p['g'], p['s'], evalParams) - print('SUCCESS') - sys.exit(0) - except Exception as e: - print(str(e)) - sys.exit(101) \ No newline at end of file diff --git a/tools/cal_rescall/script.py b/tools/cal_rescall/script.py deleted file mode 100644 index e406b4b..0000000 --- a/tools/cal_rescall/script.py +++ /dev/null @@ -1,323 +0,0 @@ -# -*- coding: utf-8 -*- -from collections import namedtuple -from . import rrc_evaluation_funcs -import Polygon as plg -import numpy as np - - -def default_evaluation_params(): - """ - default_evaluation_params: Default parameters to use for the validation and evaluation. - """ - return { - 'IOU_CONSTRAINT': 0.5, - 'AREA_PRECISION_CONSTRAINT': 0.5, - 'GT_SAMPLE_NAME_2_ID': 'gt_img_([0-9]+).txt', - 'DET_SAMPLE_NAME_2_ID': 'res_img_([0-9]+).txt', - 'LTRB': False, # LTRB:2points(left,top,right,bottom) or 4 points(x1,y1,x2,y2,x3,y3,x4,y4) - 'CRLF': False, # Lines are delimited by Windows CRLF format - 'CONFIDENCES': False, # Detections must include confidence value. AP will be calculated - 'PER_SAMPLE_RESULTS': True # Generate per sample results and produce data for visualization - } - - -def validate_data(gtFilePath, submFilePath, evaluationParams): - """ - Method validate_data: validates that all files in the results folder are correct (have the correct name contents). - Validates also that there are no missing files in the folder. - If some error detected, the method raises the error - """ - gt = rrc_evaluation_funcs.load_folder_file(gtFilePath, evaluationParams['GT_SAMPLE_NAME_2_ID']) - - subm = rrc_evaluation_funcs.load_folder_file(submFilePath, evaluationParams['DET_SAMPLE_NAME_2_ID'], True) - - # Validate format of GroundTruth - for k in gt: - rrc_evaluation_funcs.validate_lines_in_file(k, gt[k], evaluationParams['CRLF'], evaluationParams['LTRB'], True) - - # Validate format of results - for k in subm: - if (k in gt) == False: - raise Exception("The sample %s not present in GT" % k) - - rrc_evaluation_funcs.validate_lines_in_file(k, subm[k], evaluationParams['CRLF'], evaluationParams['LTRB'], - False, evaluationParams['CONFIDENCES']) - - -def evaluate_method(gtFilePath, submFilePath, evaluationParams): - """ - Method evaluate_method: evaluate method and returns the results - Results. Dictionary with the following values: - - method (required) Global method metrics. Ex: { 'Precision':0.8,'Recall':0.9 } - - samples (optional) Per sample metrics. Ex: {'sample1' : { 'Precision':0.8,'Recall':0.9 } , 'sample2' : { 'Precision':0.8,'Recall':0.9 } - """ - - def polygon_from_points(points): - """ - Returns a Polygon object to use with the Polygon2 class from a list of 8 points: x1,y1,x2,y2,x3,y3,x4,y4 - """ - resBoxes = np.empty([1, 8], dtype='int32') - resBoxes[0, 0] = int(points[0]) - resBoxes[0, 4] = int(points[1]) - resBoxes[0, 1] = int(points[2]) - resBoxes[0, 5] = int(points[3]) - resBoxes[0, 2] = int(points[4]) - resBoxes[0, 6] = int(points[5]) - resBoxes[0, 3] = int(points[6]) - resBoxes[0, 7] = int(points[7]) - pointMat = resBoxes[0].reshape([2, 4]).T - return plg.Polygon(pointMat) - - def rectangle_to_polygon(rect): - resBoxes = np.empty([1, 8], dtype='int32') - resBoxes[0, 0] = int(rect.xmin) - resBoxes[0, 4] = int(rect.ymax) - resBoxes[0, 1] = int(rect.xmin) - resBoxes[0, 5] = int(rect.ymin) - resBoxes[0, 2] = int(rect.xmax) - resBoxes[0, 6] = int(rect.ymin) - resBoxes[0, 3] = int(rect.xmax) - resBoxes[0, 7] = int(rect.ymax) - - pointMat = resBoxes[0].reshape([2, 4]).T - - return plg.Polygon(pointMat) - - def rectangle_to_points(rect): - points = [int(rect.xmin), int(rect.ymax), int(rect.xmax), int(rect.ymax), int(rect.xmax), int(rect.ymin), - int(rect.xmin), int(rect.ymin)] - return points - - def get_union(pD, pG): - areaA = pD.area(); - areaB = pG.area(); - return areaA + areaB - get_intersection(pD, pG); - - def get_intersection_over_union(pD, pG): - try: - return get_intersection(pD, pG) / get_union(pD, pG); - except: - return 0 - - def get_intersection(pD, pG): - pInt = pD & pG - if len(pInt) == 0: - return 0 - return pInt.area() - - def compute_ap(confList, matchList, numGtCare): - correct = 0 - AP = 0 - if len(confList) > 0: - confList = np.array(confList) - matchList = np.array(matchList) - sorted_ind = np.argsort(-confList) - confList = confList[sorted_ind] - matchList = matchList[sorted_ind] - for n in range(len(confList)): - match = matchList[n] - if match: - correct += 1 - AP += float(correct) / (n + 1) - - if numGtCare > 0: - AP /= numGtCare - - return AP - - perSampleMetrics = {} - - matchedSum = 0 - - Rectangle = namedtuple('Rectangle', 'xmin ymin xmax ymax') - - gt = rrc_evaluation_funcs.load_folder_file(gtFilePath, evaluationParams['GT_SAMPLE_NAME_2_ID']) - subm = rrc_evaluation_funcs.load_folder_file(submFilePath, evaluationParams['DET_SAMPLE_NAME_2_ID'], True) - - numGlobalCareGt = 0; - numGlobalCareDet = 0; - - arrGlobalConfidences = []; - arrGlobalMatches = []; - - for resFile in gt: - - gtFile = gt[resFile] # rrc_evaluation_funcs.decode_utf8(gt[resFile]) - recall = 0 - precision = 0 - hmean = 0 - - detMatched = 0 - - iouMat = np.empty([1, 1]) - - gtPols = [] - detPols = [] - - gtPolPoints = [] - detPolPoints = [] - - # Array of Ground Truth Polygons' keys marked as don't Care - gtDontCarePolsNum = [] - # Array of Detected Polygons' matched with a don't Care GT - detDontCarePolsNum = [] - - pairs = [] - detMatchedNums = [] - - arrSampleConfidences = []; - arrSampleMatch = []; - sampleAP = 0; - - evaluationLog = "" - - pointsList, _, transcriptionsList = rrc_evaluation_funcs.get_tl_line_values_from_file_contents(gtFile, - evaluationParams[ - 'CRLF'], - evaluationParams[ - 'LTRB'], - True, False) - for n in range(len(pointsList)): - points = pointsList[n] - transcription = transcriptionsList[n] - dontCare = transcription == "###" - if evaluationParams['LTRB']: - gtRect = Rectangle(*points) - gtPol = rectangle_to_polygon(gtRect) - else: - gtPol = polygon_from_points(points) - gtPols.append(gtPol) - gtPolPoints.append(points) - if dontCare: - gtDontCarePolsNum.append(len(gtPols) - 1) - - evaluationLog += "GT polygons: " + str(len(gtPols)) + ( - " (" + str(len(gtDontCarePolsNum)) + " don't care)\n" if len(gtDontCarePolsNum) > 0 else "\n") - - if resFile in subm: - - detFile = subm[resFile] # rrc_evaluation_funcs.decode_utf8(subm[resFile]) - - pointsList, confidencesList, _ = rrc_evaluation_funcs.get_tl_line_values_from_file_contents(detFile, - evaluationParams[ - 'CRLF'], - evaluationParams[ - 'LTRB'], - False, - evaluationParams[ - 'CONFIDENCES']) - for n in range(len(pointsList)): - points = pointsList[n] - - if evaluationParams['LTRB']: - detRect = Rectangle(*points) - detPol = rectangle_to_polygon(detRect) - else: - detPol = polygon_from_points(points) - detPols.append(detPol) - detPolPoints.append(points) - if len(gtDontCarePolsNum) > 0: - for dontCarePol in gtDontCarePolsNum: - dontCarePol = gtPols[dontCarePol] - intersected_area = get_intersection(dontCarePol, detPol) - pdDimensions = detPol.area() - precision = 0 if pdDimensions == 0 else intersected_area / pdDimensions - if (precision > evaluationParams['AREA_PRECISION_CONSTRAINT']): - detDontCarePolsNum.append(len(detPols) - 1) - break - - evaluationLog += "DET polygons: " + str(len(detPols)) + ( - " (" + str(len(detDontCarePolsNum)) + " don't care)\n" if len(detDontCarePolsNum) > 0 else "\n") - - if len(gtPols) > 0 and len(detPols) > 0: - # Calculate IoU and precision matrixs - outputShape = [len(gtPols), len(detPols)] - iouMat = np.empty(outputShape) - gtRectMat = np.zeros(len(gtPols), np.int8) - detRectMat = np.zeros(len(detPols), np.int8) - for gtNum in range(len(gtPols)): - for detNum in range(len(detPols)): - pG = gtPols[gtNum] - pD = detPols[detNum] - iouMat[gtNum, detNum] = get_intersection_over_union(pD, pG) - - for gtNum in range(len(gtPols)): - for detNum in range(len(detPols)): - if gtRectMat[gtNum] == 0 and detRectMat[ - detNum] == 0 and gtNum not in gtDontCarePolsNum and detNum not in detDontCarePolsNum: - if iouMat[gtNum, detNum] > evaluationParams['IOU_CONSTRAINT']: - gtRectMat[gtNum] = 1 - detRectMat[detNum] = 1 - detMatched += 1 - pairs.append({'gt': gtNum, 'det': detNum}) - detMatchedNums.append(detNum) - evaluationLog += "Match GT #" + str(gtNum) + " with Det #" + str(detNum) + "\n" - - if evaluationParams['CONFIDENCES']: - for detNum in range(len(detPols)): - if detNum not in detDontCarePolsNum: - # we exclude the don't care detections - match = detNum in detMatchedNums - - arrSampleConfidences.append(confidencesList[detNum]) - arrSampleMatch.append(match) - - arrGlobalConfidences.append(confidencesList[detNum]); - arrGlobalMatches.append(match); - - numGtCare = (len(gtPols) - len(gtDontCarePolsNum)) - numDetCare = (len(detPols) - len(detDontCarePolsNum)) - if numGtCare == 0: - recall = float(1) - precision = float(0) if numDetCare > 0 else float(1) - sampleAP = precision - else: - recall = float(detMatched) / numGtCare - precision = 0 if numDetCare == 0 else float(detMatched) / numDetCare - if evaluationParams['CONFIDENCES'] and evaluationParams['PER_SAMPLE_RESULTS']: - sampleAP = compute_ap(arrSampleConfidences, arrSampleMatch, numGtCare) - - hmean = 0 if (precision + recall) == 0 else 2.0 * precision * recall / (precision + recall) - - matchedSum += detMatched - numGlobalCareGt += numGtCare - numGlobalCareDet += numDetCare - - if evaluationParams['PER_SAMPLE_RESULTS']: - perSampleMetrics[resFile] = { - 'precision': precision, - 'recall': recall, - 'hmean': hmean, - 'pairs': pairs, - 'AP': sampleAP, - 'iouMat': [] if len(detPols) > 100 else iouMat.tolist(), - 'gtPolPoints': gtPolPoints, - 'detPolPoints': detPolPoints, - 'gtDontCare': gtDontCarePolsNum, - 'detDontCare': detDontCarePolsNum, - 'evaluationParams': evaluationParams, - 'evaluationLog': evaluationLog - } - - # Compute MAP and MAR - AP = 0 - if evaluationParams['CONFIDENCES']: - AP = compute_ap(arrGlobalConfidences, arrGlobalMatches, numGlobalCareGt) - - methodRecall = 0 if numGlobalCareGt == 0 else float(matchedSum) / numGlobalCareGt - methodPrecision = 0 if numGlobalCareDet == 0 else float(matchedSum) / numGlobalCareDet - methodHmean = 0 if methodRecall + methodPrecision == 0 else 2 * methodRecall * methodPrecision / ( - methodRecall + methodPrecision) - - methodMetrics = {'precision': methodPrecision, 'recall': methodRecall, 'hmean': methodHmean, 'AP': AP} - - resDict = {'calculated': True, 'Message': '', 'method': methodMetrics, 'per_sample': perSampleMetrics} - - return resDict; - - -def cal_recall_precison_f1(gt_path, result_path, show_result=False): - p = {'g': gt_path, 's': result_path} - result = rrc_evaluation_funcs.main_evaluation(p, default_evaluation_params, validate_data, evaluate_method, - show_result) - return result['method'] \ No newline at end of file diff --git a/tools/det_infer.py b/tools/det_infer.py deleted file mode 100644 index bc9348e..0000000 --- a/tools/det_infer.py +++ /dev/null @@ -1,192 +0,0 @@ -#-*- coding:utf-8 _*- -""" -@author:fxw -@file: det_infer.py -@time: 2020/08/20 -""" -import os -import sys -sys.path.append('./') - -import cv2 -import torch -import argparse -import yaml -from PIL import Image -import numpy as np -from tqdm import tqdm -import onnxruntime -import torchvision.transforms as transforms -from ptocr.utils.util_function import create_module,resize_image_batch -from ptocr.utils.util_function import create_process_obj,create_dir,load_model -from script.onnx_to_tensorrt import build_engine,allocate_buffers,do_inference - -def config_load(args): - stream = open(args.config, 'r', encoding='utf-8') - config = yaml.load(stream,Loader=yaml.FullLoader) - config['infer']['model_path'] = args.model_path - config['infer']['path'] = args.img_path - config['infer']['save_path'] = args.result_save_path - config['infer']['onnx_path'] = args.onnx_path - config['infer']['trt_path'] = args.trt_path - config['testload']['add_padding'] = args.add_padding - config['testload']['test_size'] = args.max_size - config['testload']['batch_size'] = args.batch_size - return config - - -def get_batch_files(path,img_files,batch_size=3): - img_files = np.array(img_files) - num = len(img_files)//batch_size - batch_imgs = [] - batch_img_names = [] - for i in range(num): - files = img_files[batch_size*i:batch_size*(i+1)] - img = [cv2.imread(os.path.join(path,img_file)) for img_file in files] - img_names = [img_file.split('.')[0] for img_file in files] - batch_imgs.append(img) - batch_img_names.append(img_names) - files = img_files[batch_size*(num):len(img_files)] - if(len(files)!=0): - img = [cv2.imread(os.path.join(path, img_file)) for img_file in files] - img_names = [img_file.split('.')[0] for img_file in files] - batch_imgs.append(img) - batch_img_names.append(img_names) - return batch_imgs,batch_img_names - - -def get_img(ori_imgs,config): - imgs = [] - scales = [] - for ori_img in ori_imgs: - img,scale = resize_image_batch(ori_img,config['base']['algorithm'],config['testload']['test_size'],add_padding = config['testload']['add_padding']) - img = Image.fromarray(img).convert('RGB') - img = transforms.ToTensor()(img) - img = transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])(img).unsqueeze(0) - imgs.append(img) - scales.append(scale) - return torch.cat(imgs,0),scales - -class TestProgram(): - def __init__(self,config): - super(TestProgram,self).__init__() - self.config = config - if(config['infer']['trt_path'] is not None): - engine = build_engine(config['infer']['onnx_path'],config['infer']['trt_path'],config['testload']['batch_size']) - self.inputs, self.outputs, self.bindings, self.stream = allocate_buffers(engine) - self.context = engine.create_execution_context() - self.infer_type = 'trt_infer' - - elif(config['infer']['onnx_path'] is not None): - self.model = onnxruntime.InferenceSession(config['infer']['onnx_path']) - self.infer_type = 'onnx_infer' - else: - model = create_module(config['architectures']['model_function'])(config) - model = load_model(model,config['infer']['model_path']) - if torch.cuda.is_available(): - model = model.cuda() - self.model = model - self.model.eval() - self.infer_type = 'torch_infer' - - img_process = create_module(config['postprocess']['function'])(config) - self.img_process = img_process - - - def infer_img(self,ori_imgs): - img,scales = get_img(ori_imgs,self.config) - if(self.infer_type == 'torch_infer'): - if torch.cuda.is_available(): - img = img.cuda() - with torch.no_grad(): - out = self.model(img) - elif(self.infer_type == 'onnx_infer'): - out = self.model.run(['out'], {'input': img.numpy()}) - out = torch.Tensor(out[0]) - else: - self.inputs[0].host = img.numpy() - output = do_inference(self.context, bindings=self.bindings, inputs=self.inputs, outputs=self.outputs, stream=self.stream,batch_size=int(self.config['testload']['batch_size'])) - test_size = self.config['testload']['test_size'] - if self.config['base']['algorithm']=='DB': - out = output[0].reshape(int(args.batch_size),1,test_size,test_size) - elif self.config['base']['algorithm']=='PAN': - out = output[0].reshape(int(args.batch_size),6,test_size,test_size) - elif self.config['base']['algorithm']=='PSE': - out = output[0].reshape(int(args.batch_size),7,test_size,test_size) - - out = torch.Tensor(out) - -# import pdb -# pdb.set_trace() - - if isinstance(out,dict): - img_num = out['f_score'].shape[0] - else: - img_num = out.shape[0] - - if(self.config['base']['algorithm']=='SAST'): - scales = [(scale[0],scale[1],ori_imgs[i].shape[0],ori_imgs[i].shape[1]) for scale in scales] - - out = create_process_obj(self.config['base']['algorithm'],out) - bbox_batch, score_batch = self.img_process(out, scales) - return bbox_batch,score_batch - -def InferOneImg(bin,img,image_name,save_path): - bbox_batch, score_batch = bin.infer_img(img) - for i in range(len(bbox_batch)): - img_show = img[i].copy() - with open(os.path.join(save_path, 'result_txt', 'res_' + image_name[i] + '.txt'), 'w+', encoding='utf-8') as fid_res: - bboxes = bbox_batch[i] - for bbox in bboxes: - bbox = bbox.reshape(-1, 2).astype(np.int) - img_show = cv2.drawContours(img_show, [bbox], -1, (0, 255, 0), 1) - bbox_str = [str(x) for x in bbox.reshape(-1)] - bbox_str = ','.join(bbox_str) + '\n' - fid_res.write(bbox_str) - cv2.imwrite(os.path.join(save_path, 'result_img', image_name[i] + '.jpg'), img_show) - -def InferImage(config): - path = config['infer']['path'] - save_path = config['infer']['save_path'] - test_bin = TestProgram(config) - create_dir(save_path) - create_dir(os.path.join(save_path,'result_img')) - create_dir(os.path.join(save_path,'result_txt')) - if os.path.isdir(path): - files = os.listdir(path) - batch_imgs,batch_img_names = get_batch_files(path,files,batch_size=config['testload']['batch_size']) -# print(batch_img_names) - bar = tqdm(total=len(batch_imgs)) - for i in range(len(batch_imgs)): - bar.update(1) - InferOneImg(test_bin, batch_imgs[i],batch_img_names[i], save_path) - bar.close() - - else: - image_name = path.split('/')[-1].split('.')[0] - img = cv2.imread(path) - InferOneImg(test_bin, [img], [image_name], save_path) - - -if __name__ == "__main__": - -# stream = open('./config/det_PSE_mobilev3.yaml', 'r', encoding='utf-8') -# stream = open('./config/det_PSE_resnet50.yaml', 'r', encoding='utf-8') -# stream = open('./config/det_PAN_mobilev3.yaml', 'r', encoding='utf-8') -# stream = open('./config/det_DB_mobilev3.yaml', 'r', encoding='utf-8') -# stream = open('./config/det_SAST_resnet50.yaml', 'r', encoding='utf-8') -# stream = open('./config/det_DB_resnet50.yaml', 'r', encoding='utf-8') - - parser = argparse.ArgumentParser(description='Hyperparams') - parser.add_argument('--config', help='config file path') - parser.add_argument('--model_path', nargs='?', type=str, default=None) - parser.add_argument('--onnx_path', nargs='?', type=str, default=None) - parser.add_argument('--trt_path', nargs='?', type=str, default=None) - parser.add_argument('--img_path', nargs='?', type=str, default=None) - parser.add_argument('--result_save_path', nargs='?', type=str, default=None) - parser.add_argument('--max_size', nargs='?', type=int, default=None) - parser.add_argument('--batch_size', nargs='?', type=int, default=None) - parser.add_argument('--add_padding', action='store_true', default=False) - args = parser.parse_args() - config = config_load(args) - InferImage(config) \ No newline at end of file diff --git a/tools/det_sast.py b/tools/det_sast.py deleted file mode 100644 index 82087a4..0000000 --- a/tools/det_sast.py +++ /dev/null @@ -1,238 +0,0 @@ -# -*- coding:utf-8 _*- -""" -@author:fxw -@file: det_train.py -@time: 2020/08/07 -""" -import sys -sys.path.append('./') -import cv2 -import torch -import os -import numpy as np -from tqdm import tqdm -np.seterr(divide='ignore', invalid='ignore') -import yaml -import torch.utils.data -from ptocr.utils.util_function import create_module, create_loss_bin, \ -set_seed,save_checkpoint,create_dir -from ptocr.utils.metrics import runningScore -from ptocr.utils.logger import Logger -from ptocr.utils.cal_iou_acc import cal_DB,cal_PAN_PSE -from tools.cal_rescall.script import cal_recall_precison_f1 -from tools.cal_rescall.cal_det import cal_det_metrics -from ptocr.dataloader.DetLoad.SASTProcess_ori import alignCollate -from ptocr.utils.util_function import create_process_obj -torch.backends.cudnn.enabled = False - - -GLOBAL_WORKER_ID = None -GLOBAL_SEED = 2020 - -def worker_init_fn(worker_id): - global GLOBAL_WORKER_ID - GLOBAL_WORKER_ID = worker_id - set_seed(GLOBAL_SEED + worker_id) - -def ModelTrain(train_data_loader, model, criterion, optimizer, loss_bin, config, epoch): - if(config['base']['algorithm']=='DB' or config['base']['algorithm']=='SAST'): - running_metric_text = runningScore(2) - else: - running_metric_text = runningScore(2) - running_metric_kernel = runningScore(2) - for batch_idx, data in enumerate(train_data_loader): - if(data is None): - continue - pre_batch, gt_batch = model(data) - loss, metrics = criterion(pre_batch, gt_batch) - optimizer.zero_grad() - loss.backward() - optimizer.step() - cv2.imwrite('pre.jpg',pre_batch['f_score'][0,0].cpu().detach().numpy()*255) - for key in loss_bin.keys(): - if (key in metrics.keys()): - loss_bin[key].loss_add(metrics[key].item()) - else: - loss_bin[key].loss_add(loss.item()) - if(config['base']['algorithm']=='DB'): - iou,acc = cal_DB(pre_batch['binary'], gt_batch['gt'], gt_batch['mask'], running_metric_text) - elif(config['base']['algorithm']=='SAST'): - iou, acc = cal_DB(pre_batch['f_score'], gt_batch['input_score'], gt_batch['input_mask'], running_metric_text) - else: - iou,acc = cal_PAN_PSE(pre_batch['pre_kernel'], gt_batch['gt_kernel'], pre_batch['pre_text'], gt_batch['gt_text'], - gt_batch['train_mask'], running_metric_text,running_metric_kernel) - - if (batch_idx % config['base']['show_step'] == 0): - log = '({}/{}/{}/{}) | ' \ - .format(epoch, config['base']['n_epoch'], batch_idx, len(train_data_loader)) - bin_keys = list(loss_bin.keys()) - - for i in range(len(bin_keys)): - log += bin_keys[i] + ':{:.4f}'.format(loss_bin[bin_keys[i]].loss_mean()) + ' | ' - - log += 'ACC:{:.4f}'.format(acc) + ' | ' - log += 'IOU:{:.4f}'.format(iou) + ' | ' - log += 'lr:{:.8f}'.format(optimizer.param_groups[0]['lr']) - print(log) - loss_write = [] - for key in list(loss_bin.keys()): - loss_write.append(loss_bin[key].loss_mean()) - loss_write.extend([acc,iou]) - return loss_write - - -def ModelEval(test_dataset, test_data_loader, model, imgprocess, checkpoints,config): - bar = tqdm(total=len(test_data_loader)) - for batch_idx, (imgs, ori_imgs) in enumerate(test_data_loader): - bar.update(1) - if torch.cuda.is_available(): - imgs = imgs.cuda() - with torch.no_grad(): - out = model(imgs) - cv2.imwrite('pre_score.jpg',out['f_score'][0,0].cpu().detach().numpy()*255) - scales = [] - for i in range(out['f_score'].shape[0]): - if(config['base']['algorithm']=='SAST'): - scale = ((out['f_score'].shape[2]*4)/ori_imgs[i].shape[0],(out['f_score'].shape[3]*4)/ori_imgs[i].shape[1] ,ori_imgs[i].shape[0],ori_imgs[i].shape[1]) - else: - scale = (ori_imgs[i].shape[1] * 1.0 / out.shape[3], ori_imgs[i].shape[0] * 1.0 / out.shape[2]) - scales.append(scale) - out = create_process_obj(config['base']['algorithm'], out) - bbox_batch, score_batch = imgprocess(out, scales) - - if(config['base']['algorithm']=='SAST'): - out = out['f_score'] - - for i in range(len(bbox_batch)): - bboxes = bbox_batch[i] - img_show = ori_imgs[i].numpy().copy() - idx = i + out.shape[0] * batch_idx - image_name = test_dataset.img_list[idx].split('/')[-1].split('.')[0] # windows use \\ not / - with open(os.path.join(checkpoints, 'val', 'res_txt', 'res_' + image_name + '.txt'), 'w+', - encoding='utf-8') as fid_res: - for bbox in bboxes: - bbox = bbox.reshape(-1, 2).astype(np.int) - img_show = cv2.drawContours(img_show, [bbox], -1, (0, 255, 0), 1) - bbox_str = [str(x) for x in bbox.reshape(-1)] - bbox_str = ','.join(bbox_str) + '\n' - fid_res.write(bbox_str) - cv2.imwrite(os.path.join(checkpoints, 'val', 'res_img', image_name + '.jpg'), img_show) - bar.close() -# result_dict = cal_recall_precison_f1(config['testload']['test_gt_path'],os.path.join(checkpoints, 'val', 'res_txt')) - result_dict = cal_det_metrics(config['testload']['test_gt_path'],os.path.join(checkpoints, 'val', 'res_txt')) - return result_dict['recall'],result_dict['precision'],result_dict['hmean'] - -def TrainValProgram(config): - os.environ["CUDA_VISIBLE_DEVICES"] = config['base']['gpu_id'] - - create_dir(config['base']['checkpoints']) - checkpoints = os.path.join(config['base']['checkpoints'], - "ag_%s_bb_%s_he_%s_bs_%d_ep_%d" % (config['base']['algorithm'], - config['backbone']['function'].split(',')[-1], - config['head']['function'].split(',')[-1], - config['trainload']['batch_size'], - config['base']['n_epoch'])) - create_dir(checkpoints) - - model = create_module(config['architectures']['model_function'])(config) - criterion = create_module(config['architectures']['loss_function'])(config) - train_dataset = create_module(config['trainload']['function'])(config) - test_dataset = create_module(config['testload']['function'])(config) - optimizer = create_module(config['optimizer']['function'])(config, model) - optimizer_decay = create_module(config['optimizer_decay']['function']) - img_process = create_module(config['postprocess']['function'])(config) - - train_data_loader = torch.utils.data.DataLoader( - train_dataset, - batch_size=config['trainload']['batch_size'], - shuffle=True, - num_workers=config['trainload']['num_workers'], - worker_init_fn = worker_init_fn, - collate_fn=alignCollate(train_dataset), - drop_last=True, - pin_memory=True) - - test_data_loader = torch.utils.data.DataLoader( - test_dataset, - batch_size=config['testload']['batch_size'], - shuffle=False, - num_workers=config['testload']['num_workers'], - drop_last=True, - pin_memory=True) - - loss_bin = create_loss_bin(config['base']['algorithm']) - - if torch.cuda.is_available(): - if (len(config['base']['gpu_id'].split(',')) > 1): - model = torch.nn.DataParallel(model).cuda() - else: - model = model.cuda() - criterion = criterion.cuda() - - start_epoch = 0 - rescall, precision, hmean = 0,0,0 - best_rescall,best_precision ,best_hmean= 0,0,0 - - if config['base']['restore']: - print('Resuming from checkpoint.') - assert os.path.isfile(config['base']['restore_file']), 'Error: no checkpoint file found!' - checkpoint = torch.load(config['base']['restore_file']) - start_epoch = checkpoint['epoch'] - model.load_state_dict(checkpoint['state_dict']) - optimizer.load_state_dict(checkpoint['optimizer']) - best_rescall = checkpoint['rescall'] - best_precision = checkpoint['precision'] - best_hmean = checkpoint['hmean'] - log_write = Logger(os.path.join(checkpoints, 'log.txt'), title=config['base']['algorithm'], resume=True) - else: - print('Training from scratch.') - log_write = Logger(os.path.join(checkpoints, 'log.txt'), title=config['base']['algorithm']) - title = list(loss_bin.keys()) - title.extend(['piexl_acc','piexl_iou','t_rescall','t_precision','t_hmean','b_rescall','b_precision','b_hmean']) - log_write.set_names(title) - - for epoch in range(start_epoch,config['base']['n_epoch']): - train_dataset.gen_train_img() - model.train() - optimizer_decay(config, optimizer, epoch) - loss_write = ModelTrain(train_data_loader, model, criterion, optimizer, loss_bin, config, epoch) - if(epoch >= config['base']['start_val']): - create_dir(os.path.join(checkpoints,'val')) - create_dir(os.path.join(checkpoints,'val','res_img')) - create_dir(os.path.join(checkpoints,'val','res_txt')) - model.eval() - rescall,precision,hmean = ModelEval(test_dataset, test_data_loader, model, img_process, checkpoints,config) - print('rescall:',rescall,'precision',precision,'hmean',hmean) - if (hmean > best_hmean): - save_checkpoint({ - 'epoch': epoch + 1, - 'state_dict': model.state_dict(), - 'lr': config['optimizer']['base_lr'], - 'optimizer': optimizer.state_dict(), - 'hmean': hmean, - 'rescall': rescall, - 'precision': precision - }, checkpoints, config['base']['algorithm'] + '_best' + '.pth.tar') - best_hmean = hmean - best_precision = precision - best_rescall = rescall - - loss_write.extend([rescall,precision,hmean,best_rescall,best_precision,best_hmean]) - log_write.append(loss_write) - for key in loss_bin.keys(): - loss_bin[key].loss_clear() - if epoch%config['base']['save_epoch'] ==0: - save_checkpoint({ - 'epoch': epoch + 1, - 'state_dict': model.state_dict(), - 'lr': config['optimizer']['base_lr'], - 'optimizer': optimizer.state_dict(), - },checkpoints,config['base']['algorithm']+'_'+str(epoch)+'.pth.tar') - - -if __name__ == "__main__": - stream = open('./config/det_SAST_resnet50_ori_dataload.yaml', 'r', encoding='utf-8') -# stream = open('./config/det_SAST_resnet50_3*3_ori_dataload.yaml', 'r', encoding='utf-8') - - config = yaml.load(stream,Loader=yaml.FullLoader) - TrainValProgram(config) \ No newline at end of file diff --git a/tools/det_train.py b/tools/det_train.py deleted file mode 100644 index d22ad67..0000000 --- a/tools/det_train.py +++ /dev/null @@ -1,307 +0,0 @@ -# -*- coding:utf-8 _*- -""" -@author:fxw -@file: det_train.py -@time: 2020/08/07 -""" -import sys -sys.path.append('./') -import cv2 -import torch -import os -import random -import numpy as np -from tqdm import tqdm -import argparse -np.seterr(divide='ignore', invalid='ignore') -import yaml -import torch.utils.data -from ptocr.utils.util_function import create_module, create_loss_bin, \ -set_seed,save_checkpoint,create_dir -from ptocr.utils.metrics import runningScore -from ptocr.utils.logger import Logger -from ptocr.utils.cal_iou_acc import cal_DB,cal_PAN_PSE -from tools.cal_rescall.script import cal_recall_precison_f1 -# from ptocr.dataloader.DetLoad.SASTProcess import alignCollate -from ptocr.utils.util_function import create_process_obj,merge_config,load_model -from ptocr.utils.prune_script import updateBN,load_prune_model -from ptocr.utils.gen_teacher_model import GetTeacherModel,DistilLoss - - -GLOBAL_WORKER_ID = None -GLOBAL_SEED = 123456 - -torch.manual_seed(GLOBAL_SEED) -torch.cuda.manual_seed(GLOBAL_SEED) -torch.cuda.manual_seed_all(GLOBAL_SEED) -np.random.seed(GLOBAL_SEED) -random.seed(GLOBAL_SEED) - -def worker_init_fn(worker_id): - global GLOBAL_WORKER_ID - GLOBAL_WORKER_ID = worker_id - set_seed(GLOBAL_SEED + worker_id) - -def ModelTrain(train_data_loader,t_model,t_criterion, model, criterion, optimizer, loss_bin, args,config, epoch): - if(config['base']['algorithm']=='DB' or config['base']['algorithm']=='SAST'): - running_metric_text = runningScore(2) - else: - running_metric_text = runningScore(2) - running_metric_kernel = runningScore(2) - for batch_idx, data in enumerate(train_data_loader): - if(data is None): - continue - pre_batch, gt_batch = model(data) - - if(t_model is not None): - with torch.no_grad(): - t_pre_batch,_ = t_model(data) - distil_loss = t_criterion(pre_batch,t_pre_batch) - - loss, metrics = criterion(pre_batch, gt_batch) - - if(t_model is not None): - loss = args.t_ratio*loss+(1-args.t_ratio)*distil_loss - metrics['loss_distil'] = distil_loss - - optimizer.zero_grad() - loss.backward() - if(args.sr_lr is not None): - updateBN(model,args) - optimizer.step() - - for key in loss_bin.keys(): - if (key in metrics.keys()): - loss_bin[key].loss_add(metrics[key].item()) - else: - loss_bin[key].loss_add(loss.item()) - if(config['base']['algorithm']=='DB'): - iou,acc = cal_DB(pre_batch['binary'], gt_batch['gt'], gt_batch['mask'], running_metric_text) - elif(config['base']['algorithm']=='SAST'): - iou, acc = cal_DB(pre_batch['f_score'], gt_batch['input_score'], gt_batch['input_mask'], running_metric_text) - else: - iou,acc = cal_PAN_PSE(pre_batch['pre_kernel'], gt_batch['gt_kernel'], pre_batch['pre_text'], gt_batch['gt_text'], - gt_batch['train_mask'], running_metric_text,running_metric_kernel) - - if (batch_idx % config['base']['show_step'] == 0): - log = '({}/{}/{}/{}) | ' \ - .format(epoch, config['base']['n_epoch'], batch_idx, len(train_data_loader)) - bin_keys = list(loss_bin.keys()) - - for i in range(len(bin_keys)): - log += bin_keys[i] + ':{:.4f}'.format(loss_bin[bin_keys[i]].loss_mean()) + ' | ' - - log += 'ACC:{:.4f}'.format(acc) + ' | ' - log += 'IOU:{:.4f}'.format(iou) + ' | ' - log += 'lr:{:.8f}'.format(optimizer.param_groups[0]['lr']) - print(log) - loss_write = [] - for key in list(loss_bin.keys()): - loss_write.append(loss_bin[key].loss_mean()) - loss_write.extend([acc,iou]) - return loss_write - - -def ModelEval(test_dataset, test_data_loader, model, imgprocess, checkpoints,config): - bar = tqdm(total=len(test_data_loader)) - for batch_idx, (imgs, ori_imgs) in enumerate(test_data_loader): - bar.update(1) - if torch.cuda.is_available(): - imgs = imgs.cuda() - with torch.no_grad(): - out = model(imgs) - scales = [] - if isinstance(out,dict): - img_num = out['f_score'].shape[0] - else: - img_num = out.shape[0] - for i in range(img_num): - if(config['base']['algorithm']=='SAST'): - scale = ((out['f_score'].shape[2]*4)/ori_imgs[i].shape[0],(out['f_score'].shape[3]*4)/ori_imgs[i].shape[1] ,ori_imgs[i].shape[0],ori_imgs[i].shape[1]) - else: - scale = (ori_imgs[i].shape[1] * 1.0 / out.shape[3], ori_imgs[i].shape[0] * 1.0 / out.shape[2]) - scales.append(scale) - out = create_process_obj(config['base']['algorithm'], out) - bbox_batch, score_batch = imgprocess(out, scales) - - if(config['base']['algorithm']=='SAST'): - out = out['f_score'] - - for i in range(len(bbox_batch)): - bboxes = bbox_batch[i] - img_show = ori_imgs[i].numpy().copy() - idx = i + out.shape[0] * batch_idx - image_name = test_dataset.img_list[idx].split('/')[-1].split('.')[0] # windows use \\ not / - with open(os.path.join(checkpoints, 'val', 'res_txt', 'res_' + image_name + '.txt'), 'w+', - encoding='utf-8') as fid_res: - for bbox in bboxes: - bbox = bbox.reshape(-1, 2).astype(np.int) - img_show = cv2.drawContours(img_show, [bbox], -1, (0, 255, 0), 1) - bbox_str = [str(x) for x in bbox.reshape(-1)] - bbox_str = ','.join(bbox_str) + '\n' - fid_res.write(bbox_str) - cv2.imwrite(os.path.join(checkpoints, 'val', 'res_img', image_name + '.jpg'), img_show) - bar.close() - result_dict = cal_recall_precison_f1(config['testload']['test_gt_path'],os.path.join(checkpoints, 'val', 'res_txt')) - return result_dict['recall'],result_dict['precision'],result_dict['hmean'] - -def TrainValProgram(args): - - config = yaml.load(open(args.config, 'r', encoding='utf-8'),Loader=yaml.FullLoader) - config = merge_config(config,args) - - os.environ["CUDA_VISIBLE_DEVICES"] = config['base']['gpu_id'] - create_dir(config['base']['checkpoints']) - checkpoints = os.path.join(config['base']['checkpoints'], - "ag_%s_bb_%s_he_%s_bs_%d_ep_%d_%s" % (config['base']['algorithm'], - config['backbone']['function'].split(',')[-1], - config['head']['function'].split(',')[-1], - config['trainload']['batch_size'], - config['base']['n_epoch'], - args.log_str)) - create_dir(checkpoints) - - model = create_module(config['architectures']['model_function'])(config) - criterion = create_module(config['architectures']['loss_function'])(config) - train_dataset = create_module(config['trainload']['function'])(config) - test_dataset = create_module(config['testload']['function'])(config) - optimizer = create_module(config['optimizer']['function'])(config, model) - optimizer_decay = create_module(config['optimizer_decay']['function']) - img_process = create_module(config['postprocess']['function'])(config) - - if args.t_config is not None: - t_model = GetTeacherModel(args) - distil_loss = DistilLoss() - if torch.cuda.is_available(): - distil_loss = distil_loss.cuda() - - train_data_loader = torch.utils.data.DataLoader( - train_dataset, - batch_size=config['trainload']['batch_size'], - shuffle=True, - num_workers=config['trainload']['num_workers'], - worker_init_fn = worker_init_fn, - drop_last=True, - pin_memory=True) - - test_data_loader = torch.utils.data.DataLoader( - test_dataset, - batch_size=config['testload']['batch_size'], - shuffle=False, - num_workers=config['testload']['num_workers'], - drop_last=True, - pin_memory=True) - - use_distil = False - if args.t_config is not None: - use_distil = True - loss_bin = create_loss_bin(config['base']['algorithm'],use_distil) - - if torch.cuda.is_available(): - if (len(config['base']['gpu_id'].split(',')) > 1): - model = torch.nn.DataParallel(model).cuda() - else: - model = model.cuda() - criterion = criterion.cuda() - - start_epoch = 0 - rescall, precision, hmean = 0,0,0 - best_rescall,best_precision ,best_hmean= 0,0,0 - - if args.pruned_model_dict_path is not None: - print('finetune the pruend model.') - model = load_prune_model(model,args.prune_model_path,args.pruned_model_dict_path,args.prune_type) - log_write = Logger(os.path.join(checkpoints, 'log.txt'), title=config['base']['algorithm']) - title = list(loss_bin.keys()) - title.extend(['piexl_acc','piexl_iou','t_rescall','t_precision','t_hmean','b_rescall','b_precision','b_hmean']) - log_write.set_names(title) - - elif config['base']['restore']: - print('Resuming from checkpoint.') - assert os.path.isfile(config['base']['restore_file']), 'Error: no checkpoint file found!' - checkpoint = torch.load(config['base']['restore_file']) - start_epoch = checkpoint['epoch'] - model.load_state_dict(checkpoint['state_dict']) - optimizer.load_state_dict(checkpoint['optimizer']) - best_rescall = checkpoint['rescall'] - best_precision = checkpoint['precision'] - best_hmean = checkpoint['hmean'] - log_write = Logger(os.path.join(checkpoints, 'log.txt'), title=config['base']['algorithm'], resume=True) - else: - print('Training from scratch.') - log_write = Logger(os.path.join(checkpoints, 'log.txt'), title=config['base']['algorithm']) - title = list(loss_bin.keys()) - title.extend(['piexl_acc','piexl_iou','t_rescall','t_precision','t_hmean','b_rescall','b_precision','b_hmean']) - log_write.set_names(title) - - if args.start_epoch is not None: - start_epoch = args.start_epoch - - for epoch in range(start_epoch,config['base']['n_epoch']): - model.train() - if args.t_config is not None: - t_model.train() - else: - t_model = None - distil_loss = None - optimizer_decay(config, optimizer, epoch) - loss_write = ModelTrain(train_data_loader,t_model,distil_loss,model, criterion, optimizer, loss_bin, args,config, epoch) - - if(epoch >= config['base']['start_val']): - create_dir(os.path.join(checkpoints,'val')) - create_dir(os.path.join(checkpoints,'val','res_img')) - create_dir(os.path.join(checkpoints,'val','res_txt')) - model.eval() - rescall,precision,hmean = ModelEval(test_dataset, test_data_loader, model, img_process, checkpoints,config) - print('rescall:',rescall,'precision',precision,'hmean',hmean) - if (hmean > best_hmean): - save_checkpoint({ - 'epoch': epoch + 1, - 'state_dict': model.state_dict(), - 'lr': config['optimizer']['base_lr'], - 'optimizer': optimizer.state_dict(), - 'hmean': hmean, - 'rescall': rescall, - 'precision': precision - }, checkpoints, config['base']['algorithm'] + '_best' + '.pth.tar') - best_hmean = hmean - best_precision = precision - best_rescall = rescall - - loss_write.extend([rescall,precision,hmean,best_rescall,best_precision,best_hmean]) - log_write.append(loss_write) - for key in loss_bin.keys(): - loss_bin[key].loss_clear() - if epoch%config['base']['save_epoch'] ==0: - save_checkpoint({ - 'epoch': epoch + 1, - 'state_dict': model.state_dict(), - 'lr': config['optimizer']['base_lr'], - 'optimizer': optimizer.state_dict(), - 'hmean': 0, - 'rescall': 0, - 'precision': 0 - },checkpoints,config['base']['algorithm']+'_'+str(epoch)+'.pth.tar') - - -if __name__ == "__main__": - parser = argparse.ArgumentParser(description='Hyperparams') - parser.add_argument('--config', help='config file path') - parser.add_argument('--t_config',default=None, help='config file path') - parser.add_argument('--t_model_path',default=None, help='teacher model path') - parser.add_argument('--t_ratio', nargs='?', type=float, default=0.5) - parser.add_argument('--log_str', help='log title') - parser.add_argument('--sr_lr', nargs='?', type=float, default=None) - - parser.add_argument('--pruned_model_dict_path', help='config file path',default=None) - parser.add_argument('--prune_type',type=str, help='prune type,total or backbone') - parser.add_argument('--prune_model_path', help='model file path') - - parser.add_argument('--n_epoch', nargs='?', type=int, default=600) - parser.add_argument('--start_epoch', nargs='?', type=int, default=None) - parser.add_argument('--start_val', nargs='?', type=int, default=400) - parser.add_argument('--base_lr', nargs='?', type=float, default=0.001) - parser.add_argument('--gpu_id', help='config file path') - - args = parser.parse_args() - TrainValProgram(args) \ No newline at end of file diff --git a/tools/det_train_qua.py b/tools/det_train_qua.py deleted file mode 100644 index df15ab8..0000000 --- a/tools/det_train_qua.py +++ /dev/null @@ -1,333 +0,0 @@ -# -*- coding:utf-8 _*- -""" -@author:fxw -@file: det_train.py -@time: 2020/08/07 -""" -import sys -sys.path.append('./') -import cv2 -import torch -import os -import random -import numpy as np -from tqdm import tqdm -import argparse -np.seterr(divide='ignore', invalid='ignore') -import yaml -import torch.utils.data -from ptocr.utils.util_function import create_module, create_loss_bin, \ -set_seed,save_checkpoint,create_dir -from ptocr.utils.metrics import runningScore -from ptocr.utils.logger import Logger -from ptocr.utils.cal_iou_acc import cal_DB,cal_PAN_PSE -from tools.cal_rescall.script import cal_recall_precison_f1 -# from ptocr.dataloader.DetLoad.SASTProcess import alignCollate -from ptocr.utils.util_function import create_process_obj,merge_config,load_model -from ptocr.utils.prune_script import updateBN,load_prune_model -from ptocr.utils.gen_teacher_model import GetTeacherModel,DistilLoss - - -GLOBAL_WORKER_ID = None -GLOBAL_SEED = 123456 - -torch.manual_seed(GLOBAL_SEED) -torch.cuda.manual_seed(GLOBAL_SEED) -torch.cuda.manual_seed_all(GLOBAL_SEED) -np.random.seed(GLOBAL_SEED) -random.seed(GLOBAL_SEED) - -def worker_init_fn(worker_id): - global GLOBAL_WORKER_ID - GLOBAL_WORKER_ID = worker_id - set_seed(GLOBAL_SEED + worker_id) - -def quantize_model(model, backend,convert = False): - if backend not in torch.backends.quantized.supported_engines: - raise RuntimeError("Quantized backend not supported ") - torch.backends.quantized.engine = backend - # Make sure that weight qconfig matches that of the serialized models - if backend == 'fbgemm': - model.qconfig = torch.quantization.QConfig( - activation=torch.quantization.default_observer, - weight=torch.quantization.default_per_channel_weight_observer) - elif backend == 'qnnpack': - model.qconfig = torch.quantization.QConfig( - activation=torch.quantization.default_observer, - weight=torch.quantization.default_weight_observer) - - model = torch.quantization.prepare_qat(model, inplace=True) - if(convert): - model = torch.quantization.convert(model, inplace=False) - return model - -def ModelTrain(train_data_loader,t_model,t_criterion, model, criterion, optimizer, loss_bin, args,config, epoch): - if(config['base']['algorithm']=='DB' or config['base']['algorithm']=='SAST'): - running_metric_text = runningScore(2) - else: - running_metric_text = runningScore(2) - running_metric_kernel = runningScore(2) - for batch_idx, data in enumerate(train_data_loader): - if(data is None): - continue - pre_batch, gt_batch = model(data) - - if(t_model is not None): - with torch.no_grad(): - t_pre_batch,_ = t_model(data) - distil_loss = t_criterion(pre_batch,t_pre_batch) - - loss, metrics = criterion(pre_batch, gt_batch) - - if(t_model is not None): - loss = args.t_ratio*loss+(1-args.t_ratio)*distil_loss - metrics['loss_distil'] = distil_loss - - optimizer.zero_grad() - loss.backward() - if(args.sr_lr is not None): - updateBN(model,args) - optimizer.step() - - for key in loss_bin.keys(): - if (key in metrics.keys()): - loss_bin[key].loss_add(metrics[key].item()) - else: - loss_bin[key].loss_add(loss.item()) - if(config['base']['algorithm']=='DB'): - iou,acc = cal_DB(pre_batch['binary'], gt_batch['gt'], gt_batch['mask'], running_metric_text) - elif(config['base']['algorithm']=='SAST'): - iou, acc = cal_DB(pre_batch['f_score'], gt_batch['input_score'], gt_batch['input_mask'], running_metric_text) - else: - iou,acc = cal_PAN_PSE(pre_batch['pre_kernel'], gt_batch['gt_kernel'], pre_batch['pre_text'], gt_batch['gt_text'], - gt_batch['train_mask'], running_metric_text,running_metric_kernel) - - if (batch_idx % config['base']['show_step'] == 0): - log = '({}/{}/{}/{}) | ' \ - .format(epoch, config['base']['n_epoch'], batch_idx, len(train_data_loader)) - bin_keys = list(loss_bin.keys()) - - for i in range(len(bin_keys)): - log += bin_keys[i] + ':{:.4f}'.format(loss_bin[bin_keys[i]].loss_mean()) + ' | ' - - log += 'ACC:{:.4f}'.format(acc) + ' | ' - log += 'IOU:{:.4f}'.format(iou) + ' | ' - log += 'lr:{:.8f}'.format(optimizer.param_groups[0]['lr']) - print(log) - loss_write = [] - for key in list(loss_bin.keys()): - loss_write.append(loss_bin[key].loss_mean()) - loss_write.extend([acc,iou]) - return loss_write - - -def ModelEval(test_dataset, test_data_loader, model, imgprocess, checkpoints,config): - bar = tqdm(total=len(test_data_loader)) - for batch_idx, (imgs, ori_imgs) in enumerate(test_data_loader): - bar.update(1) -# if torch.cuda.is_available(): -# imgs = imgs.cuda() - with torch.no_grad(): - out = model(imgs) - out = out['binary'] - scales = [] - if isinstance(out,dict): - img_num = out['f_score'].shape[0] - else: - img_num = out.shape[0] - for i in range(img_num): - if(config['base']['algorithm']=='SAST'): - scale = ((out['f_score'].shape[2]*4)/ori_imgs[i].shape[0],(out['f_score'].shape[3]*4)/ori_imgs[i].shape[1] ,ori_imgs[i].shape[0],ori_imgs[i].shape[1]) - else: - scale = (ori_imgs[i].shape[1] * 1.0 / out.shape[3], ori_imgs[i].shape[0] * 1.0 / out.shape[2]) - scales.append(scale) - out = create_process_obj(config['base']['algorithm'], out) - bbox_batch, score_batch = imgprocess(out, scales) - - if(config['base']['algorithm']=='SAST'): - out = out['f_score'] - - for i in range(len(bbox_batch)): - bboxes = bbox_batch[i] - img_show = ori_imgs[i].numpy().copy() - idx = i + out.shape[0] * batch_idx - image_name = test_dataset.img_list[idx].split('/')[-1].split('.')[0] # windows use \\ not / - with open(os.path.join(checkpoints, 'val', 'res_txt', 'res_' + image_name + '.txt'), 'w+', - encoding='utf-8') as fid_res: - for bbox in bboxes: - bbox = bbox.reshape(-1, 2).astype(np.int) - img_show = cv2.drawContours(img_show, [bbox], -1, (0, 255, 0), 1) - bbox_str = [str(x) for x in bbox.reshape(-1)] - bbox_str = ','.join(bbox_str) + '\n' - fid_res.write(bbox_str) - cv2.imwrite(os.path.join(checkpoints, 'val', 'res_img', image_name + '.jpg'), img_show) - bar.close() - result_dict = cal_recall_precison_f1(config['testload']['test_gt_path'],os.path.join(checkpoints, 'val', 'res_txt')) - return result_dict['recall'],result_dict['precision'],result_dict['hmean'] - -def TrainValProgram(args): - - config = yaml.load(open(args.config, 'r', encoding='utf-8'),Loader=yaml.FullLoader) - config = merge_config(config,args) - - os.environ["CUDA_VISIBLE_DEVICES"] = config['base']['gpu_id'] - create_dir(config['base']['checkpoints']) - checkpoints = os.path.join(config['base']['checkpoints'], - "ag_%s_bb_%s_he_%s_bs_%d_ep_%d_%s" % (config['base']['algorithm'], - config['backbone']['function'].split(',')[-1], - config['head']['function'].split(',')[-1], - config['trainload']['batch_size'], - config['base']['n_epoch'], - args.log_str)) - create_dir(checkpoints) - - model = create_module(config['architectures']['model_function'])(config) - criterion = create_module(config['architectures']['loss_function'])(config) - train_dataset = create_module(config['trainload']['function'])(config) - test_dataset = create_module(config['testload']['function'])(config) - optimizer = create_module(config['optimizer']['function'])(config, model) - optimizer_decay = create_module(config['optimizer_decay']['function']) - img_process = create_module(config['postprocess']['function'])(config) - - if args.t_config is not None: - t_model = GetTeacherModel(args) - distil_loss = DistilLoss() - if torch.cuda.is_available(): - distil_loss = distil_loss.cuda() - - train_data_loader = torch.utils.data.DataLoader( - train_dataset, - batch_size=config['trainload']['batch_size'], - shuffle=True, - num_workers=config['trainload']['num_workers'], - worker_init_fn = worker_init_fn, - drop_last=True, - pin_memory=True) - - test_data_loader = torch.utils.data.DataLoader( - test_dataset, - batch_size=config['testload']['batch_size'], - shuffle=False, - num_workers=config['testload']['num_workers'], - drop_last=True, - pin_memory=True) - - use_distil = False - if args.t_config is not None: - use_distil = True - loss_bin = create_loss_bin(config['base']['algorithm'],use_distil) - - if torch.cuda.is_available(): - if (len(config['base']['gpu_id'].split(',')) > 1): - model = torch.nn.DataParallel(model).cuda() - else: - model = model.cuda() - criterion = criterion.cuda() - - start_epoch = 0 - rescall, precision, hmean = 0,0,0 - best_rescall,best_precision ,best_hmean= 0,0,0 - - if args.pruned_model_dict_path is not None: - print('finetune the pruend model.') - model = load_prune_model(model,args.prune_model_path,args.pruned_model_dict_path,args.prune_type) - log_write = Logger(os.path.join(checkpoints, 'log.txt'), title=config['base']['algorithm']) - title = list(loss_bin.keys()) - title.extend(['piexl_acc','piexl_iou','t_rescall','t_precision','t_hmean','b_rescall','b_precision','b_hmean']) - log_write.set_names(title) - - elif config['base']['restore']: - print('Resuming from checkpoint.') - assert os.path.isfile(config['base']['restore_file']), 'Error: no checkpoint file found!' - checkpoint = torch.load(config['base']['restore_file']) - start_epoch = checkpoint['epoch'] - model.load_state_dict(checkpoint['state_dict']) - optimizer.load_state_dict(checkpoint['optimizer']) - best_rescall = checkpoint['rescall'] - best_precision = checkpoint['precision'] - best_hmean = checkpoint['hmean'] - log_write = Logger(os.path.join(checkpoints, 'log.txt'), title=config['base']['algorithm'], resume=True) - else: - print('Training from scratch.') - log_write = Logger(os.path.join(checkpoints, 'log.txt'), title=config['base']['algorithm']) - title = list(loss_bin.keys()) - title.extend(['piexl_acc','piexl_iou','t_rescall','t_precision','t_hmean','b_rescall','b_precision','b_hmean']) - log_write.set_names(title) - - if args.start_epoch is not None: - start_epoch = args.start_epoch - - - model = quantize_model(model, config['base']['backend'],False) - for epoch in range(start_epoch,config['base']['n_epoch']): - - model.train() - if args.t_config is not None: - t_model.train() - else: - t_model = None - distil_loss = None - optimizer_decay(config, optimizer, epoch) - loss_write = ModelTrain(train_data_loader,t_model,distil_loss,model, criterion, optimizer, loss_bin, args,config, epoch) - - if(epoch >= config['base']['start_val']): - create_dir(os.path.join(checkpoints,'val')) - create_dir(os.path.join(checkpoints,'val','res_img')) - create_dir(os.path.join(checkpoints,'val','res_txt')) - - qua_model = quantize_model(model.cpu(), config['base']['backend'],True) - qua_model.eval() - - rescall,precision,hmean = ModelEval(test_dataset, test_data_loader, qua_model, img_process, checkpoints,config) - print('rescall:',rescall,'precision',precision,'hmean',hmean) - if (hmean > best_hmean): - save_checkpoint({ - 'epoch': epoch + 1, - 'state_dict': model.state_dict(), - 'lr': config['optimizer']['base_lr'], - 'optimizer': optimizer.state_dict(), - 'hmean': hmean, - 'rescall': rescall, - 'precision': precision - }, checkpoints, config['base']['algorithm'] + '_best' + '.pth.tar') - best_hmean = hmean - best_precision = precision - best_rescall = rescall - - loss_write.extend([rescall,precision,hmean,best_rescall,best_precision,best_hmean]) - log_write.append(loss_write) - for key in loss_bin.keys(): - loss_bin[key].loss_clear() - if epoch%config['base']['save_epoch'] ==0: - save_checkpoint({ - 'epoch': epoch + 1, - 'state_dict': model.state_dict(), - 'lr': config['optimizer']['base_lr'], - 'optimizer': optimizer.state_dict(), - 'hmean': 0, - 'rescall': 0, - 'precision': 0 - },checkpoints,config['base']['algorithm']+'_'+str(epoch)+'.pth.tar') - - -if __name__ == "__main__": - parser = argparse.ArgumentParser(description='Hyperparams') - parser.add_argument('--config', help='config file path') - parser.add_argument('--t_config',default=None, help='config file path') - parser.add_argument('--t_model_path',default=None, help='teacher model path') - parser.add_argument('--t_ratio', nargs='?', type=float, default=0.2) - parser.add_argument('--log_str', help='log title') - parser.add_argument('--sr_lr', nargs='?', type=float, default=None) - - parser.add_argument('--pruned_model_dict_path', help='config file path',default=None) - parser.add_argument('--prune_type',type=str, help='prune type,total or backbone') - parser.add_argument('--prune_model_path', help='model file path') - - parser.add_argument('--n_epoch', nargs='?', type=int, default=600) - parser.add_argument('--start_epoch', nargs='?', type=int, default=None) - parser.add_argument('--start_val', nargs='?', type=int, default=400) - parser.add_argument('--base_lr', nargs='?', type=float, default=0.001) - parser.add_argument('--gpu_id', help='config file path') - - args = parser.parse_args() - TrainValProgram(args) \ No newline at end of file diff --git a/tools/pruned/__init__.py b/tools/pruned/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/tools/pruned/prune_model_all.py b/tools/pruned/prune_model_all.py deleted file mode 100644 index 0b96e79..0000000 --- a/tools/pruned/prune_model_all.py +++ /dev/null @@ -1,477 +0,0 @@ -""" -#!-*- coding=utf-8 -*- -@author: BADBADBADBADBOY -@contact: 2441124901@qq.com -@software: PyCharm Community Edition -@file: prune.py -@time: 2020/6/27 10:23 - -""" -import os -os.environ["CUDA_VISIBLE_DEVICES"] = '1' -import sys -sys.path.append('./') -import yaml -from ptocr.utils.util_function import create_module,load_model,resize_image -import ptocr -import torch -import torch.nn as nn -import numpy as np -import collections -import torchvision.transforms as transforms -import cv2 - -import argparse -import math -from PIL import Image -from torch.autograd import Variable - -def prune(args): - - stream = open(args.config, 'r', encoding='utf-8') - config = yaml.load(stream,Loader=yaml.FullLoader) - - img = cv2.imread(args.img_file) - img = resize_image(img,config['base']['algorithm'],config['testload']['test_size'],stride=config['testload']['stride']) - img = Image.fromarray(img) - img = img.convert('RGB') - img = transforms.ToTensor()(img) - img = transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])(img).cuda() - img = Variable(img).unsqueeze(0) - - model = create_module(config['architectures']['model_function'])(config).cuda() - model = load_model(model,args.checkpoint) - - model.eval() - print(model) - with torch.no_grad(): - out = model(img) - cv2.imwrite('ori_model_result.jpg',out[0,0].cpu().numpy()*255) - cut_percent = args.cut_percent - base_num = args.base_num - - bn_weights = [] - for m in model.modules(): - if (isinstance(m, nn.BatchNorm2d)): - bn_weights.append(m.weight.data.abs().clone()) - bn_weights = torch.cat(bn_weights, 0) - - sort_result, sort_index = torch.sort(bn_weights) - - thresh_index = int(cut_percent * bn_weights.shape[0]) - - if (thresh_index == bn_weights.shape[0]): - thresh_index = bn_weights.shape[0] - 1 - - prued = 0 - prued_mask = [] - bn_index = [] - conv_index = [] - remain_channel_nums = [] - for k, m in enumerate(model.modules()): - if (isinstance(m, nn.BatchNorm2d)): - bn_weight = m.weight.data.clone() - mask = bn_weight.abs().gt(sort_result[thresh_index]) - remain_channel = mask.sum() - - if (remain_channel == 0): - remain_channel = 1 - mask[int(torch.argmax(bn_weight))] = 1 - - v = 0 - n = 1 - if (remain_channel % base_num != 0): - if (remain_channel > base_num): - while (v < remain_channel): - n += 1 - v = base_num * n - if (remain_channel - (v - base_num) < v - remain_channel): - remain_channel = v - base_num - else: - remain_channel = v - if (remain_channel > bn_weight.size()[0]): - remain_channel = bn_weight.size()[0] - remain_channel = torch.tensor(remain_channel) - result, index = torch.sort(bn_weight) - mask = bn_weight.abs().ge(result[-remain_channel]) - - remain_channel_nums.append(int(mask.sum())) - prued_mask.append(mask) - bn_index.append(k) - prued += mask.shape[0] - mask.sum() - elif (isinstance(m, nn.Conv2d) or isinstance(m, nn.ConvTranspose2d)): - conv_index.append(k) - conv_index.remove(227) - conv_index.remove(236) - print('remain_channel_nums',remain_channel_nums) - print('total_prune_ratio:', float(prued) / bn_weights.shape[0]) - print('bn_index',bn_index) - print('conv_index',conv_index) - -# import pdb -# pdb.set_trace() - - new_model = create_module(config['architectures']['model_function'])(config).cuda() - - keys = {} - tag = 0 - for k, m in enumerate(new_model.modules()): - if(isinstance(m,ptocr.model.backbone.det_mobilev3.Block)): - keys[tag]=k - tag+=1 - print(keys) - #### step 1 - mg_1 = np.array([-3,7,16]) - block_idx = keys[0] - tag = 0 - for idx in mg_1+block_idx: - if (tag == 0): - msk = prued_mask[bn_index.index(idx)] - else: - msk = msk | prued_mask[bn_index.index(idx)] - tag+=1 - print('step1',idx) - print(msk.sum()) - for idx in mg_1+block_idx: - prued_mask[bn_index.index(idx)] = msk - msk_1 = msk.clone() - - #### step 2 - block_idx2 = np.array([keys[1],keys[2]]) - mg_2 = 7 - tag = 0 - for idx in mg_2+block_idx2: - print('step2',idx) - if(tag==0): - msk = prued_mask[bn_index.index(idx)] - else: - msk = msk|prued_mask[bn_index.index(idx)] - tag += 1 - for idx in mg_2+block_idx2: - prued_mask[bn_index.index(idx)] = msk - print(msk.sum()) - msk_2 = msk.clone() - - ####step 3 - block_idx3s = [keys[3],keys[4],keys[5]] - mg_3 = np.array([7,16]) - tag = 0 - for block_idx3 in block_idx3s: - for idx in block_idx3+mg_3: - print('step3',idx) - if (tag == 0): - msk = prued_mask[bn_index.index(idx)] - else: - msk = msk | prued_mask[bn_index.index(idx)] - tag += 1 - for block_idx3 in block_idx3s: - for idx in block_idx3+mg_3: - prued_mask[bn_index.index(idx)] = msk - print(msk.sum()) - msk_3 = msk.clone() - - ####step 4_1 - block_idx4_all = [] - - block_idx4 = keys[6] - - mg_4 = np.array([7,16]) - block_idx4_all.extend((block_idx4+mg_4).tolist()) - - ####step 4_2 - block_idx4 = keys[7] - mg_4 = np.array([7,16]) - block_idx4_all.extend((block_idx4+mg_4).tolist()) - tag = 0 - - for idx in block_idx4_all: - print('step4',idx) - if(tag==0): - msk = prued_mask[bn_index.index(idx)] - else: - msk = msk | prued_mask[bn_index.index(idx)] - tag += 1 - - for idx in block_idx4_all: - prued_mask[bn_index.index(idx)] = msk - print(msk.sum()) - msk_4 = msk.clone() - - ####step 5 - block_idx5s = [keys[8],keys[9],keys[10]] - mg_5 = np.array([7,16]) - tag = 0 - for block_idx5 in block_idx5s: - for idx in block_idx5+mg_5: - if (tag == 0): - msk = prued_mask[bn_index.index(idx)] - else: - msk = msk | prued_mask[bn_index.index(idx)] - tag += 1 - - for block_idx5 in block_idx5s: - for idx in block_idx5+mg_5: - prued_mask[bn_index.index(idx)] = msk - print(msk.sum()) - msk_5 = msk.clone() - - group_index = [] - spl_index = [] - for i in range(11): - block_idx6 = keys[i] - tag = 0 - mg_6 = np.array([2,5]) - for idx in mg_6+block_idx6: - if(tag==0): - msk = prued_mask[bn_index.index(idx)] - else: - msk = msk | prued_mask[bn_index.index(idx)] - tag+=1 - for idx in mg_6 + block_idx6: - prued_mask[bn_index.index(idx)] = msk - if(i==6): - spl_index.extend([block_idx6+9,block_idx6-2]) - group_index.append(block_idx6+4) - - - count_conv = 0 - count_bn = 0 - conv_in_mask = [torch.ones(3)] - conv_out_mask = [] - bn_mask = [] - tag = 0 - for k, m in enumerate(new_model.modules()): - if(tag>187 ): - continue - else: - if isinstance(m,nn.Conv2d): - - if(tag in group_index): - m.groups = int(prued_mask[bn_index.index(tag+1)].sum()) - m.out_channels = int(prued_mask[count_conv].sum()) - conv_out_mask.append(prued_mask[count_conv]) - if(count_conv>0): - if (tag == spl_index[0]): - m.in_channels = int(prued_mask[bn_index.index(spl_index[1])].sum()) - conv_in_mask.append(prued_mask[bn_index.index(spl_index[1])]) - else: - m.in_channels = int(prued_mask[count_conv-1].sum()) - conv_in_mask.append(prued_mask[count_conv-1]) - - count_conv+=1 - elif isinstance(m,nn.BatchNorm2d): - m.num_features = int(prued_mask[count_bn].sum()) - bn_mask.append(prued_mask[count_bn]) - count_bn+=1 - tag+=1 - - - head_bn_merge_idx1 = np.array([189,193,197,201]) - tag = 0 - for idx in head_bn_merge_idx1: - if(tag==0): - _msk1 = prued_mask[bn_index.index(idx)] - else: - _msk1 = _msk1 | prued_mask[bn_index.index(idx)] - tag += 1 - - head_bn_merge_idx2 = np.array([205,209,213,217]) - num_ch = 0 - head_mask_1 = [] - for idx in head_bn_merge_idx2: - num_ch += int(prued_mask[bn_index.index(idx)].sum()) - head_mask_1.append(prued_mask[bn_index.index(idx)]) - - - print(new_model) - - head_bn_merge_idx2 = head_bn_merge_idx2 - 1 - - bn_i = 0 - conv_i = 0 - model_i = 0 - scale = [188,192,196,200] - scale_mask = [msk_5,msk_4,msk_3,msk_2] - import copy - prued_mask_bk = copy.deepcopy(prued_mask) - for [m0, m1] in zip(model.modules(), new_model.modules()): - if(model_i>187): - if isinstance(m0, nn.Conv2d) or isinstance(m0, nn.ConvTranspose2d): - if(model_i in scale): - index = scale.index(model_i) - m1.in_channels = int(scale_mask[index].sum()) - m1.out_channels = int(_msk1.sum()) - idx0 = np.squeeze(np.argwhere(np.asarray(scale_mask[index].cpu().numpy()))) - idx1 = np.squeeze(np.argwhere(np.asarray(_msk1.cpu().numpy()))) - if idx0.size == 1: - idx0 = np.resize(idx0, (1,)) - if idx1.size == 1: - idx1 = np.resize(idx1, (1,)) - w = m0.weight.data[:, idx0, :, :].clone() - m1.weight.data = w[idx1, :, :, :].clone() - if m1.bias is not None: - m1.bias.data = m0.bias.data[idx0].clone() - elif(model_i in head_bn_merge_idx2.tolist()): - - m1.in_channels = int(_msk1.sum()) - index = head_bn_merge_idx2.tolist().index(model_i) - m1.out_channels = int(head_mask_1[index].sum()) - - idx1 = np.squeeze(np.argwhere(np.asarray(head_mask_1[index].cpu().numpy()))) - idx0 = np.squeeze(np.argwhere(np.asarray(_msk1.cpu().numpy()))) - if idx0.size == 1: - idx0 = np.resize(idx0, (1,)) - if idx1.size == 1: - idx1 = np.resize(idx1, (1,)) - w = m0.weight.data[:, idx0, :, :].clone() - m1.weight.data = w[idx1, :, :, :].clone() -# merge_weight_mask.append(m1.weight.data) - if m1.bias is not None: - m1.bias.data = m0.bias.data[idx0].clone() - elif(model_i==221 or model_i==230): - - merge_mask = torch.cat(head_mask_1,0) - m1.in_channels = num_ch - index = bn_index.index(model_i+1) - m1.out_channels = int(prued_mask[index].sum()) - - idx1 = np.squeeze(np.argwhere(np.asarray(prued_mask[index].cpu().numpy()))) - idx0 = np.squeeze(np.argwhere(np.asarray(merge_mask.cpu().numpy()))) - if idx0.size == 1: - idx0 = np.resize(idx0, (1,)) - if idx1.size == 1: - idx1 = np.resize(idx1, (1,)) - w = m0.weight.data[:, idx0, :, :].clone() - m1.weight.data = w[idx1, :, :, :].clone() - - if m1.bias is not None: - m1.bias.data = m0.bias.data[idx0].clone() - - elif(model_i==224 or model_i==233): - - m1.in_channels = int(prued_mask[bn_index.index(model_i-2)].sum()) - m1.out_channels = int(prued_mask[bn_index.index(model_i+1)].sum()) - idx0 = np.squeeze(np.argwhere(np.asarray(prued_mask[bn_index.index(model_i+1)].cpu().numpy()))) - idx1 = np.squeeze(np.argwhere(np.asarray(prued_mask[bn_index.index(model_i-2)].cpu().numpy()))) - if idx0.size == 1: - idx0 = np.resize(idx0, (1,)) - if idx1.size == 1: - idx1 = np.resize(idx1, (1,)) - w = m0.weight.data[:, idx0, :, :].clone() - m1.weight.data = w[idx1, :, :, :].clone() - if m1.bias is not None: - m1.bias.data = m0.bias.data[idx0].clone() - elif(model_i==227 or model_i==236): -# import pdb -# pdb.set_trace() - m1.in_channels = int(prued_mask[bn_index.index(model_i-2)].sum()) - idx1 = np.squeeze(np.argwhere(np.asarray(prued_mask[bn_index.index(model_i-2)].cpu().numpy()))) - idx0 = np.squeeze(np.argwhere(np.asarray(torch.ones(1).cpu().numpy()))) - if idx0.size == 1: - idx0 = np.resize(idx0, (1,)) - if idx1.size == 1: - idx1 = np.resize(idx1, (1,)) - w = m0.weight.data[:, idx0, :, :].clone() - m1.weight.data = w[idx1, :, :, :].clone() - if m1.bias is not None: - m1.bias.data = m0.bias.data[idx0].clone() - else: - m1.weight.data = m0.weight.data.clone() - if m1.bias is not None: - m1.bias.data = m0.bias.data.clone() - - elif isinstance(m0, nn.BatchNorm2d): - - if(model_i in [189,193,197,201]): - m1.num_features = int(_msk1.sum()) - idx1 = np.squeeze(np.argwhere(np.asarray(_msk1.cpu().numpy()))) - if idx1.size == 1: - idx1 = np.resize(idx1, (1,)) - m1.weight.data = m0.weight.data[idx1].clone() - if m1.bias is not None: - m1.bias.data = m0.bias.data[idx1].clone() - m1.running_mean = m0.running_mean[idx1].clone() - m1.running_var = m0.running_var[idx1].clone() - - else: - - index = bn_index.index(model_i) - m1.num_features = prued_mask[index].sum() - idx1 = np.squeeze(np.argwhere(np.asarray(prued_mask[index].cpu().numpy()))) - - if idx1.size == 1: - idx1 = np.resize(idx1, (1,)) - m1.weight.data = m0.weight.data[idx1].clone() - if m1.bias is not None: - m1.bias.data = m0.bias.data[idx1].clone() - m1.running_mean = m0.running_mean[idx1].clone() - m1.running_var = m0.running_var[idx1].clone() - - - else: - if isinstance(m0, nn.BatchNorm2d): - idx1 = np.squeeze(np.argwhere(np.asarray(bn_mask[bn_i].cpu().numpy()))) - if idx1.size == 1: - idx1 = np.resize(idx1, (1,)) - m1.weight.data = m0.weight.data[idx1].clone() - if m1.bias is not None: - m1.bias.data = m0.bias.data[idx1].clone() - m1.running_mean = m0.running_mean[idx1].clone() - m1.running_var = m0.running_var[idx1].clone() - bn_i += 1 - elif isinstance(m0, nn.Conv2d): - if (isinstance(conv_in_mask[conv_i], list)): - idx0 = np.squeeze(np.argwhere(np.asarray(torch.cat(conv_in_mask[conv_i], 0).cpu().numpy()))) - else: - idx0 = np.squeeze(np.argwhere(np.asarray(conv_in_mask[conv_i].cpu().numpy()))) - idx1 = np.squeeze(np.argwhere(np.asarray(conv_out_mask[conv_i].cpu().numpy()))) - if idx0.size == 1: - idx0 = np.resize(idx0, (1,)) - if idx1.size == 1: - idx1 = np.resize(idx1, (1,)) - if(model_i in group_index): - m1.weight.data = m0.weight.data[idx1, :, :, :].clone() - if m1.bias is not None: - m1.bias.data = m0.bias.clone() - else: - w = m0.weight.data[:, idx0, :, :].clone() - m1.weight.data = w[idx1, :, :, :].clone() - if m1.bias is not None: - m1.bias.data = m0.bias.data[idx0].clone() - conv_i += 1 - model_i+=1 - - print('model after pruned') - print(new_model) - - new_model.eval() - with torch.no_grad(): - out = new_model(img) - - cv2.imwrite('pruned_model_result.jpg',out[0,0].cpu().numpy()*255) - - save_obj = {'prued_mask': prued_mask, 'bn_index': bn_index} - torch.save(save_obj, os.path.join(args.save_prune_model_path, 'pruned_dict.dict')) - torch.save(new_model.state_dict(), os.path.join(args.save_prune_model_path, 'pruned_dict.pth')) - - -if __name__ == '__main__': - parser = argparse.ArgumentParser(description='Hyperparams') - parser.add_argument('--base_num', nargs='?', type=int, default = 2, - help='Base after Model Channel Clipping') - parser.add_argument('--cut_percent', nargs='?', type=float, default=0.5, - help='Model channel clipping scale') - parser.add_argument('--config', default='./config/det_DB_mobilev3.yaml', - type=str, metavar='PATH', - help='config path') - parser.add_argument('--checkpoint', default='./checkpoint/ag_DB_bb_mobilenet_v3_small_he_DB_Head_bs_16_ep_1200_mobile_slim_all/DB_best.pth.tar', - type=str, metavar='PATH', - help='ori model path') - parser.add_argument('--save_prune_model_path', default='./checkpoint/ag_DB_bb_mobilenet_v3_small_he_DB_Head_bs_16_ep_1200_mobile_slim_all/pruned/', type=str, metavar='PATH', - help='pruned model path') - parser.add_argument('--img_file', - default='/src/notebooks/detect_text/icdar2015/ch4_test_images/img_108.jpg', - type=str, - help='') - args = parser.parse_args() - prune(args) diff --git a/tools/pruned/prune_model_backbone.py b/tools/pruned/prune_model_backbone.py deleted file mode 100644 index 3558695..0000000 --- a/tools/pruned/prune_model_backbone.py +++ /dev/null @@ -1,365 +0,0 @@ -""" -#!-*- coding=utf-8 -*- -@author: BADBADBADBADBOY -@contact: 2441124901@qq.com -@software: PyCharm Community Edition -@file: prune.py -@time: 2020/6/27 10:23 - -""" -import os -import sys -sys.path.append('./') -import yaml -from ptocr.utils.util_function import create_module,load_model,resize_image -import ptocr -import torch -import torch.nn as nn -import numpy as np -import collections -import torchvision.transforms as transforms -import cv2 - -import argparse -import math -from PIL import Image -from torch.autograd import Variable - -def prune(args): - - stream = open(args.config, 'r', encoding='utf-8') - config = yaml.load(stream,Loader=yaml.FullLoader) - - img = cv2.imread(args.img_file) - img = resize_image(img,config['base']['algorithm'],config['testload']['test_size'],stride=config['testload']['stride']) - img = Image.fromarray(img) - img = img.convert('RGB') - img = transforms.ToTensor()(img) - img = transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])(img) - img = Variable(img.cuda()).unsqueeze(0) - - model = create_module(config['architectures']['model_function'])(config).cuda() - model = load_model(model,args.checkpoint) - - model.eval() - print(model) - - cut_percent = 0.5 - base_num = 4 - - bn_weights = [] - for m in model.modules(): - if (isinstance(m, nn.BatchNorm2d)): - bn_weights.append(m.weight.data.abs().clone()) - bn_weights = torch.cat(bn_weights, 0) - - sort_result, sort_index = torch.sort(bn_weights) - - thresh_index = int(cut_percent * bn_weights.shape[0]) - - if (thresh_index == bn_weights.shape[0]): - thresh_index = bn_weights.shape[0] - 1 - - prued = 0 - prued_mask = [] - bn_index = [] - conv_index = [] - remain_channel_nums = [] - tag = 0 - for k, m in enumerate(model.modules()): - if(tag>187): - break - tag+=1 - if (isinstance(m, nn.BatchNorm2d)): - bn_weight = m.weight.data.clone() - mask = bn_weight.abs().gt(sort_result[thresh_index]) - remain_channel = mask.sum() - - if (remain_channel == 0): - remain_channel = 1 - mask[int(torch.argmax(bn_weight))] = 1 - - v = 0 - n = 1 - if (remain_channel % base_num != 0): - if (remain_channel > base_num): - while (v < remain_channel): - n += 1 - v = base_num * n - if (remain_channel - (v - base_num) < v - remain_channel): - remain_channel = v - base_num - else: - remain_channel = v - if (remain_channel > bn_weight.size()[0]): - remain_channel = bn_weight.size()[0] - remain_channel = torch.tensor(remain_channel) - result, index = torch.sort(bn_weight) - mask = bn_weight.abs().ge(result[-remain_channel]) - - remain_channel_nums.append(int(mask.sum())) - prued_mask.append(mask) - bn_index.append(k) - prued += mask.shape[0] - mask.sum() - elif (isinstance(m, nn.Conv2d)): - conv_index.append(k) - - print('remain_channel_nums',remain_channel_nums) - print('total_prune_ratio:', float(prued) / bn_weights.shape[0]) - print('bn_index',bn_index) - print('conv_index',conv_index) - - - - new_model = create_module(config['architectures']['model_function'])(config).cuda() - - keys = {} - tag = 0 - for k, m in enumerate(new_model.modules()): - if(isinstance(m,ptocr.model.backbone.det_mobilev3.Block)): - keys[tag]=k - tag+=1 - print(keys) - #### step 1 - mg_1 = np.array([-3,7,16]) - block_idx = keys[0] - tag = 0 - for idx in mg_1+block_idx: - if (tag == 0): - msk = prued_mask[bn_index.index(idx)] - else: - msk = msk | prued_mask[bn_index.index(idx)] - tag+=1 - print('step1',idx) - print(msk.sum()) - for idx in mg_1+block_idx: - prued_mask[bn_index.index(idx)] = msk - msk_1 = msk.clone() - - #### step 2 - block_idx2 = np.array([keys[1],keys[2]]) - mg_2 = 7 - tag = 0 - for idx in mg_2+block_idx2: - print('step2',idx) - if(tag==0): - msk = prued_mask[bn_index.index(idx)] - else: - msk = msk|prued_mask[bn_index.index(idx)] - tag += 1 - for idx in mg_2+block_idx2: - prued_mask[bn_index.index(idx)] = msk - print(msk.sum()) - msk_2 = msk.clone() - - ####step 3 - block_idx3s = [keys[3],keys[4],keys[5]] - mg_3 = np.array([7,16]) - tag = 0 - for block_idx3 in block_idx3s: - for idx in block_idx3+mg_3: - print('step3',idx) - if (tag == 0): - msk = prued_mask[bn_index.index(idx)] - else: - msk = msk | prued_mask[bn_index.index(idx)] - tag += 1 - for block_idx3 in block_idx3s: - for idx in block_idx3+mg_3: - prued_mask[bn_index.index(idx)] = msk - print(msk.sum()) - msk_3 = msk.clone() - - ####step 4_1 - block_idx4_all = [] - - block_idx4 = keys[6] - - mg_4 = np.array([7,16]) - block_idx4_all.extend((block_idx4+mg_4).tolist()) - - ####step 4_2 - block_idx4 = keys[7] - mg_4 = np.array([7,16]) - block_idx4_all.extend((block_idx4+mg_4).tolist()) - tag = 0 - - for idx in block_idx4_all: - print('step4',idx) - if(tag==0): - msk = prued_mask[bn_index.index(idx)] - else: - msk = msk | prued_mask[bn_index.index(idx)] - tag += 1 - - for idx in block_idx4_all: - prued_mask[bn_index.index(idx)] = msk - print(msk.sum()) - msk_4 = msk.clone() - - ####step 5 - block_idx5s = [keys[8],keys[9],keys[10]] - mg_5 = np.array([7,16]) - tag = 0 - for block_idx5 in block_idx5s: - for idx in block_idx5+mg_5: - if (tag == 0): - msk = prued_mask[bn_index.index(idx)] - else: - msk = msk | prued_mask[bn_index.index(idx)] - tag += 1 - - for block_idx5 in block_idx5s: - for idx in block_idx5+mg_5: - prued_mask[bn_index.index(idx)] = msk - print(msk.sum()) - msk_5 = msk.clone() - - group_index = [] - spl_index = [] - for i in range(11): - block_idx6 = keys[i] - tag = 0 - mg_6 = np.array([2,5]) - for idx in mg_6+block_idx6: - if(tag==0): - msk = prued_mask[bn_index.index(idx)] - else: - msk = msk | prued_mask[bn_index.index(idx)] - tag+=1 - for idx in mg_6 + block_idx6: - prued_mask[bn_index.index(idx)] = msk - if(i==6): - spl_index.extend([block_idx6+9,block_idx6-2]) - group_index.append(block_idx6+4) - import pdb - pdb.set_trace() - count_conv = 0 - count_bn = 0 - conv_in_mask = [torch.ones(3)] - conv_out_mask = [] - bn_mask = [] - tag = 0 - for k, m in enumerate(new_model.modules()): - if(tag>187): - break - if isinstance(m,nn.Conv2d): - - if(tag in group_index): - m.groups = int(prued_mask[bn_index.index(tag+1)].sum()) - m.out_channels = int(prued_mask[count_conv].sum()) - conv_out_mask.append(prued_mask[count_conv]) - if(count_conv>0): - if (tag == spl_index[0]): - m.in_channels = int(prued_mask[bn_index.index(spl_index[1])].sum()) - conv_in_mask.append(prued_mask[bn_index.index(spl_index[1])]) - else: - m.in_channels = int(prued_mask[count_conv-1].sum()) - conv_in_mask.append(prued_mask[count_conv-1]) - - count_conv+=1 - elif isinstance(m,nn.BatchNorm2d): - m.num_features = prued_mask[count_bn].sum() - bn_mask.append(prued_mask[count_bn]) - count_bn+=1 - tag+=1 - - bn_i = 0 - conv_i = 0 - model_i = 0 - scale = [188,192,196,200] - scale_mask = [msk_5,msk_4,msk_3,msk_2] - for [m0, m1] in zip(model.modules(), new_model.modules()): - if(model_i>187): - if isinstance(m0, nn.Conv2d): - if(model_i in scale): - index = scale.index(model_i) - m1.in_channels = int(scale_mask[index].sum()) - idx0 = np.squeeze(np.argwhere(np.asarray(scale_mask[index].cpu().numpy()))) - idx1 = np.squeeze(np.argwhere(np.asarray(torch.ones(96).cpu().numpy()))) - if idx0.size == 1: - idx0 = np.resize(idx0, (1,)) - if idx1.size == 1: - idx1 = np.resize(idx1, (1,)) - w = m0.weight.data[:, idx0, :, :].clone() - m1.weight.data = w[idx1, :, :, :].clone() - if m1.bias is not None: - m1.bias.data = m0.bias.data[idx1].clone() - - else: - m1.weight.data = m0.weight.data.clone() - if m1.bias is not None: - m1.bias.data = m0.bias.data.clone() - - elif isinstance(m0, nn.BatchNorm2d): - m1.weight.data = m0.weight.data.clone() - if m1.bias is not None: - m1.bias.data = m0.bias.data.clone() - m1.running_mean = m0.running_mean.clone() - m1.running_var = m0.running_var.clone() - else: - if isinstance(m0, nn.BatchNorm2d): - idx1 = np.squeeze(np.argwhere(np.asarray(bn_mask[bn_i].cpu().numpy()))) - if idx1.size == 1: - idx1 = np.resize(idx1, (1,)) - m1.weight.data = m0.weight.data[idx1].clone() - if m1.bias is not None: - m1.bias.data = m0.bias.data[idx1].clone() - m1.running_mean = m0.running_mean[idx1].clone() - m1.running_var = m0.running_var[idx1].clone() - bn_i += 1 - elif isinstance(m0, nn.Conv2d): - if (isinstance(conv_in_mask[conv_i], list)): - idx0 = np.squeeze(np.argwhere(np.asarray(torch.cat(conv_in_mask[conv_i], 0).cpu().numpy()))) - else: - idx0 = np.squeeze(np.argwhere(np.asarray(conv_in_mask[conv_i].cpu().numpy()))) - idx1 = np.squeeze(np.argwhere(np.asarray(conv_out_mask[conv_i].cpu().numpy()))) - if idx0.size == 1: - idx0 = np.resize(idx0, (1,)) - if idx1.size == 1: - idx1 = np.resize(idx1, (1,)) - if(model_i in group_index): - m1.weight.data = m0.weight.data[idx1, :, :, :].clone() - if m1.bias is not None: - m1.bias.data = m0.bias.clone() - else: - w = m0.weight.data[:, idx0, :, :].clone() - m1.weight.data = w[idx1, :, :, :].clone() - if m1.bias is not None: - m1.bias.data = m0.bias.data[idx1].clone() - conv_i += 1 - model_i+=1 - - - - print(new_model) - new_model.eval() - with torch.no_grad(): - out = new_model(img) - print(out.shape) - cv2.imwrite('re1.jpg',out[0,0].cpu().numpy()*255) - - save_obj = {'prued_mask': prued_mask, 'bn_index': bn_index} - torch.save(save_obj, os.path.join(args.save_prune_model_path, 'pruned_dict.dict')) - torch.save(new_model.state_dict(), os.path.join(args.save_prune_model_path, 'pruned_dict.pth.tar')) - - -if __name__ == '__main__': - parser = argparse.ArgumentParser(description='Hyperparams') - parser.add_argument('--base_num', nargs='?', type=int, default = 4, - help='Base after Model Channel Clipping') - parser.add_argument('--cut_percent', nargs='?', type=float, default=0.5, - help='Model channel clipping scale') - parser.add_argument('--config', default='./config/det_DB_mobilev3.yaml', - type=str, metavar='PATH', - help='config path') - parser.add_argument('--checkpoint', default='./checkpoint/ag_DB_bb_mobilenet_v3_small_he_DB_Head_bs_16_ep_1200/DB_best.pth.tar', - type=str, metavar='PATH', - help='ori model path') - parser.add_argument('--save_prune_model_path', default='./checkpoint/ag_DB_bb_mobilenet_v3_small_he_DB_Head_bs_16_ep_1200/pruned/', type=str, metavar='PATH', - help='pruned model path') - parser.add_argument('--img_file', - default='/src/notebooks/detect_text/icdar2015/ch4_test_images/img_108.jpg', - type=str, - help='') - args = parser.parse_args() - prune(args) diff --git a/tools/rec_infer.py b/tools/rec_infer.py deleted file mode 100644 index c5cecac..0000000 --- a/tools/rec_infer.py +++ /dev/null @@ -1,82 +0,0 @@ -#-*- coding:utf-8 _*- -""" -@author:fxw -@file: det_infer.py -@time: 2020/08/20 -""" -import os -import sys -sys.path.append('./') -import cv2 -import torch -import yaml -from PIL import Image -import numpy as np -from tqdm import tqdm -import torchvision.transforms as transforms -from ptocr.utils.util_function import create_module,resize_image -from ptocr.utils.util_function import create_process_obj,create_dir,load_model - - -class TestProgram(): - def __init__(self,config): - super(TestProgram,self).__init__() - - self.converter = create_module(config['label_transform']['function'])(config) - config['base']['classes'] = len(self.converter.alphabet) - model = create_module(config['architectures']['model_function'])(config) - model = load_model(model,config['infer']['model_path']) - if torch.cuda.is_available(): - model = model.cuda() - self.model = model - self.congig = config - self.model.eval() - - def infer_img(self,ori_img): - img = resize_image(ori_img,self.congig['base']['algorithm'],32) - img = Image.fromarray(img).convert('RGB') - if(self.congig['base']['is_gray']): - img = img.convert('L') - img = transforms.ToTensor()(img) - img.sub_(0.5).div_(0.5) - img = img.unsqueeze(0) - if torch.cuda.is_available(): - img = img.cuda() - - with torch.no_grad(): - preds = self.model(img) - preds_size = torch.IntTensor([preds.size(0)]) - _, preds = preds.max(2) - preds = preds.squeeze(1) - preds = preds.contiguous().view(-1) - sim_preds = self.converter.decode(preds.data, preds_size.data, raw=False) - - return sim_preds - -def InferImage(config): - path = config['infer']['path'] - save_path = config['infer']['save_path'] - test_bin = TestProgram(config) - if os.path.isdir(path): - files = os.listdir(path) - bar = tqdm(total=len(files)) - for file in files: - bar.update(1) - image_name = file.split('.')[0] - img_path = os.path.join(path,file) - img = cv2.imread(img_path) - rec_char = test_bin.infer_img(img) - print(rec_char) - bar.close() - - else: - image_name = path.split('/')[-1].split('.')[0] - img = cv2.imread(path) - rec_char = test_bin.infer_img(img) - print(rec_char) - - -if __name__ == "__main__": - stream = open('./config/rec_CRNN_ori.yaml', 'r', encoding='utf-8') - config = yaml.load(stream,Loader=yaml.FullLoader) - InferImage(config) \ No newline at end of file diff --git a/tools/rec_train.py b/tools/rec_train.py deleted file mode 100644 index 73c7c41..0000000 --- a/tools/rec_train.py +++ /dev/null @@ -1,336 +0,0 @@ -# -*- coding:utf-8 _*- -""" -@author:fxw -@file: det_train.py -@time: 2020/08/07 -""" -import sys -sys.path.append('./') -import cv2 -import torch -import time -import os -import argparse -import random -import numpy as np -from tqdm import tqdm -from torch.autograd import Variable -np.seterr(divide='ignore', invalid='ignore') -import yaml -import torch.utils.data -from ptocr.utils.util_function import create_module, create_loss_bin, \ -set_seed,save_checkpoint,create_dir -from ptocr.utils.metrics import runningScore -from ptocr.utils.logger import Logger -from ptocr.utils.util_function import create_process_obj,merge_config,AverageMeter -from ptocr.dataloader.RecLoad.CRNNProcess import alignCollate -import copy - -GLOBAL_WORKER_ID = None -GLOBAL_SEED = 2020 - - -torch.manual_seed(GLOBAL_SEED) -torch.cuda.manual_seed(GLOBAL_SEED) -torch.cuda.manual_seed_all(GLOBAL_SEED) -np.random.seed(GLOBAL_SEED) -random.seed(GLOBAL_SEED) - - - -def worker_init_fn(worker_id): - global GLOBAL_WORKER_ID - GLOBAL_WORKER_ID = worker_id - set_seed(GLOBAL_SEED + worker_id) - -# def backward_hook(self,grad_input, grad_output): -# for g in grad_input: -# g[g != g] = 0 # replace all nan/inf in gradients to zero - - -def ModelTrain(train_data_loader,LabelConverter,model,center_model, criterion, optimizer,center_criterion,optimizer_center,center_flag,loss_bin, config, epoch): - batch_time = AverageMeter() - end = time.time() - - for batch_idx, data in enumerate(train_data_loader): -# model.register_backward_hook(backward_hook) - if(data is None): - continue - imgs,labels = data - pre_batch = {} - gt_batch = {} - - if torch.cuda.is_available(): - imgs = imgs.cuda() - preds = model(imgs) - - labels,labels_len = LabelConverter.encode(labels,preds.size(0)) - preds_size = Variable(torch.IntTensor([preds.size(0)] * config['trainload']['batch_size'])) - pre_batch['preds'],pre_batch['preds_size'] = preds,preds_size - gt_batch['labels'],gt_batch['labels_len'] = labels,labels_len - - ctc_loss = criterion(pre_batch, gt_batch).cuda() - metrics = {} - metrics['loss_total'] = 0.0 - metrics['loss_center'] = 0.0 - if center_criterion is not None and center_flag is True: - center_model.eval() - ##### - feautures = preds.clone() - with torch.no_grad(): - center_preds = center_model(imgs) - - center_preds = torch.softmax(center_preds,-1) - confs, center_preds = center_preds.max(2) - center_preds = center_preds.squeeze(1).transpose(1, 0).contiguous() - confs = confs.transpose(1, 0).contiguous() - -# confs = [] -# for i in range(center_preds.shape[0]): -# conf = [] -# for j in range(len(center_preds[i])): -# conf.append(probs[i,j,center_preds[i][j]]) -# confs.append(conf) -# confs = torch.Tensor(confs).cuda() - - b,t = center_preds.shape - feautures = feautures.transpose(1, 0).contiguous() - - confs = confs.view(-1) - center_preds = center_preds.view(-1) - feautures = feautures.view(b*t,-1) - - - index = (center_preds>0) & (confs>config['loss']['label_score']) - center_preds = center_preds[index] - feautures = feautures[index] - - center_loss = center_criterion(feautures,center_preds)*config['loss']['weight_center'] - - loss = ctc_loss + center_loss - - - metrics['loss_total'] = loss.item() - metrics['loss_ctc'] = ctc_loss.item() - metrics['loss_center'] = center_loss.item() - - ##### - optimizer_center.zero_grad() - optimizer.zero_grad() - - loss.backward() - optimizer.step() - - for param in center_criterion.parameters(): - param.grad.data *= (1. / config['loss']['weight_center']) - optimizer_center.step() - else: - loss = ctc_loss - metrics['loss_ctc'] = ctc_loss.item() - optimizer.zero_grad() - loss.backward() - optimizer.step() - - for key in loss_bin.keys(): - loss_bin[key].loss_add(metrics[key]) - batch_time.update(time.time() - end) - end = time.time() - if (batch_idx % config['base']['show_step'] == 0): - log = '({}/{}/{}/{}) | ' \ - .format(epoch, config['base']['n_epoch'], batch_idx, len(train_data_loader)) - bin_keys = list(loss_bin.keys()) - - for i in range(len(bin_keys)): - log += bin_keys[i] + ':{:.4f}'.format(loss_bin[bin_keys[i]].loss_mean()) + ' | ' - log += 'lr:{:.8f}'.format(optimizer.param_groups[0]['lr'])+ ' | ' - log+='batch_time:{:.2f} s'.format(batch_time.avg)+ ' | ' - log+='total_time:{:.2f} min'.format(batch_time.avg * batch_idx / 60.0)+ ' | ' - log+='ETA:{:.2f} min'.format(batch_time.avg*(len(train_data_loader)-batch_idx)/60.0) - print(log) - loss_write = [] - for key in list(loss_bin.keys()): - loss_write.append(loss_bin[key].loss_mean()) - return loss_write,loss_bin['loss_ctc'].loss_mean() - - -def ModelEval(test_data_loader,LabelConverter,model,criterion,config): - bar = tqdm(total=len(test_data_loader)) - loss_avg = [] - n_correct = 0 - for batch_idx, (imgs, labels) in enumerate(test_data_loader): - bar.update(1) - pre_batch = {} - gt_batch = {} - if torch.cuda.is_available(): - imgs = imgs.cuda() - with torch.no_grad(): - preds = model(imgs) - labels_class, labels_len = LabelConverter.encode(labels,preds.size(0)) - preds_size = Variable(torch.IntTensor([preds.size(0)] * config['testload']['batch_size'])) - pre_batch['preds'],pre_batch['preds_size'] = preds,preds_size - gt_batch['labels'],gt_batch['labels_len'] = labels_class,labels_len - cost = criterion(pre_batch, gt_batch) - loss_avg.append(cost.item()) - _, preds = preds.max(2) - preds = preds.squeeze(1) - preds = preds.transpose(1, 0).contiguous().view(-1) - sim_preds = LabelConverter.decode(preds.data, preds_size.data, raw=False) - for pred, target in zip(sim_preds, labels): - if pred == target: - n_correct += 1 - raw_preds = LabelConverter.decode(preds.data, preds_size.data, raw=True)[:config['base']['show_num']] - for raw_pred, pred, gt in zip(raw_preds, sim_preds, labels): - print('%-20s => %-20s, gt: %-20s' % (raw_pred, pred, gt)) - - val_acc = n_correct / float(len(test_data_loader) * config['testload']['batch_size']) - val_loss = np.mean(loss_avg) - print('Test loss: %f, accuray: %f' % (val_loss, val_acc)) - return val_acc,val_loss - -def TrainValProgram(config): - - config = yaml.load(open(args.config, 'r', encoding='utf-8'),Loader=yaml.FullLoader) - config = merge_config(config,args) - - os.environ["CUDA_VISIBLE_DEVICES"] = config['base']['gpu_id'] - - create_dir(config['base']['checkpoints']) - checkpoints = os.path.join(config['base']['checkpoints'], - "ag_%s_bb_%s_he_%s_bs_%d_ep_%d_%s" % (config['base']['algorithm'], - config['backbone']['function'].split(',')[-1], - config['head']['function'].split(',')[-1], - config['trainload']['batch_size'], - config['base']['n_epoch'], - args.log_str)) - create_dir(checkpoints) - - LabelConverter = create_module(config['label_transform']['function'])(config) - config['base']['classes'] = len(LabelConverter.alphabet) - model = create_module(config['architectures']['model_function'])(config) - criterion = create_module(config['architectures']['loss_function'])(config) - train_dataset = create_module(config['trainload']['function'])(config) - test_dataset = create_module(config['testload']['function'])(config) - optimizer = create_module(config['optimizer']['function'])(config, model) - optimizer_decay = create_module(config['optimizer_decay']['function']) - - if config['loss']['use_center']: - center_criterion = create_module(config['loss']['center_function'])(config['base']['classes'],config['base']['classes']) - optimizer_center = torch.optim.Adam(center_criterion.parameters(), lr= config['loss']['center_lr']) - optimizer_decay_center = create_module(config['optimizer_decay_center']['function']) - else: - center_criterion = None - optimizer_center=None - - - train_data_loader = torch.utils.data.DataLoader( - train_dataset, - batch_size=config['trainload']['batch_size'], - shuffle=True, - num_workers=config['trainload']['num_workers'], - worker_init_fn = worker_init_fn, - collate_fn = alignCollate(), - drop_last=True, - pin_memory=True) - - test_data_loader = torch.utils.data.DataLoader( - test_dataset, - batch_size=config['testload']['batch_size'], - shuffle=False, - num_workers=config['testload']['num_workers'], - collate_fn = alignCollate(), - drop_last=True, - pin_memory=True) - - loss_bin = create_loss_bin(config['base']['algorithm'],use_center=config['loss']['use_center']) - - if torch.cuda.is_available(): - if (len(config['base']['gpu_id'].split(',')) > 1): - model = torch.nn.DataParallel(model).cuda() - else: - model = model.cuda() - criterion = criterion.cuda() - - start_epoch = 0 - val_acc = 0 - val_loss = 0 - best_acc = 0 - - - if config['base']['restore']: - print('Resuming from checkpoint.') - assert os.path.isfile(config['base']['restore_file']), 'Error: no checkpoint file found!' - checkpoint = torch.load(config['base']['restore_file']) - start_epoch = checkpoint['epoch'] - model.load_state_dict(checkpoint['state_dict']) - optimizer.load_state_dict(checkpoint['optimizer']) - best_acc = checkpoint['best_acc'] - if not config['loss']['use_center']: - log_write = Logger(os.path.join(checkpoints, 'log.txt'), title=config['base']['algorithm'], resume=True) - if config['loss']['use_center']: - if os.path.exists(os.path.join(checkpoints, 'log_center.txt')): - log_write = Logger(os.path.join(checkpoints, 'log_center.txt'), title=config['base']['algorithm'], resume=True) - else: - log_write = Logger(os.path.join(checkpoints, 'log_center.txt'), title=config['base']['algorithm']) - title = list(loss_bin.keys()) - title.extend(['val_loss','test_acc','best_acc']) - log_write.set_names(title) - else: - print('Training from scratch.') - log_write = Logger(os.path.join(checkpoints, 'log.txt'), title=config['base']['algorithm']) - title = list(loss_bin.keys()) - title.extend(['val_loss','test_acc','best_acc']) - log_write.set_names(title) - center_flag = False - center_model = None - if config['base']['finetune']: - start_epoch = 0 - optimizer.param_groups[0]['lr'] = 0.0001 - center_flag = True - center_model = copy.deepcopy(model) - for epoch in range(start_epoch,config['base']['n_epoch']): - model.train() - optimizer_decay(config, optimizer, epoch) - if config['loss']['use_center']: - optimizer_decay_center(config, optimizer_center, epoch) - loss_write,loss_flag = ModelTrain(train_data_loader,LabelConverter, model,center_model, criterion, optimizer, center_criterion,optimizer_center,center_flag,loss_bin, config, epoch) -# if loss_flag < config['loss']['min_score']: -# center_flag = True - if(epoch >= config['base']['start_val']): - model.eval() - val_acc,val_loss = ModelEval(test_data_loader,LabelConverter, model,criterion ,config) - print('val_acc:',val_acc,'val_loss',val_loss) - if (val_acc > best_acc): - save_checkpoint({ - 'epoch': epoch + 1, - 'state_dict': model.state_dict(), - 'lr': config['optimizer']['base_lr'], - 'optimizer': optimizer.state_dict(), - 'best_acc': val_acc - }, checkpoints, config['base']['algorithm'] + '_best' + '.pth.tar') - best_acc = val_acc - - - loss_write.extend([val_loss,val_acc,best_acc]) - log_write.append(loss_write) - for key in loss_bin.keys(): - loss_bin[key].loss_clear() - if epoch%config['base']['save_epoch'] ==0: - save_checkpoint({ - 'epoch': epoch + 1, - 'state_dict': model.state_dict(), - 'lr': config['optimizer']['base_lr'], - 'optimizer': optimizer.state_dict(), - 'best_acc': 0 - },checkpoints,config['base']['algorithm']+'_'+str(epoch)+'.pth.tar') - - -if __name__ == "__main__": - - - parser = argparse.ArgumentParser(description='Hyperparams') - parser.add_argument('--config', help='config file path') - parser.add_argument('--log_str', help='log title') - args = parser.parse_args() - - - TrainValProgram(args) \ No newline at end of file From adff357b46551a45d2dc18949938d0cc31edf1cd Mon Sep 17 00:00:00 2001 From: BADBADBADBOY <15671628547@163.COM> Date: Sat, 1 May 2021 10:48:03 +0800 Subject: [PATCH 2/4] add version2 --- README.md | 14 + bg_img/1.jpg | Bin 0 -> 12316 bytes bg_img/2.jpg | Bin 0 -> 167959 bytes bg_img/3.jpg | Bin 0 -> 372032 bytes bg_img/4.jpg | Bin 0 -> 171037 bytes bg_img/5.jpg | Bin 0 -> 656883 bytes bg_img/6.jpg | Bin 0 -> 47179 bytes bg_img/7.jpg | Bin 0 -> 26083 bytes bg_img/8.jpg | Bin 0 -> 240609 bytes bg_img/9.jpg | Bin 0 -> 112154 bytes config/det_DB_mobilev3.yaml | 87 + config/det_DB_resnet50.yaml | 87 + config/det_DB_resnet50_3_3.yaml | 87 + config/det_PAN_mobilev3.yaml | 82 + config/det_PAN_resnet18.yaml | 87 + config/det_PAN_resnet18_3_3.yaml | 87 + config/det_PSE_mobilev3.yaml | 78 + config/det_PSE_resnet50.yaml | 87 + config/det_PSE_resnet50_3_3.yaml | 87 + config/det_SAST_resnet50.yaml | 100 + .../det_SAST_resnet50_3_3_ori_dataload.yaml | 100 + config/det_SAST_resnet50_ori_dataload.yaml | 111 + .../rec_CRNN_mobilev3_large_english_all.yaml | 102 + .../rec_CRNN_mobilev3_large_english_lmdb.yaml | 77 + .../rec_CRNN_mobilev3_small_english_all.yaml | 104 + .../rec_CRNN_mobilev3_small_english_lmdb.yaml | 77 + config/rec_CRNN_resnet34_english_lmdb.yaml | 77 + config/rec_CRNN_resnet_english.yaml | 96 + config/rec_CRNN_resnet_english_all.yaml | 102 + config/rec_FC_resnet_english_all.yaml | 107 + doc/example/det_test_list.txt | 9 + doc/example/det_train_list.txt | 9 + doc/example/label.txt | 3 + doc/example/rec_test_list.txt | 13 + doc/example/rec_train_list.txt | 10 + doc/md/ocr.jpg | Bin 0 -> 197278 bytes doc/md/onnx_to_tensorrt.md | 62 + doc/md/pytorch_to_onnx.md | 34 + ...55\347\273\203\346\226\207\346\241\243.md" | 62 + ...55\347\273\203\346\226\207\346\241\243.md" | 20 + ...41\345\236\213\345\211\252\346\236\235.md" | 34 + ...41\345\236\213\350\222\270\351\246\217.md" | 25 + doc/show/ocr1.jpg | Bin 0 -> 237249 bytes doc/show/ocr2.jpg | Bin 0 -> 47777 bytes onnx/onnx-simple.sh | 1 + ptocr/__init__.py | 6 + ptocr/dataloader/DetLoad/DBProcess.py | 123 + ptocr/dataloader/DetLoad/MakeBorderMap.py | 114 + ptocr/dataloader/DetLoad/MakeSegMap.py | 171 + ptocr/dataloader/DetLoad/PANProcess.py | 121 + ptocr/dataloader/DetLoad/PSEProcess.py | 120 + ptocr/dataloader/DetLoad/SASTProcess.py | 504 ++ ptocr/dataloader/DetLoad/SASTProcess_ori.py | 800 +++ ptocr/dataloader/DetLoad/SASTProcess_ori1.py | 862 +++ ptocr/dataloader/DetLoad/__init__.py | 6 + .../__pycache__/DBProcess.cpython-36.pyc | Bin 0 -> 4664 bytes .../__pycache__/MakeBorderMap.cpython-36.pyc | Bin 0 -> 4034 bytes .../__pycache__/MakeSegMap.cpython-36.pyc | Bin 0 -> 5294 bytes .../SASTProcess_ori.cpython-36.pyc | Bin 0 -> 22675 bytes .../SASTProcess_ori1.cpython-36.pyc | Bin 0 -> 23827 bytes .../__pycache__/__init__.cpython-36.pyc | Bin 0 -> 186 bytes .../__pycache__/transform_img.cpython-36.pyc | Bin 0 -> 10578 bytes ptocr/dataloader/DetLoad/transform_img.py | 329 ++ ptocr/dataloader/RecLoad/CRNNProcess.py | 100 + ptocr/dataloader/RecLoad/CRNNProcess1.py | 244 + ptocr/dataloader/RecLoad/DataAgument.py | 390 ++ ptocr/dataloader/RecLoad/__init__.py | 6 + .../__pycache__/CRNNProcess.cpython-36.pyc | Bin 0 -> 3613 bytes .../__pycache__/CRNNProcess1.cpython-36.pyc | Bin 0 -> 5858 bytes .../__pycache__/DataAgument.cpython-36.pyc | Bin 0 -> 11013 bytes .../__pycache__/__init__.cpython-36.pyc | Bin 0 -> 186 bytes ptocr/dataloader/__init__.py | 6 + .../__pycache__/__init__.cpython-36.pyc | Bin 0 -> 178 bytes ptocr/model/CommonFunction.py | 77 + ptocr/model/CommonFunction_Q.py | 48 + ptocr/model/__init__.py | 7 + .../__pycache__/CommonFunction.cpython-36.pyc | Bin 0 -> 3102 bytes .../model/__pycache__/__init__.cpython-36.pyc | Bin 0 -> 235 bytes ptocr/model/architectures/__init__.py | 6 + .../__pycache__/__init__.cpython-35.pyc | Bin 0 -> 233 bytes .../__pycache__/__init__.cpython-36.pyc | Bin 0 -> 187 bytes .../__pycache__/det_model.cpython-35.pyc | Bin 0 -> 3746 bytes .../__pycache__/det_model.cpython-36.pyc | Bin 0 -> 3302 bytes .../__pycache__/rec_model.cpython-36.pyc | Bin 0 -> 2091 bytes .../__pycache__/stn.cpython-36.pyc | Bin 0 -> 6281 bytes .../__pycache__/stn_head.cpython-36.pyc | Bin 0 -> 2972 bytes .../tps_spatial_transformer.cpython-36.pyc | Bin 0 -> 3463 bytes ptocr/model/architectures/det_model.py | 120 + ptocr/model/architectures/det_model_q.py | 85 + ptocr/model/architectures/paddle_tps.py | 252 + ptocr/model/architectures/rec_model.py | 125 + ptocr/model/backbone/__init__.py | 6 + .../__pycache__/__init__.cpython-35.pyc | Bin 0 -> 228 bytes .../__pycache__/__init__.cpython-36.pyc | Bin 0 -> 182 bytes .../__pycache__/det_mobilev3.cpython-35.pyc | Bin 0 -> 8296 bytes .../__pycache__/det_mobilev3.cpython-36.pyc | Bin 0 -> 7813 bytes .../det_mobilev3_dcd.cpython-36.pyc | Bin 0 -> 11209 bytes .../__pycache__/det_resnet.cpython-35.pyc | Bin 0 -> 9756 bytes .../__pycache__/det_resnet.cpython-36.pyc | Bin 0 -> 9459 bytes .../__pycache__/det_resnet_3_3.cpython-36.pyc | Bin 0 -> 6798 bytes .../det_resnet_sast.cpython-35.pyc | Bin 0 -> 9939 bytes .../det_resnet_sast.cpython-36.pyc | Bin 0 -> 9319 bytes .../det_resnet_sast_3_3.cpython-36.pyc | Bin 0 -> 6900 bytes .../__pycache__/det_scnet.cpython-35.pyc | Bin 0 -> 9087 bytes .../rec_crnn_backbone.cpython-36.pyc | Bin 0 -> 2591 bytes .../rec_mobilev3_bd.cpython-36.pyc | Bin 0 -> 6591 bytes .../__pycache__/rec_vgg.cpython-36.pyc | Bin 0 -> 7165 bytes .../__pycache__/reg_resnet_bd.cpython-36.pyc | Bin 0 -> 5731 bytes ptocr/model/backbone/det_mobilev3.py | 244 + .../backbone/det_mobilev3_pytorch_qua.py | 137 + ptocr/model/backbone/det_resnet.py | 368 ++ ptocr/model/backbone/det_resnet_3_3.py | 233 + ptocr/model/backbone/det_resnet_sast.py | 362 ++ ptocr/model/backbone/det_resnet_sast_3_3.py | 238 + ptocr/model/backbone/rec_mobilev3_bd.py | 279 + ptocr/model/backbone/reg_mobilev3.py | 218 + ptocr/model/backbone/reg_resnet_bd.py | 267 + ptocr/model/head/__init__.py | 6 + .../head/__pycache__/__init__.cpython-35.pyc | Bin 0 -> 224 bytes .../head/__pycache__/__init__.cpython-36.pyc | Bin 0 -> 178 bytes .../__pycache__/det_DBHead.cpython-35.pyc | Bin 0 -> 2057 bytes .../__pycache__/det_DBHead.cpython-36.pyc | Bin 0 -> 1770 bytes .../det_FPEM_FFM_Head.cpython-35.pyc | Bin 0 -> 4122 bytes .../det_FPEM_FFM_Head.cpython-36.pyc | Bin 0 -> 3585 bytes .../__pycache__/det_FPNHead.cpython-35.pyc | Bin 0 -> 2166 bytes .../__pycache__/det_FPNHead.cpython-36.pyc | Bin 0 -> 1819 bytes .../__pycache__/det_SASTHead.cpython-35.pyc | Bin 0 -> 6380 bytes .../__pycache__/det_SASTHead.cpython-36.pyc | Bin 0 -> 6155 bytes .../__pycache__/rec_CRNNHead.cpython-36.pyc | Bin 0 -> 3906 bytes .../__pycache__/rec_FCHead.cpython-36.pyc | Bin 0 -> 2841 bytes ptocr/model/head/det_DBHead.py | 54 + ptocr/model/head/det_DBHead_Qua.py | 72 + ptocr/model/head/det_FPEM_FFM_Head.py | 99 + ptocr/model/head/det_FPNHead.py | 59 + ptocr/model/head/det_SASTHead.py | 216 + ptocr/model/head/rec_CRNNHead.py | 121 + ptocr/model/head/rec_FCHead.py | 106 + ptocr/model/loss/__init__.py | 6 + .../loss/__pycache__/__init__.cpython-35.pyc | Bin 0 -> 224 bytes .../loss/__pycache__/__init__.cpython-36.pyc | Bin 0 -> 178 bytes .../__pycache__/basical_loss.cpython-35.pyc | Bin 0 -> 10424 bytes .../__pycache__/basical_loss.cpython-36.pyc | Bin 0 -> 10364 bytes .../__pycache__/centerloss.cpython-36.pyc | Bin 0 -> 3637 bytes .../loss/__pycache__/ctc_loss.cpython-36.pyc | Bin 0 -> 1144 bytes .../loss/__pycache__/db_loss.cpython-35.pyc | Bin 0 -> 1506 bytes .../loss/__pycache__/db_loss.cpython-36.pyc | Bin 0 -> 1371 bytes .../loss/__pycache__/fc_loss.cpython-36.pyc | Bin 0 -> 839 bytes .../loss/__pycache__/pan_loss.cpython-35.pyc | Bin 0 -> 2435 bytes .../loss/__pycache__/pan_loss.cpython-36.pyc | Bin 0 -> 2295 bytes .../loss/__pycache__/pse_loss.cpython-35.pyc | Bin 0 -> 2202 bytes .../loss/__pycache__/pse_loss.cpython-36.pyc | Bin 0 -> 1981 bytes .../loss/__pycache__/sast_loss.cpython-35.pyc | Bin 0 -> 3250 bytes .../loss/__pycache__/sast_loss.cpython-36.pyc | Bin 0 -> 2973 bytes ptocr/model/loss/basical_loss.py | 293 ++ ptocr/model/loss/centerloss.py | 95 + ptocr/model/loss/ctc_loss.py | 22 + ptocr/model/loss/db_loss.py | 30 + ptocr/model/loss/fc_loss.py | 14 + ptocr/model/loss/pan_loss.py | 66 + ptocr/model/loss/pse_loss.py | 52 + ptocr/model/loss/sast_loss.py | 109 + ptocr/model/segout/__init__.py | 6 + .../__pycache__/__init__.cpython-35.pyc | Bin 0 -> 226 bytes .../__pycache__/__init__.cpython-36.pyc | Bin 0 -> 180 bytes .../__pycache__/det_DB_segout.cpython-35.pyc | Bin 0 -> 3235 bytes .../__pycache__/det_DB_segout.cpython-36.pyc | Bin 0 -> 2929 bytes .../__pycache__/det_PAN_segout.cpython-35.pyc | Bin 0 -> 1143 bytes .../__pycache__/det_PAN_segout.cpython-36.pyc | Bin 0 -> 1045 bytes .../__pycache__/det_PSE_segout.cpython-35.pyc | Bin 0 -> 1091 bytes .../__pycache__/det_PSE_segout.cpython-36.pyc | Bin 0 -> 999 bytes .../det_SAST_segout.cpython-35.pyc | Bin 0 -> 3114 bytes .../det_SAST_segout.cpython-36.pyc | Bin 0 -> 3141 bytes ptocr/model/segout/det_DB_segout.py | 90 + ptocr/model/segout/det_DB_segout_qua.py | 58 + ptocr/model/segout/det_PAN_segout.py | 23 + ptocr/model/segout/det_PSE_segout.py | 20 + ptocr/model/segout/det_SAST_segout.py | 91 + ptocr/optimizer.py | 69 + ptocr/postprocess/DBpostprocess.py | 219 + ptocr/postprocess/PANpostprocess.py | 135 + ptocr/postprocess/PSEpostprocess.py | 135 + ptocr/postprocess/SASTpostprocess.py | 294 ++ ptocr/postprocess/__init__.py | 6 + .../__pycache__/DBpostprocess.cpython-36.pyc | Bin 0 -> 5942 bytes .../SASTpostprocess.cpython-36.pyc | Bin 0 -> 8670 bytes .../__pycache__/__init__.cpython-36.pyc | Bin 0 -> 179 bytes .../locality_aware_nms.cpython-36.pyc | Bin 0 -> 4624 bytes ptocr/postprocess/dbprocess/Makefile | 15 + ptocr/postprocess/dbprocess/__init__.py | 32 + .../__pycache__/__init__.cpython-36.pyc | Bin 0 -> 692 bytes ptocr/postprocess/dbprocess/cppdbprocess.cpp | 369 ++ ptocr/postprocess/dbprocess/cppdbprocess.so | Bin 0 -> 299392 bytes ptocr/postprocess/dbprocess/include/clipper.h | 423 ++ .../dbprocess/include/clipper/clipper.cpp | 4622 +++++++++++++++++ .../dbprocess/include/clipper/clipper.hpp | 404 ++ .../dbprocess/include/postprocess_op.h | 91 + .../dbprocess/include/pybind11/attr.h | 492 ++ .../dbprocess/include/pybind11/buffer_info.h | 108 + .../dbprocess/include/pybind11/cast.h | 2128 ++++++++ .../dbprocess/include/pybind11/chrono.h | 162 + .../include/pybind11/class_support.h | 603 +++ .../dbprocess/include/pybind11/common.h | 2 + .../dbprocess/include/pybind11/complex.h | 65 + .../dbprocess/include/pybind11/descr.h | 185 + .../dbprocess/include/pybind11/detail/class.h | 622 +++ .../include/pybind11/detail/common.h | 807 +++ .../dbprocess/include/pybind11/detail/descr.h | 100 + .../dbprocess/include/pybind11/detail/init.h | 335 ++ .../include/pybind11/detail/internals.h | 291 ++ .../include/pybind11/detail/typeid.h | 53 + .../dbprocess/include/pybind11/eigen.h | 607 +++ .../dbprocess/include/pybind11/embed.h | 200 + .../dbprocess/include/pybind11/eval.h | 117 + .../dbprocess/include/pybind11/functional.h | 83 + .../dbprocess/include/pybind11/iostream.h | 200 + .../dbprocess/include/pybind11/numpy.h | 1610 ++++++ .../dbprocess/include/pybind11/operators.h | 168 + .../dbprocess/include/pybind11/options.h | 65 + .../dbprocess/include/pybind11/pybind11.h | 2094 ++++++++ .../dbprocess/include/pybind11/pytypes.h | 1438 +++++ .../dbprocess/include/pybind11/stl.h | 386 ++ .../dbprocess/include/pybind11/stl_bind.h | 599 +++ .../dbprocess/include/pybind11/typeid.h | 53 + ptocr/postprocess/lanms/.gitignore | 1 + ptocr/postprocess/lanms/.ycm_extra_conf.py | 140 + ptocr/postprocess/lanms/Makefile | 13 + ptocr/postprocess/lanms/__init__.py | 20 + ptocr/postprocess/lanms/__main__.py | 10 + .../lanms/__pycache__/__init__.cpython-36.pyc | Bin 0 -> 734 bytes ptocr/postprocess/lanms/adaptor.cpp | 61 + .../lanms/include/clipper/clipper.cpp | 4622 +++++++++++++++++ .../lanms/include/clipper/clipper.hpp | 404 ++ .../postprocess/lanms/include/pybind11/attr.h | 471 ++ .../lanms/include/pybind11/buffer_info.h | 108 + .../postprocess/lanms/include/pybind11/cast.h | 2058 ++++++++ .../lanms/include/pybind11/chrono.h | 162 + .../lanms/include/pybind11/class_support.h | 603 +++ .../lanms/include/pybind11/common.h | 857 +++ .../lanms/include/pybind11/complex.h | 61 + .../lanms/include/pybind11/descr.h | 185 + .../lanms/include/pybind11/eigen.h | 610 +++ .../lanms/include/pybind11/embed.h | 194 + .../postprocess/lanms/include/pybind11/eval.h | 117 + .../lanms/include/pybind11/functional.h | 85 + .../lanms/include/pybind11/numpy.h | 1598 ++++++ .../lanms/include/pybind11/operators.h | 167 + .../lanms/include/pybind11/options.h | 65 + .../lanms/include/pybind11/pybind11.h | 1869 +++++++ .../lanms/include/pybind11/pytypes.h | 1318 +++++ .../postprocess/lanms/include/pybind11/stl.h | 367 ++ .../lanms/include/pybind11/stl_bind.h | 585 +++ .../lanms/include/pybind11/typeid.h | 53 + ptocr/postprocess/lanms/lanms.h | 234 + ptocr/postprocess/locality_aware_nms.py | 199 + ptocr/postprocess/piexlmerge/Makefile | 15 + ptocr/postprocess/piexlmerge/__init__.py | 88 + .../piexlmerge/include/clipper/clipper.cpp | 4622 +++++++++++++++++ .../piexlmerge/include/clipper/clipper.hpp | 404 ++ .../piexlmerge/include/pybind11/attr.h | 492 ++ .../piexlmerge/include/pybind11/buffer_info.h | 108 + .../piexlmerge/include/pybind11/cast.h | 2128 ++++++++ .../piexlmerge/include/pybind11/chrono.h | 162 + .../include/pybind11/class_support.h | 603 +++ .../piexlmerge/include/pybind11/common.h | 2 + .../piexlmerge/include/pybind11/complex.h | 65 + .../piexlmerge/include/pybind11/descr.h | 185 + .../include/pybind11/detail/class.h | 622 +++ .../include/pybind11/detail/common.h | 807 +++ .../include/pybind11/detail/descr.h | 100 + .../piexlmerge/include/pybind11/detail/init.h | 335 ++ .../include/pybind11/detail/internals.h | 291 ++ .../include/pybind11/detail/typeid.h | 53 + .../piexlmerge/include/pybind11/eigen.h | 607 +++ .../piexlmerge/include/pybind11/embed.h | 200 + .../piexlmerge/include/pybind11/eval.h | 117 + .../piexlmerge/include/pybind11/functional.h | 83 + .../piexlmerge/include/pybind11/iostream.h | 200 + .../piexlmerge/include/pybind11/numpy.h | 1610 ++++++ .../piexlmerge/include/pybind11/operators.h | 168 + .../piexlmerge/include/pybind11/options.h | 65 + .../piexlmerge/include/pybind11/pybind11.h | 2094 ++++++++ .../piexlmerge/include/pybind11/pytypes.h | 1438 +++++ .../piexlmerge/include/pybind11/stl.h | 386 ++ .../piexlmerge/include/pybind11/stl_bind.h | 599 +++ .../piexlmerge/include/pybind11/typeid.h | 53 + ptocr/postprocess/piexlmerge/lanms.h | 234 + ptocr/postprocess/piexlmerge/pixelmerge.cpp | 310 ++ ptocr/postprocess/piexlmerge/pixelmerge.so | Bin 0 -> 291416 bytes ptocr/utils/__init__.py | 6 + .../utils/__pycache__/__init__.cpython-36.pyc | Bin 0 -> 173 bytes .../__pycache__/cal_iou_acc.cpython-36.pyc | Bin 0 -> 2126 bytes .../gen_teacher_model.cpython-36.pyc | Bin 0 -> 2191 bytes ptocr/utils/__pycache__/logger.cpython-36.pyc | Bin 0 -> 2953 bytes .../utils/__pycache__/metrics.cpython-36.pyc | Bin 0 -> 1905 bytes .../__pycache__/prune_script.cpython-36.pyc | Bin 0 -> 10396 bytes .../transform_label.cpython-36.pyc | Bin 0 -> 6925 bytes .../__pycache__/util_function.cpython-36.pyc | Bin 0 -> 6559 bytes ptocr/utils/cal_iou_acc.py | 64 + ptocr/utils/gen_teacher_model.py | 57 + ptocr/utils/logger.py | 94 + ptocr/utils/metrics.py | 50 + ptocr/utils/prune_script.py | 572 ++ ptocr/utils/transform_label.py | 198 + ptocr/utils/util_function.py | 245 + script/__init__.py | 6 + script/create_lmdb.py | 126 + script/create_lmdb_multiprocessing.py | 206 + script/get_key_label.py | 31 + script/get_train_list.py | 30 + script/onnx_to_tensorrt.py | 188 + script/pytorch_to_onnx.py | 144 + to_onnx.sh | 1 + to_tensorrt.sh | 1 + tools/__init__.py | 6 + tools/cal.py | 11 + tools/cal_rescall/__init__.py | 6 + .../__pycache__/__init__.cpython-35.pyc | Bin 0 -> 225 bytes .../__pycache__/__init__.cpython-36.pyc | Bin 0 -> 179 bytes .../__pycache__/cal_det.cpython-36.pyc | Bin 0 -> 1666 bytes .../__pycache__/cal_iou.cpython-36.pyc | Bin 0 -> 4942 bytes .../rrc_evaluation_funcs.cpython-35.pyc | Bin 0 -> 14438 bytes .../rrc_evaluation_funcs.cpython-36.pyc | Bin 0 -> 13315 bytes .../__pycache__/script.cpython-35.pyc | Bin 0 -> 8636 bytes .../__pycache__/script.cpython-36.pyc | Bin 0 -> 7714 bytes tools/cal_rescall/cal_det.py | 53 + tools/cal_rescall/cal_iou.py | 235 + tools/cal_rescall/rrc_evaluation_funcs.py | 414 ++ tools/cal_rescall/script.py | 323 ++ tools/det_infer.py | 192 + tools/det_sast.py | 238 + tools/det_train.py | 307 ++ tools/det_train_qua.py | 333 ++ tools/pruned/__init__.py | 0 tools/pruned/prune_model_all.py | 477 ++ tools/pruned/prune_model_backbone.py | 365 ++ tools/rec_infer.py | 199 + tools/rec_infer1.py | 159 + tools/rec_train.py | 229 + tools/rec_train1.py | 396 ++ tools/rec_train2.py | 332 ++ tools/rec_train3.py | 376 ++ 341 files changed, 74340 insertions(+) create mode 100644 bg_img/1.jpg create mode 100644 bg_img/2.jpg create mode 100644 bg_img/3.jpg create mode 100644 bg_img/4.jpg create mode 100644 bg_img/5.jpg create mode 100644 bg_img/6.jpg create mode 100644 bg_img/7.jpg create mode 100644 bg_img/8.jpg create mode 100644 bg_img/9.jpg create mode 100644 config/det_DB_mobilev3.yaml create mode 100644 config/det_DB_resnet50.yaml create mode 100644 config/det_DB_resnet50_3_3.yaml create mode 100644 config/det_PAN_mobilev3.yaml create mode 100644 config/det_PAN_resnet18.yaml create mode 100644 config/det_PAN_resnet18_3_3.yaml create mode 100644 config/det_PSE_mobilev3.yaml create mode 100644 config/det_PSE_resnet50.yaml create mode 100644 config/det_PSE_resnet50_3_3.yaml create mode 100644 config/det_SAST_resnet50.yaml create mode 100644 config/det_SAST_resnet50_3_3_ori_dataload.yaml create mode 100644 config/det_SAST_resnet50_ori_dataload.yaml create mode 100644 config/rec_CRNN_mobilev3_large_english_all.yaml create mode 100644 config/rec_CRNN_mobilev3_large_english_lmdb.yaml create mode 100644 config/rec_CRNN_mobilev3_small_english_all.yaml create mode 100644 config/rec_CRNN_mobilev3_small_english_lmdb.yaml create mode 100644 config/rec_CRNN_resnet34_english_lmdb.yaml create mode 100644 config/rec_CRNN_resnet_english.yaml create mode 100644 config/rec_CRNN_resnet_english_all.yaml create mode 100644 config/rec_FC_resnet_english_all.yaml create mode 100644 doc/example/det_test_list.txt create mode 100644 doc/example/det_train_list.txt create mode 100644 doc/example/label.txt create mode 100644 doc/example/rec_test_list.txt create mode 100644 doc/example/rec_train_list.txt create mode 100644 doc/md/ocr.jpg create mode 100644 doc/md/onnx_to_tensorrt.md create mode 100644 doc/md/pytorch_to_onnx.md create mode 100644 "doc/md/\346\226\207\346\234\254\346\243\200\346\265\213\350\256\255\347\273\203\346\226\207\346\241\243.md" create mode 100644 "doc/md/\346\226\207\346\234\254\350\257\206\345\210\253\350\256\255\347\273\203\346\226\207\346\241\243.md" create mode 100644 "doc/md/\346\250\241\345\236\213\345\211\252\346\236\235.md" create mode 100644 "doc/md/\346\250\241\345\236\213\350\222\270\351\246\217.md" create mode 100644 doc/show/ocr1.jpg create mode 100644 doc/show/ocr2.jpg create mode 100644 onnx/onnx-simple.sh create mode 100644 ptocr/__init__.py create mode 100644 ptocr/dataloader/DetLoad/DBProcess.py create mode 100644 ptocr/dataloader/DetLoad/MakeBorderMap.py create mode 100644 ptocr/dataloader/DetLoad/MakeSegMap.py create mode 100644 ptocr/dataloader/DetLoad/PANProcess.py create mode 100644 ptocr/dataloader/DetLoad/PSEProcess.py create mode 100644 ptocr/dataloader/DetLoad/SASTProcess.py create mode 100644 ptocr/dataloader/DetLoad/SASTProcess_ori.py create mode 100644 ptocr/dataloader/DetLoad/SASTProcess_ori1.py create mode 100644 ptocr/dataloader/DetLoad/__init__.py create mode 100644 ptocr/dataloader/DetLoad/__pycache__/DBProcess.cpython-36.pyc create mode 100644 ptocr/dataloader/DetLoad/__pycache__/MakeBorderMap.cpython-36.pyc create mode 100644 ptocr/dataloader/DetLoad/__pycache__/MakeSegMap.cpython-36.pyc create mode 100644 ptocr/dataloader/DetLoad/__pycache__/SASTProcess_ori.cpython-36.pyc create mode 100644 ptocr/dataloader/DetLoad/__pycache__/SASTProcess_ori1.cpython-36.pyc create mode 100644 ptocr/dataloader/DetLoad/__pycache__/__init__.cpython-36.pyc create mode 100644 ptocr/dataloader/DetLoad/__pycache__/transform_img.cpython-36.pyc create mode 100644 ptocr/dataloader/DetLoad/transform_img.py create mode 100644 ptocr/dataloader/RecLoad/CRNNProcess.py create mode 100644 ptocr/dataloader/RecLoad/CRNNProcess1.py create mode 100644 ptocr/dataloader/RecLoad/DataAgument.py create mode 100644 ptocr/dataloader/RecLoad/__init__.py create mode 100644 ptocr/dataloader/RecLoad/__pycache__/CRNNProcess.cpython-36.pyc create mode 100644 ptocr/dataloader/RecLoad/__pycache__/CRNNProcess1.cpython-36.pyc create mode 100644 ptocr/dataloader/RecLoad/__pycache__/DataAgument.cpython-36.pyc create mode 100644 ptocr/dataloader/RecLoad/__pycache__/__init__.cpython-36.pyc create mode 100644 ptocr/dataloader/__init__.py create mode 100644 ptocr/dataloader/__pycache__/__init__.cpython-36.pyc create mode 100644 ptocr/model/CommonFunction.py create mode 100644 ptocr/model/CommonFunction_Q.py create mode 100644 ptocr/model/__init__.py create mode 100644 ptocr/model/__pycache__/CommonFunction.cpython-36.pyc create mode 100644 ptocr/model/__pycache__/__init__.cpython-36.pyc create mode 100644 ptocr/model/architectures/__init__.py create mode 100644 ptocr/model/architectures/__pycache__/__init__.cpython-35.pyc create mode 100644 ptocr/model/architectures/__pycache__/__init__.cpython-36.pyc create mode 100644 ptocr/model/architectures/__pycache__/det_model.cpython-35.pyc create mode 100644 ptocr/model/architectures/__pycache__/det_model.cpython-36.pyc create mode 100644 ptocr/model/architectures/__pycache__/rec_model.cpython-36.pyc create mode 100644 ptocr/model/architectures/__pycache__/stn.cpython-36.pyc create mode 100644 ptocr/model/architectures/__pycache__/stn_head.cpython-36.pyc create mode 100644 ptocr/model/architectures/__pycache__/tps_spatial_transformer.cpython-36.pyc create mode 100644 ptocr/model/architectures/det_model.py create mode 100644 ptocr/model/architectures/det_model_q.py create mode 100644 ptocr/model/architectures/paddle_tps.py create mode 100644 ptocr/model/architectures/rec_model.py create mode 100644 ptocr/model/backbone/__init__.py create mode 100644 ptocr/model/backbone/__pycache__/__init__.cpython-35.pyc create mode 100644 ptocr/model/backbone/__pycache__/__init__.cpython-36.pyc create mode 100644 ptocr/model/backbone/__pycache__/det_mobilev3.cpython-35.pyc create mode 100644 ptocr/model/backbone/__pycache__/det_mobilev3.cpython-36.pyc create mode 100644 ptocr/model/backbone/__pycache__/det_mobilev3_dcd.cpython-36.pyc create mode 100644 ptocr/model/backbone/__pycache__/det_resnet.cpython-35.pyc create mode 100644 ptocr/model/backbone/__pycache__/det_resnet.cpython-36.pyc create mode 100644 ptocr/model/backbone/__pycache__/det_resnet_3_3.cpython-36.pyc create mode 100644 ptocr/model/backbone/__pycache__/det_resnet_sast.cpython-35.pyc create mode 100644 ptocr/model/backbone/__pycache__/det_resnet_sast.cpython-36.pyc create mode 100644 ptocr/model/backbone/__pycache__/det_resnet_sast_3_3.cpython-36.pyc create mode 100644 ptocr/model/backbone/__pycache__/det_scnet.cpython-35.pyc create mode 100644 ptocr/model/backbone/__pycache__/rec_crnn_backbone.cpython-36.pyc create mode 100644 ptocr/model/backbone/__pycache__/rec_mobilev3_bd.cpython-36.pyc create mode 100644 ptocr/model/backbone/__pycache__/rec_vgg.cpython-36.pyc create mode 100644 ptocr/model/backbone/__pycache__/reg_resnet_bd.cpython-36.pyc create mode 100644 ptocr/model/backbone/det_mobilev3.py create mode 100644 ptocr/model/backbone/det_mobilev3_pytorch_qua.py create mode 100644 ptocr/model/backbone/det_resnet.py create mode 100644 ptocr/model/backbone/det_resnet_3_3.py create mode 100644 ptocr/model/backbone/det_resnet_sast.py create mode 100644 ptocr/model/backbone/det_resnet_sast_3_3.py create mode 100644 ptocr/model/backbone/rec_mobilev3_bd.py create mode 100644 ptocr/model/backbone/reg_mobilev3.py create mode 100644 ptocr/model/backbone/reg_resnet_bd.py create mode 100644 ptocr/model/head/__init__.py create mode 100644 ptocr/model/head/__pycache__/__init__.cpython-35.pyc create mode 100644 ptocr/model/head/__pycache__/__init__.cpython-36.pyc create mode 100644 ptocr/model/head/__pycache__/det_DBHead.cpython-35.pyc create mode 100644 ptocr/model/head/__pycache__/det_DBHead.cpython-36.pyc create mode 100644 ptocr/model/head/__pycache__/det_FPEM_FFM_Head.cpython-35.pyc create mode 100644 ptocr/model/head/__pycache__/det_FPEM_FFM_Head.cpython-36.pyc create mode 100644 ptocr/model/head/__pycache__/det_FPNHead.cpython-35.pyc create mode 100644 ptocr/model/head/__pycache__/det_FPNHead.cpython-36.pyc create mode 100644 ptocr/model/head/__pycache__/det_SASTHead.cpython-35.pyc create mode 100644 ptocr/model/head/__pycache__/det_SASTHead.cpython-36.pyc create mode 100644 ptocr/model/head/__pycache__/rec_CRNNHead.cpython-36.pyc create mode 100644 ptocr/model/head/__pycache__/rec_FCHead.cpython-36.pyc create mode 100644 ptocr/model/head/det_DBHead.py create mode 100644 ptocr/model/head/det_DBHead_Qua.py create mode 100644 ptocr/model/head/det_FPEM_FFM_Head.py create mode 100644 ptocr/model/head/det_FPNHead.py create mode 100644 ptocr/model/head/det_SASTHead.py create mode 100644 ptocr/model/head/rec_CRNNHead.py create mode 100644 ptocr/model/head/rec_FCHead.py create mode 100644 ptocr/model/loss/__init__.py create mode 100644 ptocr/model/loss/__pycache__/__init__.cpython-35.pyc create mode 100644 ptocr/model/loss/__pycache__/__init__.cpython-36.pyc create mode 100644 ptocr/model/loss/__pycache__/basical_loss.cpython-35.pyc create mode 100644 ptocr/model/loss/__pycache__/basical_loss.cpython-36.pyc create mode 100644 ptocr/model/loss/__pycache__/centerloss.cpython-36.pyc create mode 100644 ptocr/model/loss/__pycache__/ctc_loss.cpython-36.pyc create mode 100644 ptocr/model/loss/__pycache__/db_loss.cpython-35.pyc create mode 100644 ptocr/model/loss/__pycache__/db_loss.cpython-36.pyc create mode 100644 ptocr/model/loss/__pycache__/fc_loss.cpython-36.pyc create mode 100644 ptocr/model/loss/__pycache__/pan_loss.cpython-35.pyc create mode 100644 ptocr/model/loss/__pycache__/pan_loss.cpython-36.pyc create mode 100644 ptocr/model/loss/__pycache__/pse_loss.cpython-35.pyc create mode 100644 ptocr/model/loss/__pycache__/pse_loss.cpython-36.pyc create mode 100644 ptocr/model/loss/__pycache__/sast_loss.cpython-35.pyc create mode 100644 ptocr/model/loss/__pycache__/sast_loss.cpython-36.pyc create mode 100644 ptocr/model/loss/basical_loss.py create mode 100644 ptocr/model/loss/centerloss.py create mode 100644 ptocr/model/loss/ctc_loss.py create mode 100644 ptocr/model/loss/db_loss.py create mode 100644 ptocr/model/loss/fc_loss.py create mode 100644 ptocr/model/loss/pan_loss.py create mode 100644 ptocr/model/loss/pse_loss.py create mode 100644 ptocr/model/loss/sast_loss.py create mode 100644 ptocr/model/segout/__init__.py create mode 100644 ptocr/model/segout/__pycache__/__init__.cpython-35.pyc create mode 100644 ptocr/model/segout/__pycache__/__init__.cpython-36.pyc create mode 100644 ptocr/model/segout/__pycache__/det_DB_segout.cpython-35.pyc create mode 100644 ptocr/model/segout/__pycache__/det_DB_segout.cpython-36.pyc create mode 100644 ptocr/model/segout/__pycache__/det_PAN_segout.cpython-35.pyc create mode 100644 ptocr/model/segout/__pycache__/det_PAN_segout.cpython-36.pyc create mode 100644 ptocr/model/segout/__pycache__/det_PSE_segout.cpython-35.pyc create mode 100644 ptocr/model/segout/__pycache__/det_PSE_segout.cpython-36.pyc create mode 100644 ptocr/model/segout/__pycache__/det_SAST_segout.cpython-35.pyc create mode 100644 ptocr/model/segout/__pycache__/det_SAST_segout.cpython-36.pyc create mode 100644 ptocr/model/segout/det_DB_segout.py create mode 100644 ptocr/model/segout/det_DB_segout_qua.py create mode 100644 ptocr/model/segout/det_PAN_segout.py create mode 100644 ptocr/model/segout/det_PSE_segout.py create mode 100644 ptocr/model/segout/det_SAST_segout.py create mode 100644 ptocr/optimizer.py create mode 100644 ptocr/postprocess/DBpostprocess.py create mode 100644 ptocr/postprocess/PANpostprocess.py create mode 100644 ptocr/postprocess/PSEpostprocess.py create mode 100644 ptocr/postprocess/SASTpostprocess.py create mode 100644 ptocr/postprocess/__init__.py create mode 100644 ptocr/postprocess/__pycache__/DBpostprocess.cpython-36.pyc create mode 100644 ptocr/postprocess/__pycache__/SASTpostprocess.cpython-36.pyc create mode 100644 ptocr/postprocess/__pycache__/__init__.cpython-36.pyc create mode 100644 ptocr/postprocess/__pycache__/locality_aware_nms.cpython-36.pyc create mode 100644 ptocr/postprocess/dbprocess/Makefile create mode 100644 ptocr/postprocess/dbprocess/__init__.py create mode 100644 ptocr/postprocess/dbprocess/__pycache__/__init__.cpython-36.pyc create mode 100644 ptocr/postprocess/dbprocess/cppdbprocess.cpp create mode 100644 ptocr/postprocess/dbprocess/cppdbprocess.so create mode 100644 ptocr/postprocess/dbprocess/include/clipper.h create mode 100644 ptocr/postprocess/dbprocess/include/clipper/clipper.cpp create mode 100644 ptocr/postprocess/dbprocess/include/clipper/clipper.hpp create mode 100644 ptocr/postprocess/dbprocess/include/postprocess_op.h create mode 100644 ptocr/postprocess/dbprocess/include/pybind11/attr.h create mode 100644 ptocr/postprocess/dbprocess/include/pybind11/buffer_info.h create mode 100644 ptocr/postprocess/dbprocess/include/pybind11/cast.h create mode 100644 ptocr/postprocess/dbprocess/include/pybind11/chrono.h create mode 100644 ptocr/postprocess/dbprocess/include/pybind11/class_support.h create mode 100644 ptocr/postprocess/dbprocess/include/pybind11/common.h create mode 100644 ptocr/postprocess/dbprocess/include/pybind11/complex.h create mode 100644 ptocr/postprocess/dbprocess/include/pybind11/descr.h create mode 100644 ptocr/postprocess/dbprocess/include/pybind11/detail/class.h create mode 100644 ptocr/postprocess/dbprocess/include/pybind11/detail/common.h create mode 100644 ptocr/postprocess/dbprocess/include/pybind11/detail/descr.h create mode 100644 ptocr/postprocess/dbprocess/include/pybind11/detail/init.h create mode 100644 ptocr/postprocess/dbprocess/include/pybind11/detail/internals.h create mode 100644 ptocr/postprocess/dbprocess/include/pybind11/detail/typeid.h create mode 100644 ptocr/postprocess/dbprocess/include/pybind11/eigen.h create mode 100644 ptocr/postprocess/dbprocess/include/pybind11/embed.h create mode 100644 ptocr/postprocess/dbprocess/include/pybind11/eval.h create mode 100644 ptocr/postprocess/dbprocess/include/pybind11/functional.h create mode 100644 ptocr/postprocess/dbprocess/include/pybind11/iostream.h create mode 100644 ptocr/postprocess/dbprocess/include/pybind11/numpy.h create mode 100644 ptocr/postprocess/dbprocess/include/pybind11/operators.h create mode 100644 ptocr/postprocess/dbprocess/include/pybind11/options.h create mode 100644 ptocr/postprocess/dbprocess/include/pybind11/pybind11.h create mode 100644 ptocr/postprocess/dbprocess/include/pybind11/pytypes.h create mode 100644 ptocr/postprocess/dbprocess/include/pybind11/stl.h create mode 100644 ptocr/postprocess/dbprocess/include/pybind11/stl_bind.h create mode 100644 ptocr/postprocess/dbprocess/include/pybind11/typeid.h create mode 100644 ptocr/postprocess/lanms/.gitignore create mode 100644 ptocr/postprocess/lanms/.ycm_extra_conf.py create mode 100644 ptocr/postprocess/lanms/Makefile create mode 100644 ptocr/postprocess/lanms/__init__.py create mode 100644 ptocr/postprocess/lanms/__main__.py create mode 100644 ptocr/postprocess/lanms/__pycache__/__init__.cpython-36.pyc create mode 100644 ptocr/postprocess/lanms/adaptor.cpp create mode 100644 ptocr/postprocess/lanms/include/clipper/clipper.cpp create mode 100644 ptocr/postprocess/lanms/include/clipper/clipper.hpp create mode 100644 ptocr/postprocess/lanms/include/pybind11/attr.h create mode 100644 ptocr/postprocess/lanms/include/pybind11/buffer_info.h create mode 100644 ptocr/postprocess/lanms/include/pybind11/cast.h create mode 100644 ptocr/postprocess/lanms/include/pybind11/chrono.h create mode 100644 ptocr/postprocess/lanms/include/pybind11/class_support.h create mode 100644 ptocr/postprocess/lanms/include/pybind11/common.h create mode 100644 ptocr/postprocess/lanms/include/pybind11/complex.h create mode 100644 ptocr/postprocess/lanms/include/pybind11/descr.h create mode 100644 ptocr/postprocess/lanms/include/pybind11/eigen.h create mode 100644 ptocr/postprocess/lanms/include/pybind11/embed.h create mode 100644 ptocr/postprocess/lanms/include/pybind11/eval.h create mode 100644 ptocr/postprocess/lanms/include/pybind11/functional.h create mode 100644 ptocr/postprocess/lanms/include/pybind11/numpy.h create mode 100644 ptocr/postprocess/lanms/include/pybind11/operators.h create mode 100644 ptocr/postprocess/lanms/include/pybind11/options.h create mode 100644 ptocr/postprocess/lanms/include/pybind11/pybind11.h create mode 100644 ptocr/postprocess/lanms/include/pybind11/pytypes.h create mode 100644 ptocr/postprocess/lanms/include/pybind11/stl.h create mode 100644 ptocr/postprocess/lanms/include/pybind11/stl_bind.h create mode 100644 ptocr/postprocess/lanms/include/pybind11/typeid.h create mode 100644 ptocr/postprocess/lanms/lanms.h create mode 100644 ptocr/postprocess/locality_aware_nms.py create mode 100644 ptocr/postprocess/piexlmerge/Makefile create mode 100644 ptocr/postprocess/piexlmerge/__init__.py create mode 100644 ptocr/postprocess/piexlmerge/include/clipper/clipper.cpp create mode 100644 ptocr/postprocess/piexlmerge/include/clipper/clipper.hpp create mode 100644 ptocr/postprocess/piexlmerge/include/pybind11/attr.h create mode 100644 ptocr/postprocess/piexlmerge/include/pybind11/buffer_info.h create mode 100644 ptocr/postprocess/piexlmerge/include/pybind11/cast.h create mode 100644 ptocr/postprocess/piexlmerge/include/pybind11/chrono.h create mode 100644 ptocr/postprocess/piexlmerge/include/pybind11/class_support.h create mode 100644 ptocr/postprocess/piexlmerge/include/pybind11/common.h create mode 100644 ptocr/postprocess/piexlmerge/include/pybind11/complex.h create mode 100644 ptocr/postprocess/piexlmerge/include/pybind11/descr.h create mode 100644 ptocr/postprocess/piexlmerge/include/pybind11/detail/class.h create mode 100644 ptocr/postprocess/piexlmerge/include/pybind11/detail/common.h create mode 100644 ptocr/postprocess/piexlmerge/include/pybind11/detail/descr.h create mode 100644 ptocr/postprocess/piexlmerge/include/pybind11/detail/init.h create mode 100644 ptocr/postprocess/piexlmerge/include/pybind11/detail/internals.h create mode 100644 ptocr/postprocess/piexlmerge/include/pybind11/detail/typeid.h create mode 100644 ptocr/postprocess/piexlmerge/include/pybind11/eigen.h create mode 100644 ptocr/postprocess/piexlmerge/include/pybind11/embed.h create mode 100644 ptocr/postprocess/piexlmerge/include/pybind11/eval.h create mode 100644 ptocr/postprocess/piexlmerge/include/pybind11/functional.h create mode 100644 ptocr/postprocess/piexlmerge/include/pybind11/iostream.h create mode 100644 ptocr/postprocess/piexlmerge/include/pybind11/numpy.h create mode 100644 ptocr/postprocess/piexlmerge/include/pybind11/operators.h create mode 100644 ptocr/postprocess/piexlmerge/include/pybind11/options.h create mode 100644 ptocr/postprocess/piexlmerge/include/pybind11/pybind11.h create mode 100644 ptocr/postprocess/piexlmerge/include/pybind11/pytypes.h create mode 100644 ptocr/postprocess/piexlmerge/include/pybind11/stl.h create mode 100644 ptocr/postprocess/piexlmerge/include/pybind11/stl_bind.h create mode 100644 ptocr/postprocess/piexlmerge/include/pybind11/typeid.h create mode 100644 ptocr/postprocess/piexlmerge/lanms.h create mode 100644 ptocr/postprocess/piexlmerge/pixelmerge.cpp create mode 100644 ptocr/postprocess/piexlmerge/pixelmerge.so create mode 100644 ptocr/utils/__init__.py create mode 100644 ptocr/utils/__pycache__/__init__.cpython-36.pyc create mode 100644 ptocr/utils/__pycache__/cal_iou_acc.cpython-36.pyc create mode 100644 ptocr/utils/__pycache__/gen_teacher_model.cpython-36.pyc create mode 100644 ptocr/utils/__pycache__/logger.cpython-36.pyc create mode 100644 ptocr/utils/__pycache__/metrics.cpython-36.pyc create mode 100644 ptocr/utils/__pycache__/prune_script.cpython-36.pyc create mode 100644 ptocr/utils/__pycache__/transform_label.cpython-36.pyc create mode 100644 ptocr/utils/__pycache__/util_function.cpython-36.pyc create mode 100644 ptocr/utils/cal_iou_acc.py create mode 100644 ptocr/utils/gen_teacher_model.py create mode 100644 ptocr/utils/logger.py create mode 100644 ptocr/utils/metrics.py create mode 100644 ptocr/utils/prune_script.py create mode 100644 ptocr/utils/transform_label.py create mode 100644 ptocr/utils/util_function.py create mode 100644 script/__init__.py create mode 100644 script/create_lmdb.py create mode 100644 script/create_lmdb_multiprocessing.py create mode 100644 script/get_key_label.py create mode 100644 script/get_train_list.py create mode 100644 script/onnx_to_tensorrt.py create mode 100644 script/pytorch_to_onnx.py create mode 100644 to_onnx.sh create mode 100644 to_tensorrt.sh create mode 100644 tools/__init__.py create mode 100644 tools/cal.py create mode 100644 tools/cal_rescall/__init__.py create mode 100644 tools/cal_rescall/__pycache__/__init__.cpython-35.pyc create mode 100644 tools/cal_rescall/__pycache__/__init__.cpython-36.pyc create mode 100644 tools/cal_rescall/__pycache__/cal_det.cpython-36.pyc create mode 100644 tools/cal_rescall/__pycache__/cal_iou.cpython-36.pyc create mode 100644 tools/cal_rescall/__pycache__/rrc_evaluation_funcs.cpython-35.pyc create mode 100644 tools/cal_rescall/__pycache__/rrc_evaluation_funcs.cpython-36.pyc create mode 100644 tools/cal_rescall/__pycache__/script.cpython-35.pyc create mode 100644 tools/cal_rescall/__pycache__/script.cpython-36.pyc create mode 100644 tools/cal_rescall/cal_det.py create mode 100644 tools/cal_rescall/cal_iou.py create mode 100644 tools/cal_rescall/rrc_evaluation_funcs.py create mode 100644 tools/cal_rescall/script.py create mode 100644 tools/det_infer.py create mode 100644 tools/det_sast.py create mode 100644 tools/det_train.py create mode 100644 tools/det_train_qua.py create mode 100644 tools/pruned/__init__.py create mode 100644 tools/pruned/prune_model_all.py create mode 100644 tools/pruned/prune_model_backbone.py create mode 100644 tools/rec_infer.py create mode 100644 tools/rec_infer1.py create mode 100644 tools/rec_train.py create mode 100644 tools/rec_train1.py create mode 100644 tools/rec_train2.py create mode 100644 tools/rec_train3.py diff --git a/README.md b/README.md index eac6f3b..2e2b73d 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,9 @@ *** 最近跟新: +- 2021.05.01 更新CRNN 训练,解决了多gpu训练问题,更换成lmdb训练,需要将图片先转成lmdb(在script文件夹中有多进程将图片转成lmdb的代码),做了一些训练优化,模型结构更改(训练时使用名字中带lmdb的yaml文件),实际训练效果如下表。 +- 2021.03.26 更新CRNN 训练效果,代码整理后上传 +- 2021.03.06 更新CRNN backbone resnet 和 mobilev3 以及配置文件 - 2020.12.22 更新CRNN+CTCLoss+CenterLoss训练 - 2020.09.18 更新文本检测说明文档 - 2020.09.12 更新DB,pse,pan,sast,crnn训练测试代码和预训练模型 @@ -27,6 +30,17 @@ - [ ] 训练通用化ocr模型 - [ ] 结合chinese_lite进行部署 - [ ] 手机端部署 +*** +### crnn模型效果(实验中) +使用 MJSynth(MJ) 和 SynthText(ST) 训练,以batchsize=512训练,在以下数据集上测试: + +| 模型 |迭代次数| CUTE80 | IC03_867 |IC13_1015|IC13_857|IC15_1811|IC15_2077|IIIT5k_3000|SVT|SVTP|mean| +|-|-|-|-|-|-|-|-|-|-|-|-| +| resnet34+lstm+ctc |120000| 82.98| 91.92|90.93|91.59|73.10|67.98|90.16|85.16|78.29|83.56| +| mobilev3_large+lstm+ctc | 210000| 73.61| 92.50|90.34|91.59|74.82|68.89|87.56|83.46|77.20|82.21| +| mobilev3_small+lstm+ctc | 210000| 66.31| 90.77|88.76|91.13|73.66|69.52|88.80|84.54|72.24|80.64| + + *** ### 检测模型效果(实验中) diff --git a/bg_img/1.jpg b/bg_img/1.jpg new file mode 100644 index 0000000000000000000000000000000000000000..cab5298460b69ef9859b9930235b08f3221d6263 GIT binary patch literal 12316 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 literal 0 HcmV?d00001 diff --git a/bg_img/2.jpg b/bg_img/2.jpg new file mode 100644 index 0000000000000000000000000000000000000000..7d077d5f84aa4f4a0d9e3b8686525284d0363157 GIT binary patch literal 167959 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 P8KnOA8?d>M>qGw!OD;PP literal 0 HcmV?d00001 diff --git a/bg_img/3.jpg b/bg_img/3.jpg new file mode 100644 index 0000000000000000000000000000000000000000..da162d0c83370558aae62c8d5f52ab49af8dc8f3 GIT binary patch literal 372032 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 vq@>BY5IQ}GJ30%ec zaiQ+w*rmd_tG1Ch$XV{k+p1JkN)w`ftaWB@gs%jO5fw;B)!}+&{Ma5Z-+yc^Z(HJU z8a#;L?EV(ve*mKm^XA^i7I#znnHd%XyPm>y!5d-LD;R7{T#KPX-Z)~qZyOyN2vq$x~!d36e*=g)u>hakLTitZ2UHkkRf zJI40on$xxcz51hLxdWJ5pY73#LY!9UIRXhF$BeooPDZ2)y2wmpcE}sVi+imF@1PsK z)}lAi!&!Xy%N|s=KwR@3OTBU-G#E~*b>~st?`V~>5B>gG-9?YuiqRwk9Lqy9iF4B< z7}Lvks~B_(v0#|WUgiPi4+m8KsCFEUFLIhUpGpJJQKo*5Pw>+F>b1ShYrk`UEx3YP z6w!^9J4o^6pNse6$j!RD4UB?h^ZdocU^21WPCIG3s;Q_`?eZU-8N2E+rx%vXh%@>LvdP0t)_J`CbVWDKJ*-I~dJO#cZn3P}Y@SHL2!^zjYmlm0*3df=6@y3wG@tBmGUkc9N%}vJ+a-u!f z?SwNhpIK)+Q=hLk9v@A%OzQ^Kjg+)aQM5}C-g|Gb91=^xeAO5RLC=`-8}cS&LDf#T zaf)_nsiQeR_`|5s_|M1*ytiDOf}2?W){69)ql>KYc;(+$%y*i>x8c8|w|l$|@f0&< z|APyT;B)9)urOzinkgTXjHM9SQbZ|U@0EIpPWM_C3zili(IVCn)Oox2Abx@xwID2t zS>|%&S~Y7}v+)sDZ7WC`8XK6QrzlKvOTariFn<0Ij%vRb9~742*)Ja&e0zOzw$Kt$ zbU~H^AX17=A-3N^*rDZ6d!3C)T-l^|!ZuHepx1w*Z$Bc)ZyoQqk z(9~&R6(9UEyqbzhikPnxy5naLqt38L&!VE$k_V1S@5QCJ{K%LkivSD z67n4{q1Q0IJpdtfBEYNdkz3Wzzt*dD!;r8Gwczi0l#dV2cLZdeQ|0S&Doz$`rRAGm zHo*8_9Qv2$)pt)@EbRayiNlbCQ_h7DB;`#sIJe1H2Z;#QC25o@ZAR@6zCN5p`<|=Z z{cMYj<-nI1RRGsfI;@-)%IBB#*xdCq!r7o@lyCdh$es}#Qv;obWf#VQ-AhulPDbi3 z)5|cKDmwA*x->AGQR#%wV;8VVEcScSavT}ALE@(2&y2>*vE@w&Wr}gxEW@e)c7z0% z9ln}oRDo(jrok<1PNkYC=P8+inaeqK`?THu5vYKme`$2b1LUHF9+9?b3-xvcHyEKywwNcVt-Vtc#! zALADGC}M{`kx_1*^>3pOX@fzfzLc$zy{_%II(vV#OAQhSsl~99!7;n=m-MEDs$6Jcm^4-k#L6YBXw>5~VQ6wICyh>=y2!3w9Lmj(?AdO9x7SS}aKPv(un z4?K_d#QQ)+=H6yu{ebuC6?ip@B>vxwkHJ<0th%+ zwr$bBolu(*rn9+Bwm2+18(F8K6c(&p?Qz)((#)LQ{@Ks2Jv6S79;x?#W5HPb)6AB4 zJpcE1uVc27LnPl8b=!K=%Y4eT{=Xk+^P^?RV1>0AX}&N$E}v)J%+V}oY<2Z?;Vr7Q zCdCee6B+b#yeAgYTsQ%{K_MO%pMB)aSiUhPdU7N&N>1(9=9Mh@ZUx(YZj_jkbwU~$1zpS4rJT__dy7fcKrunX?iZX7l*cK%^o?O4h!SmL){wTa;G?t| z@9jVu2PY?yj7~g-$Lbt5LA+*urEgE!nC{7ZxW|XV?8w~yh4r$Qxd?&397|$9D&9I; zH8(w&F1;h+W9yx@UMo-T-vv~m+3!n6;I)58lgJ{z-Q_|4IHh0$vC^a3FxABI522f{ zkMy|MoVf+b-uLNr^_J>UdN!@!OPe|5{w|c~r#9JmyF~-Ai-7eFUPE24eGz5Jro^T9oYA1~^kQ^jh?opqYD z6MOVr3r3H;gZM#939^Nvx@|JyUL)fl&d|w=pvDV%R8>puIYP{tv`DIp*VCzCC7Ha6 zqJDPU@@t&@P10Tz_AK#%kH*c_ZLBjEjb>2l&sKDv>z&rO=a)z^cPx#%d-Q0y7oR}K zXx6TdcM%j>_UAdT;RFAwS@dCJ$@q^yD{qkJf?d#@6MP+b+s~+Ll}+ssUS_|@4qprj zkKd@%qOd&n=}v;sgo6P{VU{;Qf_7;S-T778BuNuL)36f#CC~3p5XVzFdV7YPY6pMO_Zl| z+vv_}CAp9o{^cAiV7oIaXFjR=ZZx3scDo zpCi6Rv%qjvkH2phpCcR&i=BWI`+YK)EI5;Y?=`gB**$Hfu3spYx-h?b%_}F0||wY37mjqgimS+jcRnQR$$sn*|HHz zjsY0&3oQR1{!w5w|EeN`*4}wWZ~~`I?V*g-w->p?5cSD)B{_2u#g0<-&r*m5KEwk zvGR!LoY|uMTmTtT)|-ii%O{I36{SOHU^@pg#$*#Di7_xv5T@;^Hn!h7@G@C-@w3eU1W)Q*f<%Kkk)Uf!8tQ)J=1L;?mXWf>~2m!7x?MN{6`n)QE*0kkCcd|`nVRF{A_$}EhaFF zPxKWP2fRx2U<}+@j_GXm$^cp8Sd2(_U(4@ISafcxtp^`WEac1gdZv6U%n-Tgo0yf~ z2DNNO_`4Ew2ka_Tec$4;TU+g3y&)cin1-rbIw>v+KXZM31O@VBO{Dw>XA!1WT-B8L zxMlC2a|l7p+m14hb{jDLm8Zn@GTh--f8wj__*+$=Dej&%P z>duL1f7IHsXS6T=h+gkN6L_27%QMB$tKvH6A--faFmrzS>XW|E_yMQdMjpYyAc`d%d?6K9altBL{uV{Nr?s z&P;AKmV=;S=a_UB>q7LAlGOUf~o(ByKegP)YdQ!H_(Einq` z-a9bp(PefYa!kLB#4EG`YHpZ;==$I08PUJ&Ov0&G(kd)49p$RG9Hx|HAr@%;B}r~A zJpJUh841EEf#J5!?rYZN0c)()cr}?6vBeh!1~bQO$}lYFx`8A9!j4xiAS_| z)?2=dO;rO#rEb>y4^Adt-Xd zL%PEXy*@KHeNc@EeQ@U_Q8d?umdHoukH2e&tTyB1bfo*>oc7@c4pgP#oUHP5 zS1u&4&`FHdN1E77Dm`{e5clf^`05hKZTmX$wlW;G4^X07YI1uqGBTmo z73OGc0SFJT>ioW0)NEoA_{l6Ujx+c1KRCrZtK+j3K-2-_kK?qo{n)0BsenuZQBj&V zyUU0UhSXsAh|*!wGbO~L053lZQVz^M3gbg^-$2F5Srbs1>4mM2oKh3T86ol}>QmqR zm-WiIr*7_3$gytAr_~S2D}Xat==Z!tXYk9qa@|#OM7`0vSSSlzK;&wrkgrJ41fg?5 zIQhcoOkR7u|HE;!`@|+7iIntYDqfQ~N|&CjZq}E4>{6oUJM7I1@rDDso%M8_4qERP zpN}?%(|_yWvVu@kU6~*B>aLGhfmw4Th_KNiYblwm`ABDrvbL>ia=(1R^|*j*lo-J| z@y#TFXu>wl`DBVIis(k%))$qPe7T>_vmb<*W?mcILjh|O15O%13a+btAsE#%VDhYS z9}aZi^)*1s#yOl5Eo$#<1?&L6ZrY6hw2P`PT0!og8fM@Up=XLF4cZ{O_KE-gx*)2T zT~kD5rl{fKvNa~yyTqVI*WB3hC2sdIv0Yh>%ISkDwz4oS6-98bQwH?{)BmmNv(13G zR->)p63?r9v2F-H)M64*x8ATszwrgVIv%WS(*|a7+?OMNP}kqywznP?@I#8h1TC!vihyQV3u2uCcxN2onWMF?@^Zo8M! zIneez(HlviJ5$5%5GPV5xagH_aQM80@dcRus#(E(0gCa#I1Qk8p6#(wr0}G7#t20h zT)*5d!-+45x>i571l42Tr16dJEEgBtBo%u*}mY{$AuF4 z*SK5ADU)M}5cg$$nr!|XSUzwAdR2pU5(Hj}hawVA0(kfwCMNj&-y?`tUm~!ACN()M?g^Xz#6|G*+jhS?bn9aO!F@ltz~@`STh9U0kqW zxszl{iv!VOgV*pF{;)SYU=omOUR(L#u{N!(m{OP%%~M}S!bP*imB5_j$Vbw9l7dM9wyBzox~)gf=HNNSSD z$IehCfW@_mo3j^h6xd}>c1X30H3)x<(H%S@kMTySFR9kF)G%N;99ttnKB0yaKuyQ0 z5u9T>u8A&v{X$B5MSDdAVCM61_b{@YNrJdtp|@>YU7pB?sH`Ufoq2r~q*dNISdv7C zeasIkj+^87yM*qhQ|8Il5jE(Pv+kI%XF}l%aTKJf+8Y2KIv1y9)J^PLD=RMbJe(kP z&cfEQ9iLiVxWYnZ>u9P@WvKAt4;b3788rYF_L=Rix(&FJS(;BipPV^kv_}fN?zjK2 zwaXwrKyP&`b-`f2j9N>ygY|g)6JD}hxU>sQKh_i1roB&$l)Yc)VR@bz+ZKBvx!}~* z3XTWJuLo}~wJ?ieI%chI8i6LajlQrynL?31tlcI%6D!DN2VNNIh0@W#7Ywap<;1JJ zw+YmyCIsjVK8z?`CC;x9eWC6a=X3#a%vWe8&pI0m&H@qk!Sb*zB9#?ov2-I(+r|%n z_VWwI3M(GDWruT}8-bUfI)GEp1O_C!4S4Tc<^3hHRYEyi<@E~LQ)=ONgHdO7wG6Rk z>b`ZRA9wuI(9ANK!3qLBYA&2c3V2#2s?iB!%X;8t+1?s*0Xbimy(XFWU9QK5o`k99 z%`o}-ZAJ-B^=$IJ;swALSF~(=$CHR{zp%|eV3T9<8f|qfT}uFE5_AC9YrB;h9t#yZ z5m!F4(0tD@!q|*js9p1(=DD^D|seo6M6CPNzI;fO!1xL66d4$tyI zHY%7G)NxXq4pJ;Px)Hr|7EivJQ>Pu?-zO z4y5T`tT5KNS1jjQG=zOI;@%xcV;i)<`NNe_OD>vAx5%02g%}a+>;qi7C;*DUtO$xQ zD?*F{oekk=hVk@xycjUZ36}W*l1LK$=$<9X4kCj|t)8a8qinOr&-xRxejrK!QG zEF>|_L(<=Td>ONg=0t$KbRfIy2}tC#F``UqoFsIn1-u)AhvJFa(oGP>GO^Z#ad~!g zetq)VI$sGDm^W&mkbb7*2Z^pDQt{SW7FvUvZKTMp9SH36sUqEU;9p&u<>wO4$9fN) zG&@&JKM&9940Z$T4?h=aT*+Wn>8z(lS?NMos%;LWjNC=@{qGJ**vg7*s4{2#{Te1_ z!1Z~yqzGeTCSCgy09w!CdS^~f($cFo;J1BTV)KT6PjQ8MQarOF{n9<`d%p8 zDx8QLv2m&c+uOqy>y6eqGPHcE03k0;zMQp{HD;1O-;qk|O;TEDbq`|&O>aL(FSBO+ z{~-d@m;T1L23xeS-eCRUnUcR$9UaFC&MyVu$3Gv#o{cWc3`n&5KR6DcHHw?v?tTvZ z^AH?;1I*!w6$;ehZ3_j^OR*+;H3aeZ!a;YTLI#wvvjp9eZ8oozwUS|7jb5F6%Ir#e*}@uGq? z?O9TZ)!*VqB>t4_7U64A-+N?B9i^ zBvtY3D*`AORSBR-CkW(cQQF73p-jnLg;R%ySrK-)1R1x|4S^Lt z2|Eo3ELfig)`lk|y=K9cr@Ks6%9XiQFMS@7qX!)d7ZbuV%HNfovd8FS)9PM(UTO=5AT*xWuU;9|E9n(+H~x~cje@6PQHng=z|e_@xHt;jspi9cVW9oSXS z5He}l4tHaP7ye20Jn?0pFH*0(2Eyq_nUn;*%M{4w?N6?IN};LM!jNA+#uoo)koUwg z#B26G);lNCw6DQwxiDcdc9qy00mA;enaA?gEow4M9f6iVXp<-K%kh5ld#<==Q=xn~ zE*Q8gcUgOs&HMOhVM^;`;Oe-g+BX+v1KZgOx38u_VsSg;VsHh~?eK7OU43W3ZgZP5 zd;m`hol<@xTc4>(l3gpT8h80ys`f->QC%tcbT-&k@$Qf3L<_T~v)a``W8}eolk{4% z9Sg6qxUv9!Oz`ql)}AUuj+_EWuffUo227Q9f>y5VX zok*f@U)_7T)XJsrx1PK@Tb3d&))*i1n>={#QkIB{NV8Xrz_>9uvrOrG(Q0Eyd3;#H zb-w$|-yp(ncdZd7)rofIw-Ym~v_fuD@NM>fS19v8pJuP;DqqW)Tl* zuw|KOX4526e7=m+JM>}6rYHo%JPf~7hmL6@D1|y;}2&p_)*V zh_s3%j_Tw$mWco03??EGQ+@0h8JAbO_sD;mN9PeS-S8m_QKlp+hsNDr-;1ux~$q^$~SU20Ejn+HXraNM1 z<&iV7eKbiO{IDudl`iEz@D&jar|g4f zJYV58)(myJbU;om3Sby8MIE0x0cKm{M{fc5=O(z|#x6QVTNzR0{w;l*Cea{qit)qe z@ZV`b4<8ERXn}PE&<(aK83k0M40@*e_XI`6USbE`4{4*9l%QCrBlJ5%6mSNkEq3dY z*F;LUm;U``Bg$05!_}POsyN1(o#lz#NO4qF+mMT^p{~{> zO>Fs=AL8|DN#56A=|q7G+es+WJdr9TIG|APYV5>n7z!c3gH7zUXNpt4=-eJlRo{Gq zR~gxcSw(+dqL;vNFqs<}h&4-h>N{dw1JhF`(P^$R5A>yH5gQ=jYG$4DctU!a15GcG znRcJYTa%A(Rrvmc+j;(}EW*KL0+!w$ixK_pgi}!GL+s`dGAxX$!h_DTfVGXzX1 zPK5Y$w8q$P@&ps_!-g-jhpxBbuh?s!)}NyhM=AJ)fdP`L5E*ZaRMTNVwi^3v`KxeG z#PRPioz==i`{R0+TO>U*&m`buz3uvBuV{-~IGGP?0BGpzRFN#|Htia5HTGU}%D~s$ z;j+@ia=9?GYTHZQb$PFwJHV8vT!Mv%ePDS&E9-)BjDq9AyiCQvta6TkBb9(uRgTzpRC`f!>$vo<~DU!$Epw@~6axF~I>ma=N zY@2;SE#Lns7ya9@5V$s)b%b^Z26r=KvRkT8HJUB3D~lkklf9Y%i4HPbQaN4sWd!kHNY} zQ*CRlBM8J&Q5qKw>73(!q8y7gQI!KauA2v@bEqnl{|{4d85U)@etpv=UD7ErlyrAX z!yqt(O4rccA>BxK4@{eIiwc`$M|CVo%tL%(YqAaSiZ9|WY__crmE$RM`18nb z7fKhvaHXt4DOvHoviFLIv$K9*kgo(TSXAPpkS5{GG7q;?vzQ-Z1dU?KLs)WI>%CS-n)Tb6XWG({Az8xq|w&j)lRmuO~`RU@TzS z9V@Wa18E#~61$H9?%ih8Rnq!dZEc29OiCAns7%G!2;`)@wA~cwiA4a zDf|1;udL@>(5!0;c^eGdO5Z}=q`FI(RT>Cvtw}X(iVC-TpR2_4P9!CVKECog#6yNu zD@vQKxYDH8j%;yp>5dp-KMm{;opBwxr6BM8NF-y{pGlMD%r8gS-{3j}-`18F(lBTz z#z3k})J{s?O3qID?!N)`h7CqjS06xj$h9Y!8na_tx%!F9;P|t_Eb>1%x1X_#AXZr( zLP*BQM-tDolYC@(J&s8{SljqZK)N-#=pYSi#dL6EB*A4&?dk2G1^Y2~fD$SE4UEf? z(@6K=gDvCqr19biX+ArrRiZE}aR*vTU@G(y1_hm}v^|s~;c8!X zmAmA5xa)afnKkUZ{>auI$4g?!H%A=`jFKR;)fAuqlfuFfFO7RquFS`ptbEvcDd;?c zSnF%`mI$O3uGlr>yBWjycI_XWZ~Z^GGun6JQ`)u|CHFw#+WKKAq%e=deFG5gCmQ8y zZksaw_R=oAtH=v^j@VAPsoKmEO(C$#)v51|w$u9*36MqM@I|PIx${PxXfu#MEVJt)bWVOvW2E&nuh)&la2ll+vaC;o0BRJz%`SiJus4Xo z0G8GFTGtN>sJ5+F+rK|jGIc|Q#xCCCZf2ihm{#N($w%a7s1!Vy_EI27K<+4!u;@F| zBOlV1s@%wPxVR==KNK)@lPXV62RBW%1YJ>P@`&_vjLtG`ML2|Ki_$9tw3Jg753)vQ za~F8LWM`HdxSi@L#gO-l+<1f`)v`mWD`|k>idNm`-xLx`-pk0O`k4|bSz_#T7EJsx zQA&q}gL%xkHLTw)CkZN++dN_KD3N}G2k44O?SMv6bQ-Hxja5@!h{?c@LO+2iBy_&i z&YLXd8hR@F8Jnd!uVVGzqI^wQ&Y1zraxH6F&Hvz-1+NG4xJsvWfPm!C=(p`#PW;Jw zp7HPGWlSn8s<|>!flkIf4!{iAl>|(@7T=Qfyd>Im`^g*>2y)Ol-t!x@5O^J`_FcC5 zGWr8Jq;6T%n6ni4KaO0o6r!~O9paSRG-18gJ;C;%)d^NYjJ2lYFz{j`Au`F{ZAcDJ z^Id#i_Z^q~@}aV2g|}Nmp^p&AL&4#IvI03*xMBjx2ja;)+ib>q;2Lt5){l-f={Fk@ zS>IZT%biEwuUJ<1FY%Q)LcbFz+_b-aGpllytf8%`?&mTsf%)6DSOQ&&jPKRhIreSF z*1j9u`_YPx5iM69TZ->~ZF*&ks@mf&ZrzA6pR+v_5@zNwi=StaQhl1aUM9NOyJ(-z z+ovy!gwn~uFvrC~i~o}ligu;`;U^}jb_u9x428nQRtg|`R8bdI1;okfz#lPLyl*; zb+5a|@zI1XQtb`ZlydiIxTA58l{T(km&G`!E|Z%+gnH;b?|;{}QeLx7tin0?B>gl( zg#X}93bwem1ZSpx?#y~qqZ|v=eHjl~jA?oADuai(rMDC_y@ZrZP6f0dljG!2o2VdERJmCJk+<0(yf*!`JV0zw+a)UXz)Z=<^(3aBi1wU%Ohh6IWp+=&4`wl@s zp;oKk=fRodzto#7vwOUbV~S##B59v!YLAz;gMu`EZ!UE$o6+H7Zrmt+=rlvz*!JU^ zF}hD143bFqfA+y(&Da#h)hRjKY09B+=ZesF_*(nLEi;h|iJ-|XMm`t#d~a_u5Qv?y zMWxxhAM6;rs)waR5wPC-t;O0*cEbOI(@k?cTxuKPn0iXvq-cOw-xF=awXwC*k^P~z zS}$Ndfk#azq!7!GOmN2|afqYD$EA9hg0uh zw4Us=%$3GlY+5SP={eD^!qKe(z8XQ^xWg%UDZVUK>x3-QJ}ME%rr;Ew!pN;mm-&Of zv1T+a4pqzI!X17=y>%`(X4cVHOfhd%oVrz)vB$9V=`sQ`94dkL+<6#6GC(6cN&~m{ zb)VqbABV8ZZASPZ%QV8A-NZ<_D$l$caL2m!8cQp8@%%T%ZmCM~SFIYeqRX7udVPqn z(~NJ_sxD@*cO;^_ZHe3doOwh1Ajm490MkB&F7uX@42=6e*)0;OjH3z` z1*xvAjD;qbSE~H>+iuGslpa(Ps>+a|*wz$joE@j>5t0==PeCA+LbQW83BedVYm}sK z6f@0heMUzoDR?HFV{Ty`!g{w|{k;pd_PbN5wI5m&Mc*PCHQ0mBa^aOrVY!Gnnjkve zGcvMf_7C&JWR;tyqdMuq0;?VyQjiXl>fKMO78{NIKwvY-)O>Gu5D>;rs;Zz&w^wVy zoO@o(oH0vioc8pHEecav`y+3oVIFU{NuNMnm;^1>NuSagw(L|)6zX|@?QKEFXDg=v zj8E-z6|)sTJj-QL2fYI|c=VCu_!fQ&O$8lh#3_9jOlzcJ^aX&#*O=?EGlxb zk$xQ>FTwMn840f(C}v1f^O5}nJ)0FS{o(yL3eku1mu*Q1z7Or>I&WZ6@C1h0L`12` z=rDk?)k6mToQ#7eNReiu=45S@%CH=XfUE&D)!ro?Ko)ghDWvT0FCc{olP3N%kr2nK zYqiLUV2x0Eu8IMhbq5SK(Hv6?Gx@igzHADK(9>qNe>wVcOvMS#n6i)7o-oHk9Mppt zG~k5M2oA*O=l=|HK7#r7w+uulvDJ31)KA$`2%VxgUKC00Rjc{4JW?Ent&-RCQgz`3dQaDo(R!fHg8u z#;OHX07ss3+9!;3#&z7cKwFRi`nYx(fxRrdz$!DQw}LIm3Cl`;U7lIk*YG7Y_ZqUPB@xb+M}!~=-T}%H)Q+u zs+!6Au|_YRbV5Sk^G@lNIK3quvw>OmzQg3ta@7x$R<}*DQ;tnZSbR!=a{HSSp&?nP z-)Qn6oQxTz*oNcIO1C->+|0l0dTCDzGZC-!g;lC4z81j9Vro-j9fE@!n4FU(OpSVS zzOBs~UR1Pm%wEo>oK9B(k_=TAS(|a%BQ2UDqy2(wqHpl12#J^5Cq(jgAw*xbbrU=A z#hfC@c7AjUY3|{Rn0AxXQ0pT<-yn+$$-?RlbXvDhv3F04ab;NMtJ0y;=E;ZwI5RAg z`p>Gi081i}3Idp%oHrrgMYegaoD0&cI-q|Nvv`oD{p-K~R{iX+L)J07H<1#qD9%4P z{QVyED@pKRAll>APxzpV=C*%u&^W^X4LO9*f-q&03ZX`1^6Kxn37$~^tk2_ri~ivM zc3@%xQim0_lEJ3e+XLF1hJMAk?kdF%o{oFw)DY7@q(?8^m%-${kb){yt?dSF25gwm zvYvoU^W|PV#B}S-p|W*lJ*uALx}R!YdW@`qJ0Q3v`{2VR)*W2Kx;Uub}`qNheVQI?Y0BtE5esRlXSlbaf}`enl{`6&F!{o3222^e4^x_ zO|XynX<)O@S{dgsgKFbfgupS*4C!8|GSnywwS3)$iGRttXdNgqLTBBX-`yob%?Z7CHaO63B|1 zTDXQsOnh~kJb6Ca>NDE*HVdYE(Jvl%X+Qv-XpRZ9$`kFan<^b$I#EyoOft;{PqG|4z|eZUMn7dcetxbZcUsqQvSKc}z7 z9^?R_YhopEsS$0mz$JVCtnkW7?P8QV=u>)7u=VZm4_8>?eZLPp|TH7?gODD-4jCfnq6Z*6RPV4c;sdJYlwHTQ#DGjW)~@Ysm$<0 z#XZFkp5q&w;|pJ-qV#C-=B_%Oun)FF(f)@oN_yt;(yj`+7GUPbY`Ethm=Bq=$ns}w z_d1Tyy}~G+YGqX5KsQ--q!MyWMIbqXRFMVNaWAvg7LMh)!vRVk1X5Kv)cc_0C_63W z=&6G+0&MF#$wmsFhE8x^i*F1vY_Gx8T}jCLz8Y9(ETo*aN03SmfHi?(JhVFRGj;tc z3#Kxw%r>Ar_>C5^%wMYHIldgUuuyu5c00g5t}igrU{xCfB{8!*I*Ue=8}f1-&vyRv zb6XEYKnUSHq3Cn%6AKhMfz-cv*;SwS{p^)>+E+>{g~c23ln*`J6dI~drR-9WNqK$a zAGij6qKi+ctKrsGdkGl%m;d0}#)QyJQ#fVT#(8JzY{tJGnZKoY!#xm36AanyTD*tx zB>(fF2N^;fRu%Rl56gsmPVYSjcvU^1PxjyYC2VhfB6rISe%R^Jy-_;`-Asz&G@8i+H!j(m{wRVoH0hPVO195s8wWS|SgDYg|y7a;-WK z@@u&SHpv;a{Pw-&k{mTC?@yJ~ZRlEpA9ranP(fEl=ejA4AN={xHZ^#5Mz6Kb=dS`2 zZ&SMogecYu`aL|`HtsYH0-4x03^&1ewoqTi$?&$MxClN7 zWvajLr_c3_&v{sQLK^J)bK0dXy>-GDo3hC(x{3~fEQAZYg=2tXP0-`o`sH%xc;P11*JxiD#Oej%HC0!(@F-Wj28S6#L!Ht}C7e1hur*dCzbHcDEeu4(MXYE4 zj~Xl^2BZ~|At?2NiZ(=mb9k9+MTOH5{$e##W@UA=@1S)%Op{e-qxo2Gw0zob@o*zE zgtRwPI0zVzKzo>(8bG!Ja+$TcfVmaYFM;o)5M=fVuphsl$B~zBi8e{N_}3&RH;|d- zPF49-S##r?Azd9OahvVsmACp{E5Ldt7^_SgmVUS8&ezX#YCQ|ZXvmJH%#>7z=Ap7c zP2Smqb~SEH88-UU64w8+x*22|Td!X80pFKd?35L-;jUqQGKs$==by2UDYd8>GwxtF zqR&#agtX}sjLI?h!hfxit&EW&nLhw{)e8947X_l18pX{Vi^!nowXnEH*|**EZnm_V z!U|kETTeMW9epKxiiEg_mw?qiqd$JQPm*B&gmwtCe|-2ow?`9W8h}fkaBX>h3~Rmo zF}_?@vJR0=?OG3?Jn^-8t62+ErX3D&Ueb1!fCoHQ+$JXztt`!QX)klBdhj*d%xi=AzI zj$2~QQV0`RKX8wTl#N(wn$pbh^ZOs~AcZfBdcy{awkY7TO8E6xQ#^&iiHHAmXQMu! zntk-TsWV!zJ7ttdfd`e4*E(0hiwN2i?C0d(5UnKGAy|%Oxr@H}SC%zN*WOvgncL~q zIP69A+(g3Q2dJ3BD*>~Yj0O-pJdcWTIUA;0CWI%FKy_ljw<|^|gr-6WEA=j8<~qs6 zzPs+S{PgYy0~E*~5rLG{CbMXT5i7z0v$i3$Ym+G$p(F_nh=1)@L88IK)p`w6cN3$E zZx)C)!spVf$Hik{HZvf7yzj1i(M@C+2uBpbYcx$4w7DiqyZSoa38odXo>T6~k)E3| zGL>Z*lAZEKNKu;P?PFuZ362iMVkqjus+~6@O1v}n)<9-x@CfMRcjGY-0cyqH2+h)SS;Yfx0xN{cii4%xBR;N2&P-Iw(}|#@O*+y zgeX#Ci7S}9_zD&a4p{=rYd;8<1?aq*#l#eJ?6cV#OfqR?-E5fC2Z0<~eeQR#%sEW5 z)71W1Tg`p3eZ*Ty!B$W`228|9+nc>^NaczVI3t-z{O^crgoHzo6QY0RSkYKt>ILeQ zklUmD45KQ3Eg9i*duVmEzCr_1rI(o4`quw;X%lLV73SWAJvdH(u4euVlnW1q5gl|< zePROps5GyN%cW$79MXcHu$3ztIAa7$O#YK{xkPU++2(8BKhs)43STwWS|;}udL@Iw z+M2C`owN*I$f&=zs6YOS?tm zSG1k!m33)|#JFYl?#OcnkU^dM!67tpjf;zDqyP+S#`!zPT(cdZB`$Ur=Gu;)1?G-a z9M!L9OZ7v#7}JASPRk3Q6ecx1+d~ol84UNn$aTV2I%hF&_3&;3+8t;JBaJn(+9Xms`=Didl^S4Oombv zX{Iu5o=2{9mhK_7am$WSRj+Qn(yE#&1}$p37zi=BrEu^-DK(Y>?uTWzNS1ogO^48LrQz`pZ z90yyldu;uoM8tU<4}~=nvdu-ZDoL43cJA>b#p!w69&ZUoh<8^Alw9dMQa>KdPH%Eg za-&2NC?lw~TkyQ1Vw1Cd&di~|-{iIdskFy?t>J}_S`2BXNYMW_Kgwl~_>ffZ-z2zw zFWps>*gdT@G(OX^2d*Jh6KjSM5jeEJmp(kvDlYw+Pl*?OrOJ%OLcQneS4yE;yV1W9 zt6#EG_!^(RVi}%IU@$^0xrk{?4=B=v=QgCPRlQIDgQrDKp3iIiI5jv*UNTw1e&<>( zCjULr8n)rh3+f~2>hp3k{a@O%C1DMv=O$>HcD>*XWO`)>*CNdF7uD75IWa(snHuA8 ze=>K{huKRX=3LT0u)F+(pHz;!6hqrAikeFp46n>=%2uB$(|G^G_kY%)7dNQ2)T_91 zh&h&7T?=(u4)nrWeF*GUSFcMz4~cxHvQYKW(cPAa4HhXRzNBv1-JnVjPjxdv$AMS*8HFCs=$G$nRuz*6 zT8M~oW+t@9f!CM!0~G2zWB=e9Ip@*J)_bj&`#wEf4dN|}l&v3UXx6)LkE+6HWxqoB z2lwO7iKwIf$B1=q)GQyUs!eWvk_zCpL_hTp4rjoRm%g4B!<^0N&YUW)dfewD+&cKi zTe?4Frmu}kUUOZ{exO#q!enuTi0D_MAOtGrys$CI?1kZD&YCinGBy0IxUXC5o8$Q_ z+!eOGj+_34Uo(^}t-dD2Q{)U@3#z==3V2H!ftw2av!M$Tk!M|pi3C4%O7sQXzIB-J zdX5d)xWyr0&=t=DX~M+GnNr{~n=5J}oi+rUy)F@Wsn)}gnT{>#$qO@pvY||yQHgmy zUiO2bG=&{Ngo~Du@Obnf*Lnio0=1m?;i*PIidXi-tVs+T$2iHUYil z$>%4@mJwQ!&p&KJJi>Yz$%MYuA;A$U=aJ7ZJ9i8pelzgYHHm}tjo$+G?r^ct}zunS>}Z1 zO2sn2-Q26C(b?jXACE>=K~28$a42vGKw*7r&t&(w@v ztJTxlnd(>Eawe5>7*V~FK)sU1v&jyLSPF#yd$@Vsz$+XEVW-v|D(M%EpiQu?bP)W3#;=voee=&_|KVd}`<4>PJsAB;h zK+Ut<%RSSvkD4cYQv$vz<5ZGBNWQyU$? zBXsc;H?Wt!T^jMs%h~^9N;UG(c)pT$-WEScjQ53=9%#=xdv~eX*(rXUti)rt0b zy3|>gN_;o_4=(QNTbcp5<>8FX9B?;^i2S@K!$#KY$J^@#7{#0n=8-=Bs?J&Y2e*tw z&m3i&I#SZC@(=FA9`>`?^kOt{{QBoBpQEexeu6eZF@)9yM{o_R(crN1+|@$uGrW|J zeN1)b0QO{K-&?`!zi)7bb{^%Mz@BU{J)7!s@=Jg|%D)r1on{cX^?6etgX|wE*@ShP zl6&|d|GN8da!8J!b&T&Rkrd*OZDZoDY(diDSMyxOfZ#l8+PK4u*JUZ$%B?#kbt;cq zB>d$#kX`#xuHbwObKxkXH2uFH|4R#d# z7u$vs7~IKL7}6HOEguI&dJ<&DmQ^Ctse$vfq%;|da)>K}2OAQ&D`AWBjb@mg0~B!_ zK796c!_R=^FIJWL*?8)Uxkseta8yT_QRsjMr$e$qEwXRpaOce6V!{`O$5+J^5p(r2 zIr@r+swa1a5CqC71NlggzyrM0<1n#{Jfp)A@=9D46^Kyv2%V32tFpf@H?-OHljPZt z0oLMxli9EdU@GTHC_h4ZjvF`{ZE?v*BLC~7pj+b9Vi=3g$A{DaadPYJPM<@~lf49q z(1FAtXIg2xf$*i6}eDi$6>OCBpBLM0I zv#E`%A1j-gkl%)iu5Z4{OC2Q_`IyIq^@e~^UjaXZs>x0xt3Rr?N%eiZx&>g>E7zZV zCm6lzUg_xSx|75xH}1lmkG&nlO$obBW)1~z^f~k;cvE8(e6wkq+@18m4P|iC?VcF7 zxUFkJHMfdHA-@@sXk!om!8uTP zU`l9)Y|CK0*y6?&LiyH|@-+UzWnpvP9B$JT<3ImK(84&&ACTVM9A9_we>Brv9y0LkOVX^&}MYt9AbuM}B5 zo%mVzn$f#^_w(h=Y;c{&>=wwWEU;PJ+;`PTeERx#^l&&kIwx&QB`$FDemBN+C8erzK@GJNpWsHK%L!*8JeKsC3IeZ+ zXuZOXuWi(pNttC}BjLPyMDlB0yaD^x0k>pEV$|-?!omN|nzp!nrK>=Lg5MW1fvGFz zMefvJ*?H*@$Opu~bi5IoDFrBb{CAVGY|5!ju}9*jSnuZmYz&$t*{2fLOLj+82G-tD zhi`We7?(s^5HU4O7Ml}Q@xPFDG@fk2a?qTi9AU#a3N2J5|*p z@!ou;6!&4oTg5}a9Z9>@U^S>(S!ZTs%xBi%bSde17WW2~88{_3J2zT{P&2eJa00bb z<&Hx{Z?w>`X3Nu$ux)klI^-A#(;U@1#2V?SUBAuxNv6S6euSC?)s_B?7OtW3%m_$f z)H_RMW3(T>F$^$1mQI_w%p>R*eKkTEC5&g4_IJAnh9%>+!>f&A5<;ic_v-Ob$JUI; z&~&z0Y0yPTVI1sXjN|9&LF5BT6}N+MZ6x{r<=2g+0&*GXKZaL)Rhre=sp9-9*GNy0-g3i5BU&!T~j_V+c!wIM;`?igG(2#ePa-s#$iVHvxw&jZg1u{~0&|w3TvM8jPt|8=A8v0n?Y+e4PtHt~nPxMrCY!ZskIZCE z(Ha63O^{xMnr0tZ+%-jF^ie$UJ*?9{i19!PYODc4GlHVJQ&^ss#A0QUY%lhKEf!&t zX}DKjjf5kJdx=BvQ9h>p?iB$dC9@`6)Sp3CXNp#gG}Cta7zF1$+I+w>{1zT2-xhFf zX7RVDUYjP|Y{TaeJfDu3&pSW3)WJ`lk5;r!7v&i;Ab^@n#GnpsM6c(BuZI>IX`_Rz zEz6-=^2wmFPNEdfd0fB>=2K8SG4m{o9D~J_c4gKWkrY_G}k%6Ukb zz8z9i$t2^V`dVDO6Q~5bHMQg|K@+@^7+=GMtMw=>`S&B^YQ<9-Xq8ao_wrJ7kpZ^*!0bWXw?e)M)fEkLtl3 z7XuH8oi(CM$OrlRUxN(ktB^f~Z2DF&-fn{{R2JE1zb?UZ*5*4ZT5_dYLG%Jh zR!bfC41plfkKRcewpE~E{5v0MR-VKmq`aoc{-R=`gOhic_j6eQg2rbnXeXr8uY!~q zGc6j6alDRZ!cHIQJA@fCksD{lqISZNGP@!GcBYJ|w8*xN8@U9q>WM$`fRVKSuj$Rm zIiN1+UIHCO)@J%Dj1zy^d$emBe$dSF7q1f%dZ<|h#&gqgumY%%lao1;3459#@Mu8K zi(FSHL@{0zd+@lfH&A5nPksTOy@S0CbD=3fftnU?TM3f4*}YPA`P=qbSIu|xtPn%HQpz%Y zc>T=wbj`}x+2+blXG?+{#Of6{2T9dcP*PcqmL5+E`G3qDWu_LPZpEaEZpiP^_ zdjq-dQP3gJ4L>b&v3sSV_oW)cM2_uwpo9`67r{hz^aE;fRIMDKsDXc#Z~sq~DUmjG zUNb@%ofCjWYO$c}u&?xTr$7m{2QsUbQ7V5wjx*fgxbrYc&G@A$mO(XTM*GT`L{F6! zeSjphfrUnn@UZ$aYwZtr!X#VS85+!A;O}QRpya7yQC)oa(5kj5&M~uiYB61Nx+#2ER`bW><wfn@-Wrj1oQlaqFRV@^}fhw`VIN?Bhzp`%ptS+PP_ZwY>d+hXv} z?w40dWp#&P`wn25>sQ28U#Becj0hd#A6tzS+Vu`7j8&UhY-H%HL8CK#X-tixF%2nc zqw~t!D$AwryD&~@Qbz~X5s4zohcRO^C+AHozrkLCJzWge|BL8}?(2J-x|^v*6HY`9 z`{rybIDE~Mdr+>HP_h2I;LlJD9@lTqI*+LpAj~0m$NfL7MLdr#(e!20r>%PTT}xJq z$HJ(OrZ*O+YiOgN21ssMV2?bBFPh4IlwYz%B?nI}6; z>$un_>&TFkx{4VFip{F``Eccso6h|ID}(14C!Nx9dxy3*SS>2g!d%)$^lel0YXTjRHn&zdC zMj_@DOjbc&2R(IEfZa~<*au}_ePj>pji3RH5!^L$>lPw$i3=bawKH4xJLR<;mf%-J zm0Rxn{EayT%oKagUxfu*0Fw(qz5M;^HZL;K5{&JJy46_!r*TA8;i#*)OXgEogHP*3 zLb`gpEi9uHUyYDgLFU;fslI@Zq00i6;CsB>S}uN=mw?bt?V};V>qR?X?lT zJ^%WiJ^-u}$G8l_656zY4+a8HdU;Op24aKY;;BhS=(2bx>VN2*@Tu|S-V6y&xz5yF zT`bCTvi85k;NJXJu-)6`Ce=3JrxgpwM%c8=M_36|1|yj#F>CC)rt`%f6DN(|HoAbz z$=e$c{UM}#U06H(6vB=QX@Gt57Uf?th)-WRC&-O41WWteC7d6}cIUs#E>IsxknZb( zh{#78t8k`t)N7byQgzWbPB#)}-pNm13G$`M9h!m|&3J%PCbsOHNB|Pt(l%p$q&9g++a?0gP<0NK#}qu?3Wl;N+L0}E z4`i7#O3Kc_oEQktP7G5$Vb;CnCeMr}G5P}7fn%w2R|!j`XXc91t27NSfE4vX&9Ad8 z@h>Bs5YP6AUmrmS>A=AHl!7OT@H`O;&8x(@;7HgUY#fyJ1%jm5AGaYn7&m?FpV{YPL`)e=A9R z99>pglFJN$E0K{Iap7Ag6_(mB%#V&Xi^Mjop|FS%c;H^iA%M_SAt%{Ya{uQ4Tz0Rx z_x*jV&Fq~@!`2?1OY@FLcAwLt|{M`0;}=ja)&fJo^^wgsXtIlcMsyElqT-@ zJ|S75O$8WD`(r0_>EUf`mbVq;HLJoXNwEb6MF?a4iLb{78qNfwEC7&rYX?ZC+S(g~ zPmucf%e^*6X&sTJaYpkLqtvi{ldo=@3MPC773Np*<=X148aA%EjY}Dl9O~VkW1}aR zCr73&GFC}&P1=urBAov3ECp-r8GT?x+*pbS1cX}vx0NGkx~s?5VoKGM*q^h)h{zeb9%!`iwJ%CD`;PJxYrV?ufpW>aiKl?^_Uu_nQXl1DCJyk7L zoMNx2Di@8X9J9}Iwf)q?r+%Ijdjtvoc0t&k*5RN}0}AnGm1TYJBPH~!KoUBkhV-mJ z(Wdg)5fZln}u_4d2Dk`j&y}ZE8s#x<^#n zS8HrvZ%?3V)>qE#pDpBA1Sb;}t0^4C)&xUBl&6D_N?zp4VA8C@Ae?=q7Mk0&zA=FQ zi-Hkj`HGq8jdwJ_v(zX(Fry?W==uNX6%EQyH?jVl$Ak%*ZXbN#FSZuo>`c9vFQWMm|7)2OuolS_&ySrYZwW)b;>xIcBfqNa9-&#t-wsvDL#*xCQ>C zAEs5}Dag_F?n9`-uPq}UtrkDqNTF2kwGhp`pJGW909=Hu0)k;3NzKNm6jsQy4v{)a z8!5kNSd>%B<2WQ_9Ej__p#AQ|tdG;Qw?%94Z1DTowX@ht(d$c3#ra*YEmt4ANQ81M zsC{3B-%1;%FmDGY8HnO7%z5c(Jx;tuqN@nB;%Rw9m+)HDMG*8(0_h zj%?l}B&$=UpH|kwNBo^i-J@}Hy+c5>NMF)bG6W zU3b~K30dHH4Er#!R*gze%)SIviG*k}B#wA(C;R#%)&Xxk`do})wT@EHaZ@KvDXwov zc4-sTLVaddT>p2{SkzG#t?~xvD-T+iGBEFeN>lM^Nq5TV2)w z4!?e8f|bX*7i+Unj3&Ng*Mo@5VE@tgw;Y?d(|vD+{=y0iwQ6iy3P`iZJ0A71zx~b`;|zz&tt;ODi$;WP_xv|GovQ|>?1T^mYB^rg!!ZS24#C-~v_Go#h z-VlQ?$w_pNI;ps_Dsz@FLN$xZ+fAEER>8;JT%1g(jGlfoH)W7fLd^Xpi(gN~v%szwdmei@2cLEpA3aJl8JZ)?Ppz^Bf=6FQGN$(d&HDOW5_C@x)W|H>94o zyFg^l-^tB%WKSSP`AuIRmml1*B;w|Cgz|>p&NjysJu%6IzASRYH0wq*!a^{{rv(e4 z4LN36WC03 zcTIa&B|GQu9u)F_X(`^|ZANJD77y!p7KD&injy+dxA*B8Z47OS8Y9++R zQPp@f0L-W?NlnY83kMQyr{dFB-nmbWUk6P3V_;0`ZOms!-m!e0Q#T4n{faA<*zn zg| zq!@V)sH1tzigcN7gEs5U4%$t^qa>+Em)3)n=?AydTIDP|Bg>Yk8QBH{GXkrpxYDiX zkR)r{ZF$d#qntkE*Q+wV&2PV1*d<4BhG?A1AJF``e;S zB{Zi;DHQ}pYzbk4WQ$$kg9mUF#-uf$*&DM#8K?Yt<@Q-x=Y67Gp_`ad*Pp^D#lLF{ zmzoJ)5pDAMtcEpKbtMBN&R{MCRp(h^a7s>Tse{Fzb5ET=viixfS#+>ofR(+(woQ)H zF+j;%K%HC5c1&O_7L1>zH*IDygdpcyACZ8^MtI%Qp|HiXG!pjA?m;q6cBPuq`3Lt- z^eoQHW5B>#bo@Q8`9?Sf2v9wwHl_U_OOI}z>q3IXv;eD^+ee~ItLq@m*uD$&u&nQ z^nsaOnO#E{oH}zCWluTzqEqs(e}VRXnXup0D69ym9Sr>{!tGFjdz(UClU=&Uyh>Ju z>~9Ko7mY?687p331MNIonipff8ISz{WR`|V6X+F2`IL=Gd&wf^kV;`tVr5?J4Cw=m zO#Bnpw@#__ZMC>8j6imws|~Lnb9yMQutcQ^-K_E;+}}$2994F#2+>NQ4cUHwuO1nC z_%*VSKX&@WVDTP^j6HhY$HIJWO91p9BxwT|C?;U??fXDjtk9g05Bdc;q{l(%@- zS-8a9t+`ty{uRx77%n##drE}4SgcA%w@cjNtP>Je2{^(@qKN0|C#sSj+xK<8KJg+` zLrAHD+G}N}O&BCm6~;laBWm$+955%BoP}6!Zo1TW5Of1smj5h-a3${>CdtUH!jEw=Fmyz$F(0#f z`6ck|3x9H9O5=*U8^?$jU|KuM&S-~*n|cYz&aY!LrZZ*`G}&uS`rn-2X(1=6wfC8M z4u@x%{4p{ouGo7WHx+sc>U2zP`0W~?Ge||*r_5-;XI9$=wNx>n#TDKsB_`$ja^;~3 ze9z~F5p7cWX^$}i@bv9m&1Dn~98;bnulexKvDf#&8U^^=Ip1gMUNvqNnYqOGIv_jE zed&y1=LX9ymHN^HDG3}Ync0@8{C%@!|FW3qIZ?aOMU%fo7*jVRCkQhIYvvLxivW67(`zCzpLu1-psiz|{IJnByR!YG(W2u&Cdg&Jza ztIGn<>g9s$M-1!(-coSx;*w~w8CI#M%9$45bgKk?x^E9g4pN&D^AYtro|Y+nC0?OY zP?dc>;Y${1jC->|3LNw7^^r@qE84bJomu^GPe@%vw?mj&Vbzl67nOH!{QKJmnz729 z%V?S4&m$EFlapKt3g135RahVai6rPCeXCPQ5c92>;I+bK0TQ+wyDL=o*PMN{-^{Gs zn?alLF6)Lk?%vp`G%|2e<>mLPXwII+9=bSs@hcS#Km(jnbl(k(S~4ILsqbax6!w{*z=9oTz6?-$@$$IP0=`rY?+ohR`@ zqYZUBhDLali7#1(T^~RdgHCWO_z5oJV|H`@S>acEW%_C)JzC^8nCT_L4hPwl*2V}GQ^o&$V#B3-&q2etHKN;E|If7R==VE={DQDx zYg3BC943QE_hIZb=N@7(T_Bvn*<9W-9~u{l_{(&6{gvyF~8{1>SmWCn zD;Y(uF1LkYIidPkF+G39K~cT-nl-8mR96ug-h6QkFj8ZQ{}62MTEqF8?Ok-UyF=P& zR{$7nzhsBVW+I;EFI-=)9ZgXmze@&%hj$9==`5Hgj$egqx{Bog)pLV^bHIuuSd(Wt zNJ!H17JQlSYhR8C&yeRFE8fBji!8cfVzVwz0+p=U@G%@)VO8B#`mOO6sFgQ_c$_04 zx85QVI&Pp)a^+X=wsIF`SY56=dpxj;1h4mf8a@rbJ?;i9i@t$F1Cvekb;#4wVEPU4 zS)*NbG-PC}IY`i^8$z&yqNK4aehD!D;3M(;MrJ-zLo7ixVk^O5hwy}wQR zm;9>7U0^kQW}8X}G) zmen0W_>ioMLWqYLT1oNX-^-l>0#xH4yaSR_g*sg{0!H5zc|+dPK^Yo#rdA`Zf+Cz3 z5O-m+a@LJ{;@)KTN;T=6of@1=tczzcMP6-Ll4{yQx=@F@X*!ny-!bw-+>@9)?wdt4B?`IglX$<8L!2UZ(W+q2tJ{pqw zThHOG@CkU@Sh+iCkcRKuFXxOmDWCe9+yInD-k+dC9aM6~mpS?apU1UoL84Meb7KPa zef&z%zBzycR6FvooDpZ#)O6bTpigr==?4$`f9thnA?j+NcMnL4rh2zb^~h) zVM;2Y8LmrEWtWlI>o4g*e;a6tMyptM7IpTD*(X%kZ=ud#RyQnWUfg3T3RsMbkc!!I z(;CJkmZ}TAqMY$r4&s#U4 z#`Aly^xm(!)a&5+VYVM4@ghJ?bAMM<>F^TBsm|6GUpQE@G^%}wqfQXB$?%5zJR+;B zq~u+Lr!;Dlwu0weebuLj!s;powQTDts7l|uNm=J6q0{MUak75f<&dcGsNFV9_z!PV z=|AFFc@iRBTh}mZc*9>l@U@0%54WQm-ApB51`cdI>Pf~Z5{TkWIihcW<3u9q$(>fG z+8eaRQ^p6JVM^r@-t?*8iWZXc?B<)61-M1K?en_E5&h`0{(A`EY2J-D^llL!2q6-c zZM^f6yKbtvhVM<#G^UJZ)sjLK*W1HPeb~rx$AH-c2HRZ|HiiS&9s4MNy994-m=4*l zDLta~R3wp_E)SPJJ2#`m!e?){qgXhUq!R(F%H#YW)h_e1;q*=K_XQQ4kNRPB!VkV2 zFg}dP|Me^;sWB=zCCwdmS|oA8b~tnRx+JFI6iy&@JSv#dAK&(1vX#mFlUhUUo|q9{ zYH%STy{yvT+24QXuJt_ZBZHKz9FtE&tN9e(W+ubb`>uKYecWwtC(YE4$;b5vw~+zn zzYpNdDjNd{>&aHxGb0ZKZy{DW|EnC%q#06__Mx^^>bvVWLX^p;HfCQ270dD|mjbBjB*vZnQ z3@({`XpYPG5!bDZJt(>+E1vSvT_Fh~)=<`?Wt~kMHa5VGOe3m?Q*^P!yc#1ZdUlh% zbB~Nla*Kujm&1_XR)=MKeeFzPRCr|IB?nT(9F;|uT{(K$LW^lo#OovFUmlzJv(x!& zMAGy(-i<>fcd<*FKdDURSlxI9=mg=pW_t_uVjXTOgEHreK~`(yDhb>@xVja^jf55i zU|0qlf>Ru=>Ldw55*70Z#yMe`_u3|nx@YBtI|?eQ>*kDsyy83LVjcmLbhkxJ!uHLd z0c2|qS+B+WuO6Fou!2~+Mx)Fr;eS!K>^`P`QE>ws)-W@&i>dyvk(eS2r62^tF?B&uKoGM>^JI^*a zGM6bfZtg3lTL%5%!}!rf$|p7um3Se`-!`cAYA6{Tf(kA|Pif|~C4<;wqfEEGj zkK-GCk(t#359>Z}-li$R1#=t&+3M?)2$EfM9?@!SR_p7EKaoBjZjlK8j63-33Dt~2 z#L`Y@hFreAL?&9x*fm+VSF74>*c;yuu(?F?wyOUEJpX%)PaZf}rKl&_#G%)8el+gTGv6Z#Z9)+3P<#Q$4&(cnvx5)Ao>8OGH>#*8}zANJnUX*PLzMO+%L zI zFl+Ni##FoM^p%L$b}L?9;9#;$Lz-bfnEhL0o1-euD{l2bZ)y7m!~9a>Ch}G%Gm&Op|EjUtxs*hu{x~ z0>Fv+BEv++u_y4eHwmvtCU^LTyc^zZsr$12F^o~dmuTpUbGBQ(I;9$OKtR%Epp`lP zkAQn>#PKfn+bzr_$2V(VQY~!s`)j8J!{2i8*p93ygnjv3t*_OOkDADhZ4}_?@%_7| zp){ZUTV<{cV?hhfAs=2@|48*?nz=Qn)?tFN4GYaLEr>gZ!iz_9+TuUDRPH zFJmzo&>rJ~)k{jucBM$-zf)bSuE!qpw~q?}&W^JcfSx-_?s;#J2lESpbR;<|DOKcu z<%~3T3YH>aDp)(E7BJ)~P01APR3D#jb%%(`w=4#kctxc9)1@1!){h&WVzwGZvaIf{ zT1J{l#9E8QZpe4c+UmKaqqvPo0i1H{M8V|o7wf`2xO}~R3||CV634Xmio#uiaw2F3c^8gWgi7RGDU>m(=iyJ=7N%0jdOm7Mc!z2@A)#=0U-=R7~6 z`0^LI0W)j=A*gOq@P`7>0N|(;JM?#_&c}RQr|&`Ba2IZYSEaXjrVj;o{63bzB$=Me zmhdmk0w$4=SHGMLV}`?Z1XOaD&COPAcnG+;Q+|@|2mhI;D_T(@eEiKGyB@`$61`l% zv(|nuY&P#1qxq1Hy2vB6(?f|9x3OIu6dRnr=$b_2GPkuVrG~b|IwI0yxu~gQeC*y*I_KdhuQ28*_svxh;;>39jFkj?3 zn$-HZEq)pM8K5IvMEfP(0(E~PvCA9&*)|J5>fh~JZstVPIoPQ(OaJRFf^uS4N8chU z49_%;NbDyojc$;R!JIHLTog;o=Os7v;!jjSX47g!Eu`mnW1kTPL?8jus&!UR^c1qc zclt(A*@~y|{?2x_Q{VKE(vW4$rGncSBbE$SOJmN$Jy2o9#4kZjNWcfJ3k+5Ob@+GW zMz1zB>Eo*rPXJpBX6DUJP18>p2TEo$Tm}O)Wz~12nDe3$`;jx*f;U>be?a{tB5`S_oRDo8H?i$!f z&a+3)WY4Du_7E-uthg$X+gt8@K#yhC0lGca|)j+KiUH3XljI zkHT%T^YyGveq%A_90PHH8TZe}TN9LtfPhPMYlBC|7px*Gi!g;*87d?{q=pgF>WS4V z&f4;aGnq91$Mlju*lVMF3~#u&_<_(xm@9~uoXGf-EVCfvrOW6t_+Pj$O)r@Bvj3b3 zq{M1iU|LIj$;~a)pDrC_t^!`$lfqo~D;}ydFZUd(7{2 zww(mczf3J(5*gevz&NWXkGo2tozVhFSjLu?BEE9B9T4L4tq*+pnsh$*KjGfqku6O6 zQ*l@3uL1fn^GMwtf*T*L-rxn8PV^=XssD@HI=w}D+>&{rxxwBuC%4F$`Vsr*z;9{c z6)TD9(n{{?9BRsE{SeNqw+X%VL@FV+x48fA89eSYn1cHmVo|BBjuUk*qBu|4hzyLe z>?g6Q&}!ztx|5@1K+Z+j8SWgc~vL)JMbFA~ITet=(kOwjcp(XTwlCmwE`vuK!j z{VS<8k}@})*@B?Qi&u)eQl&KZ_l|a_6FqsMudf7HL1IF1$=mWJ+vRGnh*Z2m0Wo-8H|5l3)#x%rOWTls zR&|l8Jgb`sYr!|^@)A70*!PfIxm=?~`P4M*X^#>Kj#hP5d-~0YQHvq8CBVI7U4oKY z))S$r-49ib-=s)qbBuHQ-Af~DU{`8?Nw%2xP{_;KK+-?o#9ZD$LzXUWCgRdKX@WPd z9QA>Xi`Bg8-1TNHxTtMpe-!F&k7I1WaK?7Dnj`L!|GnjcaH?Tikv-EE`YEr2|m4*^$$k1CloN<|DF)<>iwK=#N z4#}eRrAsLh7^G<}cG z6XNl$2gBESUs73@Eches65AYBDs+GpCl<`+n=3DOMrYZ= z=nx!SmpwQ*c`p{RZa@v6{>I*_Sesa%3MhKyq7P9I(AhDY8L@Ioc1bMetHxvKnUEO2 z9XAVN%`EvA{-y0-C-+oSfEzN?a3a~D+Q*sfL*2q`CSj+3KMhLUBoxB zE-MZqGF@xwhTXcPzcE0@@b|rd{1L(jMX4@G@g9o;O^1u7WwyBMI7#93<7{1)!`V3+ zWeZrt!*OQqI#gM#?Mtz~Tb!^}P7(`#z%m3$`wz(Ms?1tduWf9}Qw~fEA5Yhg{vN!v zD*(%t(^Tt49D7u&vBhi74MawUWOASzni*BagLkQ)$fvjz)A(*rt-n2Xio%+oa$F9GUnhgYlYg1 zJwh%oX|*!7eXMV%<0RvMOM1?Zi(~b#RhC%a8+l1a-=-K?C?YeoxUK8Dv)TTK&^Noi zD&#pi!S0#CqeAud?Oi~{I>U(jDdATA^Z4|cea>!20OlbWXEAO%yuewM#!*39cJ>g8 z%0YT#K;l7Rq#@8g)Y;Eu*2JD9cy`~foOey{`}g%S{&F;lsag|LYO1i}qhL(WebtuAzGtU9rj2zM zBXk-~B?AHK4oP>bRP{~rbr{|QxMoW)L@4;I=0rBs{zK?`%d7CtRgApQ05-koq z?I)5;EBah|o%{ahHLq0Xdau?)oEhn=^~7JJ{}2$m9b+l5^cZWHc-z`p0CXa!3`eK^ zqWu0->v}1fSDe&|EX^-i*-h>!zIVM33}$#N9zJezKd7Rlmn+War6uvQ4jEA&;h1O} zU8E83RD-rO0El|p**UsLyzuN4RsLX!X6?OOC7+C8%P}{(J1|eCh=Vzs8(lbobPMx# zBbA9VYyf)Um|f*Y@U4aj=~!^!pQjhr3_}!c?&qDprHGWi!U}4J%ivQ4KfKgs%zS|T z^ZQ{);%E3SIj9aLk|on5V2*0CMRT9)LEY(V?l=Y_56+9-B!FIqTr1hmwoahZOvQGa z+lJr(#qJ5*%>N&PJ5I_Ht*l~N;AloO43$7Llh7HnR8-gcI*K2lf4J401wt$J>rGB* z6}5?aI5x2WZT}VQ6kxa;ikgL=rD!DcBXlUgCAoa+S;fMoHxgu2otI5nnjKnJLt5cG zUxxA>_ov=kOFwoUQuX3#VTbyBpZW8lX0mBK#^a#bN$j1tTR}+Ce+U}!8&{~);mUKI zBZZti&dwf^!L|U9Q@zMM-NHOc8+5m2_MTkt1D=qFlUr&e-?7J#7$Mf}`FrKOa&Z-w ze~KO~*&nva`G1w#Q;tO@gXC8BE(6~Q7j32vGoB-)#-ou8t5z?btl0^fyxOUnuhU7? zaW0RNBO6Xm0ty_kFhbH|OG$4YgFK|tA=$2>(0L-O?`^pGVUy{84%DOEtYq?`zP_MQ_q|%FQk^{~)yRrO^I|kZ`&AcA%@R7v*pljgXCzm8EpL>yxT%u^(I|8@4f@#Mm9PdHAcW$yBwHs!LHku67dbA4Ri3lJ*pG}e$2cI`| z%iG7yOKY7Nj1culDi%IX|ejY``SJ>LDd&HnloaWZ>9N!|*Y zUP924#;xkK7038EX?Pl0IEfka?qZ=2!n{it^_Hky8W@@t0LfP4B>eWi4{qXF9%^#Y zMJA_iqwy(^KPIb`ED~}}z7h%bMVic3W;`7sQdiikt8fdS*0Z1cBde@~ASH`4F09U` zd&{(xap^Y(SY&mr8+jE=2bra)#iZvwX432@+0<6$pN~w<^c5nf<55qd%0u3MtrDZZ zA&LujAUrO979DDQD*U@6E{#DX$gwiAHhiGD;_go6FNO?NRbCDjE)>JSj|5tk;5a+)YNVr z!-IiY*Q$^Z=Pb5Rk6098%nCKtW;;!i0-UHXhO5O@k9?O_(Y_wYOTrta<+pKRafNg$ z{ce&>-_UUivZ*oWsyj^~bk^tF=XMo!ed;Qm1DZVr?)@TSp$jv5J1sxSTxe&y;uR9L zg7}mFiUdW-yC0?WUW>UvQQy(}QFVGos0f&`15yTK>+In`mwIz$aVQ%kBO67@3JSTBL5RbCc!{#-3^?D$kctXO_x!e_?>lRLAPlm8 zV`9VO#r^X`|IbRC{|P5YPs~1<(~dNZiJ=w9mYc0>zGoe^s}ufmU;Y`1p3y!IbsD9& zlxbd-xMY#gV0IO)t5`}+MK9eMJxFiwy_cPH8%~1MZ)5mO<;zJe;7552`_~t2KvO>0f%e44^ z2!sx~jNZ-0`;r%bH8CbNsQ}*}kUNub$@*^0_R`L^(iKCak8LUp%@&Hc8 zP7qqe>c}fADj8z@ep@&LiCIcSxRdvOVxKBd9>4gYVAZ7HYildUT>7ae;+M4N3C0N? zh``6DQV|)q#^cM8NT1}~InoLJp{i)uJY-NK@7it4(GR2$qYNo>d|&+0Xb4qZ z6BPEg9%@GZRwGW1ha^o1<^d*cln(Dy6U!ehO79qbIya_CTg`^U=o+_@J~JXbSNHov zxpOV2HmL!DLE;Pva;~iOCSE^qDFlOvML+F#y!V6foV~hdOy($~Gx0T7J6Ws)7Gl2h z;%OLf2wjk^#VP5g5S}|U5Di```B8o;XR<8PabkFDMA9DTg8yZ^-1a0uC=KpHxFpC2 zGxKaS)>3{Ax$M{Oant9R`Fzdbu-*aVMUAFthn^I-Ea#X;?Mg~U%rJ(h&nd7y(niyE zvaid1)VEYQbCg=H-Zzy{4LD}=J(cg{J3k|C^vD>Z^Q3Wuzz4cp7&Y7R-8pjKTYs{r zRAhp^vQ)g?v1I~L>Sla~_G0|Ib;SAawndLW0XxnbSzS!%#-7LvGyqJr9m;d45)F~w zTX8PlRy?_B>mGUpt-$A=r82iz`t$^28*|&hY0h9L_-uhjeVS*<;&lQ+gWbgEbN&U% zY75L&!pa$=&Cy+PkI0PngdVcYjSZ$ZNM$Tb_36%=Bh?H;lj}qCCy|lu_L_&}N2`+6 zS$?Nj%}(Q;j2SIxQM}|w7VScTuCvd?GNI-72oV)#BjSq(IAWi0;%3||aDRw07kzzw zgh7|~AjP_VeR2Dl=bs6e71Af*k*TZc&>{z7@^qWugY-8-2OSMei)ozthCr6sN*$#n z6gI?u0Q`)QN(1K(NVbKVDV}81Z z&#zmN&t?xJ+4PvBr2{1f_p|eDc`7&=0PSOnfwTn{o^={S)kFQ((srshvMHRIvnlIB~s-b-=cz7$Szy|j5cPsWwv7MAwU1b#U$}bDYJT=9>|4pcVx8apS}swEL|2ABDp>>r#+3xEZC44ReqzLg9kA2PNhmIkklrTlkR~wrM zsku8@kv2??;F)his$vFCY-fzPSll&#ED6N*r5{}S!-fV~1^oNod*kjA?P<=j$uAdD zqM3`;joAl)y_@=`-%WoCs z?T#Ap-LT=WWc97vy@BH&(f=8s+Z$m@RBRe_;-@gB=aNrwgyQTDM@vP#@WX;AgTF-l zb!$Pj7O-Ez_J%SnC|=3;36HR^HeuJOfpqJl(An7MK47Xm>9tAyr9b^mRa%s6gB+?N z8RsN%`jPG7g9XDlSqI@%E`_wH;g2y)^Ih5L-*a?zS>YS=0nKugt4z{29vu!7$Fb+4 z%6KY@gM{}@vheKI7K~r2xJ=Y=GTyF;oX;_>^?+j(GLGf_p#9$F+lIj4G_hF9;Fd(xotg`-WDskXgb*U6&4ta%ycgg3(|uf z20q|BCL}8CCCuIEJnXp)CiQY92E8En2T8N(5^|MNH4u9LfnH6~RIGr z(_p1vAm%6Ecqs`QAQ>G3Pt^X3QHf^k71$&Zj%th zXu(&YRLxl<`6Y_Y_lCt%i@$r%BAnP#TZ`zM^9@0PX6fEfRf!qJRj>}-`_kp3gykVm z6T#AoKnhH0uWCnt=Wr>>YplXbc+$em+-xPo$6pH@W0N3}ipRRA&DOLr5aY#1f;gi( zD4}49tZlj~NB7L-h>9K6Wc=38+U5xx=ldGH?}8WTk;~h|hei*V@`Sr?>uccchbL`V zoD&B7jY#3_Vpu}AxgYys5zvR0>>cXo6pY~yM59%O2n~nGQ04tYYZ%bou~mXUrIew4Ewu8L0gyibA`P&J|EP=*RG zi}*lm{Vi;*UUxZuWv(OwHZ7i}de)k4x*#O|I{A7AIFA_f%G|2sZ z>O@?YP^_xUSJrZDa?&G=AFa@FE4*zps&`qA$(ng8RBAv}nTN6Uk!Bh8LN#%n@{y7C zuX=C^@j1V?WVJgoL4^&{azu5CfMdIT_EvZ~V?RmT9VUfpk#+iJPw#I}!`C_EHn~kQ zzx;`ZN~eZ8=Vy~G8+1QE8E~^{c3L2J_yXRcwTrs16yk+n>K=$i6vLa=TGI8^j^7?? zckoINdI*M#hd&8#G8qb~-r4LKr~FHoil&m(y30s;zz&kc1A#}AGg{_jm6+x+6V*c}Th+{`#mzjR<#67x9WbPCKLjI?B~(d@qLFg6leU~D&g#_;sHZM`A; z=CD}jhO!&$6uWX4M{?Z$Oa4YWru#*ODXyc&0DoE6R;11$0k6lwCq$>&r_h^mf=+!+ zUIVNexOL!kC?BZyNqe=IY6mc&86p`qGHC%JeU=%Be*=^*8W?NGulE)tjOSbpqh0ZQl97g zAHsD66YoC0P4AGIj1xm-Q>3pV3C$*rJR}6{(Rb69>Zp{*pNOsfE2)>~Yb4Z>{WTA_ zvs$c}Ah;unvUw`(U003iEf!q}$g0x27O6UgQEDh*k@7d8JlS%|%L$qB1$NAF_tpB0 zeZ46xlt_%LIS8rLP8064^3OxIcD?08_eplupQzxf)Wm7)0H~#1+tnI3{Ac!xa?B88(Eksvi?Y<>^UQ zz(Rw;emC(Pay7vk)n_=klfiMx~^mK?3#yT}xKF)L0G10>c9}2&Cwg`cWWwV#FaF)}&I9k=3&?!b_!EKRotRg!(;L2>Rs5dF!DO?s^e9{DSF53%i(6|`mY&}{+a_`; zC7uuQk*k|kXvb0!bWSXi)cKx8&5k)I`i|2eW0uA8P20)+X-_2TTJXQ-Xt{%?&p?d?V&lS}a=EgVAftBm_%Owj5O$*}s!H z7N`X(r2U8ZZ+DsapJ&tg*~L4eKjnEiqomPPxZZwZQ*D4eo*6Oxfeti*G_qJEROl`9 zBlYuBL>Ybal0wt#lZT#`(Igf?$LSXy|Kn6eB~~fc(B)%+9x;lC0s=zzY_tjC{!*%YQGJW zEPszE>UHWdpA5DNjGexqhUt|wYGMKxrQhZYLc4WLgSQC1t?(4#8siR+SpL_wnaeBo z5z(7nk;=l&*4IeQa;f5$IfR*x9JYj$OYpoguF*Ou3Dk>^t}CNj{85H z1@+b(y>kXAB0vxLc`v^SdYwzaIbjGo_};{x-o<)w<(Eh~tsVw!SQ<;b5kyrp>q%-Z*eq@8-4v$dy@B9sev9U*D(vzHnvkdUhEdKcxx(c*)PLv zyzNJ_Jo4s+g2Zj+{5CxIarFBiLQhf#{7E7CcqqJ{f}~@nc0kxTzIYPX%Yg8|IoZ#} z$T2Z_;~00at>-I=CVhuHG>FoTaVs*+n8>K8?4j6J}U{Lx(N z8$5CIik&gDesVA~5V+WltRo1`=yZm$h~(N(T@iRks~{Z!&3@bwWskOTnj$3Qao8QD z!SEnOOA!wh-op$|sPP zB};muj#wIenxlk;NQm?wf_{kvUi~S})%FqA%W#)hHR;N$d>fCp0j~|DAv^;9yUNA| zO}e(>GPMBsyDSBTw?sL=O*3j=|3%sM8Ib5-VUW3{p%0n znk{(%_m1&cg#>BG6=_^0o#-Z`qe7q&&9QBK62P8tHHzNSU}>nvxKz)M=nxtF3#&bz z{N=p>PAk`jfOFoh@3@4BP#9r25~?9t1u1^P=bD4+4adV<2t;E(UDz})61MQytrhq# z>3Bf#(hAiRld9Y3?KL6`IaF-U`h12Gvm~mMKEc`)3bt2&izc22|Ggy5);7`(hi9XS z?Vw*fm8@yeZYRHyX)SIDO!{?}Lq=#2s~-h7e#VYgwX5rXhMFAwfl*;C3O3kZ=-vD3 zxv7zV&rRF%J|71b77uFY;NUeyZ#4QT#Sjo64{dM~5Bgmv{#;$5 z`Gbsk9RJIq;rSo~`vy>dU!u@-YW|kmzAY8Cscq0O5qWBbn(qx-5!#_phuzwpG_~Ci zW;zs&dcqfhT+WBx-p2+5L^x)urojFWlZ3Q>y{%aR3l}E@GjDZxHyuT@y0NHe^%m} zY+DN75PWMQ4Eoi&1fQCPBIfcU^2@v9MoaN+i67zn?kU09yGsLZ=CfRKbc}_Zk>0g; z7GY@YMQEz`hgdIVeZM^7zhXN%%K?>YHS>z$s>8<-do>dHI7 z%H`SB(gBpOz_1v~hG(4tDmMH^-bkdY(=Sd3H;cdOSBJeAN~c~v9IaN4jPQLiwwaI0 zajyhNsyvQfAh&zIV*bzETrC-M5vykyas_ia^Tqe#g56f)~4K}p|8}gWBGtdcj z*88vjVq8w3VKGDUvsHT|a;&9yV?DYB3*fnY_RCE#aVyFAJ79!xk!PBXtv z{@x22{$n+3Gakm>Z4AMY;>_bnWj!KYPs`89O+vKrm;0ZWh*$D_%5y_yVNHP%12|(} z>|M&98Qy`yi5U{8r+;x4FN=^clgXr~ScJS&ijnu2;mUMx6RI0XGFDjq1pzRz`|HkA zo*d{yJdI|>KUyjhnHdF59gd#*&e=)xMA>1*7BHpl8 zz))euzaT+{^>p5ux-=g-z4U9%Ef-5Rl`Z*H(%9ZOt}su` zQqs00VDK*0bTySMtD8prI|)z2SZ(mYVC}0VEuHtj63$t(sT~T$u5yKT9u`%QS zx<8$9wp=Az5G>@UZOH{ALGiu_HI4ujQS z7!ghq+71D$py1PeLMhH|;d)dfM-}n+<>oMQ4{zU@m)IhXB{=#k@M4Us=tb)hByYa_ zn?g<$39U;KPio4XpR%|udaJHOd_9|zDM|Pw=VAqIreV{Zaj99l;4`S&dVB z^lHP6py4cSv-$8Ar5)y*otsPEU;~blMDu($ffUlx@;3jqxL)=<q9K z*8z#rOOOIM^Kzkm?nsspW_vc~$FnzJzqj_stGgV&5U4p? zs>L$ENc-=_3Scvh0A=JEZ%Jv_9FYjEu60McE{iq>_QbC6PHdJw)4jxMt7c#GSqMACd%AG0(&`$oY!eBenHCGC74D)4vd?KM7xsR(bWaZBi&z2ita zy)$rz#|xCvj^@3cqlk(R($m&V4qz@DoE+$?P%k^EzY^H z4TN{*wmdk{K!lD$B*?$~txfiZh$k1a!dGL~WmMnS44XNxx8eAR+2FyKYvepUu$-Ju zNPvlIeVpMKfuyeb&`fMAPwSD7!6#SNvsjN?l^K!At(nkoe(()Zo@R&ldHE(oxVFUk zl`Ar8U?;9K>TasKK}%iDO_x{n!~sjAx*;95;eSyyq1aL1FW`av zimr;3EpJ+RzE|k$N0i#!xOmJwhYu5)LAuxaszd9axwq6cfpZ}FZ^Lc9SMzO-m}~I) zWnlf-TqC$Q{Fo6n(3Km~Fx`ow(>jp04HO6_pT?-k^&Q&}Gp~IME|G*1yqpd}nwoUJ z%Qp!X9PRCnb-&^*FgL^*Z~ekQhJWp`N_91Y`dy}t5%Fk_XlWFv;dhUr8psV9N}bk* zrCDH<#zJ*2v8f(;MHfhF8E!mZYkXtvEOT-GqNhR9M|*KXLCT0DHq{(8om%tq@1kw3 zqkMU$V%o*Wb1}cbZ9f*CdJ-q{<|r0zOYDKDyZqn{ObDEW>)hs^^)H*337g4;Ye{s|I6GHYi*;b7dP6b z$2F2YRRpog!r}CVXA_7a(-e9r%YPK@x!W`_r4Fa>UEZS#8kDGJ;Q}YyRdL%l`4Jq4 zU0>&(&EB6Kw?yMeF&8Bq6xI&@D<3G>6|y8g#JLJX;SFaMtG{)7r{Y^vA}`$()`q_A z+a^T=N427-@H*SF*u4h5vAu?iO5YN7?|-M2wC`w;m{!AFX}gsQt%CMERsGg@u2Wk_ zruH4pQR2K2wp{7_?DqJ?--}mW35?BoO&0)Hlb}~+ra|h9&vVzDleTiYcID^_6La!= zEDBiIm*B!yY-xI-k=y7p&Ao<1Q^G}qHB;BRI8tP_5Qf6hH_?*|YEkm9-rM16&Y$)R z{TMTyzGCi(A0_G0dq+Qm!A(u-X&)mTf@#cBkZC@!sZe|8Nbc!A=Yc~~6tR+0=IDl^ zG8~d#`V@}M7v^0m2VaP5No$wZMCIJH{p?mrtZIy}@~}_SfN)DjRQz@}CCRMvG6P}8 z)iLZK?zRgt{s>IAlouw=BSaNZj;X#L^QLEA&Y(oSuVyrS2W6DFW>a-Yi)*oa9H9vY z3n$4s;&VOfyLc8=+yqt(fk z(X47Ds!zvpoG`mWiFGnLuQr-0?mIr&&x;4kx5nZsNpqW8U-}+nMgaQF$*mpd0!)R?tDg0Z2lb~Axa+1qt?6agK;Gr z?%l15s$AH6z5fWKAz4PR-ub=0P$CXi@h8#|jv@+HKHbywMdnr~`CO4An1aj(qyPdb$v;s5t) z2ORvw!~MA#N`>mmHVx81(K)zlB*elCrsCg~nkoN)|Gh6K`y-DFZ(;7rVolE8DV_E* z1aWR$A$=^n+(?pbTIo2f)0jaj0K^G0$Nb|y&>E^vr5|2spl~^uV{BVaQe{d~m7hxv zpXSu`C&8_y$S>a81CyjVH2brS9a3pmWJH5ysh=rpb7I4JR5)F)2*F{!4F7kKC463R zQ80))UKSY5MO`k(dH;pKL&G)en^*(wDtNkwSwY_uR%1!rU)`!bmy4(3tfk~|{i5{D?*e9lW@rftf@!#4HiXjRe=FagL|7CD-WD=g z6Bnn^ZPt>q?67D1Vnc`j7in|2HzzS~SbxVrckWT|Ec+-T?M>{nuwus(>5zYVy1uuM zG+tzDIBy%%&F?{{ew1v4Lncqw!0d0 zs0uq!RKTqu6hpKN>@ms+N!gX6-+v-ui>Ksm zB(VODuNquFue|rDAhcEy7s#SPaYU6KLYUy@v%_8>BHQ!S6^EO$ zSDpNd^ZF%a&2PgZ(=Mkrb7;__eyaJwTS1oIuc627J_t34Kqr~WXuB;Up9;T#!xQ&w9nTvhV49O5U&E@LySaZ)dJwzr3ttQ8(UdZ%= z>8;E{MAoD*vw_Snttuspf+dl{D29BR>=QR6d(Gl>o^^)5Fa=w?fejij9BhCz308fQ z>^#!D-U|p!b%`bzU!Yx~O{Z_bOEkZ9SfiTKxc)4|9{MpF9JjGWas|>KS`N7}4AdMJ zJ5u;JyUpvJntY&P*3qaK#>wKMeHuPZ8gcHn?AxEx$mvr>dtm$ofc}lPv!Wd$KTm@= zwX6o^onzTsw_9Tk*|S`b`cx`jejwfPDP+%>6zb{_Y(%(dno2ZYoB(2r$bO9Qo9H zX0iG%lCpC72xjHNkFBwOv+2g1bwV`ZhJJ^gtUs|sx|jwuIe%9ID1+XTNCX&O-upnU zSoHVla!b#8Ct|J$Xc$Zxw^ZE-IYY??@*9tZp1v)-XF1>0JabFV(-H37ySKAk}|i=zrM(E?-^KLFD_HKl^%*c`dPH9ck;Tm$W)5KgeIAls6_xjMKWZzt90@}19#i0v7(~{i>sw|M3_eHRx*IA2_ z&kcw*{eOjMJ&ZQEhCRO7tK;&F*~C5ZDbRi+-Zi1aeMm+U5XAw=h41_ebN;anQM|7t zQwsUf?GxwsT04H;i~LY>T?Mg)({F@OJ0|Zem`xF(BzgIm)4TRTjF2bf2zpZgTyvUp zP9ZF-lTaVP9dv+Ym5IAwbpNI5c;-fI7lkl30N0k{{mzS|6ei!N36I-%SIDMTMW$a3 z_|Syw{PsWkBL}gDt0jO*n*xR67BQ2)a_A$>JvYk1uVUz;e%5FW!jcc`TQa@h#&`OU z4hZls2gB*=miIv!OkLx-8QFC7cB-r+peHDnA&H|s73{9YRz*OhMPM#?wn*wS{>mr& zbN$+=CBtQ~C#=vcRVBd-r~L7q)TqG#GXa7kJ`J_aOVKqAfA8DT?RmEcOnNhQAnx-4 zciO|8hMyahp|~g!Kif~R3wwc?V?A?m`@6Ar^{D3yc=Gt6pszYQqTF^aAWDzGRJ>QI zVa{HTDZlBfdK12hRqlu};oxy54;*MSbj&$;!0hd0Fa2Wnn`9cxz&D2s1GDl~w^X~jO?4XtDv|rmx z4X9?HqF6CSp$CsT?cJd_&g?t4EmSgsX?TXm=O*CmroYZvg-gdG5B0cz@Wq5BpY_%Gd zw;waM0&TSG(mT#Z|fuezK9z?Agu24ibkuYXS?sYF z$?_@C84^hG&!vm?o!t@xtY+4Pa0*pgv}K5|y%(PdL(TLLU79zixu$&p zr%@ZM1Y1f!+uT4ZV2%N6#IcKIyOofp0%ODBYB-b)1if`frJfJ&bb6MlE>YSJ$yqE3 z5xqFB(db&ns9IHQEYRCcU#= ziUFQy%XZn+5f(xC+%g`Y(f27ul(fX^$G+>B zmLSePGai-CG(7W7DXZL!tu~b>>VuraX?_+1xScfmrv9{Ut*`bi+Z%K2vzf#40v$5~ ziL^i1Fmv)mj>wRju-BBQQJ&BeZ+mX^fMnQD{Tx_9`DH&6t38%2Ba|=Jp}~A7hax@) z4J~9Yriy^~5Ct=iTgw{V2^psoRQMU0jim5Ehm_$SPcV*MfcM88)5Al!ZjDVXM1pa) z?6%Fk6}F2Q^>L9igT*yNyg++*5~pFa%V%R~kE%q|TT(TG*zUVCC?))>yU_AFlP(sZ z6dx?#M`y9c(>)>@{v_KYtX=C<8N$5L%^cl%{xAiN)&D#mxWP2r5-f&s_mPa>h^@Mg z4e&Nw{v#~QtFIsQEZ9S&dOg|r9r`m&|l9Ow}9`n7R$E=e)Ef zEk0PP&~4LcFHe8Da&9fW60waTMe*o5ZVa{gwJjjreY|03R3GcSV{P zHmEzE6D7}AeM}TvGZFGf2sUz4@DC=~eaHG%q-O|IB*7mUCm^3w@|^8@tZ;4b%?bVh z%QD~FR}9E!l2OxI|3wNLT2`W8-ot1sMjZ*52&#ydmzQxB3`<>?>nqLI2Ibu2d`i$u zX)xx$>uYG5beIa&%t(<+ZBV>_)(yJQ`y7yk3NTI}YG*l(nn2LrT?ilLyc@iSI~9EJ z9_y3@!eC}Q`*r`6<-*dIAhju-H0v8mVzrmZ3_ZG2oVZ;_D@!ynk}a+R+maP&zYi?t z=TswrqPf@X54E2ODst5Ffi!sG$lvaso*W8qhAUT$^9Clc%QGFP5>yulvPw9qzByhB zloUc*^>dkkVL00+e@1jIHB)bHq>`ojZXez#Noz&Nn?D?dXeWXr#w4j)r^HxbFdjEufaT{QaV>23@YYld)Qt@F=kXU&6>KP;*VgVZdQTg2Gmv_%#0{q z^_gwHag_Ar4;^W-I!-UTV+%%dWvUfQDHhgScYN|;e-4Y&dc*FmF1*)ntt?W)mu2IW zic|fus?t1c68!nAAA?+3buhR^dC!AI$-yg`zcWt^Wjuk!=VD1SVr%09`4D20M!r`+ zRLrmqnfu_C;6s4jKyN0D`&8<#tUZ9V~cYri>x$Y+f5vKEe> zrIq!W81EK6NmG9G4rR9gIRlJbP;1}Ko%|vf8!ANz`wLB>fD!84tbUnCf415?L@TF- zfQ5tR1VyXM;7YXCiNo20maJTY;+hk@xS4aaU==vYgp>Q@xdA|L(ZQ$WMdr`EjgvPw zA`%I#JrB!u*A&B~dm+U81yShMEFqdE3p6$Hk3rU`!|$z!l`Of^aW_=#(p^6PnAJaz zH4$RZ$j8d($MH*+GC6laT~e6#zn)u4I{j>$`6UgMQTo0ou2t@$CIubXzblFP8S3|i zeRgl8v#SF9_w#~x2FS0Lps@4wN(c^Bwv$8ZT4su76)( z-B=}oPXSW^gev$K7=+2Av4a5#P%gaf!`kF517rDf#t~HpwZ$9E(!-FjrB?cu(BBe( zE`bi7eg;n~BX6^BMu}K zC0zExBvvy!nKdO;CsfZUI`YVL#%vN}5I6Oz^m4RKmn&4IG8kNk= zaTAn>(1+b| z56jt5mRo!&oJpCVETkVs^ z)%;i+uY#Z&%*(E(dOiZ zY3KCn_f|JaopPt|XF<)7SZKbr`VR!X7rjgmo1>4^T-Xg~pHZ9BN&2EW`ZMJ3Zzf_Q{@L4_&MTE7Z)+AvTs!3K-90WaJhl8nlY_|f0dje785>kT5$ zlx$~%usb+{v+xi_!aB>Ti}xhbA?GW-F&Su1%6T!y8DXK!K7GGq8Qm0qsl1H(*Rd>Q zW1#O`{Pd$L@r}hN3KSK=Jab)HG5wFWHv*Ee55{edEGcRKp+LDJLsi0 z>4bN3>(x7{Jdl})EJt1UmHjf<@tzA0HF6mo{()8PaP<(NiPs&>f72a{hT{KbszDr} zm+2bP*=WHQ(Nn4iC2;yU@eTf|#zr*}lou7{m_NKyf|)LeTZKJeJ)d z%eYd~aVq1U7|hx!a4o3RvpZdv#RVu*DIJWxf%GjT6aK6euh1nBmv+S{Pjk4Fi-BF@ z$U|maY%WjTJ*GSK>;*5;v*_m>&jQoUrBw&_wny^DIqpoIG2QF&KG zhz5XOT&*f)UQWRZMB5*)OWj15A;`{4fQ5XTN{c$Cv-C~mj-7OW^P5T;&*McH*?5+<{_&4zD3;1s|* zqQ{bC(s&wtfT%dxx?Id1Nb?)}LFuC@!NJuGy^Q5jZ++~zsl%|jKuOL&P04FR>J5s9 zwB6L%4<*w(Q)1`9Jrxmw_v*cMsu%drB{(_NRAFu-x&7Vd|IlYl60F2 z%WZCCVYfN-$te-68RBaoLlz4>5uAl;1xI5cG^Fwo{Ut8Om2C8D_yrWmux{CpmF)OY zg7sKRJBLYVH>8?b*+)hbS5xpVkjp^LVyn4rrTH}hhvUz;@&|KgeOP3=&b6riMiOlr zzDmKG=h=zroM@LS@Ph?W}?CB z!vpdmXc-cDRF{O#{E-vsBU@OJjCOXC_`V75vVg)Z+^gW4La1?4v$fIgK+ESVneCN4 zCLNsy6oZl0(+y0{5`m(3U0!9fRp%0f$z{iD`baGJqCh2>Xw|&oY3+)tkErVdr%fz{ zX!%4PcXDxpf2?uAKIZ^gHomm+I#1`kA?2U7o~K>j0rX%?#_HbA6kp!h)}?wz7i{s8 z*gy%v75EJ9XZnW!i2zLgYf*M&WycW>i@+Erma=gs$*^D`Muq)J0@g)SQ(P{bkq|ID zgeIT``lDdd%)ZM)4FYZdGY2EU@px+OlUT`#H^hi3aBP=e`vJw=4}&nF{c0IardsA(qcjVW~P0CrQBq10ZjQ zg|V&6^-ZotK#HlYIopgMP)7G`gb`~c$zXEg^peN}! zH)2mlkn8+X_4)kC+tC=aTP(BC0JPbIW*Rl&w8i6Kp{JnyP{KS8RS?)SP5*)n|Nj9^SC&9MfruK;^cnY$0??5Kx zN1qN&1Nlm-S7{9d^gnm}yz*}<2dD@S?!(O1`aR{FcZ*{xSvLHrx43I9m`a>?Lsa++ zWlGa{U)c=cTB&hM9xE0G!F+=My0#ZcJrsoV9$SSyY08{pFmZ(<7%M}{@KzO>y8siR zBAYUea-|e=M*vYB+(;hU$Irjl0{Xpx$9_oc^A3f{n#xU2NEd;+R_KXd_VwVw ztXv^P63EwCC|X*ct%-sei7Q)Sw{|XFF-@dN!k~Jc^UNduqTv!0L2l=HWwWuPGwC9< zGpg%ZcS|5W1XScoqRC^nnz4q5B^BOJ%wHdqn?uTnK{pt+f#9pyzkf8Xu)z1Ko)fxT zi@FtP_Hlu#t7}Mc8>W<(nf` z@cY72YK!+Lv_s!@R_)d?ohr*T5s|~<8?oAtg16~xQP1y@k8A1~v7IXg>di4>71(>W zjd0snm{G!716QyliW6BUx$-!H;%4? z+#Q=0rMAJ7J~KvR2sCFQ;VsrPjD}6h>^2+Vm6(cKCyIr;2R@fV(aTHjW*s(V54D-s zEd0XkN2H@Aa#Ka2+M&~?sSzAU*rt6*F7?4mLh@JwZcE=UWq|-G`oN?}o=iJi@{LC_Z#x)*KaegJ{_tG0y`%sCj4v6(2tM#gn&OQFX&)>da46R>K=ZX0II zl7Rp<3IPHx_iFnce*0_nXhH@T%nI$YdK0mYzzY<0xv-&F*PG-$-`5q|eWw4(wJz)_ zDUR}TtRVi-QJ$s15Vy`*1-!})&s0=BX z1t0Ha?KH!rHX7iMV9QkU7sgZLL{qU}#(dRmI%b#ay7FxgJQUiLQe;z+q$gEo2#tw7^`e+bM>u;i|Ba#&6$#D@jgK>To2q`Lsy>bC?7^@n~d0 zGqUTyoOZRsG{bNPtWpNxoPJT_#fpyrXp{bh8rrJV`(*R8Pmq#(&qXk;BF~FH;ZuN7 z%FhTO6OB*>cI-J(v&s6!8b?1NoMB@*j&;TNPYG>vZZFQuaz~ovWgy>EZq8WtA=Yo( z|Egzsk<-@3M{_F~STaHeBjVm6WQ9o3bVj4j0d zv~W_^q=!vHP4Q%^3XlH87PvX)kB1ss34$xm4Ry}Gm>wcE31sOD`ghMkXI_DeRx~5V z49%ZW;LZ2lwO`8=5oX(M8E8LkObEe#U>80{1z5tU>8Pj_k4xjaq*?6YbosKK1$-!D z(rYAzBjHbE+2^o|_zUxV93|+w!m&4aOn01Q>)GwLhq5k*#eir|P5&ry4Hb3~(Xe)t zKA22Tl%(^pcTjA<-u2|c+B~cp2}Af%6KTUdtqKB_Z62bS=)l%M?SiR>?_YEE z*X^o3^}afz*=-^p?AgL6t~02vVF#L>#j;C1+2 z6yT8ciwpeZeYz6#vbW17xg+J+Q~pQ&MqrJ!tB$f@fz~;%=+KB0Lx1#QJQX75ww3kZNCrR#+aiInCnDP1%;U+T~D=esI`3 zC)>FU{p_aJb609TKShu7LcLrR-?WGuwZ!h`t-zAAx?qnpf07(bnj7kO|4ZnpyCYSm z1n8&6M9u3z52cek4&qvUS!DG(YaQW=EFb_LI&ETpUeX*0>Mtre>GS$07!cX1I8m!J zI?V1MxN}Z9CFJwRfhJm~I{($DM6xRF^2|CoDg(5d-0v~p_BiCeX5{_L;=_y$?#UF% zdmSKeT=|&Gi>AI<1E3qx0-3(fP$|M=6*d(!jzO zmPZPhG{86TsOVTV+{cGF3rc^-RGe;$nNO}#`W#MTtRGWd-bRMXdmqXE9k!tuTpIwc z2|Um%%@8-6vgq?;PT-UP`A*Rb7LkD*o%{Z`F^f)Q3(KsLgriEnHG1k{NhTJ}P+$iw zv%`yRb*x$!qdeuPVVkXk&H_zh`@15Tl6+zRQ8dX@8or4J;vzB3_c1mMrY{IpC_k<5 z2MF_=*1QuK-Zjl6e&D_JPF-0MxRp-67oSh$GkvNwFDqCDQb-zHj;!!TF%WCGP zU*lGWuR+V8BBl)?1pOi4C)GAoTL)~1w};bHyj;`E`_h@qT%o_up1b6gZddj`Lr^JRaR z+C-R%Vfrf=hbxWGyE3E^ahcoEh*jo9%U9Ul+@k5w49ZSb@`a`Z+N5Vqia)9>{)O>k z4JO!CASA?@tpnS-M*Uj-!x+C%9&t6G-M?-U6qu=+X1Qpm7AwOE82z$$OmeR#WvnYy zF`b@n+K({m>YCOZQ~$H7BF^IEoHgIlKXa{`zk>}e)jpDb={1i>{W|P{v1TTq4}Yg| z-iT)6H%fNV+6YO+)|X(XTx>t)YMdcZpdpebdgyyGe8+K$5Ugy28E(xL{d)|%A2x>W zT53s36Tax0R4r2V26AKkXkOWRJV1&5Nwny5tREwa0@OtC*N~ zIaOZrNwsCM0^ej-?%3>_{#oS@EhUq&80pdJIx58i9AO43p37XWQS#qTy1eZFg(+@% zht907f-_*MMlsHDogz1mf0WaM?h5*pB#z7MufN8lM3j5kO>7d8V9Hp|4n!CD9P4`@uaDg0&ljt90UN)7}Q8(@kF@R!ytJ z$!52`uyJ>$Bm&Ns%E~hCz%MFOTo*~V$ z=PTz4%&P*p^7R6g{=$T85V7_}O#ptlMCA&uJXZhML6p%~`7#<6pijJ|G##{+9#N+l zMqzXC<;`%)B}Qb)p0a{5%8l(hz9P4dVk#=agl5`kK2eA~8Qr<$)b0>rN6B(Rk@nu+ z!w`?sI+9E|lHHezK7o?-jk2Y#mSntM21T3oj~ z{+&_Odj(h5z?y}v)l(00!i|PD`F(JV^8RXxM?pW`kP{YK2ZhLg5DcO4b0}0=HB{U3 z^#R3bt%6QvMhCSaJyPZ{tn9^=Ais}7xA-;4KdiaDq9XYlSKtK7^r3-|`wtotL`|#3 zS5hBt0S9KruzpT%aiB|t)i)w9C1az?t&uQ znkRSxF(|;5A_p8;E!%QPwcmvrwq(RPlL;NlC^_r;6@2Nmos>0f!(k#lttI6>I!AU_ z$$_PAuK(KWk9>`Ufy%N4@Q2y27hn3kP{=~sdu>?im-yyl7(GeY3JrmA3b~xJZB6Q< zfQag|-Z`u8T=a5LL1I@3w~7T}y}*XRu6)5fqiCv3=P&{LWXK2?6fuq=74LEnBuH35 zn{f?rg|G+)+i7Z?PirbxA4E5NYyV8K`LowL#E`c`s^xCh}b@;=C zeQR?o9I^Jx^afcUHm8H~)k{F(G-}1ZZff_wH7J%PibZJ_M4zmw97!)|n=EK=-;tfL zC@rOm>gMl46rsq-bcx`pnw!+7`gmkyp8`=KbGN?z{^9^9Kx=*t=CkDkishrc?R23F zc#o&WBSL-543+CI3{$+metbgnh*2gg)FZ-H$)3*9hZEnVkH8f*u%-}WpiDeFukEWN zd`Uf~a&j`1vj3A~yO?8fZ&=>l#QiE7+QiYROO;kIFwZeCi{{S{=~I7A*=)QHk3Q6w zqvpFt7TCurn0X6O^}Rn^m3eKE^t@h~P=J>qi2mV{DBczFR8{b4^QcFP&&ET8Fs~hq8K8@<$f(a! z!OC~Y<-KgK|7U5)Y!x~VW2^ezDNbBV?=sx?yf{(=f2y(%u z!TG@oGc>tbMptB=udAfJ%1K@W^ss2PDfz7aT25opO?X!pRkcd$xEQi8`w!y8_&R6Y zQMXAnK2LD(C*|>_|Cg6c9O{Ow*sZ3A4|NGG5`b%a9>nc*3g$>_7P!V7hKcE2)YG=p zd59t^8`2q%fYwWM$xJ*tF!LJvHaB%Ps~edg4%sCn|H5noW15_=gNZ4`=T(wE0&BIs zs*^A4*q|_Bob-yM?5{g@Vq8IWigi+Y?jH~~;BG7n>H@~s07ac;A|^Nu80&rAJqs6r zyTk~bYV*+4p8LTl3bq!>l49Lr^&?^Js8dANH0~}+{(eX|p}u>!O5&eKk}`aU*l@Rq zB-w#wRBdehIYknZT9=+czM(J0o3Sqhb$3q7;>vo}oarBJI#g#Ap~{{h$K)bmM=Q(J zh$&5YvegPV0iMn7PWnoT?~s`;>+NLaK?4DW#4ouC+zVcqY`=JppOx zx4!(LIA<&Jgg7`y zH}1Wa0n8Dm#()g}< zWcScVS}7x&)Wzm&~@arevPUQj*9Pm7%nRz~4s^6Vu0}4h;ht zVo`J0;zI`k3Wlpi$r&G5wi(BE1OX)S>DQOjFIvK4|2u$lw zp|4NjDbeWQ%?vS|EXsW(5cW;a5VX&?iz|E!3TBC%7=~;rKpjA_We9cYMvo5=PE$?k`M*C9+I6Mrfc@mk*UFd70$^kP@UNQQG&JIi?tGBZ%@==L{;)o!o$@L@g&$GZ9oGOD{=o0U^Wl@GfJvJ z6_=po^pv-eEU*_d>cRJ4a z3&R@ydnj1}C2d;g-k1*B*nq{AADsb=*tM_K{yL3yCN4(ippO*8?Cs1F%XTIIUSDCj zD)!F$(K=3O`qIr;Sf1Wj` zH%uDJ^{xLXTg?{23T9h6ljT~9uhYdn3-IJv#ik9U-=7h)c+mjkad7L3SQ+MLw%HV1 z4vu(5fCXbdXK(KbK=df>G7hFpl{2Xd=)2L0XjfPh1#hD85z?SisNVtV*saiTAzH>$d@L1x9pe!>b}UF zknM3Ti9%CtJpF5{E)*RR9lDFAubR6J5oUaR4r(in*T4a;qm*xESYTVWA2L%QBCbE9 zdg997U;cC@FfcL~5j3p03;GjU7FXMPV_nt=VJ*w%{BPmJ#EZ{FI8My1grW`&d7&0y z+Nj~T8{c+OtdGTtP_reP@5-+_9yNWo=nyXMu~hoGV?i>9{C?Wb{3k90IJ%0cC|K7m z8sBCpG)FREMY*%!`Dn{9tVmBv#kBI1@NdA4$?kTIztZ8rUzj@CEd7PmcSe0Ch`;%E zYG;n7SMbe(ol*Pgr~O~w+PrOwlhV1q)v!qd0^3mx*;Ag*6wu&ej)69qSK)Yv*9=03 z?z}1ohdz+9NI%!*Ir<5%yVp<`u|sb(rQjih3G4hiz)r6GME>Sp#9usiRf&olXgnG!gI!J6V2 z3~1S!wb`EMA5+_)^GB?k5sbcrrEgM@2wmm3BHmv+nC_bEgMVR|PlaI5f{CQ~DC9L|%9iXS zzFcM=p-mclT(JaC5^aYY@ZTE|l0zXV@4`;UERceyU$?X$?fla6$Q6zj;XA@B~!j{O3Y$MycVAsWO!l$#mA_-V}B|^RG3!DBN zA)Gr->y&9+nUqWIU%6tp1^ybA_k2Tg4(|z_Je;~1fYX`N5ru-w=~&C#`+P1zK~W#= zvLf>$lHcaJN872{-?Qw!)NJTqiKf=(-bWAw0$WvG;MGm%K5APndlDq!W)PSz-G?Vi z^|?UA@cXrQPoKFrTT=L~4nGR^_j7+4UjM*lYn^HGHZI=yQ;dWq&bAzon=`_+97~~n zOLUJzz3KQBt5%%EHoC=Vw z*a?4swaEQ`%qUcf2SF^>rhr5Q9=dkNlO2@=<*DJlnEQBM#Gt4kFnD?yHHQ?%er>KuQMFci=XonSRTiuM4Ref`ZPS-HFM_i+t@9Qgf#3v9YF0L-!bA>)H+A59<2)Ve> z(Z*c_@lvKTVZZBuO%h`r-JE4MVzBUk64<&{a zi@*DNqj2f#?Km-A6FCwlLo)BrSiuqWt`9-?U}&u?!{F}JBQF!{n| zK8zYQjiQm1%I9OuZ9Eeif2po|Zzi=rY=*6vlJ-@ngS{1RvG>maeIu=dg?9?OWU0=y z3?pNI5OA6chO3Gw)rN!Z$Cx~&We6(gJg6T`Cx~59^ zZM5FoIpFB(0)k!K3$Tst{JjQC9R}RTPU{?f zP#9Gl&tH9ZisX8NHVi#E#{a_fx7U2t%;x`A9Cl1+dY?=XXQhBF$MchnI0DWtP7tb- zI{4&#fiIaV1FjZkvtzRj$-*PL^hLnQg}0OXd5Jw!HbcdpUdWVsa;it`g~w14Lsm&B zo27?=CzAB%Mxut8ICb_XN_xUP{ac?k%(m`1{hw>vzys*gC?RVPWGuMPjL6(&4o zGp&AG)wd*V#P_JHNT_vuL(Pa@J77N>#$(DaAZr08_esfZa3U)d&F#4~IRZz{GQTXc`V2Y> z#u+ZlPFT)I8%_Vz+_guZ_2n$`wC59}HchSnTa~{sDPXE^+ubeibCmx(>*abqKFdOf z&l&Rp&^b?!6oLEgrXualFvc+pOPkeiaZSEH)vg#$ydN(XUp?K;B)DdAZ3%aY?5YQpTeEo3ebq|y2B2m) zlJ90rVmT<`hCo68T)e4K;?*|BYTE~7sc1vmkv}qTJo)A~sRc^qX7)<%d3UaMsd7m+ zm!@ty``k939}|biQcn%{iA@FK!#tf2!tN?Qi#m42QattVhS?kv=2LO%D=%?&{uoz% zGjyPQ+u}X$pbDG{*X~Jrqvf%?>j#?$&U8w!kM;;w{=5vWB8ltF)|O7SIOG7pPPxJj z(AE&7fu$=BEnl#*ez_K)XEiZ*%Np5b+uY!-Zl@xFO}H9(G81dx=@e>CWX_&{rOsHM zpJ343oB$+^A_%Bo5ln%m!{O52PRy2H)i>616X%e3gUo~g)nuA{X!2g8fMz#8nR;w$yW*WXG0QK*N4O85y&%uOVB`dep9 zf*1RtRBHh1#0<@11I^go+gEe-j!tH(=wLkW%^H2b5ERK7MGviShXz6Si=slTig zm#-A^A~#uc#-du>4j6s2@C(R4x^aTwE{)`S6Eo^Fdf*vS&#D*}Fgj&Qpch#doWxz8 zXF=aD!P361UsOW`1%KAOU2>0mxVRx%nl)pvXk)G|DOE!@5jx&H!^}~in4>A569gYB z6wF~A=G4FXdbTJ#8(2i5q{~a@6e+=(MUhZX3(B3HhA}1-8hK#d98r1)aDk7*^3^bd2yEa%ou=x!Vm>sVpdvxR z`KxHWyPw_0x@#rf2usMgq^Q>1bkmg23mGRU;Rw(!svz?UGHq|s)OAdd!!wzP03 zDbmW+M5Jbhw6W3kR*3eR5;Zv-L#B!PV{kfvOk%`DiqSS2C0ZHHH8xQTFj~t@oT>I# zriC6A>-FlRD>y>!bxBIV@$sQhRm)*6^{90AVbWM{LbCs^M!MQffHZwo;$Y6&INh-b z?;Y(vK);Bq#19(o_ajvyO=cX7hw>)dp;zePE2&cqeD9J*iMfL1BU7VAS0g()t3i1jx-4>wB6d!YFWmzL4|A&CF$xRs-|2hdtIi@!y}%wn&ZeRsRv) zUwy@n)Eq-8u40wZ6c0z;rc`p_|1!`dAjCJS$ZWCDlQfF^RK4TD(UmKXFaZ*dztKGp zm;LxQs$@zQtIP+SCxO&3+2iTMlFw@Qsn{5fto{~xbLPzQFO`2Uj}`SA?lt1T$+ z4dzr7`Pg%%^ysy;IdhS;Jv;f@%}rUb1>Hc%lA}8x_sK(&z1L0kZgs~*OkZ8=EE2eV ztTu&Dm`eC&Jhp*O8M`QK{@IWl=Vwy|4A^d}3UVk&aI$zv@{)*5dE2#=q6`f02A-hW`qO4%uE{; zoGm2L?phF_mFLQ7d(dz^W&WG&VwLf}`Gxy0j5I2*+Q>(+M@V?NaA<*huz&$k@Djj^ z6V@P6Dv)DP4$EccNV~y!tO8H*h}|ySmtr#xXy(OzpKLJNKOma!V#eX5RRhc)J4r#E znFiKP6IheaNf9F_9ll{;uRx^{50NT3xZ_=4mq=zTtwrZc&zdmB)zZ`&kKxTMaF0p{E0q)O#D-aSlzlxuoGTh22EJR6g) z7#6NHTEG&-3`0++Z-Y!j#ixIAJ7jJcD;e5jDYFlo=671G{?mW3a0tmA&$R?K;#u*v z(PiwF7yUn?-hwTv{%ykrr9~Q~Q(|D~ZfQiiTVNQv8M={fq`QYkx=Xr|?(Xgq;n}0_ zfA9ST=9pvRx7NDv>pD*hs4rFuIajU`s84_+8Af#5Du;jIHxuN&9zmg@OPf^=mT#O8 z>U8%=q70ZQuyoq0kyizJ(B;>i`lYRlumxG1VctbemT0&w%RIG4AkRn{Mk}d(|6yr+ zl5`RiwTwJ-Q=u7&l@t>7bZ$Q9fuv{xgbb8n87Jo~|nal1}%~pB1qb}}rxEEtuENYsrQZrZt# z$#aczT?AkDR~)cLi*6eLYwpE#|Nhzct7P!#0Y4ya6u-?bt}smU0&fzY{7DA;K-#+_ zd)i%upe_`PE$ZlJmVnNne)5&#C5`hSW&mZ~V?t~ShiL%^dYxhfAzefk}(E^7YdiMZX$ z6nCi?N%VX{fZkC0}|4wbRt%3}BI+Q{6|t(9gpE#k+CN zRIMv>1&g`KMcH#+GH*$GOQMNQiM>$@<%MV$yEVo_mJMb@K zyiW6tGTqLbNayt55^~*%IopxPLu(x~9!B8(Z8^8z6EgUS=xiUv85`Kj@<#we22Ph+|@habCriBUjtZ{`Qe7f|0dJf zE@HFeD_yB%XEMX}|A-kqlDHipC! zv3*?Qt!$qB_Tva$IIBGzpRrIu-JaM&={~TPZsp3EOi-5W@3+pU-q0IZ>OEG@Y|Q=L zA#0-rK87=vv}Bz4dkv>m=S<8KHv5*#5R+&N-Os`W;|j#&MKU3aap@`y=ZbVjDBt>R z>I!@^Rps=ph_bTmHdskh6y5bbdH|C+=DNZAbPzG%@1348M!O1#2%kuxeXx+X zeYSkj6$i9rWv&8R;zW)PYfY@bHS#EAe9mvUDXg-*+4(t#_6n$=G z63`u#b;PI(R?~9@ zE4EaGxj;F<6%f@V?(KTs6P^o2n_Hdc9E$Ewi#O>Wu}lC`2gt+ngVW12s>uaqD&k`p zkPEyQ^Qa;vHOT^R#*VDL?o{?8rf8?Jr(S18Z9dm&xv7HVp{Ob4Q1_qd!c>ASzb7Hm zxJ#Q{9qdl^^ENvfMV;NXi9A;KS?2Z?VHd_!Ez0BBK)~8}vQ&e~P`=X@se$jv28L`0 zlm0VMX0!2f61&~E>ol^cD(@crnq4`!OR|>3^w{VGCNJET(BBH@5sN8=p9wT6naL;U z^EE)2)$uwbM(X`99Q&UwETI-_$Qb~@bjW4@4kmUU2LGUuWgC{QPI}- zg12GaIiyK$RSu~&bD%^Irlr5V3Bmi&SFy~yHR*QayvYj7IBxcO9$%5iXxalw?Jh(a zNk#Y&sjbUl&yC<%yZkrl3F10>Hbia`SnHf0KKJt?+w69QI&7>lkGgq-gBjBC@1X40 zeK6=EZd+W`-1I0lWR&7ThMd!i}Uko%@T2!gI0-nHZ zww$@Vb6)&Pir+ExWFQ0o^Vg|n+y=H~3Tg05ud6LFbTdm2!9Y??Me;^cA0r(;)iVTW&m}2-8P!lKXbf znN1nDV)#4`W);HYE@Aj#7i&ziGCM{kgoD#*+uXGv_XmDM;UnAu+B7XCZ2NElj zH96Q29$tZ3MqBTNpm=c*V}DmX<*vKH65#hl&#-#o9~Xf zW~whZ?+(sM8IwyY@imyM+vg@Mop0cGgsq6tX6DUZpGWA7X@_gf#;bOgDs1vh$BBBt zfb&r!A-<={B+?Zoi8BeLdzbm@ZbUC#K9!vm=2`8aP_vyImE@(fk<|J`m{n#hTq4}a z+3OvAVbS~a_Vh85(rqJZn|XfyCdplXR_+KtuY!k9uAtx#NVwQ0sb1or__+--Is2TG z&++{I48|q|D{wRwc+TdPo3Xj;LLT>ePvriN?<3X!qL>&LG@N3~%If@03GUDB~djm{qaEqBAooK&2`j?y5K|=DfS>4iy`Ce zQ?R@nWfSw6h|M;a^0Rs9ecULu%5lb|+Yn&NGi<5vIfOWP%4)FBeS0mk;kyb~!l?i0 z(o=B=nxdFrlk1`lVRGSYLL%MQj(&m)1`Z;_t;UuI4<#gm*?a>iWQNZL> zr1!+#e-r3}e{pcA>EO+w5mHYpkc=YRhnsZ{Yu4;!F)5$12nUGJz4$GY9&5U~+}mLD z(QR~+vt6vf+M_Uxci@)0y+T!ZyCE1^S!v8IbmH4PExC2X zMMsq$TEJMkDQuFpYaBbm6-XI4n7Ma+4))q514nI%DfhQMxIe8E?y+V4MT@=94WQP# zCOs5$yf+d=0Q85?dJbC_D(r#!u*Qn=mqI2nAR~p-uIsuTx{rhMSZ+)+AS|Am$zFAidW`j9RnhHg@#(0l>611#r&EiuomwZLz$)#O0yD?sKqJyiE0H>2 z>>Ekfkw_%v-<+)H!~zw`+KnoJ6&`xitb zp8Ap8FKa!d!{$&=XkMw+bVY}0?rN4`5pMfJ>zH1T{wKj51zm28nBp<0*uZG+3a~jA zEASXh<%|*I(5npOMEkA0+28}x;VzG-W{z*w=vemoE=*Ql6+o&8cfNFnQRx3)bqQcg(xl7hY{(}myU2v8F8>xFr`qRm zUYt_keIi0-IyTu>uP`Rmwo9!AdOXSwd@R!xyUJ{afWyYWiF{wnk!?Bp0txdRgAEox zQV~jX-PW7V(eDGJit=ZW#7$*|_ekXw*C))7<}niL8PYfxh4AJ|nO9vMFw~1^ow}MZ zDti-1ItJdVT7mF1l^ao9xGhWAJC+4qeM)N4G+{3kFsday(GqPVanQPEox4WB#HW1N zptv-V3h*p8Nr4nmuoo`A>UP)+oSW4Hs`f1~*GbQOr zE9kjS9P<^~>7|M5lls{kBSepbVDvY1c*CiEI#Q^y<``Jr-t^=Wvapd{lbxH4LeQXL zTUh4K5eer~4tDDFyML(`fjC%g z(;wf*khfI3#WnBuVN-i%TM-rpiQ{4eUBczaeP)jF=9mPdx|Yr}|Dp_^+ypw+o*RBN zxDdZ=5)Ux%v^QB^{Qc{6Q|~0o%pf#oqxO(6ig-P`X!34HhD|YH#ab9cK zoX6MnYUbE=)B<3~S38OlwI$emIE!SPCue&oD~jfuU0STLqJcqEGIZ!7t}AWfJzCa^ z<^exydHZkVLGxf?j&wN`pC*p`Yk-5rhyr5tgS{^TI`t8FmdfswKHXPP<}QZI1>poX zZYuW2u^^^*qbhPK98$NH)%z%zQ0!3w3lqe@!hrm%Y()UBI|#R3(C3u?OiU$WJm%U- z4!#NWHgtP(qiF>mho|K|yUl~nGjc$c&e!`#h-T)uu$f*pq0c6}i!){)w#P*-K1=5^ zsqO{Yjz6^VPHf`*kZ!I@5ngMEsm({@Tf&e)?KK}#RSl+2x?grBReCG*5CW5{1-m`3 zEAg1wQfIKBrO4>cF%N+&1iZ-#`f)K|R#@B}iu2VZw4}B6DUNuO;$pP!h?q*5J}}Rv zBx7!{$r^IEw{+D)X4~4-(2=_)j-6Vxy#-@z)Y{xrL0`phZ5M;*b`d~?F>gbsgT!3y zTLuPxj^D=ME4b1@}CNyL+f|q0=Y}-z8_#okqD`sM| z)Tw#51JUP#>=Y7HI&1Gl{qepiK+2wq@;b9@#5F|_LyPv!a@3|uAM;$i*$|_x`!mEo ze+{}xdLl+(f8d&m*K)o~lDGY6eE`DxaRl@PvX0E-G8vcs7RqmNP);vMK-qps6C zhx+e~fw03=93VW=qDt1FIRAWF9ld%5AEWH*9dCbWevBjALNes^xjiexntYdr2t!{b zLfhok!{2xuBQoo&I^=H@gbO(7Nk9v~$gS!$tqk$X8Q|8BxYhq0$}pT#@HH19qIr*V zMv>Uwv!UkJaGU$eM926fOXK}0+D^P9J|W_)s7u0DVOLrMuI#X?jvgI!VdWe?%d#y$ zG-SE8+qhfL+pM101Tbbhw&gz|Ek zuqau05ScTvrj)GFu-j5+<zd`z73SP!Ts5`FEw<`;vA7z7OvCR7S52r^)r}jrJ|#A)MBp>6!oo#>tsey zuO%y3CRYwbXF^!h_}R8k++)J$<@TPzM=FPg=%4!qouLFAfm92bz*3OBj1}_Ul73Ym z8L)OegbUF1-5e|PAunA8Ol5Xeh6U=hApP~a#Qk;hbx8J+VI z^#!Z{IRU)GuZ!_y(!Rndg1!6S_Y+=|9;FB^n5b~#oY?szSS6-vB0-*}k?f|gUKR7L z-;h+e3Fb*k6gjs=yh^NI!H-`_XnMrMVZ=of+r&A*d-D>3p)il76s(?*-*XnX1D9{! z613rCx}Ey?DL+mrccbK?X#kTKR2_R%*MJPIehq)t#D4v|H<-oZ>3Z1mSe9P6BsRh>8M zwnI37{aYA3>JAOlVE!|hfZ!ZGUzCMCS54rAJyt|t>NPSg(`}goVt}hLYAxK*U?mHy zrjpb=oi655%8}G(*u}VIGczrwUt~phq)6!XUlg3GSOh0}?j9bNEGZ33OA$t>j# zKTW&ThWMBOwUPo^8J+l~O`V-GL2dKCT}5083c6MYCj4L{?Xl2?<|1&OHNijctKrC8MkuIc;dHin#*Oc{ISEXBXV zh6^Pnf34^)jDsA)W4tJmp@j~q7v}>i(G>bE2Fd&#Zk%kjXkSO>omkkTg6)p$4_hV2 zPrkWOG#%RENBONF(KHz;`EaDrtH#&!ay-~OugW->{;H}><_*uhFt3w75uiEvQW5?; zNW*j{@Ec3?aUDgjaVMn1CA{ML+m$wzKvCYpumz zS0X6&5yk&ND&K45Wx*j_F|N?zB(jb1oEH zsTw4P^8W|N_I7eI)MUob33%>jJB#2l|M#@4mNkCly&!OJs)xM zRFHDoL@;L>LW1>q@iYwT4Y=)=>iZW)Jf@1602$K2f;5@7?L}Qllkn4->x-Hiuk@V+ zXQR$zDJJA3%h?KCshWBG^BE=mhRLU#Y4&2MYK@iEpYf+r+*^mAh2QH5JZwbGEIS7A zt(Jd&KxwhdBaq~_CmOAJ&1-}cJq;1$DE96X>i6(cYN?j|z@#+VJ>n`NG7)38G*I^5@dz}>0`1D&DK9}b1Qf-D(4GCrp{$Ktjj;YJan8S%>-=3SD~r1-*d zuHu=pV3&uOF$E@ZbfYj?)4VJ6+6u;kN^tB-{J*8FwN|hK4xJi&U--a{xgUx+{3e$+ zsms03_dz=fAvaYN#QBjx+o7DR%9MMV9(_?~ZNjg(u}v_+7WK)utriWesy=&zzx_-(XO=n2YnU2Os1BP^t#(H_8Q15yw65EL+}x;uB1S8>7xM{OSFmL zihg&@&-O9KAs%OirLSL)Zs84XkEe0h_&9hip?cQ9qGijSOvfD|DEl^TBqQ$((FkUo z0Rn0=w&cEfrS{_-c}zGqpmK54DC8G9!#1MoKuR6g#L2(j6TaO>AxVh|4oA25+nxJh zo=*w#Fh~{ZQGT5pf;-KrsQvc17po(N=6zdH@@65jn7|3~d`;#k^DloCB~$)C(m^o5 zcgO&tJo%)%30}Ts#h+pdBF`|sR47~b&xM=Y?BOZy5lxooc#rmLdu!e>F6DfR*Vr-M zLD$1>@fO9wu^AQ*-U`%etj~;ej308kU4WP<9w?JZ6a|3A@*A!(JrNvIw`Ff*U!I9> z#%lLGbtd&o9l`Q3O_^TPL7S`m*<}Uk@}SIhz9qn$-2aK>xy3saB<*A!YrNSIxkZ7s zIS2&!@CeI9=Rv+8H$llsGZNO)#jxQe~)lM6Y`Z2jrBGgRm%q(Nt_ZyQ2W z9aHV5)uc5;S34c=j|ud%{2Ir`3K%9xey>zY{(;2v z2i8w#U|jYYds(;Xk@A3xV8|cnQ2AnhK56!goGNS(jV?otTDw*;^3k|c`Tu;nSX^mb zvkJsR3M;%)yb-W^d7V$(HH?ogHm zHPb3pB?+ilCZInwQ9M&$N#@$O05gnH&?54c4b=PQu}a4j`g)xVl1OeKisD;46-WxW_`oLrTdq znFaZc9pLHfto5I@ZG$qfmk9QhD%7&+l_*ghDP~Z9Ma<2bLva!fsQSJyH@s$6jJZ}D zWYgT`$sp+*_bdo+DEi`2q?Gl%k4O)x)}zOf5#_gkQfok)*;*817`GdBr)vVS#d;%= z?bJe=|KYWW1u9ePaUj912a4BcTo=h}a4*imZ?b4r6K?3HeP$Ng7l8_G!UE(qSVRkm z?pq_lcV;##>kI|457@1*~VCD$JZH5)%OKTB5m73n(>#%qqlb7YxjG2qd%e=V8qtz1Sx6ZE<3V& z9{Lshh8DbG&>U{pt8=*P$!;#O9n-B(ydw5QqX?NQSsBZL9#~$C8+_JomEPwb2l3WG zf%Fj!b2!xNJv6Qz%i)`Gy*Z+pN4e=Pgl8hx)eNM<Tngn6IA>?&cr@a>aQ z#$VSt&T=G$YwU$u3cCFr`m@m_P;rBM|I7 z=NGS=)5x%b=L@Bj8vrYF?N%K@UK9Rw`$@x~ zMu2@!gA%mQj`iD^tUv${VyVbcyaKq)e6TnA<^`%2@~vllSH?V`8sbQw0dyR~H21?) z83&`-oAzZk_QrYiff;q2tY)4atS{WkySoq8IEHbp)<7#~EJH+J_y_&m2O^nC-+{8#3#pM9>7{VK`YOt$j{(P)!h-Sa(pfv zdds*65T8d+G?lcrozk~9H=pj$Ra#F{Ae|G;FMT6J{hd@_1W^Yksjjx9EdjGydtC&2 z9|Zo${o?xDG^@56nTe#Wmi$aII-WqG86*Tl5JFo__Tt8qdv>}2eb2CJK(MH+78859 zZffI1!RSiOpKQKv6A`Q)S@$xl84VdN6xk;ty;k&8r_cvWier{MfdZ4f@{z)!bs$AM zwuo4%t%&f_M0ly%Zzq?}fB??u2FxHXzB-PGdSc~<6h~*HU z-(l#m1V8uf-`y3ENfAiecH6T&jOYjQ4TOk)e{oEx7WrgZ3M@Wj80yyD95>L> z_<}6DSCWn5r-Grb<2o+**rXObW109TB_ZrF?TVugm*nt8reNPrMm(LBH0#Tx_Jgv2 zY`H^dFPK$YO=jtg4d|KFBr<^ifu+#gUa)3=;T>~>VqXzWLhPm*$?xV&s zZDYTF@`xCH6IPOEr{q!bg`%SEo9Pu8Vf*w;!rjpOzg?3lTt$+(^VGS98X$RXSvi6p zbW2K*&d#Ct&-&v8p9z{L+C9W2>V=NIql!8c^s^gcq8-?yHK7XJJ?#8wY}?j1iwL|s z1-RzttZ%_Txr>MAror5t$p3)NEMuht{G+c3-7MF?9Wq0S$uK!Yxri7V2Xy%0q5tnA zy=l@1l#H@3eh-@;MA%mRqiC_v1A-Pz(`+ax)@ zZowMGv&~$!kbOX<4&O>_Th0*jpm#}`1un*L9Q8X3uStU-bK=se{d6 zR*s@y$~Bh!0CPGDIogOeiBZ>5`J>B4slCr;+NzB4yUMUkIyt6JEuYNqW^?ZJ5ipU(- zClo+MtkdlL|Ol`IxY9u>=__dDZ#JUyZzjO4O2qW41SgOB53H ziTx)m%yN<=)OmFgON4wQ1eIX5a7k^B%ivCABY=qWt$Rlz$z(Z&8W-2Gox72lLiE4seS^a#6WC z8I$jLIo%CJ8@2r`BeRN(%vw3>g7RH5G*L9hFXtdRK@T$~EmShgimRWiEz$pjD~q!V zuWq@VEq3@YjdvfrAP~rY1-I5@!(Ie$<(Kl?2hQ{7RwWre9u;c#&&Mqa8mW}xCnw(V zhvW{HK!l!J$v5=cyzD_Kp;V&xoXKnJAj$ioug&E7nvN`+37Y3*K*^Ot7Yr9}Ez8%I zPri6y1OmFXOtbyVW2AJyzNzsE4iT!5#wpJZp2K_)>BVm$bq|jv<_At?&L2 zeGRh9xgtZ}wC^ie+NC7|qnQv6P;H$*K1w^oB15SIL0tDXpe$?DxC|2k3tPg$)`0{X zo=*qGKGxkzR>R*=f>CM?#kA2I&v>$`b6!y5B+2O7*uttbr!YT z-zXwI8MLM4}@^OGdaH5V?+l*4pzoLQMm3g1y}4kD=p{#$U)6 zH{8%eB7xeKs5^%XJLq)G`^9EWiB~8tb^T1phCguY%(-U&Y}{Z^Qawj=YOz!(toih} zw2(@aY0PTMSWXC6`bTTED*kZa9Z;IhW?&)ZRkn}6Oa2coEH*Vn`xm2@ji%pG#Y`-@ zaqQ^A1@bMfWXZCPs{Va?1JVKM6IkT8ZP}7cQr!L%$02*+(RNnVlN0SB9QL|k~prh+)B9@-!Ke)7=#GVY; zn2md*5cnS)sx!vu9&)C*deSom^Sj!4QgqR@sj^%Bd`nBra`lUjcL=UvEy}l7A(Q<2F=?Nd^#ZUDuZBaomCmN-_Bxq`Y|w+X%K#R^ zREnQq-n$T;2gmaQWBY%#JaowH2v?gt_k&}O!`|&$ZK5J#tOP*`tgzgyr_dIx;Hs`< zgZHO8Cx#^V?Nx4Y2z7X}gY^C};qL}ks!5THOaQ|BlKc$y0IrvydqrRGo+uPLPSue< z&Fg_$FWR2BZDtV9@cSk>7OP&~rV4$%opT}_1MWymPM=0}2Z{z_6uMw(Thx``hKQQ7 zOC;O=cLQhgZBHIihaI#?Wz}hEi47TVxzrcE1fwn>+>Qg{rP*Y#YP3ehDt@nj8%DlB zM_jM(P)}qSTnBC91sZ5Yq{0Mx)_gvR_K(c`FD3b@tx=W8^81CB$J3IHf0)GWz-FF% zF$9b>Re=>Z7sI}>vja2Ba%ad3>kglHT(Na;vygt!2I2fXa0Q%AitwE-DV`;?Iehb5 zXT@#Ir-hACpS*@`H<-JK9l1v-2-e?@nz9wGF~?cdErTuIP~v-1e3XHX4LOMy3+Eul<3BGzWS;a5 z3AE+X8vJI$Az4QTNw{ROz5TQBe{eoCODL+8{~K$lV||o+l?WC{Cy|D+dIpPgn-|Iq zrndRq&Wc51Io2r}J~X7ti{+N65$MFux?YZ=Ug`MI8cvROy=l}WfgZJsjTLf))LAV} zju?|Rxk;y$0JK}c3TxagJxKV($!8oTwi1wMuK(Z!H?czaj1|)V?uml2yohH!` z^;W#^5@(&78_t*f5%khJ_w(F?oByN_OE6tr-O1#< zjcGM5x;PO}t4*ahx;fg=>p{lr7d(ElZ$M zw{I`BoGi#OE&y{nuRQp;wMnYpJM@T^+)xV>_f>^FmS>IrNsfMp0{imTlQ`7A4N1p! zbMCF#7ZECO@~4;+bBH~ICvJY3Iv}pJLMkSrvwElT3Elk0f5bRfrISqOKv>Qh#xF{! zH_M7`A#z|U94>gSYLohcA?w08M_ssCmqJoK29i+Yv7CC*IYKcU98*zRD>z+2?!g3) z#S-wTOIO0rSw-Th(o0iY)XlxRO@>lJ1+2>}m&&_jK8W-1@vE6tVKR@=Z9j@^Q`yp# zo(}W}v<>bwB`?{$ugPSaKaFsGl6*sT73=%*>)*eYzL_mw`8fIj=84?NbUp8Tmr(M< z(QZJmx5WEFuM+YOKO#A$?iGXF>nN^6VAq`b7z`b|Mn?rk-3#AR}{99TxmImm*hKp();oJ4~F?|MLX@5mpFOy&min2|j zKd8hJ{|$t4sgy?OX;x-a2{p;_J8#MUqeUU4*WenYNh*rdm7L(+kkzgYu;aN#xgPPl z#NsJ%fP<6)X(yT%s{;^?#9B!`nm@h4EGp*%YN=asF5W|Jy4!M-Pbpyo~m zyO6nY(>z>60YS*K{qG#OjVMbccF{(Kgryk6?L$giCQ~ty!e1R!?&jRd@$+LVWIK@) zu`7^In0qU>Jw`;au2$)YRH7X5vi7b#b_ou_c}0my79J2!L3$yRneiV#bCbZThsU(S zsyXr%*@jg3c#ckEn#;_c9>xs6hNns_d}DsxcI>_BT-soO;>uR z1E=Ks`7x_5LWNu};80W&aZ4+?tFqx89LD5thhPbIS=r}Kj*F$OX!Kyh6bZVoW~r+| ze}(l-qEeo+=T*}NoC*@2bt7A*E6`%ljDQ5R9nOkDVY!zt0|a^S)fZdU#?RlU;Nq0D{)pucJ*T? zi&QlR6(%v2L|%D;@F@*AbIFy(o#R-pe@dve)p3o+y-7`qYo<-UkV>>Ess({POvV=n z+B`{?z=r4dyYp#+lr1(c@0XIHUB+!nFly{ozTHxw}B-5vLNhMbY+aa#Cu-Y07`RxpbtNMpGg*r_4eQA145Mk8vOE zfe-+KTeX(f<)-H!`V$LXI{E*+8WBgO`lP|Us)LpQAJ>?r1NhtoiSfPQwv2X-{}rWB z->2uV|BR^V-aSLU@i5*IIp!!VIrIygIL{ca0$OBG3EDfRD17dbQ^bgvkFW+4Zov*2 zwm!!~SQ#v*!s{48#fB92LZouO8i2^hnvU<78SunvAN#BYpgaxUn(+V8;! zs}38NNeq~OGmwGEh0LsFRsLLIZplhEv?4S{r-W$It)%`&uBmLKp&0&6EesaG%|3+(kc#y(ZSpkoq0J{Q^& z71Z=?Nb|Af43#E#Dww&(r#-(ri-cIsPKmu)q!yT%+^KcSm~&U zG$G!^q$a?=;GXkYdCLotd&#y(|1enYLI;?W*(AzlI#VK5H+HrEnoP(u@WXe@RK;W* zl)24o*<=s&D%yW5tt{S^{39<-PLsPovY&t+v7Bvp!&~Cp2(;eJnu(L2psihj8!yE{ zQYh+x23Vkq^_*xS=bIL>pqaAFk&)_npMxxHJHt3E#SC1$Y|C0UVJJ%Ebwe?usxh5a zrTEPmJ@0tRYM=qzb4Ho8NO3|z+R`_={N?XE!Gpv;|C0L-!1n2gGZ{&cdc(0FtLuh- zwLOaM!5}d(izDt1;;OeI>SP@dC-!w!2l@F}dd%Jjv-CE%LGZ?sARd^JQd)O7etr=g zD0&WV^|(y^E%zqfymwlJBfcPdk&Cj0+e>1pgk@sOTpXRwp(aWqO(OI2UAA{)l-F%} zvFm?u_c8x+!FDo=s76J+`Vm$SlM|v=xHefUp;lDB7Y`T$s*!rz)+58jWTM~TfWKu}e8eMBS!NT}~2BMa`$Po9+5^;kb$1Dc3@0x;44 z@uj1Ts>vkmFlSN+62WNxka=);}MWJiqOJi&DNwaX3-XKn^NA^+#U4A3==NJ>)Ny3@^LjkBK$?vc|! z$sCd>{12}EaIN>#%wl?1nEE~cOP*~-i+{a+yxF+1oC>SKOmmtYL(;tVpaIJ!`9T$Vta&rytuULLM$Td^)yl8s4M~lJ;ZXVN z2t-a{s{z-;B#Cv>^-~(5Mi~p=Jgi{S|KLjggVUCg5e!y)Vy)9Xrib=((tG9yN**B0 zWS@InPud>;8)*FP@ZipHn!Tu>;`$@^nXgGu)k;jA4Tf-9;4;1RUT)A<4Z%Apae4%GTD&K!zE1?>3!wM{bd?eO42bB0ZopYKNyi z)A7=Hv!!qS8G*JdC&`Bs2#DP+F>B9!s-v*%AFzrRH8oL zVIw2%ka>r5yv9+32ueCQ8Hc5*iH&3)ID?)@= zXPo3uz(Sp->7$1F3ZXM;h@pz;N6r-{&pz3^zPNo7U@Qd~uPq%UyZsE0^~Bnwd-%cH zs(qAxhB&$3Xwe>5g)NLxbY_p}BJ4DOIPd8@i~RomQNI|cr5+8!jBY{cQlp0+oeYaU zG%&nQr4ReFIbYM(ZC;##jH+a9e1OqItza!S7XkfVc|&|(1`#LCkkBmmQWh{`Kt3$| zTur47GO&N(N4DywGQv%N8*&b~8t9>Ghz_SqPmg%qTJB-Oz+g)~6>FWGVXqpJiCJYx zm!Q~3wuoYz48~R3)sgjD`5+bii|8K-V+&JujZdd#-riny173~DBh`E)t-kO++oT<` zGQtcFxI=G2W7YfKsS9R;mHbgc!mqS(i|muKv@yP%KCZjv9uFK2^Dzs1}%)3y>PVoZ%@rHQIxVg7Xzj^MNxo5L~LsWdLStdTj=&FMrpotww7Na;TL-tU^GrKw|YuSUG-;-1!?%R$K>7t z_#Xnmcos{`*ce!2lS(t4yR*%#FXBG&!z!yGvsns;CLbLu)y;4TsnplW(SrE+TmJtZ ziMt{hji!?0Mi*-jx(H+U;sYQ>xPu5dX4xy9BMAaQ)$Od{q&i4YFtO%|WJ#b~zk$Ea zhv72v0W0o)nr(kJVlhY=h)sOICJ(cxK6e*1ONlyz6HE`X_V{!eLtK(VI%dtU5iIfSat{F&pWktcs^(x> z)lx;RPnn7XJfV+!T{g5?v@~bnIm!l#^Mfg@4*8!@r1_XcyKt7C?n>{heebO<(r>^c z$xZEB_BZ1Ql!Qm7bJdv`UW{Jt2UwV0?ptVon>K;D0ri#f7_90D%G5@BaPG009^!17 zRM^F8oMICUn`*B2H&hV9FFY~6&Gw0bJp=4tETDbh-bwfoh$L6&tj;w%+yHwGHu*(n z+Yx*y%zc=i^fRNi4!hb%H~bGJ_BEd(J2mR}cR-Izo)3m*_(hao@b^fK#z9mjvy2e8 zNW3zD@qVR}|6V6Yi27VEorNlo{pp5#iWz0wn9YGl%@5{Fb7M<_!*Lq(xvEE*M2{`( zC3NlqRdW>(iEPBtPMGJ6aO8HP~FRA749#Z|7hkjDoT8 zr^bO^0v@<83CwBbAgZ;M5&a>h%9J)M$cQm+j#df_;h-y_DvN>@+R-~f&V{Im9=rl7 z0}v3HHVrSjL?Ujyv`=~o_c+650mqWaII1iEB5^w4h&6)q#x^Uxy{ z0i#eEM40f0#t^B}1yKatxVFUP-R*u|rLtIo81f0aL`V(O;wdN%6SsYu=2)V`Sf0ITk^rw@#+ zV^<}$zpEy^uC~`Itvc4G1h}KQ8vKfNLo@%FC2_-Ub7oDtu+;}sD@6Yvo0PEC1V(s4 z8IKH3XhN8oiyoOuo`NG9yhG_Nt|+WnDiHAFKIp~lqMHLSK)EU(p5_HcoD z6-$Y)By*G8nNXTafTIXa+)gW7iw~->jmL$byUii=h3RXXARSc!^Q~{0Q$%=v-S1H- zy@&gjb)Mq~MbLD?i4oz5)BXq^A_NSse=$qzSgAKXd}G^q!nV7AVHzC(l0vKH=Aebx zI^rANKQw+B6MR&oRYk755pGCpytGnUz1|VxCV8HUwCs3xAvg0Y(9$* zwFIPjIERvKnPr($ZXq(Sm7dB(^#YA!UfH1~3S0zK)HHM}<6|OM#K2sJhy}(^yJ3I> zB7fVMBUO|@a{Dwd0jc&g%Ty)rqGh)b+746XPH);!2MmZ}+V$6bbLvLWXZ`DAyeMbV zLH4c~J_!sxU-eN0gw(R4Et1|6hjv8Ino)08nV7M6(#yQ&7mHO)vY%eZ#%Jv3*ag!2 zX+p|#coSDcdB&QBnrBZrT_xD?MNyfh`V z>`C2rntVY*mWT*wBh4H<#86P?ZhVi=ADX6i<4;wEu;X%3zrrTVxX#^JF8$Tiop*LE zD0OqPz*a#=oxAcbR^L6Mw5e^jXezIdbAqPZ;ssl-e^x+ZQOQ-)sp~^T!nG>H1AizW z`%sz{mkFukeFqfEk1eF;kaa$B`8!6vbq}Uiq-uzz`)aN5*fM5;gL+01FSJ6Qo%`{v zksBf|0XEDCrB61u-h6Z)NA~QCJphu^xZ#PhO*yvx7Fn%XPv*zwFovr_r|ohtqp1OZsJ=+uFf2h4)^1_g)Kk4o~jJ@#4BnIX?} zfH@L$_B`j6ceNqm;b-|P`0@325TRaOd4cWS#|1;%Scmf`6xBdfC|5pbY?SY8AOih} zVFm24T~VuV+Y6%a(c@8p{2R=MDhKTR3PPAbRL-(7r;J&x8zeskAokj-o==4>&Z8Q$ zwYB{}Ouc1Tlnu0oi_%DUr*tSocZYPtP}1GqAl*51Newl0H`3i9F(M$+T>`>)2KV0Q zoZrkZ=6c^*@vQq+-H-e}+sO?8z2jhGI_C7ulrh(Hmw+rCXBW=@jzxK6{vwvc$Mpp0ukk^k(i|*rc^x zx>|l3U9oWN*M6rvwvyg(6IczNB575(AB~B)`Zk~YI9rWm3^`7OSHeH!SqTtqb!V(U z3p;7~HiUno{a1S8H;|Gu`ASMhQ1#IKlYI|6%ZLBq-1B#MC==RTXp_#K$*RfU>9>{M z)cxKPI^f8+!JxJ?zpKuoBUnEtP0S_32CVvWG9&zP@T8c1AkvG zB9T556trz~4NSV_a2?}yEQ$7^ZDD+rCNaH8G_s;fc%gXS9a=YCubz8siSuvW>ric0 z=YBHP7eb~;i)XGXAGA3lcu*4RVQXK1TJMLHvFSPU=RJw^{FGQLh+U?aHWrlLk^jMv z6KlPN^>HGRMyjL;me8++nY9{nMabH$k@fgPS_srS5v+t1UnEuXAKawH@x8^yFrH!K zRBlgHmkL6>4n(s#mOb@dyWQeR$_ze_j9qrqa8_GS-J8dACpD-^bv4W2eb^vOr$TsZ zTmD=iMZSH3UYXe8>6zBhz7Zq1lPh(=S^=re#B>wF{K>uIkO#*6YFh6-K=tV&UrsjN zIH_xa<(pt@jSRlyvG%U#?@2Q)@J zw!Gg1^A0UH!Rkv@w3-awN%P9s>N`1keHjk46hy}pxoEq^D2{IBtZIR&`GNBZI7Rek0Rk zyaL?)tk;c(Q_B*^Ke2%N+JJn!O{dizW}%Hx>-m+yLJI-77CN2e_;E~|s;lcsMx)qD zMyj~T@0Z4@QW%FA8$F9DSUT*7`cUcV$!~LcqWRXoZdNl|DNh{LQvR0(x&o5u5l-SH zQXVNy;}q|vclVH4*9bRHf;0B-lRho>h-oq{-=C}%PGXWMuA{xB>6M4aNzt<1RYfh& zlu%w3pop%V>_+SF+?UNUxiYujfPI*f7I2&0(j`3w%mq~UQ2qzzq5oRO?%-6*{#Kbj zgyZ>!9x&zOy`U@fF3Hs|j$Q2N1irH-UZQccwBdO=*ZeYk1h2R{ZLNAd;ag4t5?d&l zT<+9P07`UB#v4mOEk6BaVq=u%dEu(aH4x#r{5K_de~otdTVXK_S9%vm+RDOqV|c30 z%riZ&q6b^=x+Z={M$t4&Z_?a;;`eOHar}lL>L&r|^EV3S)Ga=wW3t-gX~H%Elh&k0 zY9vZwfJS9JRXKLd8BTV*cMind4E!DGqz& z_+7mlA-gwcIB#tsPqFtV{yAs3ND_#_j#ivA8`HTtlpg3%)-pj43mxEN922b1^#40W z*GlE9{)jxYTutjVk5?W>EMccQ?#o}-R{1WWOs^?o3`zQ+b*Z8_mVqJ~#UYBk9d^i* z9&_2zMzJPx8YXM^zx3jtzcmoF7`NV9$n!XS0h2*WK zO3RZO9NYeJbcFE3BSfXZsBckOx||aU@muMoJ=2&s+{t;$kYbX-A-C5au{h& z=(M8%#4&{nvOtji*97$VuEURG6#lqDO@u}`7C}?)Pk(D@;9m7jlKVWm!Jg3 zZF*&b=aXpd)n@#lBHNOO6Nf zCuffa2l(QKn)n1A*fqQ&%3aK zvDDG9&@le2pb;Y4a-ZsQ8A*iKIJDi{rO{FIJ z2fc}yDxM_$@$zS{-ff2)$W5r{qbh8`i!}E-2Rh9c@#ha(GC%`D8BBE zYHjX(lC+2A%`iTmgWn^@q73Z@bApe5efNf_+XMguw1dLx*S0>Z^La{5TyI!qEpC+t z!F~TSX~YBVpEl_YFKik|C|5$7Z>@?|%8^<>-B#W7aUfxZd<;sp!2?IhY+vU zo}?RUYbA?)Nxv-In0@_ga&vUd=!ne|<5Z8~&^1x*`6Kwz_LL->e}l3&jPR9E9-%)z zFu`m|bu-=>9Q{nQ6}_;|H+gIMm%D$xl!XOqF;&=+IlMq{ma5I##fVHA3`cRji89C70;`uWXEW^Qxe9c-`zHTn znHE=ni>f8K!sA_-n;}&wT!OlAAbxoui8)@86Y9=j?}d*r8ioER0F0rYCgRpHy_H2e zZawF$jLiwcwOQ3g!RbQD$Z^0%gCw^aSiXqCv;iFI8INoo@UxZPOyZshf5#2@Tglcs zIp3_evoMVQFg8{selF?MGD5g>94l~$`mCn)k~W$B4zn7f@LYM0o`<6fq~Z^dbJ3JR zsIHDjDGbL_=0)F|uw(0iOs%VaFh+kes4QRpX|K7;gR_ zT*B@QClqDwb(v*`xR0bag0OgpSv;9SK4DROpqs@c+U9uI&zYi9?26UH zRe}(~h;B03giyw+uIr04QaEgJZn3CQtlD*P8TZMG2YHlte9K@d{o}tr+oM+8AA-$HU_9bn(XiQZev~m-FGkOXgwU!9Y3%_*hev(jb8dZ)^o@h)F0-u<02j}WQs+=Ej~t5RXedOYJzf#H~* zBAvRr&`yQw&Bf!%8pXdoOK;gvAn#h=PrQ*CMUZ_&5`vkD3s?|XuGbonURO&`l!QR>* z$@zl6Si77W2Db2S{5u{GS8a)2HH0vE@}4Nx{W5WOC9YQ4dPCP5@KeU!#m6c>>XP_c z=+W-l#mFT)L+KFI&$~DvweL;NNow^v+KCdE&dqt;bH^d0%Ek^a>K5f12*viQA=j?x zAN5g5mA6}bw~a38zIK~9*5lSpFEIMf!yD$Qsqq@CS#sSGwcovR29wyv7=LEpJgPXt~P)d#n4BgWOKJND1j{)0$jWMePshNoid;WfN+R6$v zw?E5t&Q5HkDL5Am(#;3~dd{&^XqHM#9yQ{I z0dk8kO$IgtQ#t{3{H_6L)QV}fb(35xe_Xy`$#4PK78|t!zbuC+1&oFu@F$39q-vG* zB+ZW)67Tp-sP1=;AJHNuGkNO&;JaszO^LF^LUGTdC%08F3j(rzAre^usJO}lU!jy* ziVVeec@dnKnWHAYIB34V{xwMnf-i=OM8hzY@V6#B4s~{2{WgGgFNDQzTg3X;%em(r zvG>A%aQdB+Jyw5O3E{vwLn{VIwz*RU);`ra?T9!u39y2Jtg9!VlC12u7|m8JtMSUY z!TOzYi8ePIYkZHuQ>Z+|!ZVue4T(eaD$M(`1XbI6=h>ba(#2+c4ze8jYa?F=qZQtra?*#yLfS z)$a2joa-AK9`@W}-lN561#-qN)I@xsLY5={K9!*(5h4sfm&)0v1b@JzIm3dLm<~eI1 z>J64+-i1-Z2& ziYlsF^kbP4!XVquVr)?;3b{)Nys^{9YNJZO0f!0ezWMqaH~rg2oK`cHdSI2mNA@2G z;zbNRslZHH;H1XoCc3ml@AcI5I6OTi-rL6O$@&tB-8l7b>~5?&62~=+n`?7(ScH6p zU@^|9XtrJ2I@!7OHB&wa6(6=5gEW`Y-HMTj+I!y~(Kbf4w=|H)h@WOghR3$N(h6kxADnjd1Bx0CAyrnB z7h5hFvR@oHr*Fd7-!IIbpC-;LTRt##Du{nO+$G{eQ(sgC+(;F(59dcc>gRSze#aqr zLFu{rTENZz88vjbgrpNn-a5k!DULlin!nTDLZji)G}I^p#d=^mx_|` zJkb@S0O$g8kAAS3yK|gH7Zw){6To0@t=pTKQUB%EFz_lkK&8{lav-sU%Qh;%9zqVM zOB5hM!wYIB*R8TXD$nP^QOyjN(dc2EUT7*vC@7#ra-#|jmKMT2nD<~hZnN+!M8s4 zZ3#pVo?y&PYNMHx-|WqL?X7vue;*=_Mw+?y<+F+#DV79c#zrMT! z_PMNR))v@nZFIf>3K5B4D@v7V)lPuxLhAE;idQo4Pg-9iwx6X+RcvJ;-Ce}PG|)^A zDN+)IyF|W}q4t!NG&YK>r((@PSD8@Ea7W+x&CGF@f9(T;bcuS)=6;R_)^~QaIW)m;ORFGLo>CVZk zkR2dCp~lu;U(!8&5iX;4xMQjIY^wPz!+>*zoo5bJl3wIpw|<*K*kXS>0TV}AD(Fvi z*Wgqi-8g>$b#_71LXxq(yV*{4noc|5%uCuhE+K| z3L)KD${@}W%}hB5QHl-b!-^$K4@+OWvyG_+X72R347>(Wz6~r*1W>h-C84$_iX`6E zp8x*2)DlcWWU6qvHZa(fL4+8kovRb54LW|&ckO-V4=;LyhRD5K&0pWRi(w`{#>~B! zo*~nA%W3ch-Ps~?UyK-uWS$26zRk&;3?kLfi==@m%o)7^_dmzL$^g(Pb6k^d8}rF_ zo3M=s_O)oB)xoD(e(Z|cYFQE5_8>jOV#VARj$7fEKaL>V^iP_`+48Q}Q@Om#rMi*H zs#cJ5f6iN5t0!qkq6L=7a*pJ7-~yvhi%%0b?2f(Bk!4HUVe8xCTClEy+99_eOQ%#< z3U^&5Vg5ke66EtYh*eJ>1OI|lFQ2^9C=J&8bG(jsS%Jv>qmE4ZT#mn`${7IH?jLaw z8RGd1Fv33f+gQtTCfa);9Nwiy%*+^EUzB+kdj7~-1Cg(^yA|`givF~lBwPkf1@(1z zXNEXGY8?2wKA4PuYvo-V(!=Og{iE#g>A^ILA|(yKb#B{?Zd}iv(Y-=yR{ElyAk9X#Z`S)t!sR!EhBGp zEGALDYI3bW96Szr@NUC!e;=2C$wRwy)Qa2dRDX|gDFFxA)2t#zu!Wib3+s)B_+e12CB zzmj|Tlx=%7e+o`n46x|nz&A;0<}HWt42tPI6l>m9VJI^rY-Dq;Ipqb+v;GH{``9C= zrD)OW03D$gr9+vt4sSxEkR(8lwVq2-O0hzCH90e+6CgmfW&(|V*9y3zgeVV)$X-xNMPSY^DJSZ~63 ze*Dh)53hcJx3qxx89~8(GhbX)w^D8f8|>zh9y1)l)}|lF^oOKJ|0#LGmM z9I(x}G=G8(H>3JxlY#&5**jAUs+Pv>ZWzQ}Muy|w;m<<^;UBB#QIr4Rq<&yra#n^- zx;8DW4hb(iESmioPo-rxOW*Ijz}ey2M}wJsNuLFG>QT9;>AJG=td&oJy^Noov)uXt z<@T=*xvvy@LM$;i9*@5SUDZ{;Iu4GcD+5f|xUh9@2dM%yZs$&^MSqK$HaT&tq<$&P z8q6M^ajNO(E#tfC^`CU9;$EvX)IQ9aHEOvAEm0Ks#xsYhO4-W!O8gN#su zQcX?#)!C+Dhdk(}d}Y}ZiLX9gTD`=j@i@^WvC29R;=mcd zA6kU@KL%yat1 zGJ1je14<^aH=>CuwNiTuFmk756YQt+bm4Rv zkal>DW$G^T=v#Yg?w3{wQwyYHB%skW%z-ru6?D3ysnIB2uq8C&vn!|eB$uRtudJNi z6((dn=ZLIGn>`WRBh#gtHQUXLsY!gnK?+=>)7j&2Oqq4^eZJgKEP~BrQRvBp40rcC z$tydFNK|wspZ|k9#kLfr#sUJE11-77s}puy^S}(7To@bH(nQSM!lTk1zkaWEn#AsB zsxkA;q%co>U*&z0P!?Tf<0=p%Wo%VB?f;TmXBT5|Q##c+A)s<;Ff1J3CQTz%so^hD z(nG2z#n;%6QJ&p2frtXMSYPSAD6I!okz?BX&Hi=lqj;*w;xCA`$tq81O1@AR>fgWK zr?}~nYHq)SfAvtH)vzMW<9)$ryj#Qq`B*D3P$1T&YYy!xKDVG-CRq1vvPki}9~+A%%u{!lR-Z6Esqm+)tNV zVMPH_Gl`wbb}je{PF*7UF_HeFT@UIJ%rF>;95Wsp^Tj4%X_30s+~vr$(8u*@09&b& zW?q{;#t$kK$3&p4-kI(?>$;i$eNXQE)4GrFf2EBScA8rBv@Zm-6cBx`|@6w~V%1PLsk(qvL#F!AXeP)4=Ph>3>*rM9)1@(Go z?gw=Ue0GgVL$r3jo~&81<6;La_b-6(XUc{p&OtG_f-$pd&|RB-ric)z%c1i=Osn@M~r(eBnf(|itUt3DA#_Zb1lBFEKp6!cK z(4k-9-bFs!{fKxwbNPLL*J_6yX+QlqsiVS|Me{&FB zTNioP-NE>2`gS&X97?Z}$tP|WnD%flnzm7Q3RW59LR6?%hxfdgO2 zrxsbmDvASxrsW5K((n1VDcUtjU)fMd)n(3aPpCzuA&bB(OTiIZvw{Ytyy03-5=BlO?!c}~VjBijtL0{{F;%I4v5-b4lleg}PJFee}3Q|m*078*=I2_;M) zGS2G>89A)beoIdYTsZru@Wz(aXp!k0VzKJg3tQVAp(YdNH^G0U3d~?Sf}w^mp2F)2 zXSV=RlQl-IJ?e|MKu6t|*r@ujvOE=w7mPcm8PpMLAW2&)Re>L@aht(B53 zi^*fJ3R!IOhg<{5zt&)Y?V^ke#r{XeNkM|W0>48udKP$a7>)8>eSG3TS15&sCa?F!m#bUiBIWRU;cOM=_8oPaa=B!esp#+t$?o+RsJUoPDyVlWVb_O1MUY3gdd zx5<((`+m%&7`4^Sv}5}aq)B|x)SLg7WLs;5vvUIV`jlqW!}QTf{3Ac4$wQHVfQHRp zbRK;KcPpPM$8I@K59k`%K;tf9=EB9M$S}N-Zv#8%D^=#@@dZ*;^VH?yC~0bHzHDHG zjll`{6yjd8H%_Ov_Sbqh{Fpyr@^Fl86m3nT@>aL@qp_-t{WXJXa$1YurD0&%Hrks@ zi4t{*hQT#-^Kh$DP}FsAAO5%1m56cgj`TXpd-;$ zY`=dl@q~%zglQ5D2WhBCQO)*jMD7Xw+KA({6lUl*y_$;@ZSnA!sCg!6Id*rw z1{P1KWifgNFhbn{EfkMWFD~^E|7>wJuwk26&J+G0%^1f2#)~J3d%_%FwkL*~V~1aa zKPXZRj-A@{FR85AY-| zd4DG)B;1+_31^br^9Q_s4a7aEwfb$veJ1>f%QcF0mk}@a0S+VYau;-P>e*V>HTRGD zhIzp6*0}0DpcuRX#ev-sVva3FGbpjXH+!9$MnD;)p_mI!&WX7x>O&UTQH#OkO!*>j z*V^;JX?ovX+bI+7gR>sdV%znmwGN|?jCoa;HlcTl9zHjb->~>)v*Oa$9_s#Ncy^@O zkE9~mrm(gtjZejppDERT&z6OS1_krQWV72fS(kJ9y;e(+Ukq#j^W_H#7Snmwpd~H>7TX4-VayVzV^pU&|7vg`;1C@8 zj>F+(`Kg`bxOdhwW+(@$fK;YY^_S9S<cRm&4D5LT=gm5smU zhv=23-F$=EbIX6vOE>qefcy556bz|3zx#^hXe0Hah>}Q~*vm3#g(BaG^nJCQNaRq~ zfA^8^W>p^X|MF|VmAAA6I_cL@^bUNrfwx0IF+c{Xc0n_K!fzJe3f zs|f`e)M1vt>8A+4<1!|5P~`^Ae{h!>yW~C>Q#PUgpA3TK&QC~yY>!A};iGVo4_F>o zTXL=RUf1xXpZOf5DvF2bBi^F^a2avK#EEarXQ|<_`Iw<#a88?zf_Iz%_>`@pK|(mh z*ynQDeTgzUHjkznqxEF{fo#WQlCO0%VahUH6j2t3kYsf3II>ddLVPNp%n#)u^YAIa3ANABeU5$dpJ#F=0)e*p@R(tbHOGd{Xf51S^(?5L62kEE{$8TyA^v6Xa{PnZDo$$$>7*T zPGymtMAdS}As0-qiWs7e&$5CKl+TN~3l6-z+$D{E(BD{o`9Q10I;d16Jztbtfw z)ZlT%2U)3L9WSm;+E+Zod3v?P56)+rJX6__a7#lt{5_WB^{hSA>xYRDW!IoV7?Ro7 z?$zQVwLs&lr@Jn^T+PQy*=`lx?NZ_oA_h2cbSg&CJb#ux_)*Glnr92@vgu5bTWh+R z!36wp>161i=u0u8-dSlf7_MKhcF~g0hmNDMwpXs$x zrZ3UTngFWFyBS>)Cyk2YZ7AXrOsMw?m@ylJ{n~NqjkCUU8f?(uAuL=+sz1*C1GI`u zkakysrPJ6GOcHCo+oLBjlJ_$hBorSu!b}VjI^LXe>>i+|XQy%0ydx~~MZf%G&Ea11 z9?ZjkzcXdbY}lFujiVyu=Ce6JTdy(GSuPow8k0WTVCXCfsGhx=E}y_H0h#N{KGP=a z{eB5Jq0ZUS&*{nOnw}rkc-!2iK(0FrSw4jD1eG3El-TDkmtjZm{<6T=FH{980QX)X zs^fq4yw8z}=F_Wmj(_hvfoASx!enEhE#f7s%X>7vgZsqZGby_36FMiXuew%32n`!?qVd>ZM>fp<`21=IU}nl^{8UkkLf>^6@`$S~!> zHS%w_Om_z|ACiR(YMyqeBexe4;}yTvOCrc{|4|KOqK<gqwrJs z>?U89^#Uzo?Ler;C8Ct@Te4~@z0D(nK5*r~rdJe5JVJK$p{;mesEFwj0Xvu?dVlct z>msZcS-BiW)evokoI{}w|(0_*W>3?d_?@Al$WJQ^mX!(uhfVuc$nTjpuOP4 zbw0kncw4ffVVT)N041P(oBW`$V{b_?9E&oXUoCW(8G+zOCMUoNRS#5J-{`Fn458+l z;r0G|xF4XcdWh8UnP?-T&M}kNFE&@v+BbhTvW{O_zQ`Kg>%6!k5SIG;bc+E`;CDng zz@WqiH*m>+RB{t3CWy_^fwj@!lOH~)ZM^+$m6jQlMdwrcMEKpjZR%s`BZrmimW?5R zrAS@nQc923cl(~$b+j`n^$6rWqtAXF#|Hn}R65HftCXa-UVX9lzxZ*54jy8niCGKiHjUYK$m2;r|Yvu)BP=e>eO}~osQEKbC0qv-f77a*-SJVY9}l9 zA@UCrPO(Sn5v#rR=?Vuaz6GIWjZAVJ3jO|a;%P7b7zXAc*xXYISV*i&@sm>A1&sg! zqs$>%d|;%eu}~f;=IfQ?6X=5^W`xLM=Nrdu=<2Vx*36Q#Yc+Fxa-!>P;WXUw&d2Ll zFaN-mf!Amd$q6ZElhnODTaGCEzz}tBJ zuGHs&3Qc@iCfM<`KWk-_uxrjz(~wcCqBD-cJ;mZ|p(`Ze?!jU77)`mc1%oHe^LY>a zu13SY{rYx-FTJv4dErC$R;rC^TLhSE$``phH8PMty)s3*OfZVE`<|I@=VNptaEQs0wMvT4bYpgrM^m!BNx^;~dH4>C zfPO2HY@5^(VE}5A-3gpB8`;8&Mw!7LMU{gnAp4yEtN{SHC5vVBd*8XXpOz9jKSg_CQ>!Hf+fXa!f0zSMM+je5qR7 z%ghIPeUTNEp^M8K^tdW?sp0e*H=Z8R95C8h?HRao=`i@}uK47ChyGWt&r$^N>&|Iw zePQz+f6OE{v0KluqcPU*4GOsF+4MSTy}&6eo1s3vI<3*Nm24wy=UZ`U45H)wclS6& zIJjYW1ayi&Q^L;&A^d0a()!>Ary2L!vYlAxv!5jAWpgU!nGU&`{Pp8?IAR6IJhfq# zhQy4#4-y{r@9 zGIjF}%=qmczmyKFp&N(Zj~i_xK6SCH&!h?VpSjW?zhvMuZb_pwr}QRyf~=+tS=gzn z4SQ=?mgbc*FfZ|8f$bo&9lFabOc4y&%|ebq^;T4E7NuchaTBYG`l*Fnod;qz5PT@w z)1YqklNZ@w-tdK%oIjF(ar2KK0YM{z^0$SAQ(~>*bZGS?%IF){Huj&`6&cX(LCO;( z!*)$yMXw+O(~TsZF-yi|syOekz!v=V>?u)UFe?ygJEIH1a>=Hpz%ljWV0o8aUU%8S-7Jucg@@ zJ>~wuO@CMgfy%(C`9W`p@v%URWN=$nhBA8{I(0&nuk#x)s*JlK{l&YWEl*bJ%~q?H zgeOB^2IQ23ZL9|kNmd5N19#Vl>hHEXM4xO{;g|Lv7ye*Gr{kRR*(R3tQDcH>=p+XM zugWx`A>_PB6+xS$zh~C07Hxhoo`N@#UtiN%*$MW}Q6E;VJOHQ=UlrrJ-`;|*|C^GD zSEq$!FY-(9q;;tY{|&kUz(RFRda50JysA3bq*U~G$3`-Ar38MH!a~-x1|Uzn0M5ai z@I6(|PZ|w8xuWX)2YC)-4wb5t>!Q}}&))K!hf;)n7rjZsTpD)3mtAL8kOr{Jw>{jI zu<*qto_9XS46PR}v3~nMxW#L(Bd>eZO2wO4$ufUv&t4F3ca|p}FAKo}2Odu&j^do= zwPjmyfcsi}Xxh526;OJ8=l8;Jr;0el>h2zi(52%uR~4SzWenTpKp`m3dGG$-H1b|k zb*g0k!bzQ1qBxzj&Zg0taUOeq4>4s!oErfxN2NbW^tRT6hEgF z@SWdiRTkQ->nk6$&hHgX?XhIYZY%*Ac|3EqJAW!pc&0;<;1t2OA6&2Jm{0; zJ){i}-wl=Mb^on_kYOtGO4GsQ!gt05+X@G0cX49C{o|gRdQ(m2LfodwXMG$fp>18u zPBSE>dL!>HCeXWD`I99d?jZm9<)UGiS?w90+kE{P6OEzPZGufn86mTlV7Oq;C{^yZ zuz4b2H@d@Xfx>ZLsHW+YLfR~33&p9vUjdFArsqXfST<)s`k0r~0Ve+&aDnDg7Dv6N zSRml?ub9)yaV(Rm$|<%S{}0YVCzkC;N%gSMe{h3A^h3Q*caiL(DGj>KdPhnDVxF1g z?+4tKgk&}9PJiy_*K|NREoE1wkE;&XXQ~l7(%s5-GIRDyUN(@LaIUyVr3P*B({r^8 zAU~BD$T%^;IW56XKRS%n&3!i4`HCDCS-EwT@U7M{wUnj*gTrtBYd%63?^^z~WR7Wn z@A1i|=3}aT1|!T*>t{G|$i&P#4oH3yuV=}^`rjyPrETKVGoJk5L#;mkDgtcGVst@I<96 zEC#(R9#-4#SdZDGHj?6RdJ`%ymTfcUEouw)hie!T9TT62-XFT5AH96h131NT1KVVB z!o1&ias>}5A&(&mDEOt@sU$W43rbHnr$b|d+}9vZHEHBiSyueZ=Ykp*fQHs$@6ZCe zL_NnxlwUlotvM@L{_ZqGb8Agpt^kU_H{t69Qn=&eZ@qGQ1+J5r6cVUwI|q6V1>{eK zLNs50wL@E+^zbXwV!w6(6=pT-Rs($ye;-fx6^jcbr;3%2|b|stZz=ihG=6qeCny! zs`G&F2k+`c1%0U;fwnu&Xbma54NuZA>?UgVUYsgqA8k89=BoIrHI+&`==|$HiJE3P z>=Tc8EGw%X{S8ztvcQ!PTOnb}n@!9^ryb^g1dC~2{o+5NJh4t7kwD8?)IT(Ve=Q7$ zh8C3pK&4jUle3vjf>+%z()T7#B{?*dR2qcwO}eD4(}@FW*o;y#B(0cP)v#k3&;f`P)C*&rwe^5tPMc+=66H)8AvvMoyelt399{A z$Uqg|doyU_Iyq#E_|J{ITonNrVv1Q zo58X`%P7;Y|5_O%?b`?oRz~f(vJ~aX$u=~`4O-koAoAj_(k~*mC%;u>P&}1Y)LC+U zw)XToD7p=XQ^Wp{c|B|8;fgO7>HnvD67#1-O0G8B)I!MM*xxc`7C?Hg2o4vdFjDG& zH+(-U^+6e@gj-SBl3ekeCDF{mtiZAM5db_wc8we3J013;a|wQ^H8cah2_2m&2O;J2 z6ag3hlrbz5yJE~-n-~N@=W`7)@Nf5v*{ep+$)~QFk)ZVs5pYOaMKu*v__vZ$I2PTo zMp6-UmlBcEBKa15X>rR}%shyKeEn7k*=DyhQN3c4&^Jy{dKYw(7Ha?hM>p%Z<7>*` z0yzwVjkn87OK8*$*o8M%=y*FAe+z&YJuH6691vAAB5{R*DTnB;pudC`{p{b5T%0WS zY?5n4WoB4ufC`Y*LQ0qsNW>XB^!w!Og&a8H^bb3ovLM>!)ywjpiYn$ z3pgUeHM3mIwTH^hlK(f9;-?vpqjvfr_YYb#3Nys@n5bXjVGh3T(L4E^%5Lkdw}TP) z(V^#D7JrD!@}1sht)MnluN>tadnC?!yzt-8s25M@YUyH7^+clNAmog#d&}gbnMbcg z=zMdUg{?~z3-H!DJuqHxZV)KaZL~=tm-9L@gq4tO8FFbIZOWualn|uH&*polgjA0E z6>nDmcK5Z^|D2a$X^dmC#K+qZ3@M+G3VE*lz4*UeKeDh!RFZEepq?O-hqFOJ5(M5h z0Ham&rPktF6Tr2RLIhp}2%~vzf6bn#ZS?n&!lF~AXR(u_wFOQt@(^((7qhxEXu=g7I+oI zsbRmYym?Qi?-AOfXM?#9=^r9|)A+mm4K_u?1G;MxuA{^!i*L8i zJfkh+0;Ww4&hZ8{1$Yxl63D;SexE?!1{DP^U@)s}oVJy-pXUwt<{N9{9U{7@#QHF$ zMQ<6GJj*2!U!eKnVrnw(^>ECl@` z{c7uUOH#Ner293yHSVwL&oJ73GV`JO=7JCM>vWv?VGpvZKOuBJ<`|j#z(x!%&0UXvM+3}tm0{E z4A~k(E#_A#R12{m;zuo-yq34E9Gc_vKkg4o_Hu~)+A^ScBa)p@{7c*A_%20>=Q3O% zacwUHzmx0}@OA!=)MWkOm~b(h^oDc3hRy%ecj4fq_`#X6g`egeN;>;MDZPd@@YlN% zoXJFTW(0VpLqLXXt84lXA!OmFR%`hWuRF8I)6JXshK<5t@0n!j*JEr+ZR)STWl)No z(VR-TqJqA=lwy0aOfarhn)nRfj25y;z(b{sD5S?yq0(bt?HR?egy@x7c;1iQ*^A~o znS(D7$H*Ln`1k@0<~W5vLZ*HXy-{jC$!>`P1?cB#Hc+fEbreU*eRr3@G!X%I)R}+d9 zX;=UdAc1ENM%1eLOXFB;OZEpA&2J_+B_rhB_^Fn+sX`f5X}5BVVJxkyyv6isy@F`Hkxe_$5Z;J(bHu+s|r)@$p$9Jxbt)@NA6YWC;BGYutV5vk^F z!sjU<5gB8+%C>mRyn!qpzZ+x2S(>W%58}U-zvqey{QnFUA!Rs{=9+c6#t}){=^U<% z0vAdhV%b&>j=VSx4)3D(m6Nd4>UIJK1#BBV)7Fi`6<1!I-AMX)Xp!HN2J*vL&(XO`YnSbRr`Z z1lSmzoJtC=(@H8U1ASNPgBGt`NmMcZcrVbrMy`5+o>(#QA$_~}{(Cz@vI&*qIm7Y2 zq<2}$7EK4#I@&3@kDW-X{=@K4B$#Mwa}XA7=`7K%{%N^j&2Ok|Z-%0;i{s~Z_coc! z8;wFPmi$j-gTy`*)BqQ6&Mn&N_#x8O!pzm#>}}1)SBq0xdj!$u4|tJMZJ37|Ly_|} z{wKDr01E_M6$I&41@njw-%0EB@Tid{?-y0lYJI+0Ni6?_$Mc zdlQ&{Jp!e$XvwG21WJ|hx^1A7dZc6}?X+tOjo@jO0q2(UM_m;#ZL_nDT*X0R`KB7| zldSaS3ReUTR#e03`WSF5ltH+w%S!38}9XS(9aq8S(u^L&4 z%t02-ZwF`x_`%Vog6WpC*2wFQo(qNYdL35N27q2^Z(~a6qhzIimB)HJy~PzuoAdk# z>SqD$;Tcg;{6I1MlA^oYUK~*YL^KK-v37@Zf5`9Oh<0mx7Ps?DkeUs#t`pobc$TbOD*~Eu4a*fcw3CGmH-rf)+I80dg3<^ao*B;i?&_1(AJ2b-~9IWovG^`r@ z+hv=i;QgWy>v%Aseag(XJ)&#IZ^LHTQ1xTf(BcQq06$uBx7lN39ZS_hr(M#yh23o) zqi#8jNwLF|mZV+zt=V)@kg-nZgFA%c2*g8-*Jr zt)jntbp6K?`{`fRb6rGlmh+xI-=d*oEm0jso{?nW|6%GaxY}H_wp}RD0>!Pk7MJ4g zUfdm8Bsjs{i#rte;uhSscyJ5u?oM&&I$`a--|zeaV2nKEnRDLvb!92a^)J^N7Rz)l zrBI1iH~Nwic;nXb%I%Kh8Vk)j%0OrVB{ z!T~f%U4qM9TX$aC#}8rp0Y8eE?+i_f2dp?Hb6{QOA=)_V_(aPPH%;HtF^b-V**G=|&>m@cq@{%#LJh0^4zJ+a`T>g=<%_mlT%eT<2}?w@9%j@jHd+~K9=3EyW)rX~ z>y!X$oWMyzf>upG+K%sHxE14KPpBLdGuRWA^2c-j`eq#Nex#G8D~(9hyFO^Zxm~Cb zSJ}k~J>T)PWCoK6Hm9Fs#7A;@WoHM4-Q%FXzE1UKU*GD8tq!Wf8Pdcw$mUeL9MlD4 zI6G`{>4Ho$_`4=2U7Bb9m4?EnBCivkcIJ>w#S$F|-6X>!|9C&sB*~kAi*qNN_Ycg| z*o&Z_%a5ItP&Aa)6N0t@3RYFgbBVqC1AZ25T_Zs*9=@Q`qB_%8&z1NStwwtFBRu{C zs1c$u%ujcp9F{>RCu_o(!;&SH?+66FBa#1GLg1!G3cpifi!4a=Oghgxq9uBH-C{y zsRS?cBr%dnHgDlR4wn zfl8Yb_pD4A+v1)pwghet6oMfA6fHaVvvvH_ zzdM($(dsi>Ss*542<5wHg8(xk%MNRVLK0$nP#jLvni0@gC*OH>&IlIH8GvN0BCGp)$e@+d5U9Xn?9u z1d{!w>H5xki%gB4x4OjMJyG~X?_mJho_-NiJRDz)Dof&5<=;?yXH zP#y;6%F_%olB=DyS)_QNEaE!qt^iOBjpT&_0U|mr(E0&qOcPlGXKIg05-{dAxl<%< z_-I35cQ6*$^3GC6o50KY9;NhnDIPhEO#9>S@mPP~v~3Iz^8ek`emfSQ^`*+;JUz51 zcCPhOkR*_$jYN_V=-F^5_`bSZ1!gzO>L=P!(GIyVy6usc)7`Qj1G=f}d^~1sr(S39 zklIXj(i-*e>|P6s!=XvKoCFqO$7Co<`61#C_j7wTf7qrX-IUQ&gs=}4m zs{|w-A-fWp7t39(bvs1APm8pidl^V?B4EGYJVfPO@N5_0TwA5INZ* z5l+JW$8tPh%HTgXCa4QUQGdk4zE`=abIJeDvJ7Jlk2X#MlRCJj&mA?Xg|2~^E3 z^`R!8h`~M0=8Kdi3E2GE45gJ(?ewir8|lVa;S}}HnOnbon2mol3aK&DNi8rb3p8BI zn?}U>3$nqfmTsigaoc-#oY@R~qOBjAtmTQ$F}}jvwT=vd{kvcHPQaLaht49)s_+lm zAke}%Bg1u`h-y$laOD#WSI`cVDub`MeO$B!DQq%cVO6Q(2BiJ_lFoK80|Cxt9%(w| zm?rMPcMExBYC4Q?@F@ohMR2P+D(6Y~|;h23H;F3zLF z1E48Mw4w*pd&j}%Iy|dcmR{4$)jKU!Zb>8G0Mbu-wtMl@AB@AoMRRB)_|9=sU=Ih4 zXFFv2m0?rNi(tanMBh)qk;m}<-?h6&S+#c}b9Md?u)!3A7^6UkLQDjP^Fxc^A~v?nktG1)T7J%Q9Z}OgYpnCbLkJ~yS?)(C zOei96zB3i4u6auL)R0c=n2SB-g4f}oj|{UonSlKZi^`S@6Qo{Uw3c2^rrH8&sL5fo zTSJwTwuzjI6?;mE@p7K(5B*>*SU^H!<+F!3gqa3}? zR+f|;g=agLdpI3?F^orPVOQC3ZfqibA+cM29h`joS{iqnvggAg3 zMuj}6jxHS#9{<4-i@3dgr#V0kk&%RErg&d<1lJ~5DT3ILhU&tNH#PP$O`SAdQEM%m zOws(>M&ge($IIZYse9Jf2`U`^L5{E}enV0IQF;U%b^nBJD)JG|9)%Mou0*$3JPYUd z`AD<#84Jp=LPSZT`znYMiHsAk@fgs~a2~V*gNzg{|J<`pzxLF)x1F1v<+&9P43Ca3 z8PgfN0`4O{>b*`o9-1fUg77SB{fdBCfy;_ z%BcI{REVO-BQ0iP(J*j7vPOjezLm?;(J?c+@IJKy zjxXMYSqnJUPu16?%Q>gQMb8}fHAf%}h~o>7XZzBG7d@J}muF^n+S}** z!Bndmp@O0NAhcHgw^z#`++`n0gCaUAbc6-z%JP9Z_RyW*Kio!CE-@5jRS)D8wK-*!T_thZ1|ugN=0`8OI#O@4b61yOq`va+VP-;572jazyIQ5#wRmL*PD!iYqBMt zvD@iwt#sqV8tnHl9SD0;r{9RyHOT=!YWueaCDutTmus(LocMj-4OjU{ur)p-CR29# zW!T91W+TaRn@nih_7StRi zo{+(Za`UzPL6MMD#bP_prs)a^F;kcIhqn0!&*8gVrqcy3MjM1vh{K-1UNTJd3iqpz zsWLPQ|JR5hhmW`}YH%6HI@fW$PAVok(VVF*^*+S7;MRt4j-%D7@j8+w-$8%gz^mT# z9&F|q!gaVJmBi2ACiz5!ns$v;QZHta%J@i{$Bjg%=^7e7-`~7otZTrds{2&R=lC8H zfDVow`jMQ(6?`!rvAZNZm>?0j4J~&pFwjZhr+Y1$-?8OSxD0}(o9r2-+ITLs=|Q>j z>P=rU?@@!Eq$xT2cy|;vrl6(6Y2&8E-n(OrL=`cvs3?m8kiP&)7#lDMl2+(IS|7cb5;^QzseX34w?F=Hk@Rd4>L zNBIE37^BvVKVg%^SqI96bcv}jZK%HA9)Za$>b=I&)}G!{spHW$Mh;daEBON79^8B3 zby~2dyh3ZLa3# z>p{CiEYBMU?42Tecac?2=Q<5;CR7MWFTRx2G5_F~`91`?U2J0Mc#8eEk@}U-@7f~BQZolI;(4QAG}IIZ|B6+I@6j{{=k0!r z@?QEFm%B2tqp3Dgin9-B+@5o!_T!ywT3IH@5_N5z_&yX1oQC*TA=KUHK}iGaXI}j0 z$<0Z(O&N2KWhFRA<_^@*?zrJF-@kjOe4sZ<#?2#u68+kLN4X~zo2hY@b+mJiMb;wg zkd6QLN8u>%(lD`*b{4?omW-|Og32RvmJru={L*!J?-#gC<8*<`{*zu+Tgc|>e_fMY zebPU1UAmFan!R+E@Pc{SFXgH$Ahu2hG40k;oG+#|?!oi}3~pZYU%Z{!q#_o)hK~vr z_iXx9^^4VAz0!({7M-JwFZ*0*UL|d}eZlXKp)`MI!X8A0AU~=N!Mq8ojXWa{?2mZ_ zm+^sKHC6olwCra8z$o3{)Q*x8wW@NBrr5rkdViKOTf>~ia8I{gIzr>_r{>j}AZFSe zsBul|P;n%SLy^hBQT)%&W?RZgn?@&8vQ~LrnfSq&t~W*EZ=x=kdtx~JOALXl754yr zxDysMa(#Yv9^fNEFqm2HSE*yl=0tig?|6YW7o^T)R2C52t}D=ROL%Bn;}|bVw`_?= z%_w_&x><*`?4Iy+eTRWIfE5pfbg-Rs9fGoMT*2E)w}BomQ*7jDN}9PFFTzzXaLwhAA~1|S^jB|i3w7*`rd2K|c|1th zv14*{h@1t9%k-)Qno>o9DUYP2m#7n!YN(RqDipcTR+-2OC8=)kLm03Ah%{eeTi`7Z zB4!i*Om75`6uoNcSrqt)CO72uM7peed&lGMHNX-6fbcF`C>Q7lnhZGa(LN7|(u!T6 z$7BeT3`W6L+xP#HbcGQDMSPAvC7ALJVZw9U4XrDLDXT7*nd=Sg;xsv7>y&hL?(9SB z>PC^FOQv{-&AII9;Th1Zt`fII$*+zES9tw&yKe?((~9zAe{b+7azdkP^^$WcS5FLS zhSu2^N(`cR4HX)ea>T}(jOY4JNdXLYdG(4$5nylP(>y!`={wt7Pyp=B2(SCp4;0eU z+n{J^Qeo#p3@3FKe+FvBr;j-PEuv^O+$|xEr?eD(JIsNoj}Sie()!)E=C&$Z_I8o@ zmADS2ZeuAQ|G+y7}nnw0xAwGyF$FmBk5$ zMo^kv;wEuD*^JA$L7?pgRHGsvsPPI2jQpj+U{0Yo3zld6FBOs}R1S>m(9I`-s&EX!NqpJwh%OMPxcht5(-4P_f{Ly%jI;#p*L zO7m^GtCMUa!SGp8!WA*4=BO`tE7UaU@D=sz>p<%Ba2OCBx$3{?U(ODP`O311EkL}j zxSKUyvFc&nHS1+@uT_P*1XmR0!p*va3+3exL?)$pxiy zwA!gg`)ma|CL-d^B1gU@OI7H!{`o@FrrA_^J!02^^Eum&I&S$yfbWugndtSItZYkjWbz7J?SFy=-n3CA- zTC@2Tee^E9b@UV?A{blg6EuYF!>#u2S8jR<_A8V-G{8z@Li9qoPM~a`JsVgv61lIV zL_?2cEMEh*6?oZ_#ZrWmJm2NdS^zYddVJ_36F5TnAO|f!?{;sisHF6&+@SuYYWh#0 z2Udv`XM$x{oH8HQlkabQpHNajDSb1ogN1h*Iln3w9v|cm3Rpo;3qFFKdgAzUj(PM! zdQZ2{*@#Q!i2pOh(eq<21=5UXZYuN@tROoPNBO{{8SLz$tRB` za}DC0?RsAU+!x|;X_h|rBjJL^CpQ&s_>DdF#9W7%#L6n&02f^=6b_o81kr^mR0IU! z2Rl+f@yrpwtuw-T3+_5YBFvPZ`dYss$?1WOuRhtsBe}JnZj~E39MH{$$?s9CI}w|7 za!m#;+QI!*ceDE@R9r)6Lnb?M^BP~LnwL^j)Xv9k)?&)t*Q%78RWh~0&Dx^hVfGF+ z8P>dNBfid=tlctxr0z;T(1hS>Gt(DEW?g9|zQ`GLI-jVgJivDdG@%lym5w!qu-E>~ zez-?uF*JB7Uc(34F8NNN&?rsYi$vl}`Z&j){bBbX7|MC^kiLPfu6GtrZ;Y|oeAe|1 z6t1yEO0!SuYF-%Ky5D19%Bt8K92=NYxa3;|RJ(@*fZ0)XKL-x1>F);#&|vRk$f@7b zChrLvO;$_GJb|m#-i)Oq2F9qDSw}A1PEF^r8;dH)m82;%BPG!+D}B-L3C@bJU#s_z zS$@1vSR4C7ph?Ok-*a~v))t13hIz5VFS-&>59U6s!jc15y6E>oy-?PM$7(zDLx5)H zsSoz<=&EaSjczuS>i8L3MB_%C6q^EFo@$6T$;H(QE>*#1t8g5Iot%+@BPF$<8v329 zZGs8Yx_?t;qDa{ujcM!i2dqQ0A@!d4q^!Ywrp^8gBGMA>()U#B@X!h}$}z!~i%)p% zY*gkRso<$pfpEg_$KRxm#XrsKH}9vTK+saWXrlwbv zvg3X>_k}y8(MdX^T4?_ z1U`4x(}@D6efu$PCSZlaZ&UAIY%vk1a0!Ufdb%!pT6(WO#x#uNhblBp#FG36Gnwoc zdEL+YQ7WtbbrtW3ZqB4ThL@q{l!A`vj4zR?(o1u_u8n+%P}WK?xElJH}9t zR6k1D+kG-%H3v#Cjtrj!oP_F9~5n)=EEF+Z>{=;JpCe z5o9udbp6?EK9AS^!>i%(@*s5@87gDDADBFww`J`m6PiCUeAkUX$TnswyO~_^3-s<6A+}~ih=We)@ZxhFTe-3Szyx&D_bk6rmr=Mt$g}G0N&_KO zo}Quq!2AP)EWo2u%JNxw!&3ZFxt@-o!LE0bu~5}K;~uQ&YbHSNoWhGroL6F$0Ij## z7`<4);6f>lW8lNk}Da*yxn=1t#`85+B7P%op8No=7w!8b>G7By%m_J(G z`bYuA4rxr*v6a@QE#7v+laX?0CG>x6ecT<)4rc*=CcU@oy{#>cf%XwPxhYj=XA5{8 zUww7ht!KmY)i5!X>lFp!WdX<6Dd%GH#jSeC4`5;1?0)dHUUCG8ItrP2`2G*_Mprhb z;@9xSu;jh9Q}+>)$5>ANZ7n;Wmh@)f*2~0*a;=&du&G@JA8Lx^MCjw_4d{c?RZAI{ z=F;&7K0r1DzJ5$s*7rHvkCNir7j#}XQA18*lH8e-RB!G+VpswpZMN|KyHezHAIga; zdA{~{Q*=_RK)YGtm>)!tFRGvs8Ao)Sa>vx4#_Ii`?upvIR272uA6tnZx?mj%iq(lH& zazWLGbR7D&u*a=jA1HgX{`xMxe}~8}5g%;u;oI2nk2_`_vaU36;9VTb++Ve9|5~d* zxSf2X6r+bvnL#tbBzD*eRYyeNx|^H3TKxw`k@aWUzG$tFKJbef0YzIVR?1S%<+IO= zmFim>9-QIwv%2Esj9RyEpVlKzw!>b#Mg8yi`qt?87j3b@stMw4Jjwo4R>ZT`tg z-0;t>>Yk_>h#F!=Z6{l{rcus`3Nr$&!B1C5(Vwl;^S_oB@73Pi<&Q#MT8Z|bM~$NC z#ToHz5}!F2vDd2S>(UDzBTWg;`oNaw`BrbWAmeGmY594s@UtYrKBiW+ALtD~&@&ij z>MP3YJ3P$rv3{G!mFfJhBH@UIO25mOgm|u-iiok$5GaT6u{& zVr>vB3lMLI?_+uW|GXUkOB*2JVkP&|fsBcEFR;;r`Fhz3m~Xh9`(jZbFnvMXWsP#v z@@JD;NtbcGWP;++ArPgyF_V+*(Uz));ZIS#wvTwPZtK;{1|Smp?vJ^z5STRS^e&o8 zi^CI5!wKpW?l7Fwci0C~G-?}IHyM{ts`lg!R74J;gZAx6f{Qp~HW1HlvdK5M^XW>}lZF$kU#mozm((<% zfuTQ7hyu8hD<>#0;wC))Xby4d{c20K&&A_+9jKhBTy8BbiOMCEc>6z^DbJKni;gk- zvEw>#J-`#E^v%-Z&1n{^t;s>$KXcAw&ZRnZJ|fvZe}lOUe{m+|1pGG}5i<3b=6k0_ zSWmfGuETF_xGwEar{Em$plbF8;P_7ZP|Q9`I9^|Nz9~X+gShwA;FLwEpEx=%segLJ zNCt@WD}ZB|PVDb0xW%s_p!ALRY~0Ji2`_Mc3%mQ(Xg@?hZ&loH;bE6 zz8Fc<7e9#=FI(xes?}6hXftQ>N^$WM#vIRE`L`Z|wqTbIg!Rha=~bt*?3~5C*<~TF%OxtNaZ= z_shlB`?92}z=Im~>kEJ~c0|GbYw_U0Kp}go`q76c_U0TD^m?JLCT0ZbJb={!gZZynR=%W3#n>0RLG^W`k%|TOoC1y6~+qMzj9_L@3P&dAB&4@8p*|>te4Efx5&H z9|@+yeNtB{T|Oy(MBe$L4 z4y3P1zH%QwFB8jv%=&aib?W<9xr~^z3=<|81h)G;0mkrNSe7+M|&frLu zEy3AjyvsOvv;%sv^F9lnxY@0w?JIPxdI0wVAf>7u;~5kEhue!VZx9dKWX*6Ws4q-< z@wwGAm>0NIyo>BO-o_Y!E&7x3F|ep&eO~$~jyWqBL+wgA$o6VY5?V1`H<@tT3KJk% zKAzI8-{|FA0R(2353niC4`WKk+xc}}ONVR^K@lzD8W^5knEKQ}L zIgy~Jy9C02_lMRLZ>b<;mpWfuc{yx@w6(2u{GC)5oPa}$eJd!#T+O9-kK9&Pv3EFd zL|q3iSOaR6I$++BrB|sGCJFrp;w^QAYF1#*VGHp}L!N)Haxxadq*CkLaVP-L9?l5W zEvv2SyjlM=Z85xAcu|>QV)FM%Ue`_KUq5m1UNpE^Ix9#HtOWoji5+t|-!E3Jn!uhj z(p1VBtD3k{G!L0zJWYLt;O2GQ(q>hXl-3H=(989$D!iMmPSn*`9CH+n=w+Z4IEC90 zNh+;#nPflO@qcF*Y_95i?2mPLZgXu44K?~`6OPKeT7VEw`)T|BAWPDhL%IgJ|NcvA zCVM4oQ{?W0_TNTHre}#P=iU3n9KN+9#z!s4-Xg>RjWP;4)4WSROAX*T|ZH%-u4@O-2YY8HlDaavwD}3$EK&H2?373{<&2nOt3Z&zY7auC!O?X}Wct&w4UT%S40@p_)VukX z9XTp4`-nDp4UDMl7vU-=s8I-E_{=}Nx=xpG3LBkQRI42N5~SWE!px(p>-LP;oA5+@ z&NU@=JME?rrG5T3K5Ga+j@=#~Gqv(8rPTeNM|FkHhkb`ZY~)D4yB!W?mCHJPmQ6z- zv(uFxc z-JL6z5)lykA+*8puU10zMsv>G=LnBJv)X+Iy&?v}_Sbmqcg|98=BzdMlKBXNv(9~P zg`wr7(QlIqt9&!v=@%JA*2?roMgHW@8mU^AS;ej*fS-Z!RpFJMT%m}JHsEpZ{7UX! zaxRF8?wdI^VNcr1FnqFI;EUF8;xnk&8Kdvk8vvb_H zqX#AZ)$6-e-tp}*Zx~4^99t}pwZ{9RC)hCiEUs2^P~G85l$f)pL<74n*NB7Cnjd&#UM05q8mQ)4F8o$q2E!JVn;Hm5)5yBFs1ZX1S%H zDn+nKM$DFHISWWi&g2omcC%}(9q+A3sZgu7!ULpNBuo#J@SF1!CKecgeZn}o~-&u|DXSupUzpeyv z@ZW>3WvFN{n@mjJXCTe$CIQ=E$5fWKsjBK`-N)H?ysLSpWWZ6+Fy98Tpkl5M#o|l! zbuXL(p^59!hx&nsPah9>IIUFTuNNhyHtzBwU?M6!RF4-Ycumo8bTBzlp`S=3!+EG- zdKVjs33WSC|FrAHhc27iRa*MPNoy9dJ%U=;*|yR7py_yquCCv;N91tQt}J%@KQLNs zQGqGL7mGMIILQNFb%PxPlPd#7vfSu~G_*#Qxo^YYt=)tcQ^yA)f!a>(-DNFMr+dZ3 z{Yu~ISRrDz;yKFprJ@Qz@GYHa^^v$O^KzR_zGCyGGm>$8vr%H>TRXE=E+H)tN= z1CGmaae5Pk7bm>2LZ6`$)8PREY*0Q=zFKSc*`wb;SNEQ-kc1oxQ;M*itNh zH=RvpS^&D3XQwAUzugI;c8MifdZj<4o7Hpy_?g+&3l_1(w)!nu3VH9`)QA1mhm5$*)}^{Qswz;ScwC@6%uY9MwIZ*Ueo+4`kxQ; z6CP!51g}A{F0#ZD?J9k947n-Ts4d(0GPYLm*duci-9u@+6p;JKy;ngeMB{9JedW=p zKWe~VjD|!WWZ#MB&|+E~B<7bzG_{m5)vnqjd!a))OJtMiO3{b%9kNhUIr1M3pL<8A zv)1iBC~dT8|58jqjcKTc2h+C z+m+N9PMosR3d@?6(WspWg72bMCC2L(CzGq_4v)RkO`4S_0s<{Yp=XP6%<%yWv>NxH z^uJ!LQbIXMcow=mE+)n4N)l@;?dle}9RS%pr^2!1iZ0)4%>M!n!py5N8HoVf4dg9Y{h% zFZpx;)fS;DO2yESj|ZG9!y2q}>DCtFV$1*+zeH59+HIWHBwF%1T(^}OS|Q<>y^Rx& z;l9|Vjltr5+Y!2;mx{JbgLxC=hNm(`snKKYQ_S`y-eF^^jLnSo-)NDL$rxh-$k;=< zix=Sr<}2-=M3wnFF0pMZiJaWCvSxSv0gLJGMQMkQ5&G{=K^BvfEhT=M!pV1G}<} z1n>}yGRA>KfQIx0?W%$zBuJ-09E%E+>-X0<7M3-J{9o)0i9+qp9Ip)g%U&+YR3Yvg z6id?O?8Yg6sLVEsx4q-mMN1C#?y65DIf5(vwch)d_i}8YoXP_G96@}hITE$qw^;Xd zBWr7mFbb&*7vU6dF8J&6y7$^oXs)>AO{*lUG(t@w54WPO?f&}&&A2IE`=m7;>>8Nd z8X0Nw4d~AVxP+=Ya>irUd;nuPTino{3f9R7ycEfyw6IoSY%Lo>CDM+3ac;VEwk_$w z(ufJY_}ByIBZoA>(gL+gFncJ)W=^d@PnSGNX3-yFeK9Vp?)3DNhTh>ae?|vebVm-> z=5H+w^N10$bzzPkMawapDP!$BDFoAACf>@-)xVd=>{wmiY&Rjab3s3FV${|4yH8mD z61z{Z5?_uhLh&sNZ7XAS@eIi#>BVI1JWWH!sF^OBbm_`FE9iorJA{%Lf=X_fHcP!M z%H}nr=|amjq(GCUI<;G*iszj^p{%jDv#5YkmtKclQ9pq!d1RTwj+VNPLZs;`ul%Gh z{dKVxhkmoDn62eWQD$*(B`|Lka*?EVu}1YOtuGO5RaLU9Wxd%)TNI$()PY1#oFBxJ+yvadHu{6n1hzxVjA~aWeMoAoUnng z*5W-|!q$bK5Sv&3mfZ`vf*Bf6fWRyNtSv^B!F;x{OHm5v2P@`AjzDv7PJwN;1xpra z^Ep&gDpRpUtzNzB%SYTqa~rz+aGMa`$OxKMF`Oo^i3u`70ByZ%>qf3ZA03TCLo7AB zko7E>`CN-nw4}zvaNU@9)Ldd;0yY>Ssg1c*Mu+_uHba69YiIO`2~x}|rb+yKgy)YS z0!NhZ1nzO~$g_Q|qFpYr524SBZ(eW_y=+=ir?P8{b2vIl=8;3oc4ib+r^ znwQEz{4F#xjDkZJt=u#(3-<1dk4y!2cxTjf_OWGyfjbb!%EiC@*#$JqC#$0yT@2!!n2h4B%z4wI!)7)+!v03cJc3t^~*)ykp$LBa?4P_3C1|JbO+de6_PAeU}GTU%mA~z`5 zy?0-LCoDV3I<0=I^qH4~rP6iW%#0FI8>i_fuTzoJ5(iJKklC7b4=1GepwM|#_^Kqc zu*A@qajpoThp*d;{`sr_7crWmvgo58H8LC>`Yk{$TaD! zr|EH`jpft82u$+$cm?CsSflTlgkB9e8OX!xUm}Q^eJ_}d6!00#jDXqGCA`AZs5{Oy zdDx1sm=o9i_L6|UP~Mq{Uv>K<<3`VY4BCuJhVC-nBtc-xke|0P_Rpz~2eA%Wrm@Sa z@C=@5YwP$?#p&PG8YYb{h`t%5p};NBH0$J)pka>a%LH4^E-;D@_JIC$h7Rg5SZee2 z9$JKN7g+*^&0nz(;I|H72^n0HqmZ4WRyI~0aRut4EFgQ^oK;oV+h~-)=cv}{@+kgoZLK)N@797G zV=0;~d=HrbS+4)^PfUqN8|hs)0u%ysBtY$)3?6PDr7+ZrV#-2Bh@@3+$Px5DSc|S# zhe~Be#xKo5JYln^*CQ`AN#^h?%$d@hTZ2S5DXe>PXYs!szB{o$Ba=~V@uP3+->pTx zqB}sdT&6@7p}Jr{G4ucx`k53zqms6n?Vb+^q%^r2Uz;`q^B z&e5POcb$fP3wDQ}smjw89!t#}kRpXuoQ`x>S{O7TEd8^{HBFh>F)g6Nja!2loy2h2 zk0a%!WH40hD}|6$@P^i+_y_AGaQU>(aP!B#>XpQ2A?n{!M`ZS~6t&V4CY7F@N;L z{^*$i+@we;vDAk5xE!Sn{=%~A$2GG}7JfK=s};zcxPno{&9Jnu%t2{Jot+Xy(>_`{ z++0D+R}*p3S#{N_mmq$uFUfN2Z}g3Ub)IpVF;Frlgh0+6U;l>dIiF4#2rReW%%?A_ z*u?k!FF*zZ)l-MS)UePr0p}6N~xTW;enC>>*zCe&M^f zuMP}gU2r>}LPpeI6RUtnVgM^Rn}u9km4hY^Qs-^9&U=PI)<6+3?FfIRD}AN`tn*ti zPk)`!Ilzb6BAbsFgBhWu6fZGZ!k)LNT0s+#upW&FiJv#sG0B0= z-5B~zv>F3(n$1v@vZyz$9={po9#V!_PnTI=YaXTr^G8zVyZirK0g9zEl@Jm_3(JbR zSgE|rp6P;G0VQM0Og7CYVQfmmI=!pNaZPom?w*4fBt6Z!#0+v5U(73SM4W^S6bVhj zK#cDitB7kID|8cMV>7I2m!msPC$L6xRGnPuaYAgq^p9-kNDd1XD55uc_*=dAC7HgT zy9TovK3&`Q{%%bCu(m~rXun$r`9^;;*wGbeKzU#a^cJ(#rumyctTO!c*?7GKBpu&z{%AM^#g>=rG*M}E;NQQF!B&!x(rT;XnplrfRGRDW@qnhIKBi93_0s6*TtaEi%mbvTDf$YSv5$6N6fbl%f7qvwb-636=iQqK z5Btsdt&JC&Uej!}8QI%r(k-ylOgf`=`6)8XH$n2uN}#KkoD`DD<@VG}lcaPPS)45m z$|9j;?`+<+{ocjUNu(J0S<$tc$u078_Sog4`=G|3NK53|ipLAH+I&p}#CjUHkHQyy=Qt_>&1r(L0H;t#oz=QxK%%m)ohx(6 zm7nZGHjZLC;6Erej?fhR5{g}@4v^S`Iwu_55XPVO|6hAt)8R-e`aL}e`x=6IDlLwg z<>Sz2ZB=3*`~!K4(Mfn*h}Uw>B;Q6_>G|2)aV>Y0Bw0)-(}^~dVqsQtYFRKx25!^L z%(jL#GDMe)MS)h$;ga+p7~Y8(ZLOSp%*OHAUh@#;R_BQJ$1R#bIW0887Ao#T(1Er` zPQ9PNb)QfjArj|2dA<3lkv;pM$!>^08?ZX_ODHzH76>I>pYAdsVqRI-y{ZjG@TE6Y zCu8Q?l7kEstXY8K$cza7uMM|>C)2iY|GjcZE9eoHJ{^RVU0iA^zUSn)jI?-(Yd_686tVV zG;=%Q#Txjg+|*Bpg=?Z%j@peLCfqe~YL7HeopQ4GJEJG^#YK91?Ofg$BM96(9z{+P zV~lMBIR*7X=u%RM0OZE#pw*&5{NS6|Wkcl8&DgFEcww)(+s4#r%gzBMM@MYPhO`H@!UT6O7 z`*r5~++z7lrG28AG-D;y)t3_}o%sjmG1%l(wiAv)DYnRb+~P!NVvV3DKdy>X;;W5Cm;)d^Z=3=~lns^#y7%i*Cd8IEcmGf@jL0^6si9OKS4aLSkIA%PArY6OueD)fa-T zgt+=N)dXDm2ZketrvoeSa+Jv1M2&l0AJjmhY`C|S5>=M@ODHs56&SNDRckEJj_*K{ zi&@fiJw;D+l_Ke*SSYycEje$Q<6b*C!S)^JVdSH0bb7eNUy~nf7&molTcQh$=wS{c zWDZw@bw#MFH)-gAMD`v4=Sh^dtTj~@ zyeItRl>Sw#IIdnBq?=XZ9a@AC@j5uMk~Vs^&FHE@DP)QtJTGHE2#Yy71i|6`+^kZL zqoJ^tk&9${m4f3aude;RU8~C{hG zROet!I$jL+s*;I%^ct$=#6LqLm_ek3VwczS=w};EhNwugC@3z2%iyueGo2IUwfm(+ z`6Jflo+t2oVF%+yJwJV2QN+cQez-cOJ#UFaZNHytd_(hzai?NnRV?COhbj#Ie@vZa zRFq-6wkeSg8M;9_q&uW@XzA|ml#uRJI;Fcyx*LY>9J;$hztQ*Idw)OS4{Nbzp8I~T z>pYKxTzF1p*Cg4X00z+dpCKO<=g8?b`zdgTCe^wEtJGQ$w^BAjW=3(U8jwClA1D1P zo(pcpQ@)+bpDu9?rm~yL0HHhSVu@L%_eQzFOTH?=6jWv`vl0W=#d-t|FnU$c8(DF4 zpT5%o7jZgV6N|pt+wJ-wRP8Vtr->Itv`M4!v%TyBw*T1#amBBgKVQk2<9rQ1*y-kS z$QR3J?QVMP>l7R7a-&)Fb`CvxPZXdzadys4FN-Iy!{hUw{zKmCht1>ndU_#mf`fF< z6YLcQlbp;$_^~kjZM#HP22675j68hI3FdVAoht8KC3Zp>#FLc`c`uk}6<-92#65kW z*m6s(DuEX14^G`6uiA_F54%7et#7!%C*u34^Wo*j6yahaz*IaudlR3g4Qne z6%w^9t>moECMzMcV=xtw8BE0&&8)B`dhoEK%}dYfX3Ebd^s!2B&3WauK?`Xo1y?P; z^{~~s9+_$=O8_(xGLO>JZLplz5E@oDHUj!FO6rtM%DY&)INYzHDW!VAu3G zst6)jTE&3uKP2o-o|iPYJar(7!6Fr$`RMaMQ1y6Md*c?0@@zd{8>QZ<<`1rXR01di zUxipm)WEsA2gteE6tH`=0WJu}gg57Lu9nbHX7q^*#`WxYb`_O-Tk;=tp7j4fAxOr{ z-m%a3c2txlm96$xq%p!cwL754O_Ij{_=^XC$CR@iI{)FJdC64`6vKNk<2|LTKM{I7 zkAW|uinzx8J6%GxN6L#>R$LYG-fJFKBw9wMthY!zT>W*F$KdX6zo4>SM|VJX`V#$< z&8Exw@sj2S4P_Hw9Hc>6A;YSEbK^ebLzNS3X^QpQlEZcF!g1FVdbCL)h2thxC(t!y* zoEC<-v^m4|zSS3o!%mIQUyr0QaLslonvaaLZBc@veAwQKI)47fKp{)}>+8OCPBUgS zzMD?n2{pPML%3*7Corua=W?4RhDQ!}OVW=qRlznPN}=DNaSM+nF}ciZNE~qnq1aR= zuFY<3iDQ*uaMTn!=zS?tChc#Acl#RgTh2lI@Z4;uHJoYrjP}u*Tl+PL!}#NT!*yH8 z_&y=T?Q6r+smu@S9%%*g^xx~H1oFXP&#p)$Jh(GAT{cp2hBFlKw$vwdNsyg48ro<# zoMQZ#MD)4d>r9{jJ6lE|m!z*9n|XKl6$sz@ z=+)->GWQ?%aKjg(Op%QDZi0Wp2Dj5T_m?Z4<6lx<*u*Z?5JE0QO7L~ArgQwR6Oel% zT2Gv7w;_l2_ng!npQ}mM|9OSRu%5^O*iox6UK8{OtxI`q1aQpfy8XULh8iq1J~X_( zqjogWVF`YkjU2fm;$uaNq(>-9*BlfuY84`vpPOA(agU-*TD10H3*^M^{irbbu80tt z+5tuNE=Wb<6Z7Q_BinNPbRzBt=Q#r4tyvLYI`-0=&`yKA4;U9A(kk(w`2z5uOCHVE zi?Pm76atyH`P9OS$PTXeO@T|Q7)_^X^+`8e-rVJ4T}1@bk-02RI`xPx4JEWz#pSSZ zXi#6D_K4?-mXROO<`m90&!N%v$jZVL3pEv2ksI#F!*2K=L$Ft|SEC7yJub*U#$hU@ zgfu|{7^!(#DexPVqnXtPbVz^ilppKM4QO+5SQR3j)vv_+w6@tsVRs|}rJ5Ny&I#a= zQ!eRzETF3E#;NZXSf@f&rwyH-eQz{mso4FJ{?MT7E!V9>@<>H1#u*$Ymp4@=;NYT zsKc@MxIB|~BpudjjqBalk+^JEGbH*3vL$w@ZXMgK)!^icS>8OAdy|z@$bg0fS46bY zVfM~S(om+Tw)5^WEr-1;V(T4`$8t$05kLi zoiF!iE@^mc=#}Q08H%L|PN}$>0um!ij=95;GY->pDlU`4q*HW_FOs$QdN!At(D9$I zc$XxqEzoSo(<3pAHG8LCs#x;K)$SAJ(xWE*-? zgj9(1+j#J+0!`F99u(be2v$SgkYG)RvJ{eo|HBocb0LO^`S|HNMMuSW7P7*QuaVi} z(E3W>NsbV?+#G{qn9+NTh!|6*o8|0pjJ%R+e}4nr+EiGiJo5_dH0k}8^_ zQfR!Ft%a{h#7*MppEo}`=eyWUOeDsyhv=Eni+MMK2?+!0l?XNr<-dm65kYt^D>&2F zLip0P?bKPUI`_KaF>l$wPigM&U|m(|qBV8>X|uLlhJ+W=50$=@8{ya7&fW+0TFs~z z!(XShT)1PHO#H0+ft}VMVIov(gbOJjTf=?QI3lT76E0hSR<8aaV(=;$MO_Gjl zWMr<$c79vp@F>%CTBTHzMH!FLh#Vp7I3?mLs$9z5XD`lJ)IS0^^soYl6&EBKQ= zSf5ec-uLd>n(j&0B-|knnJc10cVqp{qoX?b57ZiHxRtGse854<`Utkjf$$hpYW_ra zy1R!Yl>Jq{!$Bwn!WgFk8(>|YVDD@yV3cN%wsf>_?;PPoeW^6~GH)k6ANlbZM&vdkvd%X(D_jJ2wc{$;hRJ*ksC%gNrLF1rB&6I zP7FUmOBbFKMa)#Z{LA;6FMT%6GWFuev^FrGlP}n=$$?ZOWlH5XaC`j8 zSd9o3YX(lQqoMLOg51)oddvNe*6#X&orzdt<|C7m+%|dDAzK%`H%dj;yQgHQx005a zUNK8aEz#66d@<3D9{e-nx^6y9Wa%G8+(E?AaX0qy^UX+`E zmRXgHnu`PLf28VdeTu;?Q7v745$6SE4qn)@p%Ly7e62>!xBYU)*GiKQX@E6^>3D%o zY>gZX_Zmh`KCBpKZTn7d9?)YuZ!3;zFkRf6q%LTShLciZp>RTILCcroA(-_a2!J+rdrbHf~v z14rtuuYxtM+Zl;5@T_hkL(BJ7;e9Cia}xHheSe&;F%kX8HFnlnpEQI>dh1MD3S`W;I#=eVbM3}ftMzc)sxV#cdAPQ8!kIx_SKwiU@&@5N>yzBo z$KUIu0+jRp7J?7CQ}a1#GKm#tu~CH3+EzClnQ8?Uk%f)ndEAkHwwIfTwiw}%Iaz$Q z)Cy@iQxG|+7=aoD;F-AD5$2(+_N53YrdwhEv((G80H?KA$a~0~FA-8zvww52()S2k zkP`~rDO+uSqT zWvrXN6}>FiP*7{b%E?_{4@|AR--IrD2xR71&@|WTU2@yUxcVqiOoDnpSIu}hkzIYy@4VI8s!6>W^LnlS)qcxGhT2UB#?(%TF&M8(;mG}ID^d&Mk z!erf~QYS1NT*Df}jzuAILA$Nue(%Ea?;W(8RD(WDbAjncgR*$b7rn|*vhBd9A!9e>}|``5b@^alHwK=qv_&<5c({t!o7^DX{_6! zrd5KgdM-^<`&6;2a=4TCm3|JCtJEQ3 zn{$Kpj(nVEXQwHaEEB8$sT03j{I_?-LMtM*>pI5;j#6-(l~=C;`89{xyV2#ArfjZv z5DWX-az{v;A_jm(ps6tXC*0sg-^E=XVu4J>iZxfm#ik?oG-OioBEp2JaNJ5^P3`PG z?$#Y-&pPI=X6|p+u-t!{)>Xv>_=|CllgwX`_os^a6%FI(5(moekxW!5N^E!Mqef8}>2ACS4++tyzLl%C~;%yF~TCD>hx^ndCz&2Z_S4}0x} z%SQd%DMuk4H&rKCt5&GG@v|;faQUC@L_1;TAw!G;(K5d#7W-n&SCZ7l^qVtQz?ut5 zePvS%YOKQ%Fqb;|4y(Ze?4cZ^x-G`%xEE1mo-rTQPB$jyM}}|!)ZjwxTxqjWm7R)q zjEg~@vq68@e~xKcYNX`TfhMuuTOpQ7M27-MadLHZNeF9*1+I~>Y^Bvsqk-u%K8pge z_)RWO-2qH;7>oG>i=(hs*ZclXjSzVu&k^CxGB8$M_K1-@G&s1J+C?v3o?Cy<& zSgv>z=y{MyO+=o=7nxK#e(hdQxlRASTn0ALExTV1qoFEA#i>h0$E3h~a;q=_b#pM~pysdrcA!N{gy`Qz!S|fV+h!D;>!`Im4;>+RX zU9P`84LAlRy^Hxshr17J4fZMhZCa&wkg zJhKbsK&_nQnOg^hkp#TmV6K$fG~RV-Nw!d>g+vyjEBu2lLl9PP6tkZ6@`Z)YRhl&Z zFkmOcRRcXyhI0&6A4HhLGs(lojzcP2H!-?lPSz)^bpk^! zKJ_v3w_zFyr6woe#9PCxfAPh4^f5sNqQ}$!vzK>A6qF2xtZ22 zO5vJKEIEB!ms1b@@S=IMIw<7FF%1p9;0cyUv>(7KF}9vlRv0pT72sCI=D_(YE6Z3W zi66A}G&HiFrPUb%#d?jN$t8-#hmCccjsmFP9NC*RPT1n_Q=D!w);^Rg3#ta|U^dl$ zVeVGfUs6e{&<#YBiI%X>R*rS

=-L+Wm$T#c*21AP9I}aTy)8do!>;XZ;IwC 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 |Lb1*);tRH<~7`Ku72=9*(W~l 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�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 Z%mF%=f?uYTTlbBt{{RqkD1-n2 literal 0 HcmV?d00001 diff --git a/bg_img/4.jpg b/bg_img/4.jpg new file mode 100644 index 0000000000000000000000000000000000000000..98edb266f1925459579a32ca4e38bc2d5bc531b2 GIT binary patch literal 171037 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�ZG-Eqa!+WBZM6CZV=f6dvCOPu4bkfXtBd5YuE39{nMTp2 z-}0B#9zW7Ag+w^igLvK&y#_%R)@kDu5u{i%m;p9n<9Tdead3|2le?=+LG$67G>!Un z8NF99lL%Rl1W5uUS*8oo)#OCTf&|Rkaym)=JaFC45=$!RFiq32m*KAqyE#hD06)XE zy{>7&{zTMJu?Pk>L%A~;WIm=C^i)pGRYoErfu`VJ>N|V*Dt?9HrD-KfC`;|f*CSdz zV+|po+N6dqw0XbXxLj5AW(fukR|74#I28%P6{QCC>_^`#XOC}_&D+g+F{U4 zzbA<8{^9mspX{G6Id*U|NnvgwU!~sqj`D@luT1J1XVv5&45$=r*@T!a!w40QN%gsB z)vca~Fv!XE`XviPImS)Of3kkBgh7-I2igt8SCx_vzQhxkXq#d@lG@at_4mYDE4013 zMXf1EIXyt&IknHwT+%;qN7RHSlmS!$M3=kJTBy@qiGY+f!mQE178WO2ZABy*kv4A? zV9Rt^gMQ^ZyR-_VVq^Fq5VqnlB;&5!pv=eH!Vi!QZmw@GFwFZB!Xv%1AamvS|II3) z4~AHBW#~t~V~fp^%6~u%M?sPWOnSUHcvN0u$9j)ZykpJfMNQ-hop#5Z_b-4dJFRzq4WgwA zm6*z<<-q+}cyoRL{86X$tdLK6EqOz1tJS@!$xzlHHm`US*P-X2@$KDXi@+U0mIfLX zA2OR(Z-)`Nn>(#id5~e)J8xWyja#k!h&@HvX`1f-)qs`lh^d+k9dlh!O+r|_fs;?y zf!p@SWlM7gS7@t=gBK>Tn+uI^auh5JjT{sIay>N;fSqV=IY&P|FHZTFX6r7_(~8&< z*yZ8%9;<(ZZ7dKQJ5raNsc#T27Is>$(B2Sr+mP+oV)n=CM}!El8d&U{nFfiJ6Qozg z!nak=Ap|kp<3MVNNhKW%6gZeDRs>(17G~`Gp)Y3-cu>OC0U|J)H+{{wn?DhV|9vytUF zl#4=NFf%^}fNKLb*eGLi$j3XM((P#H4lx&z{$Ax&2m`XES*)ISz3_qYD4o=*bK#e= zD(Zy&87G)CZbvG?+}eiw^#j`f4rTPkoVVijrN@L@Gc@i1a0)$B=&ygH-a3r;!7F|n z;uKQ2_2bOnIX8jUJ&YaKjAWKg-v{Qzt6*OVc}L*4oDgbo)Jb7p6k6^npw&S+Y{=HN zkeezFM7oZ$5<(Rn#jeg$?FUwJb+5nbX^4zg&IVFqF)iL_pOkiM1)Ov3jKU_S!&%cd z_mgOro@)^2coS8n+xpN>do&o`#^2LSw~gJq$hY@hMVeCH0w6zFCr7urG#{5Vr!fP$ z6Yhf3vInXuNLXRh4#$u`4BOv0Vwh4M)O1H}(ajKQH3*sO+VMT$>{>qr)cV6U>5wPDc#q>6sZO91I2nYt= z+I|Crdp^iNZdsfuJWbv~KW!XwM1$jTrTV+pQxeF&Vd^9UC54cv*_WJ%IpjOqM4THa z<|*B7gG`EUE2`gg z4BLgf&8{TviEa~YxgyD1PUPgxxVpfOWx|s^;Ve%Q?#ld-pgqTn zOM{T8Wc?8K1om)pr#wk^hjw~!c6rL~Wa*5vH-s)@Zlgyu2B|o}S26OJ1(7XI3CrZh zVGe8jjs`-gvOD=9!`sNcvETPLx;lssD>ubD_i*Q){0MjD=g@;+YT75v zq?U&1AN&kVl2K_BSnxDH^<~pu5UR6!ii4G}isRQpbPQDLqC0QmVRko?QYM|DX{6Yr9hV%23f~(?ufkvNs5R*?fedrep2NXmO^(6l z5LUJTDk0ixa-jr)0x!9Mr=2RhqavkQ@WdBJ*>OgaKD6Kc87H6~-GG$eDteWL9hO)? z+!YQ(&wCJ?QO`|%;=KD8-8#rS{!qBTlB&BBIKGw{gigs_VsX<>X=mymL&+Zhp0H@Y z9j7snv~X*rzC8#znwW8o+YHogaL^glW6EzVv90~`%`j~8^#Ju zt~{~=Mdj6rFxS*Tm*c4A7KI&WEB<*Uji4@NG(OTLHdHDN}9)8HG`Oqx3UAP38^_^*NZGRbyqpY#{u-`+7cjYy1{DrnuTfhN}j4 z6Nkg5P|z6>QBqZ`Df9;83?OI(ISrMRUNdU0ik#Cpn!3Rt%yj2tO5Mm$>_8d{86e+{ zcKzBtUa@FgN5@0b034(e`rCcagRd-&fuq}b0GqyJPoUGF6HrIB$g9i?t>b*Fx+m$p zMW+YsG;qp${>e>eg#}0{#~#623{pyyTVm{cLu-`?qG=6+_(-w38z^L}^ED&~!*}vm zuTIR5(PhGKH1D|S!q@aFyPY302%kPLCcRX8zcU>_fi>yN@&<21 zFTtajgRvskZcd<;p-Xh=wBC2*WYR8xQJgmC)9JsbO1{~s~ zdAuLibQ)7q!LVB91i<&S&$NiOcUZ3bYW;r2a}c=sqEzdp`&5ZB8-;>d?zW&Tb3ofo zd2u`|xp7i1vi$az?Zf78PcxlDeDitlN4(rH>O_>X>XG9ihP-N&CI6oC)pS%zrU6@t zFR^R7unKlS)=VFdDF?HpGwMz#w$A9I>Uxf6I{Mb!wdID`UOXVbdsf}Y+5MJ`xlb3oQ?5w(_`KuZfp6{K`|7*LuAtX~=*Of}BB7}gPU zq~A#D`l`XYXT_q@3#a`*U@oMYRRYwQ#)N+sgU!cmE)tR|;pP(?!_@~cTAbm&q*D_6 z$wVvUT0fvPOQ7=Mn1ZWG>3y5~Ig*Z7={~8#P0!v`5v zSPgR3quejrRJxlt1VUgxLSWf@@PIOSk2F21j4^|a%QVH0oqFJ@^?zcOtSTp=LtSk4 z&`@Uq!fn+!A$U!F@^nqQw5CyEv`DKHbtckG2IO<|W=Mp7a)(c+h^7}=K6ls$;xy)v za69RQ>P5rD(14YMZML2Z=98My3S7Ou`GgGv(FWQWE}lJ-?yZx}pG>PEQR&$57l zhw;g@XzBcx`%>`kiARgUpCRgb9Y|~tcv-aXnF}Sy9AQSLLwcaC&Y2DxIMzdV5;nE| zI0EpmA^t#QPQ{b8d+@43TwB}+M%>Xed4R27!|HU71E>WO>r!BbFdh7pm*Bp8_LgI2 zmXiRYO4uVdH%Us*NcGF{g>LMd##K87_XMw+^%c5tW@rBhVYeHVROhVOakFln0ON*> zRb6%)NnygY+}_`?nMAeK#Irde) zTX{@bdIO^qjiB)NblBJHdz*LCm@#JwtToM({o|uL_(NX&QOycQp&c}FMJbEARc#uV z*H+g2FWUE%Tkq?}3G`zka#SfeYdkoT;8^p@TGm`Y+N0A~ZVz@;RDshf+zh$N7e<>|&*k(M1=zMKzJwGc zsL0@|IMHku0CB*p@R4Ekcft^Xw#<{`hut&C5X-(8|sQAraQBT^Hmt82F5jYE){Q#&fn zuG&X9)nt7R&(VDxUWA{-KfLWZqexyl)T!Pc$zxxT(jHj7n^>HOfiyl68laRiO;57? z$(O5lR9Ar27dvLdR@QR?vFoKwcMdtv=d^y77oDvkZ6s^?O*dO)d4GYOEUQ!e;EdI<#>rl*w^mBGM=lW{ zh_w-I_%M&Bp68#07sD~7nxflzAZIjEM2AfCa_|TXE2-en{sAQczy|eNRFdU(R4N3% z71}@p0Qk%c2=`98YLTkucfjW)ty&-Xv3OGA#g@7NE{8D~F(`Xg6ueTSCb9@Up zsG*!Jct5-`cMpqtLmM9K&%!yvdJoiPH<8_TSlMw8z%ih_>369mX= z46hcN#H|mziKjN9Em0O+7!bk?{KfSVvdn^^D^%NfTM$t%gH143!mj{FqpncZZ~bE_ zZ`Fb~8e871Fq8M_;TpBZj3*qL!Z4kqbp?1oCh2yG5AXkWUn zIZUK*KL&gZiOxEi%p|b6s9IFRO{;gictz|PFOJ?9u(IiYN-jeUcOBQJui`#7Phl}H zT4a&SA?;S)JToxEb?Sz^p67)3AQ}Jw8-6c9cCY29nP;*`xFBQx`dlemG}!^{pfsp~ zhq~s?MOALR2r!9r1lF`bw+gQ#KwjZS)07>Z7I-@E+?Z-I!;3X57-emVxhR=P6_U0S zr86D`@a5-WfSH3mW%x4MpZFhojW`XtGo}`r_YRL)lZVnJcy=Xv z7v!F(R*M~8c%i2osj|W6+6P$J*=wDb#|m*MVL<}01hxe5l3`s^!jSpH7X7T^pM_(Y zwu&DI=gFX&`U?yJyfC@Mfe4<>+59!^IOH)QV&7sKL?EXhPULZA2mO-95~n`V{eB?$ zgY1tSHdjI-$<^=qhx*c50;cOg&+4!t4^K^61 z41WpgY1R#L^3{M^IGN*rG6dP1DWu&Vouhk>{ck@+IY43|xL3+N#!JClk4LnOq_xnI zEBv7Qw(%WMCtNXrww;$6$^vj`Q}%Ti(_~ zCdnXVFo@qSFWl{kq_w5z*B)~kwvBZ@0Z1Yw-PAZ7mQx3DWxb+WMn`nf>wmREz=E1u zFMs^o?twi=^cgdX0`%Z^*GcGaINA6w`vISRAgDBm~a8e;nuMO8Bna; z)g@4_HjlJfxw7Y7zMsRo%-C@uil0 zteG#f;bg9~=1z5xk6%?&KP$}$b~+t9$(0`3%A;|u#m=%Bp%ZPB{8{7}XdL-u*W~%* zH`Zij@YjNf96AW=Y^8lw@7X_P_0&A5!CY_yJ6_Mo7*=_s|7j(d&QE1;w)Fywe%h|5 zvH6WX*c<4GAkB>5-BpQ_7dUiS(UCmL5{w8bm%73sN@==hD!C?2H%&i;GS77X-$V*#Nh$cY4*fAvRztBI zv=USy`Dabj#>jts^p5O+*T}5dVjIt4F>D+St5;D=+zxux)wJe?R1-|};SdicbgtA} zcI!wSkNcREF!-QE*#UKq=7=A;g^2S)oA*g$Y}!*pOoy(ND?VlTFOE@b4)BAoXkO`D zfN{~I$NvHv1$o~~H3Yuf80SsLR32~d0t(H|^?LC^}zpGe=z>LgAT*g1hKbMhOd z^Kl#dw@Jk_spma-fsh_f$Eqv*LVfga3wvxIOxDvCYVulQznSODX`$-3o*7|868<4t z1Vsw=&A)=MS~bduUwrQ|74?U!UJ6URy`$ugv(%(N`CeOJ6lpl7bj=^N&LP?$_o~y) z3k-{%PRpuo;?fkI8dn7IWzE?KUNRK6UCk#W8%AY!NQHz*aDPO2MoZ=&Abg}9oc0Z( zuHIonrd4H8th3;JHBi{V$)gcj9H_Fpl+(ftSlyNC@~>84O|xp`6+DY^rM9H>2Z`bT z)To|f<7x<2nme8drR7_#xx@1xKr?;PNHsXGG0#Z3)Tz8E#pNgDc)sg2O+8Qu?dxUb zuM(Fs9cKDuT-c%D1Hu75*4*y3^7VKL`a}L5X3SMui~7Kz`Lo)J9uXbb!0457G6S-~ z8YM`}JEW{wJpx)lRzKU@FLO)})L-z6OO+KRLEKh)B&jW8uS1?);_wLa+99Ik{vzsY zf&<`~Ch!~>;((8v-XEFfy@~h*u6`@nD-GPsr+tcU1}krR#j?Pm*C){w&Q0(I`z*A3IGD{*QM(^R)D^8OW2#pSaDVT!G#*Kf?nvL*{sy{umnKKF{Tmh{i= z%^QTgp)vZ}VgwcY%_movF!hY~<573FpEdXs9Qxp)Wch{eqBnLe&`%lx84E4Uo5;zB z8MQNijd3kS)Nvbhk>`h|`BoM6|7tx+$%rLctiJ|xsb)-z5;)GtQYc%qzzfed1SOs102$f~5Ry0BJy>SIx|Qpx7B7w_F(3Wbap^+UCmb5kGY}(mtbnC{AdE$Ht2xT zoiPz|4q3ALH{^4@8>OiaY@a!=o~cjxME1K>oj6)6N@}Nb$ixuY19)rPQK=dt%3q+% zag%#qXsnm0<8I!R71}k#H`KvzkMd8NJgZ;&29{cs<|fU`fPo1* zHX@8IqUv7t1T^;%nE)G7adh>H&j0h7c_as0^ z(VL5MnUrXc8_q>CH2_K;kn}jC+4o_(uD*_=S9}G-8wQVDT$M$wgTWa zRF3sUJs4AsKLeQ#(A0tGk#s>4_aM99IE(E@#aV!d$6bvVQqCdsd6N8$(zD=YAt){k z1{cgUcV>0DD>-N8{EYK^4Dml-O!rgzf7(zPMGX0m0Nq%PeD8^jumGkc`o6Teo;OO^ z=+5Kl)TDv0Q3?IBzJpg18wIT~gkVi7gPEE+f%vJ0AzWVpClQugmrm_m=01u-CD>gi zk2W~bs)BYg8f%A8^P*z=PTnhC8xGdL&MR|8Aj+p(P9R_~MdW5xIAUr%}fT1%4IjZ2BKKjy< zqUYjm5W+f~u@6XH`@y;RTV#8@fw^A4d2StT7U=E3zyNXdtrMPTgK1W!VUb*5D8w|IxYmKSth29`iz#p#g4X{x@5 zQ2E8qi!z3%jB2M+iG+F#m-ACU)aqdbI04m*6H%=GuIQkT?kXFUm^O%J)v8A^iGPQbM1zMC_ zMiK$d%#KjNu`h)ufy@9n6SB^}7e?EL1O=AKsS0erIau_hDqcUM*4SY_WEl9qp$?F( zGC0V%u*VUQ1m!(9iitF4i6|K2Ma-)^K=^44U7u05 z?m>Bi!Y~qPspd^SV*Z#%7Gg`b$uQRX9W=1b7#YHhWgSSjNcSyt&Aanr#cP-e4&Z@id%nCu(?d1#~AAV zrlPMn>_OveNPaIA-nMni4TX42*tX-_qE<@pEpuzU$yr`_<<3b%WBE%kk1tlH3f`%W zL4O9`tskHx@$`904&0pMYtjfLAov~UX0pl(F;CqvW6(&zXbW1IHk)NWS{@b>l7|`h zBDlYPVj>7cPo}*?$}*i%mL~1x9tGxS4-+cY=z2E#H*>Y2eRoWsIw zxu=&K@`8S3T;p#$P!MOuu|leiAm>}jwM1tn;M;fETrV|9=*x0X$!U~c+K=~bQMGkkl@&&|D*enlm4+s+SOhVqj` zhIeNn;A8Z+I+lYSv<_JOtb}!p$n!;R@&vMd56gTBIDq)I8NQ+cik**nTTht&(JYhmvKek3!E6cC#E$4C0S9UBZln{ytBY0hW9L}opWi*!Z_aBlfa! z2-x1tH2$Ntys;7M>mOn8IH?VM@Yb^6e&pILJwO7R_O5#OTaB384vRb7?VgW71%O)+ z`R2|`2Z(8_kZPyxNN-c8%7VYQhMQYYbrYO&_>dAD`#~@}NwDjl!?5cHagnV1E)L(xT?^QT#l=_yCMd0MV7;%F39*NPammgX zc($R^7G3U5K6=`%JV}cEbgPOAsM$Dhh(Rv@(<4%xB_AUs#*2$0gU9H$EWn>xx0`oQ zQ+Bdy`>w@sNEV`rd92+(-Tlj(@CK3Bg#(ZW7a>yvi;-?>Zg`KMpFi@Q0x3s!63 zQto;n-RVRw9cx5A!CLm#BC}qW@-kY`dzNdJbq*aR9_$Ie2VOr>1+wR37Bv3O9t%-b zl3Nv3=nl3XST|&2<&KOt8+Dm|N#>X7xs0hmeWX!=1`?9)LxEN(`k~?O#@o2#?e^aj z_?{X!pBMC7w%n?ABNen3y^i};%LK6zy|EP2-S}fzrlA>g#JMBY`7)r6jsLl?eOI>_g8crS^uMDh!aY3dll zt}J&AgtgS93{QnaI?oyxtL;Y)q1!94x!wWm@K z#k?NVh$iykr#CzHe#ixTn_Nf+{BhDHN$m}mu;#WNg=8j9a^YfylX%)Z)OHQ*iUEKT z4LB|w7%aZFa&8vN8n9MF`bj$@gV^nR?NV$WqXKK-bM5c?EaRy0hXx>S!^et7y;<^Q zq95x;rQDNn-I}{ail57fB;yohr3W^pPmx#Fc_t5%-rPpXS2c*`P3JA0!DG0uAi%3$ zOVqq2$oaG+1K(HWbZ$9=6YS9aoJ7k_PV<*4AnN8p@m%PLlS)_ZagB<%ELCFaGg>1; zro!puaG9X75$2Ie1~6n$Izv;yCK-b$B=$JO08j`EUuy{C;$7=)ahX9dMK9Ua*Er;S zmdl6cs>88xv}r@{Y0!zbqs`GrSCfC6-)&(+J&7KGa6fc}GR*ZtE!?2j4a1i z)vCL&CkX|ngW?2voyaY;^OM={K8Yq5VrAY>IBy_pBeFCO?Qd{XI8Z?nbr5!oqk`G8 zT)I0C=_r*U%B4l|d%H`LQuTISi#d;pJbl0QQ)hv7s4skZD)r4EYe02bx(x?8DT7)c+nmL~L9=aF|X8mPUiA+DFz z`D#A=DG~qO=~JKFR^`QPL?x|s2o3z%_Cr>)cX&PNQS(sVFxS4gvnIp!*aP3b z%DVmVUyiVl=DBXin5P-*LKCWw_|tD$nOXRz6U=SO4%hDFLNBD=O5DAW7y#hUA7SEFO*4b1BGiGm9W8dt`Ay07zpvP!)r&_Ku7V}?Q{ffU& z{StiJ*Fr`x6JFrJ%{tyd3XCzkLNa$p_bjn-bR3bDs6u14x|H5_a|BGAN4;bke+(1g-@$1AS}Jt zv3onYWvm2gje4AfBl*IpWZ_H!&`@NrGql74g?RwB`Vd=5w(`^PmA225uBK``@g4F28s9IM-RQOSmj!B6X& z`xL+}%tXk_Ch2R4w1=1XdD^;zQF$%gx$e$^)})Xxv;5azj}@T+4mfdHHHX!MjqXPNuN3$8QI5-Rwlwc&I5qd1iTaEt;Xz#_Y*E|GiKEnb*_`u22M9je zAEFixB*~2)yC5y7+tpEevio@@f$PC<>zMD!HQS(jjsxjHW%FVAjbP+&wRZwF!~7}4 zK9KaK>Y0N^*?y)0uiUhN;$RJDW3%0ey$-lKqU+Y~_ z%$Co_! zj6?{TRlXPN>-(w>WzZ@(GHTQyS|WPsx+Uf$;HanvRY)bI>Zu1I@>A1V&g@E`jcR+7 zMF4YPf*3mWtmON47ruHVQrPT^{zl!c;239r$-2w`PO2qg^BMykGW<+RA&MCK(v)Pj zx`&}R`)i$qpX3`Wi|=#>%aPvy?2+=5dG6EoVPn=$1bC=s+Hhk*zYT=BI(w&AC<(Lq zD^D>>@^~1!J=Bo$HF^LbmWex~bx->p`Xj+W7b^BU~hsPS-|4qq-SNw15rS}s;3Qtd9{3j@L;Hj<^ zzTKVePc%(b)R$)Sv1H;n6yzKUA53R2qtd{6bUZigDLFF584fQ7V#DjT_N)4LGRVRr z14HpDuo%EyH1(_&2xd!h@~1-lh9S9du&tgXD|m$N_3bLC`B&@oBR*JUhj2Vm+YU&z zA0CE=ud4B+%)s!~qHPA(9B=8?$Xa2@-ZtRl{^H3^M_2F9U7?;6J<^mU)wN4KK^HKa zlZ{hd&kth`gSrO{Gr&M>5&(ofm8J16)s(NQ03XFKp}UlIb}Vak8pq%ok*{0dSYHEB zM-q;H5Zw<$^*ha4HsoI|-Nx;xTWS+NAz*1O$_GLik)d+Rj0ITjVqEsex7*A&JrAnC zUYFldz*CFye`yfp0##`C=8Bi+F&ipgV8%Eh&*7Ft>D@qHl*G4ru$5n{^&6Y1gKrzC zx-gDGyITnG)30*0>p&RoOCRiuDX_Dzk5e5@dZP&Qi)?9kH~%x+H`dLef{N zVFuvl8u0Otk8&hNtx=OShuxS6;T6U4GBCyp37{kFQgHQ#MabhPa`rF%VFr#kNB#`j zoZkbDN*K`{uHLYBVKgnz*U*|oAYu!LK{d#M@ifdHeN=#fI%*BdG>G`{`02n;30{Ucegta*7WnFASN z1_^EPldC|<@X)F#92z0=tlwSvSL;W`MWitjo>H=}ob*!?PO^|x`f>u3_a4cCm2lOe zGq`k|uTSIc$a!Cdw#rJ?-+v8PpejFTl=*352mDzY?=~&`mz?ls|Fj7VYc=}NEh%$v zYU`Z;#V)&thbvX+{{SP#i+A#9GLu}NYm)%)n>7laD(ulx!WgBIyulV^8{8084te7v z7+HX_0U3eK!tiwU`Js2ipiu$GzY-&nnOJ@SQ!)5~iZ$sqhTZ+(n-Y;`2iyT(AY+4F zFf-t+aq%vi|LX6M5Od2sz?vRpbF8WF;rM$eRGM)ZzMZ#8Uht#h@XgqaH%Y6si=iVJ zFRAIh`1c;{>={VrLzqinFCAoTjPbU9Y*qBoyt&1sjmhw*JK@1GVZ(ezpsWeiK}DB? zb0wU_-fyX9$B8G3M*%;+;qqt0X#6BSZE~&rT9V3t^_x$0-71oBe+_^&pC#W7x&;Hb zuFDGlIPfs^rJe*3p}#-j$HBR3y^Ue0dH7!wFkt`ocK5Ysi(vS45x@gng~GVmtBb98 z4nOQ_zVgjAxC!ZSX$$CoHSdqXQ~`3T;~4EJ793p#HWVt&D zfePTh4vE!azb!68sO1}=@t>IbIP&tj`7927Qo#1Sr!qsDRA+pn1*POS9h-bKS}2u*7W(kf2`I~i&PvaE3E{v zvc4iiX0%pAq!>YDk18rcM9PQ|R{Urcl|o7=q(C7mOJqd$3Z%-25izoYfPjQeSP2k9 zR(~h{{%S8T#N<7m=RD`!_v`KmRPS7WIGO4l=RDPcPLz2gpzQVnGdfdvI2tf zftdxh;on-Q`RY(RVPBhtBK(PR$U|R^Zut*V6#c_grlnoqwnZVa<4671jL%tr2KlM1 zk+h>MT27v+zj!!;>k1oD^9!JF;e_S)PIgm$C@a0USjIxN+nQG@7z<^NY98*W<9 z$eV-R#qT)Cmt!btf5$S`a<1a3xgt;Syyf_1DCmY%sQ?TuFjtv)zNU3fS-TsUCt%%{ zLDmRcw1OtNH^8_AmZ>s3*$v+t$6C}faC!rD+QziEY&r>#RfX4~H49S*av|vkJX8dH zS%}WE@dLvNMke+Xa|MdLs+q4Ao1VPlI;uNp%#7D5c#JURe_wZ8VjpWOiqma=F!Fsl znTr_S*dbx*(;ssjy}jfD`-KV8ySDHP^7qYKdg$oG`P&{u{9O&!cDp`JViT}ueg_Fc zaqEbUh_9VcFvxrsbzYxX^T`L^&c5EX`9C+bU4Fv==5j4QKs?B%cTK3bHT6Ph$9m{| zX?@iU99kgLkcNe2}xG5&flk~xx!jWByQGXvB zUg-+A=MF+I%XoV!OCQm$9c$t22o%Bre}!8JKn@9G*iCb_nP&fpr^>^_%g#V_1i%9b zKp(E{Z?Ca3IL0jT9UL5H>7^y4%7RUnoSJ*%nZl@vL|8f7FkIA@K;IwhSE}_7Eaf3U z78D7BY8wsV?;_ba5g|QSFA7~+&Arvw{VXBX1jyTH1nA&?Rd4-mj(RP{j;*KA zG#6BDLLSGMR-h+)#_p-U8KtL8NF?yZIolrF_(hogXqtWW#6%yG^loK`>=XWQ)>A)E zxc!Aec2{AVbz(-TKNgWUG8dzLP$v}@>U2EG{2oI3gL->{ru@=GjQ}=&h;{2c``)3$ zZl#nn@B~nrzJh}e&1?l+T`k2*kkNPF2+(e9O-sDK9T}hvllcySvGzc{c2=; zUDq1c#D0}v$1Y^S$)FU9nRNUok2%lw^2GSeVj$PN}toSdWNWO4u?kFOhzN+oh*-8|2B569Z=kW(v{JTr9 z#lC4pULQsG&~$lM60B}sd+|de!p;xJa?b&l+4XP-wv0lyGxK;pmA-`tQJazH~<2O=nz zZG`sGZoU3s|Ky#ta>WY`^!|7d>O znLr(oZ>yAG6f_Towk1FqB#J4D3jaJz&uq_^m2#D@jCRr+qi0MJ8iB6yHmNPDScEXI zh8U=5faTzuxTLRIxIZCR7EAI-qR+&&v`MOZ^Ok=hAsd~`3Pn6L)qg1O3Z04<%#A8C zy+C54aw+|wu~F$nGEb&`1bQK8k7&lDYR9d-h(}uOFun=HdWdf&`Ewn%e=hG037%hq zC~E<-)VkEJ|Fzw2^3HmCrxE-;2yg-x=1KL6!NI81U!d9vTz7)gIYtppd|*bd*0iz^ zOoWrYEfOZ(k)Lx?@&3yrnC}V5l|q7HHzeIb!Tk%bbeTiK7qm_LISLb5M>hYoa;_HI zds)x@(=Ac|&(owYB-!6Ie<`og8L9Lr~bm{L4$Z@XeIjT!$D z%AAHsGr*=NpVpIE^%u05_?}jbLQ&`s@yu|74olJj*|YlDeD9Vq4lR_sLysacau@o7 z#Q^~mJ}VyklY;~bPA{iUl~mjmY`&@RhNy7QlK46f5?4}ONF3zI!eVi$+|({|5guj; zWTQe*HlQl}mULE*zIrs7DFDm!SrSZ{uR~;mVu3$8O+C4Hs>gZ7fxHti{|52iY1z;@ zu+KWxl~)UsHD{KPD4zD&`BP=w9N^BlmNtekTG;7fT!!jnD&i}yK2d&@59QxFOl9h6 z3EC!=qW=k#oZBI(3qbp5kRlXa=lcjrPh^cwJq*HLBK>a9I=RhBdZMYeHv&=RIU5#; z2ukf7P^@)NcHPxL9iGEj!2g#JwF7G!C?ChkJ9Obx@ChoNj-xZj;CsQqmghY@+PYo$ zG4|yv!_}OCH0?LA(*YYGL8$VP4dn%pL<=*{HjUNUU~)0IxGiBoF(>L`3P#ugPtoRi z{Eh6ZuvcBO9F5W;_kjo%F4m)y2OuLSKWfjp+VTbt7$<=rN)1J1dAmKb(;4=Rb%6M0 z>wJubkhv6Mybg$O_yzd=xstsiga^Ng`Or)whPUw39H8P4LCpQpRb$ba;MJ@w)XP`F znztE^NwDI&Y%1UDH`Ap!>RB1@A@EL5PhaIb&lviFOkM|hq&{lCz`aZp;gq@Uma z!yZ;O)Z7dHlCy8-cG^Z;cWT3=GTG9I0f!#<2Lk&m$=0)nK!;WX2>myV8??c@A)Ds> zUpMc2nd-%FT8+QfL`Td$1NA_lZZ!1|J49f!P4yo^-?8wu)6mND)$fSixqJ@$fLgWd zDL2^7IaxUFQ7eDn17uWC?th3qDsn7}3iHYiZ*}XM^ZmY0hqSOA$b<^BeMaD9%cK+n zE$1w0{wuK!g4ND^FIC6dAahvqW(*IQ~}osCUmT8l!v>mbeqsve6|l zl0xSu0eJ@&!6`^22T9RaZDrZGYd85!O;>rNYI=yVBPd!6LS6oR{by3nRqC?UWV88= zN@!UsS0{S8ku7iKCPNHq!-Z{s$A4s8=IgZ#S$?0X4?5rrwe=}*1-;<2_gKvu+L;W; z+AsfIw``E&{mGp`#NCynfZjGF3jepkm68|8&;KI~!Y9zpRh&-p4;2z2 z;*oVLPjS)ai2JTJSBb()FjqiLM1TDLv}Vq=>#@7K7SJsVcJ`l1@6v3B9*xR<;irL; zI2qmAY$JPCP`SMH&_>tzffQwsbFFRA8#4tqpmk-$Dbj0oU#fqhXPZ_wO`*gtxKyr2 zQLN=htv}}7YvNf3LmW^wq_xIN2uiSwT63dmk<8E zkPF2uf$Vm%AgaCRVxO@oD3^K-h%xX^zNPboh08Um<0@O3=Jk@{J=nTr`_F*XG#{4} zK5Ql3kM+ZGeN6Q0e#mmAH&l`qcJVg|;GdE-=rfi-eK#0I#14EL3gtNL(Ihf>!=5Yp&2v=G*m)*DiZ-|_hxxi_frir>8ePGr@P<<+|m^=8KUa!*gR z;_J^7?mL} ztCI!1TJ{(Y409jsh!d)aqzZkzmrq-A9yyT-%rYd3o&tYDIUr?7AD!II8%!^LGRTyr z32_-VZ@w&yUIq~KRnJKDoG0#Gjl1|tJ(!d4U*ZGYWa$@PW=&f7PyDKA5v=C3WoEGP z_Q6;g)atI($FY_#=5qEV>D;wmxPn-PKye#}BtTc`M%1kX(mkKEkDu=|(vkHs(xCsZ zThhUrsO66&6nYMR`UG1w%Xkn>6K)lv)F)A7eG@tiPzTz;0tglIh8RxxsvR;=>9~^) mL!=4LP|S8<7XcdZ{mF>=csC>@mnfdJ@c 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åvHc(9`HyL=2_(aP;|?Klb7AnYF)&D+d*6 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

#RjsT$q`l`sh+z%);nFVOo_u<^YsbAz_hl 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 K<4bk_`|y7YnH``2 literal 0 HcmV?d00001 diff --git a/bg_img/7.jpg b/bg_img/7.jpg new file mode 100644 index 0000000000000000000000000000000000000000..fc01ae2979f29d77812a329597a566ff668ed837 GIT binary patch literal 26083 zcmbWf3s_Tkx-Px~A{GT~gMw!5V@x9jwYtQY!+nid@e7{Z>Fbv(NMV|HqcBYm$|< zzRUZ)-(~pb@Xy@DrGF0kGba!TxFz_H8@|K6wRr2gwHy~5%z1JgHx9>M;jFPHz<=!D z@ZY%r&PpJl|1JNlZLI0v*fC?QZEVNd+S1>+m&U&|Zk*jXTU$GOJG=1{@Xz)o2S@t} z4zx}mLT{&c;r|KaY{$_V|Ns6C|8MT)ae_|kPHVwioYl($>z4(?mpFG^s||h;{V2;n zftB?bo3XegyYV>T>_l9?wY3#4)@IBYoV^XlxiK%>yfXEVi^fh0TV?y!XHI|m&;9x1 zroE@V=p25%d-|M&jXPeld-b)~-*B1n_Dqj|^Yj+Zo%im1pMdul|2c3;(9(#=sAzFa z?DExrS+h1VX_YF{mr5O^{wJ)LE*9ECyG?Xr_0Wq z{qFm7<>!B>x>S9+=E~LDA8!~MjZHUiHQ%{=ueGhcqw{{(qn_SA^W)#1Jng67CE%?2 zpEdf+{^@tUjNfI2XE4T=ewV;%3;wKM9%D20k7Hk16lS~Xvq^9L=|9Igy|+JKd-0`d zbHclw6Eis8AY>C?SiI>|O#Y@&I?RS0R zd1+p@+`cWyzK#1hKoTAxiRq>jJggHY*B+Q$Teq{PaA!~Y+^IU%RK@8#c-1NGLl&wf z=0kW>N5j|?dEr>^Xp}EZ_xA1g?LX0B$lb=p(M719nrG(uzv|ncr7zyT4JX@V`u5B9 z1$yOpw{33Q0z0mEG>kbIxhZr^wyRU9U@`u}3IwutLcv$)PW*(5g zmF{&tT;ddJBYP`T+ZxtND+MLa?)&|nyXJRQ2l{b|`qznT zUS88*+beQTaW{DtJ=%F}=(Mhq?c=H*xm93mm|M}55^+gUEg7m%hmMh6u87M$rk34U zD%_J$zU_l><@iI2>eVgDQOkP6)#Yb>xQ3jF;?(%4`5Mp4zl*MUShY4~$FD1{%nN(k zCzyUCN^-nFU)L^Za{JQIKpCDeU@eA@3=ce0v z`gm9^)!K+vZz{*{i`wOR*g3(DE4PW+p}H82T@#}+HQ~6WGXfWLL5uO??CR{w1>&vh zkf;u&U2mhL>ymkyl+&+B5Vb-0cBy(6ClE|H0viyNfODC55Y($y$hSlHWX)sYvN7dZea zRE|FwC0htdofVn8P2AgwV>LJyHPC+`eL6O~d_1_LnWKMJKAuZ?`5TeLOzwnVpkEvw z*4S-rt6QS0+tze#E3(cp$Qs47OPj(^$X{vzdOu1C$BVl>CF$8 zoey!27u#y$A>&$=Sfq-N+LO>z{;aN+n?2j}Fq10XjZhmoexvGXAMW$U0{kf7e(pet zK0I=hU<#hQmW1fv^s&9hFCk@IgbosR)wGbw1Eg<$CwH{qX!`UW>P1mqOCYO|t`iWh z^mi6Tzghfg)GiOJOU?fs=RBs+FR9e{`$UKnFUZ$x$dOVkMcnNu{5OzTH zR%R9xIK6h5J8Z~XJf`X;b;%%!6 z_p+h4>(ZR)5h>$^Z~C0ju^D;MzWu!Qg(b|=tA{xfKkEQJ-u18hA(l{(yyxb!jEShd zUnoL;Bi7IOCEcv-R9 zFXSwj+F$)y|AWmgHT{Zv=G*s_j-p56pAP8sOa_-cS}e)iJS%(a`p zF3Hm=z7uA4JbWA16dd-{JjLwF1$7v*;|q5{`^dJ~1cpbZ%1TEJ60Z4#U!s2bB;#NE z1k0rQ+%vPiNGRv^cb)L#rRMr#*cJ$(ne35WxlIIQ)c>_NRc{t|NA3%?sfC+KBZuQb zifpx%d<{wD0qB5eVL@*vr%2u_eOpnGn?4Ov7*5u39E8)5MJ9KCm(Ky|-t=j_{=cbw z$#dymy90LIfx;b`Sa?=DPj})X4@#V8?)SGPGslF5L?(#*k)Sn<{FCkDukDWnk@RUN z2scjl;q)YQ?j)Ee9iPjsa)OL)$k+intzV}%aW!;$Ag$gUUE}ITR}G_UoW94S$18w@ z-JJ=ywdhL070Y)(oFhnW)DkBMd3+&++&7ckTUdZolrO!h^7MhdE6_t^I4#LvgvtYk z5Yl^sZ1ZYf?69AAT(N8CIy5&brQJ7?2x}zxYLF6TngZlKCP&In2U(^P}-%!@3xZP z)#s|}@$n+NhTO6a<%DOo`rNZK)lWOE7sACeum7?kZAKt$(7wn|!)&-1GQ~eLZx442 zoCp1hhchO$1rv~^KUx4tWmRyr057?jkm1~dTxNP-)<+SbkaBwq@XWcr*oP=5RL&gw_WFp0@9|)2z`F&=y4`kMdJHth!>V$>WyJ@ILCVnE*@JtM1t}|n zdEos&56M#XMMIF|$aYj5>gR_G&-&++x>CgS#G^--LuHN=IxCcgC`yY(w>j8 zCu&!k4<7cJ!Qx9kN4QNq&d$q-0QL^GUBn>v>MBVz0q9+8OWd}x4Zt7fE1s++*HX^3 zkpcL3Bz;jld zseT{|lFRkTCPQ>}^2-gwoLcjjM_-BveUMOdkN87*m*INp&{uho^vc&`u|@jfdIZ=u zFWUw&wx@vjXH zW8>h*%Dsi;7{UM`Aiux`9#%^{FF8Z}$2-puXAGtRUy`|pJ!7bafC#jJq#Z`CfS_$! z0t~(vuL&?0K6?vu2GaulU%nn5=5I4G3{ZI=DH;JA`sgXJ^YAVQ?DpDeZMdND$WJ2= z3T6aB4_-DL#H#!L#fVO1>s;11Y7^sHawH#plNve}km&W~BS+$Pq)&&mc`nU@>m>th1do6!hQd8H*ANFmF*8Cbx8@h0zu~81GD#r9#7F*>3HVJstg1XweoCu|T&{-eN+!Haq$!W_rB&7Q|J`g2~zVpf%x>@iHn-FfT!-=@V zKGiW95lV+2T$hywNaBv<9$WsZG@DCM6$;@mkMgkJ9mlysAojO?O^YP_X8rTcEjXItEnJqAl8}^qn8d%quzfLfL<}7{ z(Ha&#>cdqq(;j#^N=O+&7@{%eX^s|>MC}DirllrKFZRvUczd(RF^(beE+2q@<+x52 ziP|{NCHNG`{1VT1J()8|u*f{ne!&jlG{gXrX*1vj5W6|g;1Rp+BR-@|<{_bud7){kOq2+~$2I=ln#q*fvoeeJoe&?^>OE>?26|r~Mxv`$+NeF@M)% zN7|BOTf{Suw?wxcY2!A1lN$ZyoM_j>jrn{Ci`bTzcfgv_*fSTLM}#Hl467Fgb~H+k zD_?qBQTFg{6?;`q7+ji6;5jv1K?DZZW^Vp@AaKRy3gM?i(-Z_|n0y0p3-7y4(=2#2 z#aqxey)F!U`veuQ)DtSFbyJUtvD7QF=MY-|Z9Cy2`$5ryhDs`|Ljm zR)1bUH~n478R?0+@)d>6ei1o=;60wX$$yb{iL_m!U!+{yFsJRBf2NkUvEW+UZ+ISV zUbA`9y6j~e{*?ai(=)Yt-Hnt}IUlN@(T82ApL_b@EYlh6d#5*8>F`d^wK8mZ3=J_36Yg_@46xwc3~SgDLArSQK0B4dpO&} z>H}DIov!Gi(6h#H){{E`I?C^Z@FMfSCzMxw5ThKg_(mJoa!eiNx0=L1yWZsT+n~47 z-ae3RW)zuj_x-3fyM8{T5YJO>%^Few$4E3Fy6SS`OF!Gnr6@g9e&XIlo( zA7H224qh1gM@$COC_(2p((3i=&R0wRmO~6#r}sY+=zbVVlsD2mdk5XaOA7)GMb^3- zpLi+pxBL1-ufiK^l@6z`B^$~V<@?=uLhOmGSfO`mCk;}%v3xzGA93G2F|(~QZtdwwjhKsdeew{Ox8 z!C3@J>=WT;Q{gJ6G_UzMI5CXOfBO7k?iamOPR@e`eb)!Seds@Bn1h25=X_Y2g#!gS zA1WQFriHzMr9butL#f=--~GJa?KD*O*V*39b-L@F)?eU+ zcW^>aZy;2dodB)#ZpH~xJ)Mw|^I>uCy6l_51&#C1$odogr$A@LL;w9->Cp{t1*GNV z(OA6Mv}o?PVZ+?o&l|?_0VL_#$D^)IKI+=w^7^p5VXTO73nNk16T*|@Cvr6ug_7!x zM4_{p8alr~z(A8z1D5Aeq+8q@OMVTi2fSCD8sPs1Q^2!t?^NL~;!cWlp^Sm>S<4E* zDFUPsb!6o_XP#*$ru#Chu*KMXPH)XJGHHmQCXI@0%I+21Q4G}x{qO^+*UD$(C*DtS*IuXOzIMq1{5 z+*e&?pwUa+kmkdg?V;mUi*%Yf*m~eSxt8nOq%c=KR5X%l)YkZ z&v&!hmG)QGmh7vK>8_6^@My^?Y-ePIh;j;3nhpv!rqsgP1q$0D6g}zFbvpHhy4u;Z zz252RNuOrWc;y&pd$u2w#I_otE7{5;zDbTM0`P2&1$j@Uh?M4#KuSjv4?^6o9#@u}wi1 z>tu1+(_v7^c72@Ub3%AzQ&w!_d^LrG*Pz0iCh=Q&3Kxwxg@zG8z<+ArS_*+oC55FCV2B9mkrZvV93xhn<5W}+V%zA zS@HSc%73?@(ZgC-W+-m;Ir7gz4}>5bHT`-y&IOL&wJa@HUF$nN4wEe z*IdnQnv|RtzagRg>DkN^b9r;5%!z^GXLhtVRo*|A*z!9MtD}9XTXLBu%PrnFh`Hob z=Gm{{$G7u~4)bhhY;m1J%U(Gh>@dq|Z#%!>elq&<>T6j##yk=adY7(b3#I*@)#S^| zx3R@3L`8UO8^7RwGVq$8_fJcQUf#q17@vaMT{6slJj{I`cevRcI0(pG=)=4H!CdV} zE`ZE#`3M(oCixudKi^Yf8&P^=k)hMN5$Nbb{n#Fzrs`03%hruO>3#u=bfsm1I?aNH zPrOcDe~708RFH73r`&ID&-b&itweBP-v;~4LQB|NGR`49V`HHvDX5jfL?f}L96yQW z-R2)h0xnb_k;F=~+kzyL7%YGcG+u{{LeB={Y_CI2NDI2h8%!BZXMKLc-_HX%nuUI`Ua_xL<~+ypM$M(ztu4+;R7&?t7DWe3I9Do`%su!XE>W#qk}wc+^~$~ zp!DMnD~A-3vcEeC3;`;`zOJ;ZRqCiKX_;KtRLaMSXFF)cQ*jrHkYepM5mB_SYm-3R zaXq%XC6dvpxOzkC^$r7!AELk$-okwEMnv;FAy<>HoB(jH2c|f{f=1hUOj*4O z8NBwN@)Id*VCm5m9}R;bw;%h7hiXa-2!H?;C>=g|=2l?Y!Thz~+#7Oe(iy7W1cuq4dXE6bpTOftk zlP~?DkwF?hC#1CNu0pWP>e${oc4p~9wp07iH;SR6Q#vvR02Op?Ql?<<=XoUQjPB zW4B=F=26##av7{GyK7(N9$PV1u1Z+`bKl=^Y zFe|#ssw`Sb=1;PBOa8g+m#MCf)(G-YA;>M7D|U5#8OfE`N~@5cnaA0aEwu~ zd%+{VbX@)PI4E^+Tqh*LRb3I$$J+23I@V1q+$c4-ZF0HPSYx^oYC9VR2V_FTv3KW} zTpI|K!0E!4^j5x-9A(&A+@rgFNs-O1GBqPFcUimKZMva^EDr}`a_XV5OK%irR*^AL z=EN7luBK$=#9;ZIhxHj2SY;=;!nlYX9kkI?n~JO(rZx_9_m9P8#FRSdV@{4IIfJEX z$cT8pZ6Waf>Qjx=lnyL^L!j0xhbU@To^o<}%$&+0f8or{eM!{`#j=rv4^jqZ2(0Bi z1NZX=tB7b0 zp3!ZzFY!`}!492E4;{OYgFiC^~>-d)+h zZl&mnn3SnvQk9e<1IJAbXPX~*ZzIF7yL0)2%?bw|W*8B4pNCB7^Z@0BK|7<4r6D&?@igU(XEzET__i=m*B5F;vYkNh^ZwmS{hXXA9 zhYewul*0@QObF9%pWV#b)G)hJ8WR43c^NS%z%YbQ?(&N&gN`>vXAHVkr23BGk1}9S zXaDnCFT2A+{mvFyK8WiQvJbRa6w9K@Vi~i-DQW?trv+>ZZV~+!|H+3G=G85X8jOwu zMq3}B;=F9bGHtt|@^b{o1oVIm2BWwxx>nw9-Lz&SlBO?L^XKDHZc(=3I_wz4oKag97n4-{)t_kPP zrDc{o@Tf2bHfo`3f;vDQY8yMKCGWR!nESi)%*TMp$f+4%cf=w(fNk=Iv^{U|B&5PK zR5P*hfkwQQY z?n16p+~YyOA`V9^*_gTCF3&~UkXu!MJW6t=wv%#LKqX{`*1*7+Zw4QaB1)9vG_WqF zGay~IC^m4PB5SawC25%JyLO?j-mQ#cwJ%_!Qj^R-F+xVl-L=3ci=}xNJP~LjWH6hM zH-NycZm@M9lp`oh%cCd`(cCa+$t}dMJFT2|-;=e`&1q1QblTTta{@CH~_UO(Esl|q9Y`aSefcBct)RL{b+EOwKzEl%|{aJRgDq^5fLM7 zkd|m4)T~im<#NUPM*Xe&k6hLp1jeLZ@v40BYl!IcWj5TFT<0OXzL4=vN1nu2O#7iv zfP25QVp`QcWVoVT;0|CjYBHuGV%k|Qa|l>*`HMvV$%^&DhIOZm+vSNX-p)z%w?XQ0 z=QVZgt;Q?sJNbNkGV`xSR{6xQH;i3L3?68RwDRXfdj^Zr&j*2wNb%v?z}Y*7mk|XC z(zFb9LQQH@24xr4RwTxWcKy<6eRUwpULyOisgzH;;(cRUJUuy>micF-_eY@!N)cEL z*EPC_<#a^9+ZSr_XLl+Rbn-<8)0yrTt-ss#JM~gX^m+5vl}Ls3h1laR{pvQQ zsl>wGbXPj0Kp>{T(lmQBJd2>oxmC(w3!;B>Cf8TzmvCmP(d!F1n)h6RY8@YBYUXO= zZa=OT=a73jD9}pO&}mKd3TPy6L_Dn83y2+RH}8R|8_90ejbu0YWCy>g9vs~=bIB<# z6vTbPjM5s$tCq^x2A7=dgF|XRo@AA)Qh-B%(=)l7NF-96a-p7)0N6HqA?HYCzj{il ze|RJChu&Y6K;uSM(G-OMF_*B@t`PR!0k1AtW`RIFLsyPZWN=0ycnPV^AlNPgKgo~Xm@kkeOM8A z_lw=bT#4qxZnd}Ir>z^WUDz}BTDi>r7VY$JSl?;AIUU(5V#5*c)y--7 z_D5pM%1(~M(RU{MWC8gKwU(kbu4V)hn^*~DFuWo-h6pf~MU0d{gx{72?h&@5w5R79 zvY6M9wu~<;b-c7WtqbYMPq%x*s<=OjdR}j$N#d(dWRqiA^~ofv#qzqs>Qo-C$kb3lx+{Bb>))Lk;pPVto7#RyXMSagw6M;1S~^>;s#+HDD_URw`<8-Dlwok z;R#62Hp(Y7ZUkyWe)hdLN>BseifS_;2h_Ubrc*UB^pTN&b(E;(Z8-o)G%Hl28X%Md zS>p9rRCE)|1TK(|+Z!{;YB6J?9J)zRsg4j-B(XZi07m`XBlSmd&}Q#ioY5)97Nr4;kavO*?!$r7`BUL-jvJ}>c3lms?Xb{Lg)To8;9 z$t6mA!I|`cGe+U&LBYzA5U7+Y9}@>HDWC?Hg8d_fNt7u531uuS?y(j9o{9oAi2zRp zDhjkQw-I(M4+*qbOCiKnkU`OHZAY90y)AN;colM}?NYzCG{r(thgz7G3hOF`9;uwk z$RhmeI}hOTyR8-k6Yq}Wpk{bF@&O<9koov!G0HzaSws*69|WYMO(e5m;3Mev#}qZ4 z9LMm6Yt7>aVyJS~&_9a#0LQ-mgLps>(rk(rU|~XS>Q~2uGBt|spExTvoRtGbv}n-) zpt#J^sF6~ZPE}#YH@F4CUU^^y<>7`B$8bZDRqzMlVELC8=%AFxvr(qt|4Mvv)FFu~ za?7FYJlKX9MUSdpf+VJ@UPv2A;!--3r8ZGOyAqiv>YL!pAyESl7m}YSTys%8tJ6C5 z!-rr);#$6KIUnqX5X&#|PV4KwUiMoQ_D4P&0u-^X+6Pj+;8s)6@mt1txqX@o>_kvv;_`{N z4Bs$w!9@UkP{>;vkjPhm%^?KJ*D*zq0pU}O)^2$E64b^R(@+nsgVzi;qMlBZ#+Oo>(E^2`{#i?#0b2XBr}HEW;|(jH)r45&JJbB z36l&t-`nn+F_`!y)OI&PkL>oYIs%nR#=8h7DF+U54ljjNNfKZbmcXR9$cr(WW|g{07Oa$Vl0{ENR{Dz)A3207()$c zHezFJ7cor3cx*J`w9bjlMvG7kevr3Ead$mc7SE*WKh!xurVlrOy*~L8wFMlK*sp1c zHMWAZ@qZmxYuZ6(4Wi7)~s3aH1+^KML_a;88c^$(!-m)2at z_%{eTQ0k5hn56+@!yI}1bl;GgeLw@loZX{Jw}7Ft0k6ONN*w$3-I~{M3KelrX73s;)&7hs zw)rg&>;E1IjEmQT6-b$Hl|H=DUaijx-8&nBQalsQD;_HG@Pg%NsaxJo2C>ric+X&r0M{RjXzVpi4 zPHlH^`h`$MicxY{j1tnE?gK(3>lx3Aj|4FV&pv|q&Sr#($B>ahWTR|-Bx>5OqA(vz zCqXmN$gZ@^yLXJ9tv;2+=Kbgjn2qFDMht9BCih?}!zrJ2o18)4q%VNoK`&q%JAXv= z8G)>42#07JfmYat+8aHtt*Bk69G?{&i^dOfXgZ};PcpibkiY$5b87VE97)_ybD~i~ z$8C1m(6$LKKF$qgv{YER2xG=;%3 zd=gk%@Zm#$mv7QkHi@^4U$SCmYD73%c$8XfB6P62hx-I_sAY?G#oOqW99%sZjDY+B zx!lUV6_Zw=jF9|f_LIt?<>LKM?&0;+R$7VgO zCMQ^bK*SxzPy)VAVNk9V!?`|-T}Jmi$F^?dLB4>;UO!A7st zU;yA_l9n7*bbAhP_)%P|mT`W)&v*uyr59=*SCjcjK>Z_%Z222?iV?{KDC~nYp1P*& z5K7+1r_@r#9@Gt4`d+F4N6zW!x?*%Pj-lrYCB)vB^^qMYlO?Q0y{v_(7Y9*Lbw^A^*G-4uHp7ti`Rq<@-%4O%wPr4b(V8IAM6Pd` zvmNG6?yX2=u2Lswev{uw_v4oqmzu}Us91e*u+!S$>owpE?m{E#&cJ8A-%ZxcFU=)U z+TJJa^1jw5&?XKQpLtXL%%=ezZ;MzT0T;Gx`@v>3-bUz3vScc1BlT-TYu+Z8yyOl5 z(YsXd8^$_D(e%0PDs|nlPNXY*U(!uqwpBAAV=Z}?+0yG0zxeyYsj|A|k7Vw{T-{Rc zk!*^rZfTF~HQF-kU7GfjLLL;f4^1m|Cl$;ymF$LZJVdbfcF&FOlY0V&mh-D9fnCHG zzY_RmzLB|0s)v;s8Z$LEG1O^PSVUcO7CRULf62TgtR?LtTtiKs;fuly3`bw$KPnE* zOe4^GbXTFJb$MSs^`O~Mr}ztf0+xO>YJ+^CPvDhclzOrVi3&c+_aJ-{20|uMNvPZ@ zvq^<{Yz^J)ED&xI5w3+RBg{>achH{14 zh7D@NW$WQ(E$u%@T)@GQm_NtN_E*pPc;)3pfOt;?K8OSrp|l@2qHySiuw9fWS^mwI z?D{HU5-D6PV*R$hqS#8+sClIjf%~|30Mfmjhm&}ENOSt63nM|^S@y=1sN2$ zKetDyibbV)1cm%`XA(%MA{mTHpH2$Yh=7GJLWf=v4u$7q>PGeHc*FD?AOytJKFRk- zi7a8yr8(4_v?Ja7P2keH9cO?dl}yW;X2Jy(0G!@?58PDV|DWOY7fy*1?0r$_X{ z24~if0WBpr#h0@fHSlK{B2~7I>LIr4>pQi*mGl;tRzpsW9Nx;n(kwW<$={^qKX-V9 zGSPBAB@ZHXn}L;v)}T>q#p?LcU#Gm*P?rJ zP}mpwjX>J)cbk2W@!FQfEq-ezTll-pV{ZuI-=KYA!{06OZiuVNB zX~w`AwWPA`#GQ|z`ZYO2%eSLXbC<_aSPC?_c2hGFHC{v0U_1hb&KHvRvs$`0bMGf+ zZ&|y7PFU|?zccCj^ zq4`3Qd|+Mi;Txd>??!hLGE}GA z$caJPS06hBmZD+omyC;l&Z|8~y<=Mu__{7T#gCJ4*a1O?Q&1BD#h2A~5}F;|1ugTR zOc;}doA)7%#G*F5Fg#F-wvVR!eoPI~K;#K;6dxhR52FM&UVBca{!Z7zc-5=B)xv;i z+N)c#^X=OPB6tC7B*qDWs*?{+?TPQ}K4-^L%;m64HJJ~4U)-`SFHLyZb*MDiFefi{Fd za%40zzd13*Oy@Qe258Olq8Sf7H4s%g6xUIMMirS7z+;>xZy0l|Yr7@@vLlamV z@0>?7`@2Bk{OTJRw-D`Ne>>5vh5h{|@2mC6B2$VRy2s&ySPuttV;0}T+B%lfI15bD zc%cJ)RD~?fg48y9RO-P|UJO zu*jlQY~lxxZ=gIdxfUox+))X%tV3xDQUb{BP0N6TL5(Wd@7Jg2zcPEr`Y`G`@$*zY80@4b$p5FEPkftn~djowDTrD10> z{vS<3NR~r6^zcyt%bT#OvTuXejQ)!Ho+ggy3D_()S%xe?wC}uZTqxfWj)u$=q$y0k z(ccGAm!oVNUHBIN6FMdfVIp`=-pSyVwd+GM`XtXu&nRkDhc#1OMECLdeNfspw~WVl zh*#1eGuFf%N2FD6m%c!b$ZBi6?l#DgTvTK5G9T0ZA!;&e2HfZMpEt{G>xW9y{jx63 zk(Fq`h&(?M!XH`vLm7^|kAjZ2A!h~n&#X{eC6cyb$Lr(b<&qVD&XM@L&N$o%1EYrl z`I2%7bE4!6X*>>l5F?%Puf9!W?j)iO(*LbREK)~Tw>IiclR|ChX@cWrcQyA)@5L+5 ziJaMVklPlmpxFu)5o|KUOlnT2bO})&7Ul!Zx-V?eL26@XDxryC>oa-vK$;cKW{VpE zJ~VzH3i&9e*Lb;L=sWF0kr6R$=Z>!{o5K8GMfr8qR&)cx6_#2H3m{Xu}KR zfqw6HU=PumNaW(hn!V6UcWA_5QxYy5f0;oLxJ<0=tUB?n}U{~%( zMjylh0u6sy=#K4diNexc$DF|xAoXGI#POn^pH?nYmiFuRYm){+l#|{;3jYe&C z>JQ)Nb(tE_gV9q-m61uHm^u~6vVey96xkLe01?ITDn9B8q2i>}ViC{f&ZF8Jigmr* z$?eT#QSW>3d1%ET8v__l{TL{<%0cS2uCBJqfSL%$5h=f5FYP%edKu`}_TEdCMvx25 zpMVg|{ewcMxqO4BiA;Ocio&JK0}@OddF<#0&Cso^$)Y1n89)}o_G?1fh!Q?J^02NS z2pT$n4oM)i1v?@nod>G#^I8K!8$VQeA0#-pe<|wTERkm-31s2P3r`sgs~AsLh_@Y4ir$gq32SKW4}WCd@vJ6sEpye za?y{D3c@h|DbG9RHDQMADYs2U9HiUBs+z78E%S)4rYg$d!Fk>T7JYS<5Sb0?uY<^2 zlB40Q2KKM~2qu0S4LKgnak3~Xx)3o@ij9?WLbk+tM&SE!AcYpFIWg)AKa#2;V<>h= zs7j1Qj%Y-p-9{vOv%;B`%`Ipsp%OYyn)uaP66Jg*uUSu`labkGb~^Q9FRXEB_sKSi zJAcl%cVdgIjlT0GsD+WlC*G;6{gw3<1*5MhcrN;iu%tH21s|X|gi<5ddRWr5h!wh~ z)sK{kje<;t4gq zD>F4Bqd5D?550qHq_&wFnj(gZyL4YYNwWqVHH5j*cLVn6WW0dS#=eRJx;!ezgB*W} z6KV&dAnhE8e6%7i)UL?b)efFa=^%@E5QCv+Oq#(Opy$(%hISAR!O$cg&d^*CDpY2D zqBfaq#ZIMZCm=!XxC8FyMmAo=Vq*EB zPMD3rn$=k`K3X1;G4M-{51=Vz_|!@RKj;CG8@oyjGuyScIF06;G4{^VRQ)Bh!{->e zj=5kw?M^}si5|saa55agg-;mW$`YpQw6x-GNW#UDNfK%UWp>n0FGL|zynOHbQI9l`q^YIPK!V{vR z@&gaHFBvgUlmv|Rh_Ri8EV5jc9EleAg~ufRPH1r@(fmk(uD9*eP&gG*Ev5=BAo180 z9WO}e!^=&1tjq$U*QcieWg`Qf<}jhGV?x;%!j9;xBVe-c233Aed6iN53|DA7P6mgZ z#ne6UK0Y&P+>$nYWN_GYv`&vstMP!)fi$v=r(wp`Ui%5&~!HRZ{VXwXMBtUI#~)~ht10}Y!bL)t`rzSepZyF zQyby%XalJZ`hx*DNAYn?Bpu(%L{@iiCMMdoKAft4ntyWlm6U}^N!IVMaXc{p&KQ_L z3Xq98Ic)R@lgdw7kj^9#xg45MH<2Xqxp{@r#XKW`2&=Xr%`5ZZ#5nd?t@% z6g5GV9k}>}N`XLA?o3~>$L>Nf%4r22Mdy;Dm$0_}iFNu*JM4gQ~7`4akr3EpHb7Z*ur-)F1JdepvgKv~j zlJ{S4v;2J`^YpNhyr<7W)y~S_9s2f3adMyBN&M}Tz3K06+YCpZY@%YGOs6A9Bao5lnf(5J^L}D^EnSX>&6$qmXs* zgJMCF08@~v$$f=`r=;;+0#`Ux%Y+xpTc3|jB-BVbTK19T_xQ&Dpiz!xWSG){Ld>NJ zvMt*lb@R+HnMk3^_Fb8{|3seIKp7=-XRE#nU626=yuF_<^jn>Jp%R=_XP zy{Zz~d?3`3H11m8X-%!;LihdrbSP?3OaJ3QCf`RIh?1ez^)xQ%Cd$lOV$n7tv0v2{ z+7u>9j2k*;j~4xESi92Rn4Kt#ufn{GyP3Trp8Ixm4v0^@H!cZlmC9m;1XC`4aVg~p=+97k<%a} zvWJ+MgyD~NdugQKED&D5XTAFU(iF~=Ki!Fbm{X{X-iX4|Qw_F9Efq|TB$JhViIb(c z35WCukrKaJZ~CHpTOI;`N4s07kAXD)3Tf;Zn4idMsa;Uc^l|L{vu-(wD^N$xMPz(f zLaop$hi{{)*~i+^(zEmvY9Exv@rdGq+lTgltdg-GZ{!~W&+xJk`lxLh} z(izS)cbNBJ;1Cr+x>Et93ko2Irh%!OhAO9#BI>Jr5%qR9qKJs((X#36UvlTNxkVv- zxs#j~G__MfQv^S_Yxbi^Z$XFj0px#k?zElfh}GUk{#bUBKrXzEEX%b-pfnbJu!v+5zJn4twEc85$Wo7Tz3#T`K03hBX&ADbB7 z!=jUtro-~2JA5-S?}k(+urx81&2N0p8N?&}W@AY0V7QqM!6V}{#Sj{vjv#CX$>j$- z;v#@@AsrUovQ9<8*eBsTF9au`!gQ$%s@A8fFI-7U4c~eC;Z)@W31&+ezeIr%q!u$> ziu(jpFw+GQ3$yezKob6gkGdy@j^)85m18g$4z`nT|Fb8pw4}e(;72YYd?%&EWLug- zOtVPY;F+2G5dvX!5^nE1u}0X@)3{sT$o3?|N-ZjAip>|#ENEIqoj)}hUN=x!C#j?* zEC8}5H+8&p-h$<=Lzr$TU*Q%O6^K_uZ7;MlT(s zuegoL`fM9OJ*aloH|A(ppi^kA;ZD~ zcV2q=k{=Xs?u(K?T@!`J77;CKJIRK=fmP=b8@`7R9YDhZupao`Fge{wB4)H)h-~l} zhy#nz_{oi!TmlYy3BxP1B2m>;N`Mbnk4s{hkWj6)4i@8(L2Y77BRv5_zIwrxlp?l3 z@xPA1f@L(`U!k-_6L?{8Gx-NXBRtg&t4yIGi|6xfD7NS6T#Q$ynsVTVd~`#xpuYiC zjvBCC;$(UC4q!jsB5NbAElp>|adtjUW60TKPqrf)= zokR}qROJh!ZU2JNes(2gV~nyExKJHXZ((2oKAXn<&c z(XHtgOY)KFFF-AIRooCbUS##Sk<3>Jw%%nY8gZ0PUI#A&`!MKM%v?Cl==;9>l09pcSe~8HFaa6Tvotb4B#Ax+MVnU~Lk?6T|m_YqIiTu|9ntL7Wm+Rn9`UFv{RTP;sc0$OGsK zxUK1PV8TIN@h}Q36?;cq*-4~gosfh@ui%~APwA^!$*~R9r)Fk;2k~4E+4u@L@-`k8^4S!n& zzutVU4B)=Ny|ro;_WOT-t$uqo_Vv!1HLKrV`_9_6*q?Rl-&?9rlXbGF9Z?9PcuTF>W*l;JDqoZw%cp3w~w!% z{}+Eh_$4KXdgz;PkA#MWM?}WOAN_%OEaCX6)0{IYsob=SKmK&-^3PYU3NvqHWsCkN zC-?V)!lGNZ?-bvytg5c5t*dWn{Nv%H$4}%>70)_4ySmjqy?y;JN5?c;-T14C$$7nD zVbQp>Y%*i>T7_HvpK1M{nf*WJwF&0+7VN>Awb;B?y_EugR&QGK_Fq4LXY+x>YrkWE zu*37e*KPS*#&6OG>mB!my#Da}RBev?&`-k+jU1j5Z^>~PF zP;yMKG3?Nft>D^)p-%+GRAx~E`$0;a$G{4%Bd)1Qpftn@>5Ir>$N4>0A0{!-u}Vny z?DylWn<*zfUBRibal2H{he|o`U%$&6?I+hnn2@GSTha<{`(5i7Of(7;5z*) ziW3@Y431P7ExhVk$t&}g+((g@d*duVvsT$Xy7Sz*`W4*r&{O|El)K9 zTawMo;F8}wkKdXRB`2{>DPa!L87lj*s;bMeGCma^NBkU}wKE-cMGPG^u2vrh0Y^t& zn?sCc%*`M&ei&?>oTSLoDYkQC;Wh_x*UaM$5!D$ z+X_yG=niFuR0+Zb(~V~u>}wu{ZKh}ZT&wb!2M$GRL>a1oYg!so{p1=T#%cdQeeZjufuf=C!_?)+!f>v0ilO?F_SM&wB) zp8mwjVZgQoXk{vegK^>7sIncZ;u z1uB_KN@)u4h! zWGi%3OT`{ELhi&}!5PU$(&2u&{l9b1F(y8T1frK|CAKP?HvE#2rW!J_DTzzrBpvvC+XL)iHBMtskU14~!Wcul8W)ctqu16X=~9Du3YJcur3}m41h2 zssB?*cK7f=)8ME_9zj+u-(6q*iJLnN@d%qRhqh}?->$msb7=)vbyWCdax!RUR7lJ7 z)6}it1Qni*f_EP}$ZV2yHeMW4Dop3o=tGaUKpHvJ)qbmSzNR$g@Pout&emq65K&Ue z-Exb{lwfI`Ccm`17+lNmq1G6PHF^zy-3o5vw#_Sl!wPQI!?;qG&qtpt;&kCisX%G# zuCRIOr|z^boQ)yGW**}A*zCR)23ZyEQ_?IKuY zLx_AeWp_YLmd+N60~pnEt%4wm*(rS)#dvqwZYd>%%5}|vZ84Wr3?rrgNtwm30@q(K z{xQm}`GnLEZyWp*9q$;glFV8ru#%XDLmN7nS-^L8}RZ zJUlHh&vxOQteSEviM4`rK;x17SIK5(4CUfWi-U;E@e(ug@M4B!zXP&Yt1dDM-02Ge z5hRtz3a+O6x=+Z(*hWii5kA{{9!{lWrtL)5_r{O^+J1W3NWC?DkeD&Z%|Is^du;Jg zEL4XoxG&pwK6goVV>Z+_XAe^$4P}Sc#YRh-ARobbB~RSMg<0ZrPl}9p91VNd#|wSO zAX=r}XZI08s$}(z!l{`a$uR>tg)j?8PFZ$6?hom5(zljxGfkq8q${`*Y*7}TS1qI> zRf%z#t%ksEfDJj2OYh(=he(1i4G*j7duH@hi$Y@{RhY-W6s+Jra7!De-66K{dzLCG zc%)H3+3XLCS7uerBHBV2c&WZwLDov(F{H`n7m8a-b}@ZlTXiy(h{Ru9N;W}8Kkwgm z=$Oae;ACV$B7Mv>LO~u|Vl>12cri!&+LC;Xu~`Y6L_%L800_v-+R_k!BoAMgOLt1| zQdw(~skfERIZn4BK1y=QBJOwN@_pB9f74Nkk|x1SEW>KxpLb>TKgxZ-IEk1NDH?F% z)A!g2tyd#p%{b!q^VPYce%GWz!`}DSjR`Sqqk`nMf{SqxgAV zicjegLR1sja%c0m$%u+>N%&7%>Z;xzL-?|bD3pf?=#9dWX@kmHwAES-aS&ln`YEof zS#?iE$l1b65|Pa%cY807o+MxPLK=%ZCTQim2uulhsHRNjnvr9cBmZWbDj{iGE3?U1 z=}6p7ZRuv}s5**MGox8=m6@1Fbn{O9Jl9_i*tcdpW@{-#iGGn_T`-WxZ&JOB&?|T` z#oUkM8$tx(_xS7KI301GgF0AS9(>mRW@&U&(oR$^bcoDUr zFTK*E1`u}=0KGe_<3cX^p4e+QB)8Wvn&I)2WYc(f+KvB09?62menerEz|)VU8tyj< zLw^ip8dVZ<4xzu$;0S9?8nG}V#k+F@lqK9hr9#@Zip5iM^zLq)R@a$$VkVwXn;FT;MxPssYx`5OQ%zs^cslN$e#}DLOm)uFQCD!myPJosRC`)Vpx_PqZ@V)#cy|+r!5$O!`6c6icRdKp4Z01-pDtSZit@JOI(w2Omcl z(mj?CgGzbVe6lF;qPF(|Q4*I;cOOQ5v4?-XhWBA}X7JTu@d+V~kOvz~l1&wzN)fMz zw^}%Yeq=PkQa^TqzQ8b>%EumMB0o%=F-jFqT^Ug((I$n~RNDfp1_waR&AQAS+vs6Q zd;a7JeKCZaZBU>kN|>-X2KTSm%L7Mj;>DRSf~6OHr>#$|Psa^JLp+>v>|}OQT?$_# z|8}o=Rnr!CNbvWr0?WQfJVt+Dn`=lkR^TcGw%cKWMAo*Xf4iM@jcaV^DrmG&dNo42 zyT=|hrzfk;EA4#|6JQiu!6M|?52iT8$1>k!k^P(cmiwn3CQq18WGK>;ULwN@>>;`K zu~D!nX3j#zFZ#}!yj;VXr^|CkDaHg?^D1k-1{qSuyd&iX!d{p1qS+&IB9Cc>X^J^x zhA)UC$N1!hyq^nfgHY+k-(q=x&*GHw6^EAYKz4sJN*<;XH}jU>bPrCab59C5r3dl3 z@;5*>#n~*YM4_YU@v-IM{TaRSjr@U`5U!9)fZ=Fwqt^_(fA!BA&fD6@Y|IYQ+BD+m zWS;RnaXwgEUQ{G!X()+!hJFN1gM~XZKKoJ-Y$0JauakDgIa6skKA!1(#bf5r6`TZm zp(Q~p7BiFb^Ko)VyAgmQ70H$`=>uKagwn=yp3uNuhuopb%nAB5` z&ZLA&zvqmm5 zb!>HYRr~b0@A7Tgz8e0>j000-sfQ*^oP{^cPjG^^=67e&l`U`+B^;zbLQyCs=l*qS|v zmXsWP9ia)(AawBtilHoVOgKq*f4C&%>C2B9Vqzl|&KdTtY%V~8^F+1R+3?f#)~D{g zc1u010(2Y?d#cxY>=3>eRg$;;Y%y8;qc}bU8m6GBILr5T)3VA3&em!JoTf33L*A_i zs7z@atb}|>F!v#VRN|JCLNX6@2R`JG`yFelY=eN6JZz`#E{V6-s7Xo8^x_zv;adRA z>5`>T*r*vg^gWpgm9eaL1q;1?2M(TdhVvX^Ir>dc^+1`=x3;0K*)`%pHA~ctJcsOBO#ZM>P=E@5jpnwc>lX**UTyG^q z>xWyQ?eq34s;{{akeB0RaMXXQw+`GH6Ie~0-^TlwLwja6nb^EpvZpVxV$bqf=GyBg z%lEn<)f0{Bm*+gl9Yld8WDLXlg-B^dcvX*(b;qjQokweG!IwR&WDBn!9OoQ86uvc&c|KnlT^;qG3r&!NB*O*Z;_S`__H??(XhtAuUNO zC-eb2u*gi~*ddv^iHmN2O*g(@%)}?B-=ZyRgg}09)lV*RiNjl)FNW)mVM&I5a%~VSGvzk28EOEEMl{?r~#HX zDn|YgV&~n1_7(l5=M+dcV8Eyf&wEy%5|hy(1}dITZeN!Hi4bC&7t9xo4v=fY2PpPk zk|rTC%#|&wl=<(EE#)rdyj@;k%VWZkFXfXLWyX$5tINiv^NV>uYb<-aQVyE{UW?;f z7}hFbHAy0!@{t@hxD{m6J%cm=OSVF@^ws2(JTFNS6ffJ3N%9`01u)&6SGcbHprgV` zH6U9x+0y{q$XIxOsNyB#l0GJqj~XR z41lD3f2rN2u@{^v?hLb`Grp$I;$vON-lfUk_})GT_46Lf?)A|4_p}@50$o-)q9wKI(4!XUJrD5N_>4xcK_`aY@hVMUaJ zkf)2GSY&%_-@+oC_wq{3zYIq#!ZNZbS$KA+w>ej>{{ymM3QX9lg0O(1K+b=NZ;z!o z_aiCJIhGaNVrAEu4^tqr7poV)*r*@7XVXYnD>!Q7wIv{DWQ!a@L7$?S#s&Pdl(R(~&+ezd z*&8q7WS=ti`xr|Jle5hVqJi9<{(Av1MEz$2>;i)wf~VUY+K56 z?kmWI^#ioya#@XqHy3b-3C00r2gLiU)efpwR4Jr>Y8zxUXQC0^O7ce4-Y#Ce_^3=| zQ8<=bluy03mt5n*MpXh>f=6gep&zc^*>k)3MO=+=GT>$)WAR_W5xRyTmS?!GJPO^X zRg5ez3Y2xW5%Y+d8IkhKP(p1b#dv3|PPCLg54~n~@nqx^X@N57ovf7fv+!7r!I4j| zG@VZEf6lFz)zAT60wAVX`r`99Ec3dOW&BhH6?+UK}jO5nFAU8QZJAyn8S(0FN{cO?AW9DmW;;rS2 zG}vAJ7z}m@>7>Kt%;d)<_Jjx_tt@VOB&o{sRmLQI#!Kan7hbKjmxBCkD?!2`qD+qH(VM8TLvkmv-9%VetHyQeS zN2oe3ATdM!W{?eBbvU{f+9Qk(D!@)2oJ)#W92*&FC_d6enf4I+L zuN^3?t-sc4(;!nYj~R~c^|Ikma}98-HDv`?$<=bFK|rk#Q!!r_jjeP2y`I0%+Z>>b z@*^q6p%aWRr;nb=BYM}Sg!*S0%ZM$}?0x{ivTmpuZ-Pa-f?H2a+gEx@VNs?w%LXZE zEn^QTAljS1o=Gbj7p12YvMe(9QTighQt7X^5grL+sca*|Gk}Y<7aGWFlSJRqcx~Sy zQk04yLa*U724HdE84dFN_T!f-5`UQgyO7FCj5984_;k;aX9i@ZD6Dg@ON@6gd5JB@ zhrA&Rq5bl_w~%_WV(%1r<}=4kv=5!KG0e}pnsd{)$MPrDXA_85f%$dLv?bzB(2P^c zVrR)x+BwD)seY;y_JM45M0)iUTNxLvYPC?z052sFGr^?BmoJ+eyUI=yEvL_PP%W~> zJi}b*%*P9EERW!?|C?lJ4D`c)r)5?2A|3H{z`I(a&!Ev*Iwk>@y>)ukPJyim&9E%bm5tWx3xH&mS(st2 zR_f@+@1wYTJ%(drqT!gN5E`$cT%g>Z`^#Tz{N#=RW*EH_wu;<#>}N(sBA3&ftcOo<*(dJ7g(uoC!1Bf zsiX!X|NLit6TVXKiIDSAL+1-yQUI(a>1!Y%Q zzi-FZ`B0I)AjiJuakpHA7g8;%Vc2iqsrstUbAAO=pD!bnu8PugBkuil&#`Af_-V&l zq1L`wlN%^7D#NX9E+OhUS4Sb$xk)fRob-n7M0U#rfdu3aY68Xmgx_v}pQ4J|9y&BuN`5Er^Xs{1R9@LIWa+Q)OlR1s)Z)hOw9~_Kwp5eh#q}^3_uU>Z<_bzXiO4i;0@(zlV!KJX>nn<{~ za7@#cFC3Vb4gvu_rvDvsy!Q4Aj+Or&VcxB|MlfBaNVLcn5Hkz-ZQ@-+Fu}YG@lLsB zgBn)wT-sG$`sc+Zpahd+2LO0`c(mHaMIo`VTyN;sfh|zr5rf(v_o7+toJXr_X&-~M zcd%X$6v%K4QUah7&ruF1)G;Q0p}yVLl`MB^YliMOycs%NF7z$TsN49 z?KCKDLnCI{qGS?=P|j)axinXuzIIxwN<4)Rq3Y=Hh6Do0kplFA;BYt5X6s(+bzH#( z37{b#F^@YjmxZe5-9(FBAFA5IwyofXX@KtbxfFJu?z2)Y0#9EJpbD}{OUY!tY%;l? zYzCo3&1DZLoJwOsSnoLR2Q|YbqAXD(?rGJ_xES@p33Ffgc=1~Cr3dHosGv&b*r>^m8klK|mtb z_rQUY&(7@)8gjOH@fbP8Waq3Rce4X5l_DE!o&P^<9Jp;S~qyue5KOirN z3T^~4uq``pR8lD?8?z-W=l(>9`NhLfFI?ff@a$cy`^Ybu4>qtzA_wG7_b-bQEf&Oc zmkj%!OJVNzisi2ggrN2)E&JLY!Nz`elFS7`&4e{Z50}Fzg4pHYI~4rL$!@25z79Tn z{6+>kx0H-%{JWOpM(N96F4E3MML6mgX+Mt%R5s0Fq!do~l*C${|6cBsBB$}IL}38I zeK>;5byR2{Z;>oKCj)t()e*a%Tsb`^{kCf3@=5h`G$6b~w=ZyNsgesA z@sN}|8t&n>{i&m%6{AwK*&qW8Yz*}8C7G4&+c`G6gL^@BT%JjgO85^Mj@nwVBguJy#qrMO23dyC8Y>7Knp1 zd>RAVxD?BXc|sTFQ!r6^48_+sHnte{(%or5phMdMn=&bu-Wp*Mn1YZNegJtfd)`*C zgjM%MvbG4v$vlQ~fVh#L((?2eg0DOBTQLOXAJp%Nxrs}C1=+)NcQ8jl0-A6-Wnej+ zuGOCq`t1M?1U5!(;=DL!HMSo*SI!ESni&m}#@j0SagU>KoK@`}@|Vl5kl(Kh}h2tX*qT#QT< z3J<@G@t%6J>G;;jvM$LnP=de2Oh;^B)Ee@alh94~f0&=)wHX5{hD=bVp|{)bkWvie zKC=z!cjO0YvF=tNSUjSbXnb5Q7^OXR>?$klu*!xYpJ{;};PxCIrC)B`r@McL5vek= zu2hh_O-RC3DN1DZC-pk&1KW&k?m=Vqf>s-di%pPTME`bgd5{!$SD~FSL{sb^Ta&pd zkVOo9e}im$u=7Q03VvQrJcB}yJObNCzz&uh36?)*jsIe|_NSX_DTn2BvrLoB(~~_X zcWDf9V2avPj>Nt`_m`pAaK+oti|l`r&CZP*NPUPU26y~;WRlg%)0X`tV9Gw^4|?^$ z0}?O#Fa;ydCJM90Q*swFS>CS`LhHyU8Z&J>0+u25Ls5*L%I|4$PQCa?9phv-XV;i; zJF;8#luzCrK?I*utH2g7X)5=y9HLJ*aWySajP5}-S1pDS7CO#5s&~#i2wr}+SNVzS z$;4?#;l95?xTLUHL?MV~HZP!1!<-9->D(Up~4PT5O#0Wx}p`&;>LKRp{6#qTtbDJ2B zI$B%3ccSccy8Gx^Kr_BFH5JceW<|@4#)AB1{%Am&E3=jgyo0uGp5N*HLSL&4A}uHi+eFecF+i)wfARR0O{MvxEi$rL=JNw6fjpTF7` z5Zf`+;Y)0&%BDl`a+85SJ><3BZ)b-_*wZR_W&!G(Pqtox+U4I>)ns8<)9D^oJt*{; zgrKrk7w;4WTU^=yOGw2_74~uwL1e4|f5L~;3xt_oL>;YQZvl=09ANw!*F~2ovgZ@a zjM_83N|}jrX?SF6*Iyt@5I_!R5k*21FP_JomCZ&Egnir)YbB(@*mal{mnr?Ae>k(WU z^96^=Nw=?-90%qiNMq}g!rGDDOIgGJ)&8=h#?IYh+cS+uXULNv3ywSC{$&;!Pk72X zEaQNr4g?HXE$NXq0)@Z5>Dm>dw3a^w1jvrDC245jMJV~}hAL*=o10S09UJO}>tLK) zbI^*Vtd5AO{-i6ej-BTe?s1Lv^_K4sIL{r$*yCmFY_$4Vj+SZI4;=C= z^`L){>dOnUnpy0n4&44CUTCII-~5}40Md=170zQVl)VPz-Kn0=G7J~x=V1PcYLSInSvRjzuXPFQDNw1Y`&M_gEO$D+o zS{yH#e6>2ixvMm3hj6_{7&NM%X_lF$U;`%QnXFqQgE0y%kmq3POh#Tc$}i8T(~KhG z8Cid*egLc#Il6=3NMgpk{cHPj(v!$@*`CqpB)uy7AM>UR8YoYEf1H5QoVo2sQ3xZU z)n%ftl&cRfQj8);(}kCES+|nbtm$5`kq!k-F;-KR{Ey!08WH0-MDyV z?`QFRFnTSnE42Irc#M>?>poArYJ%NUts@?-LfREaXlxE(u`H!T^$ib+7g_8>WI}MHklnYs??Idd(>5|&e+LSqsGObm>rQexSE_%F+ z0T2(-+uhe^o)qh%O<)TefwfYREzJG)@8lJ;MA_1Q)27LG&`FxGy1|9UNq7y|tbme$ z2x6wvLVBDj`?t$?Et-J@?^68f1qf4+ys^!9$|Vo&;6eMd3P4`WfmAMTCZP zB|yN`W=ZY%e^y-`v#o6vqP}Q5evuK>8J|ZpxWn@rVK?Yia%Wq0CYqzxypX>y(y>5L zAk6Kk=aW5_TA%{~a_#H29p7hHv&uZ%~0=9e;7=zF`Pr0$pMhrVU8XPV3K4ffUNgN#_2f5z*x0Wi*0z%0Q z?HC0G2+aW$s+@PeLhLFp2d6i^YB*8>J~qQ5Mj64*s(;cIj2|Q>AF#;G6X@NN$!`u6 zA0%#$l!HzV;(`FRy?A?{52d?B$qkg+t8y%|YHl{zDIvF#UR~PG*oe+4Engv1WNt`8 z)p16=ezsiJK7(+R)?h_ust|a@DzuL{a)OUon7Xguxg;nhZVJ*u=;PL?fdZ;%9<3P%@7D$FhI#H3DIa#%1$XONk=_J z$YN^;!GY3yXZ1G^me_r5T)dugR)+%GBD+WKmfZ}z==LjUZJ&>ux<^Rj)-1YgsXS9*HArbJnAUMD{cjbF+p24 z9hRs{S!z2(SPq&k%%zUi);HR;k1}4oL+JA=QChCJBGrZU_k;mCAmp6oKpiD%V2#_0 zBYKy6{xH5y<#Am(LFGi@aK~9eRZOHDt(xwjV6Q3>{UXnQ%+}GhgE3S~15}qI;9{G~ zn|IQdUo>)o5eg~~>|_IbS(fdK8H;eI%*Ko5$3CaB;);<18-G|g>N`swL+PG$+twq~ zy3QJxvOxOuz{eWP-!lNwgF^|<*j@C$=4<#BTSYsw9Imjph#4XxGQS95zx*UiCAEGD z9RV1NiXAQF3}^EI0;cq@*CrVK7Si2^gBGulJI93mm_6x@yf5i$raJxF5)b|_&w3DT zsBX}7S{LAmFC0CNoS@{t`g|g+y;ok)OGPguMCAT8FoiRqdd9e1uBz%hy*=VnzMosm z2-~SuNVh1E6bOY~Z|(DW(*6)3nzKt1iQv@)-m&BwU@c5T@vnHlh>%6x978!}123ld z;IF(SXu-e~+XzTZUYw9t^zTf{DPq{BLAgDoy$W*zR>*&uY`I_X+`YoFv3)vTUpCf& zNeJp8>i%up5`RTrXUKJt6Lpm|JX=8&IWYPl_d9rZCMJ0$-J7TyoT$qHO$aOtAzU$( zuL`JR?~X($@6z#-la?^iDcBI@E{rGYR-fyC+>>>C5;P=%5kxZZc)7{|UjNtPjJIET z&3i$P4d@FCInScJ`l9pnz%|V5tn2rcb;KEnwz-^0$q5S+=sQQ@$P&aMdhK_;-cL3z z0hA8*?DKwp)pNCxrtC@zp)U?fyS{@leB}C$1TSO@q(f8gAz#fCjnSDp>=q*`kUZpv~B+2lRT#$vwe?Q zF>cdH!Is%vOuFe4Y>AijMT&B^AWgD*f$iHY0k`;gSnfhvSy353PuGW8rRSVvu%szb zxuWxnQOb3idO+R)kDTOY8E}vj5Xg7l3h=9FPAoO0oh;g!#{^?miO_e{He#*TPqoA5 zy+U`t7G*%A6~{#gu(!yoI#3YkRajYbEN2{8UJbWQPORYar$qfHC0oQtn*qs(^^?GP zk)hU(UmAN|E=o#g6;pN=e@A4?Nxb+B;Qe6pl_2Qs^%{9sRq(2xC(u!gk)!A=oROh} z1e;eG%@;Gy2|HUG0QF__;1LNW$+q|Ed6(f}d!5LsM+9EBdf~VX>xJZd1Hgn6ME{DC>)G0`pmJWg+ETm z(hC4EF0|B;EJH~?qs1QIch1BMrqyUP5Ylwd@B%;&!mzEF==8l1GZjRE30A9chAT1U z1ut8q>Bc4nv8AE&bkp*zBizAhtbr0l(Y{!FpyJrl@Q3P??&TF_vKNj*S}L*5He-`n zs~QAgCf8pnoo#u*gw`wVTxI$#V7_TvB!=e=VUg#OV`8XD-x2x>_;X9b$Dja| zcLsOHU2@^NH!A|WlVBH8oJAj2KwIpJEzXTqr~YV6T)bKS8}W>PV$J=oWNOmB72Ird zg$JlB7~9kx=#UyHAKiM@3zS2mfL>DtrOg>;M8jvGvwpyL&AEtP@Ejh1|Jmb6V@D}Z zu0w87&^jAI2}}X-p5m-Y@#^K^qJ)-R^C)< zn*8TaFIs4A^#(Vr0%ApF4;p#6l(p?-9*5~v3NOD6a5w?XW_^H|Gr-RJyqe@P|4s0+ zc8u%Tcrh4N)l>=lhda^OBCL-#k4K7fPv*I25yAE~IEa+g2_Z*=W%9IJLc zD9~}U{bR3#hZ&7#E8I#Yh8YMxys&oX>!C8S2>N4v6omu*9q`{Zyx9Wv-fQ7UI~MK` zyUMCT(^$4j&`2zR;6MP&b)3~nA1(6;7g~brYMX1ii2NXEju#>zVTIw51(mYGr{|XV zxyK%*!3}LE*eWYhQF+a45@}`$w5d3@OEmz+5SN^=BZ43(ARm{jBz;!5MmV{Z% z+O|Pj$Rk+1lEDiG+qA0;^An2p;~k3Zj9EYaLI<+F*$@aGfCY$swmMyWKkn9JE-O$_ zOt}MeE)i?=v+$B8%zY>3bj2S37hFpFV;v}HBVWVaF0*Vm&~)};&94V}#ASwkQTL#j zVJsBM7SvPVht)x-F^ef^&KhHFM}!i5Tv(3>0(y1|d9ee$-Esdw`>HNN2U4j(WvwN- zSvSjO?;GvC&%T0d0Wyyn!X%7Z%weFLqGkxO_59$S7G)!7PoVQTX2ZQZ@MJ-A7MmOF zkQ5leSODd@8Fq$iQ9zd!H~f&)gGL){w?;iYfW}BuYj0}1yf?>0PR9Q5o5w5)lFs-N zCJvk|*${bdseOQ(LD2#;qoV=3og(IMXeb4WfG`W!qO^q^Cvl$fX<;UqiYtaGI_M)S z-k#14tO7wHWALE!tfuS$)8T%p91lH(D1>4Zc-W5Mutx7~P3=6S0?>f)m$5tO^d}p;G9Qv>LE!L8Axwh*Sxeb4GO#JiWP}!VKAMXu8E%P~)iJu= zs`$Oz^`HEJV~gP7cdG!oTJU4zzC}sDvv9KCJdA?%6|H1=`VUsU9?s%|1=Q}IT+5ps zLFSym$B`9FSOa|1_|Wmjn$|OwZ!nSivt6sd2yPb4%nwoV0Otsz1gM_+&ejqK;{3O{ zq)5Wgjs_1|rB&g00c&>9G%5jXAGV{+3HacfWpa^yb?!N-;#ATaK&yq&P+J$uzzg|N zV`AYiF$XZZk39h7PbD~?01JLsu>3H%^JKM+)@t$o?QVog^vi)HB6b6lF{>&6!{ftV zQ*BBtr5}hG%T|^1Y;&VNK+#OzTbd25d(;FnL2klaD}Y4*lX?7KRLwh;ejc45Rh7uG z&KJa>SAh)MDF~b!t2%K3J>g@@%lq!5+?hDx^GRP+ zZln)C_OR^%s`JLGS6g%Ul@}rSj3F-X1l&k-Cv#i@7D^Wgo=GOAF=+@ICbGF=&(e)A zN@G$pX;n2fjY1vSv|yp2o#jan(;jUzUU%-}b)M19g0dB3f8_s=%b+H%0TbJCPaSpFH?scuuKyqga|F%Ly!W$pWm5;NvjcT zIEUD3-i2e>{9W8j*+DnQL zmT%d34dZmg46LD1Jr4CgvsKj7X=3-IvtaBT#CUYq$>6h(?xo)yrpdHaBPF&W zwpsoYBAqQAlk#;`w|Jl&vAb|;Iee7$nK5{6>*cRA&q?kj*3J1dOU&ompiGE!u)*AQt#G4P>}cx2M!X9-#pVB69(YQ3EA(?>-+z zqyA4C>wl)*tYDZJrIKfgwq;+uyL>Y0XXl!R253gIdB7m%Z$LK3#zc$^m{LO9VK^QR zOlys89GpwY=&kz>MIZi}-I$aEXq)0}t-)MKME@rZHWB`V$q#3w~f9s?T;Czc6eT!{1%^_Ujft?cF!4(|h@(p0lV%A9i zwnvg5P^G99WOnjUq#Q3Xz2C5c+c|ZhV`f|s+|UJXN6}OG78kC27q%~gv2NQ)aN~tw z@8{?!xfUq^Jgimhces+j{P1~A720t@7c>WHtceeA;V?p)(GGRoC3-bj7~vKOc^E?w zrRjuk3~*ICpxCYD0Y0P8R$)7ViBYC;U8pWEU$M73bBMYH#-A?kw8b-;!6g7Ex#PS` z{}*e#J4h*vANY(Ru#VfNTVjqU)FN@VM;U{IKoCWlvh`T-6x=nFd}4|7_ra?Hr*^pj zIC}VR*#rQrK*3I_@k(I#DSIrf!u_DL`|w^4EP~2!_xPm+ zq&9WOXs|axiL+qtfW%nGfa`^{0J1X;Y()C8YiNYb+%K3Bax1C2Xec+qP*tA^&|Kh; zic?Rj&qk$=U(or^BwCw(2H>Lh0NPD59A+)d`^S^vGw}v@ z-UBv1B|`yM%yNJ+0Q|&OUw-7aHn^&%K^Dz`#s75x47vhP9N3*m}3 zK`t;rX5B4#B>pgS`Xi%|l2{B=(!vFp3}k=`CmW=IM?R7AtEBsf-eY}2f(Y(W7?Wz? zqxTU@w%z!A_?S0{P6kpjv|`BQUCE^duGc`xwL3sMkjFb{T%x`JPOvZ(ThtjBi((uE z*KQ(skV&#ilLQo`|**{VZ)wV8%^DEW36zYJFNq7%oADZ?ivEaFEU1HG|SqG*Y2 z5@FX2dcfrn^2M!@)RW=pLsE7JaJ(sOjC}D|UOF~Ca}xZ)mcJ)+VM`@&*VB~X4DD(D z#x@$|^$PB4S&~~)eiz&%7(qdw;!5StIXXxrs=SBEsbLSKa(jV@t+myTa>3UD!QlUX zo@@$I%6;vN_iN>hDQz>SVZ zfS>{&xEs2^n5VR%IJ$)F-1 ze6coy>thY}TApD_4Q}z!N~pQXP>sHVPN9hUQnooJy=81B_UKYyyunQaY;YSN44%;~ zc}(&Z4^3y{$%^tstzhPJIT6}HN1K-N&P|Oni9-~5e-2L}Y?2e^j;(&-;o;F-;hyfI ztP`Q!!Svf`pE_pzr^FM}?w?+I62JnhxZ4;Aw(^KH^1t0nM?r|A60u9w$q}AZZ*YvBhTGG)w;X?`1UWF@rAev`@hCB?yZc;P6 zy`{swS+D@-26$l&(nEhkX?ZTrr0w&e)eF_5^6B{~Or0aEQgXDMY^@QYIWJb- z8fLr~mFjpwN8UY3g&tVlkIvbmGrEz@sdu?=?o0MhO-oG~rmci2sM@!(lv56*mUe5@ z_z=auR>EO|w=qL&k}Y&Dah#0{X<1!qH#XUBVchh;+Jd(m8_a``3C)q&HLlR7BK?6voPub=KA(f2-DL*P9yTc zASi*oIfDIE3xAyOSX>WKW3!s87+}X%F~t6Rat&1nH(xS=>A-|;0h8+>OCY9sA>Y;K zq5CxOlT)sK1|)Go^QC|RJ8(O5JwP#MnicW+8?Qmz0)7@~h3;4tgx1oAnq70Q?w`Vs z=y)br!Y)=%RJ9?tA{PD(Bw40@W(?>dn4^C$+99yGlPeX(lugiDC6J(*d_&QaYh#Wd zrr?vKqWf$T7~d@^HG)UHL?gqMH<_o$Fr@9T3QD=ZNgj#g|3y7zgzFV}iJ=os1DQqN zLuuqc;lh#j9TO}en9_!$GA$CgPnZLNL{_&)TZc)~OAL4QdS1J$I64orsC>QZ<$F&T zwr>ncUFh>DclB`0q(kA=Li)7;`2s-(@TKUR<)l$saq}l!&gi;(&7Q!_?oYZYO8sJLWW&;76Mv zVi>cq;I#m^#iY(S^a)%p4)`B!JrtH<9^BoX_;z7C+22Ls++wlI1K&ap!J1Bi;4Crh z4rn`M_Y?Ad9RJA^UGU#fDv|G}0YVExO`IG`xO(mgu|MX1*`qV+{vTTy{X{^`7M1Px zemu13Q!3|2XY|};Ml4qxElmHNT=^InESGEQy`Sx!`GzJHlB=Q*I`@@oE-Lxq7%5P6 zMwAUdXXRYIh7aKfbNZ?zQ|+}qqNqxoYHa}RP=J_^;!opPJ(yO@-A3J6cDuiC1y|6Y zF7|U0L4FZui3*l8_k)Xeyw{ZNdC=1IGlX+-e5q_ZH{*E52n7SE)&{`mG>lSu_HuD* zAjSb;uy8%8Eo4L~Tu;=`9EEKF*)o=;mc0g?kU>n5#ARal2zM3SSrmLs9i;^us|KiA zxG4(V=&%WZU&Fq&!NTn&SKJF4(B5lHVIM$J*(IC0N!h5q{;PC|N<7m+W5bPPFaptl zvivG9#^$j?aEq7odzNj+wBe5Nl1u@4FyX^QYjBs?A&&5O=%w~1N-89TydE-qqr zKO{#xpf3MD91yWEkayMuF6U&*8<`Re7kLj;fb%+2iNH!eWmr$1cY#_?Ev7&Z$d+#a z-GMx;Jfh2)b9MbQ73L47W-@w>U+ZPJaR@A<$Hh~vLdGx^HeDHcTcco}a?T)#z^n?d z9lvn{mjJru|Haamhc$U;ZPT|MZO2+hTZ@&gl_0IMOc7aRiH>7Kq%;*5b}|+yA|eHh z5Cqa!r>-@rK}dy~*s>_HiEILqyw)Ygh>p%Y-35-lYwaSoh9>oANm)<$TM{$;fVg8(x1M_3#hAV$yeKPqz!lvx3lBF zlehV)>>AWg8}YgSnHg;onEQ!W|3*`1bZ^A$Zl8a1uGh-@JGuK*0klXcwBmPIWBEY0 znc0;wI5(_!ay<~TvbK*Bp9sfTmrzECTNPvE$u}SHn6{;=-H8q&9+4norse+Vo|<6O zG9=X5uMt}n4^t9Ayl}PIZ}{t8{400M#r5dQLK8Lpj!>3v#{GcfBf0o9`KR)Yb!@rZ zGw(di!|+>*blPxIpLwORUH5*DTWjXD!HVcwhQQOBRJgcq&nZ@BobgpQsHWmaXnja< z!q*r_cF%*8^p6%lP*F*`Z;ByP7^Wr;vkG9a(2iR3V3KrUx@@VqeSdhjkuzlAzDGPf zD%nD|^@EI)VK0t1Uc5P#Zg(2=V7XZu4@hpzpFrB1y`Zcz9(j;)z9^O5ssftl`e%0bl*SJ$@CA zL;%t)+%h?JNN(W@NHr4F4f77lG?X|EM{|_UDnS# zEzWZXpTWF^@4S1cotKk1E^i9&Xisq?h&?!M%&wxF%Y09q-!C4H`?#*}C~FLqO+<9i zVsp%B+J!&}28&lB(^jH36pbqwEoZ8+$?dC7f#dM*yw>)5z41ok=> zA#X2Y@CM^o(AK8b(JI;R&h<#OIL09RF?$9w<&%O&QP#wrFhf2BA;9j?b1ai|9pdnj zAE!xq!EN*5-?6stZ&2G1WhDYpSCH`-%eS-D|Cd(r=RL>ger10ChITFm4}e27XkJ#E z@AyCBx0^2hv!DT4VS^WZr!qUOLC+YX5fSl^wqwFWw?ef7h1@Nt&RVE~#B49VhW{3; zxbD|Eo}uQ&&Nnx zkmGeQyWnf)WYR%cT>pSZnJN3(`Q00sXAwtVv%AF zh>rl&k(%|qkt$s2jWmt#>k~8=>GP=W###?@2VmBaz?Ct>hka~}T@76C(5J|ZDM-}P zkEq_fEt!Yi`~fS$_UEL~h~0_SR}qJ{5l$XM@6ptR2T2ie&LNTvs3Rz(z>lpDbNzx) zlSYWsW&60-p%_EuA~~c!3c25n}lv1R~@fxJX{D=nJ7*q-D#xCP-?0_+Duo z&`|)bFIGA^ol2Rs3(peGqAMK_F7X2-`~+HK?7PaA5uBL>L5Shv($$N?|tlpK$VD6oSp@t^yX zQ}2J)3FT-;3^B2J-7SQLdW8)BI(~G8zKZ$z+r0K_*7mVJzhEzk1H9SLxwR3N^Sr^s z&84~f`O$Si^g+#K5zkGSve>0J%|j@$JLP9R_rxe0Mys$CyZ^N?E9lIC`$YO(y{rmO z1nt^ACY3RzR70_OZwQXAS=^%=h@wNb0udV&niVW&vJx!kl7e~^;1EdG_jH(t%>Q`) z+|>0sld#h?a%>hd6SVB&?q;&0fbHiyN#IGS?ab=8=npSbipDN0wAM%JE-vJ#sCG;J zx`lw$gVuKYtN5PNjvlO$0>RiV-$ONgr;X%l36s#VlIQow-oBr2xbrR)#cu`)|JVHf zx7y>Z|JEe_D0wuAVmoqG6Njk1g#Z*pZy+F)*YtDy7H@SvX|HA;??$T_Y$*hcj1WjV zvR6c_`*s>H4^u5ps6fGbHB`Pb_)&zj_k_8Y9nbTu^(UzqAg4Fx3>w0YzxBK6`F#;J zI(yBX*d_)LFkoQFxG+?eHM>PLwpSQC7^DVb_JA$BQ1+@&e%wO9@;rf|R z{^N6F8%F!5Hw+w~JO-&R{26Q1TJ-?Zc%qZ}c4e^JrR!A&`C4om}RORPj= zdZO32B>9lot(}ybCKQQX6Dl-^)5Z+t)6?chBZrcX)1;7y&Y9z*-@oO=Q zB~M;3ex1(e)2jVrED~k0_-a>!)0*yC-8*zc{7zCBdfzOm0Q{n+!K4BJ<9Ngj?`nnx zZ+m(Gg+w%%>)P>&rQ2UkOz86v{kgI$e!`KY^Rgbdh242R5hc}OOOh&Xv^<|rbvzo$ zTM$ICX>%bQ2{#ra1^`GV`r4+iDu?MoT0ZIz_?()YJ(Z>Y8YyjKtA`C6u+2y#o3;hR zVGfb{VLqj8MENk_r4Jpx_x78le_wYy>ludG zdpSs4AjVj)0X!7P<342l`I(cTNTLZnAB7&1c!QT>ElrsGy!Sum1^z-?skxE}Rt&!l z+KPk!-oBdCg=5^No;^mMpG8X%z!GRjLN~DgH?A0%2a-J1&k*g@8+O)T5X6;Hh;#@d zSb#Xs$V=erSq~ldc#_w2KUal^X5=822Cuk-SHpudDq3Cl*giv%CzM@TvQzzr1f0_q zzMV$%aWme*rP8$hf|v_v99l;JLf|zi|0aBeJYh}G{Wy$CPG~jX5zLO;!2gS(01Ty;zM*r zP}G2w;Rff#S(v^f=uSD9C>fF^4Ex!$@F*e&7UMcXeXZs>p;|Ic&O@J3+;{v&to1w5&oKYA~ zXaEG?t1<7=M~wBId)Hk%*HFi&dP$#%Iz(5EC6K}$nEtHt#Zj-j#y0CbJTFRCSq{QU zC+0qx-Zt$##sX=T0k0b2NuGT=H1A+FCi`=Ves1zue(TYBKZyQcl*-Z_q%2+U(dVKyr$kNGr z$hWA=nglK$#foXmY%$hCO?>03rS;4Ou_+I;K;>O%WUZe8E!&*~ybq@Np&NQNY}rlb zoaQR_yAI_ENZ*?mEIH!YMOxJL-ncax7}^yoL@;`tM|2N{R-_1{BCm8RPCYmtWO!V{ zIGLOn2YKDyK|?3rv&UmioP4d1P1PyS^-8YOTmrtkmMCp|-Zzpk8mzs5+S1W+ z$^7x(yAi}Dj<*#+u|Ekq01aLms(Dvu)8>%HoJ zZ%0r-4o16S3Pfg?56D*EH{?}>F6WVWP3jw2eGpJVo=;)N-F(G%H_BjM6%tfN{+1sW zkNJ^nDG1w%x@f$nWIH+O2_nVIx~OZkp%5?1CCyFz9#{r$5lZagLZ!c1R}_2L4+8Im zY8XVm13S9985Zpk8I(!?%9`jy5X!KYP)?vK9K4~o4V0&_L^TL3mVq765@iOmXH=*+ zRW17Q&~xOL0@ybNP`NY^T}8o_*d`KRt^3EjaAlmVfQoCW8<7YEchVNsUfp=kAzic; z&n7 zUgq{yR#?`vdbS)fqXHs)ifJq?KFsRsqj%eYY`ls(RY|x;WY7y)9y=1U`tj*Fi?pMH zQ4m1*RU+fm#b#@r)fYA@WfM0@$yK>yw#QYpM`{ESyiF;NZAkc;;X5DnO6{U$G3~A< zTIYR)yqvp(?2AwAggWbM5FOEp8lUtCrIP617)i4Gogph9HcCT_l?Z+0Oj?B(;O5~R z^&rnhQ*JhoK>l>!cJ9P1a#Ph1Ct8{WwjI)aU))5zU+^p{!LA@7V~EooKgjnKR2o%& z2mzX3hFX>hBzuIHK1`0CRXkY9v+M*DZVc)0KIwbPyclAd0>qzCZPY|0k3TtO$J*4A zM+diN{IP@|-hwR21=Uav#0I^|$r(AHM78I$mlVDt=1{+uABd8L18$@s`}b02N^bZ= z)~G8v8uPTkm=U#1Bp)5~C~A~DO)482>YMHoT87#HMA>|BV^iTe5yk@EUP7RAM~!RW z!ub6Ij-jv+6dY47Z`}>{HQ6}bTFww3P}`bEU)rUjN=qRUc~dd~5Oe-(-eMhC=u&`y=y=K^X$W zW^^Tq@*8L7&%5*n(42Mta(%B80+n$y2TMikBWR_EePYhpAOCf7@+d46gvI2*j`f;M z)6+rjSL;F}AT3}0CKa0D$!^GK@!xr>{vgucq}9G zYwpQlV>1pM=sFy~V2wlmG1UDiCNATh%T(n0AhLyt3=TpJ!_SO9Zbiz0w~^&IrcihW z4eC6#ne=Sk(+9KFUydET6HfI?3fqm`N8JA1J3bEAnCRPk#a@iJ={@L4H+dMa?D0=e`q1BiZ9dG<~Hi zz}#Vr*WQORlW9B0k5LqG7S_CE3Zn74XP;#jQ%h5mX*G3dN^Eg0DVXE&ghP!YIu{bJ zsnncPp7Im)!Dno?mPrhA{&*;I7~%)3<)?2^i+6hQqeQc_Uby{YLW5g9E^HTClD3yA z%>wALwFp)RG%Q$9;qcwtJz7idPNKvWhK52qjO1%%dXFu4lud=qjDedqt6e?Ke}#nw z3b#b#6dkdg(+JExcG$Ehida>j|QmolhX8nrFWGfy6 zvGM)*5QJtl=xk8STu-BiPe%!SGFuHJd4#kdsUL5)jv`i+IvHP}j)KHPY>~MiA6^O^ zI^uteGj1I&w>bq(P7vNtPohWJa<*LLxPQNH}Darf^ef>Jvoh=CcFDBy(~cs!TA5D>-_$dRZ| zn>~`2aH)oFP9N2XMq9*(bW)CuA-)r-6y*blZwN0g{4L{*-+KL%oVAnifccXe#fRDE|OY8Z7tf?#~T%6!IBr_%2h za4?ftPosNCLoeVpSoYrUy66t!MeSV_#Z zTB1^hlRG+hzNW9fA5$tpd;l{g0AxCjf>z;_^*TxgGi$U`YO4&iRxKnBX)8PTmipUi z!khBpgJVYGf~R0MVJ-F(s_Z{h=E3kePWCrsN$(;}asIz_^ysyqnLdu4{&stT&P&wD zb|1`Y#(y*n?VBleqrXWsPCvVdk8Z5RqZ;~4L`ZW**v@iVaafPx4oGka3`1BlG^_l= z7`yJHuTD#|CRJh7M3P|qJN%TP8_O?+mFyumZs`Wk%bGZ1;Aii5u65d!6(JI~7vRIx zsGun!#2P5T$SL+Bb!)BtbfnG! zM|(7v3|9l*Xvf+ns4=gvrkSU5{oM{!rOtKD_5LH2>C-**l89HD3lHSIp2&?aFaFB{ z5BQ_wuty&a+Nj9;3t6L0p9|HKk@dQCSk?iQ)VeDTO1w`!+v^L`z16?h@#SL(fTYb` zY^*0Uc3A!IOu2g3*{`ZvD@3gcE~3Rn`0`v1-l0Gnt0q-^eI{X|H(=mBJ9a0HXY?{6 zyt8=AgE{NflIx>sMI~(wt;lrqO<7pNLPc69{?;G#XwDZWS$bPhF zDn=zUW|I=Nf*AEH4uZ>;M7=~40XU>EZBwQ0KPGF$8>m)w`3i$zB88=vBB2vF!}Yfx z?W5vU+ReFY2YAh0Nt=&|rhWjK)*&TRFzbXIG(=ZHp+=PdF7*A~AxAT#c%a5ULS(m= zwJ>e0Z_oTXExSXBygf+Y2K$Y8imAvs)? zk9vgRJ0jto2SsmuDBY~rpWdyxtMh-P&oM@KbQ0%!Erb4#mcg^Z5X0%ViKcD1?>X7j zL+7KTVG0hnJMUwqb^Z86UBD4VJaW7-OtOTxG%VBjHd#uVnk36SXt}GMF7FYCR}F+nDh_Q^7+nH1mmF5@>3*Nmv&FlxUKyKO6DC zu6b4*dv1_qQx=>V{EYr-$73Rd_lEWU_T?BovH?TFoGz48Z764w@z(v`gl}K7<(DkSVDoeADX}m*sIWa$cK~h`d+}G?36;` zhYGPUD=}VNx3A?j5!4jSHN7KFPITf~3IqsxOF`a~yJ&5!sJk*$3@(l5AjFb@L?dLf zhTbI2i4=M?q{xW4c|vqDBUdm-ODq04`*}^(1;|AKZ%#Qyokn8pkbKaVXx~P!B^m#& zvsY|;q$$xPEM_rT-7^TBB#5V)&)@cmgJ0KSB8uhgUJl_HP=faCO@%Sm=Uvw8rJ+}e zU|X&cMF1JBc>A&~;H;g8>iQGMC{=;sb2f87bQ0kNDvt9_Zu-wo0|y zdOw2C{H{9aMSVDsf-x550LUy)P1t!l;Pzeadk%lp8I+0IL~Rm&4&n-+8#zg?rdcbZ z2%lm~0;V*_l5w+N;^cK}?!%68(E`f{j4HbRY=~JFjajJW?o75gv6f;h?zT~ra$vT# zI$aueB;-nt;%H*1HnEEWVxtO&Vb30uuBs}w`m4J*2`B))lc)gb2|$@l;XeQ3Q^5i^ z91$H*gI@Y3VH30v-2VSMvuu%gyg%)kb1;IY(ZH+7^KcGvf86fBN3FB^lj`sL5Aaq3q^TD6iG#Gj-cyT^4Hr$58S7%SFCsFKx6lJ?vjhbvM>N-Z>AUBqb;O&M2yZmF zRuGQY-<@NvlT?IAMzkqp^f-Ib>4}iOq`sv|IS)R+xxVDX;QOV$%pa)mWU}4Sx$I@> z8tOkdZQ#TuW@suP+}FwOgc=vH1xoOKAMIDwPEwAF(5-XJQxQoZ6xBc+#zLMXI$=4w z7Vpi!i6sU-yeP=u8*@)T04gVjaeRRsQ&Y$FWI_cWJM`i$x1T@FB_q}KZaL`!8B!`b zMoHk<+efCiIRu7_INvnswq3fXM^?j5ooox9Ugh?RBFwrG*tpf6bR@tZ>S2h_bS%ia z`i<=+xeHUrrX0)uRnL9ygiTydP;n1$H+eAo)6mOKdNiAgLPN0_MbEh`%%p^+2Xg%j zCGQ~7CILqBF!2A-r$L!WME7ist%BS)iK;;OU^%fGHzl4n$XgVvaR(bE};%0gxvY4AcALx2|CeZg4C?pX5Q>3 z&M_n8_ejzwa*qcH=x?a-#0Jw->EwZuAPC`G)9>w~>0FX~jjFNme~{J7L8y7c#h(gy z-HM$Z`qVK5xw;!DCUuY0Xu+y6rK zW@ja`V9iK5WV|-~5;=na-I-!bX1|`QKfoA4T%qfL{JDn>-;?9ij7Y6mSY)|BItGuq zef!4E>|otNw=WTrbqN#R0Xsa{USp3dibKW%UhX8K7<{5G_aa$_4tVbwgBO#+(1ao+ zx|Zvwg~$hZG&oU5P(-nn{$fr6I3JS%81a7Et7^@xv=U5z&sCm7GJRFDVdod!1bZAT&*U)4mxNA*qkCl* zyfqYS&A(ZZxHRZ_O4M6!&s6YUH9E z`Kqy|!k9Rhz5NKKhu0kA|Nhjh0An*$ipV05aeyIr`3}c#ZsIV#l?P|5zYWhX!9J_q&Sc$76X zj3Cy9I{<@`$+OO%VR|lq)*>m^8JrqqWlI&>5nY3ccy{Dcx&2gy)nmD*c?KdYLhr)| z-oF$JNrV_;E{`qG^lsN>gVJKJC(0(moqYb^IJmWY#aJ$KcN}yXC8ab(nGl3!5~`$x;>p|VmiSDZ^%mVhcV%sdy$f8b#Bwx(;5lxV72CAA zRexTIqi2|+1niF|FX#AMlEno7nmZj1SanpO!n?^-5& zUjG5uo*c@W9}(E9@sk7MalnEa8POU<~JhO7Gij?1He#$sdIQohadLX3BjEOT+!K8VW8a zKVt=Gadq6q7924vA6bjoj};yT++p7-rD0;P1W&ahkec5dH@GTj>PB07{YI|uq!K9} zPFQNT`l9R-|I-=E4k0%L+YWXQ$F?Fw5MPWB5N-bXCAWym*|WTXcQK&%NYLT=o_Y_7 zcbIe)3|^6TLw5U#+ZnmTo^)rtB14dk4e>cE^5P&A)n^ddltf~N$>HGUP4{jwRTM%G zDtnJ)=r|hNN6MnSNU-C&&wt^F0TvmjgrfjM*>rDG+6RRUm!|LY;xMg1#_5Of^W}e< zEDRE=U*8sTnS-qi-+a}?AZ}{}{r|fL+nEaQVVMd2qcVPY}<(+GSp*X<-a0NQ6yM`0bslvOjpjk&02aEy)M-TJxu>Js( z!Cco=QY0Z^Zxav#mP(3@>JY(9b~x)_hWr4(!&6@M4n#l)BCT=8UzLww`=3v?#TuZ| zs4H7R+w1-*P|KjR^-^5PUWqLNmm9#WeY?`laE}BVs>S6;w!+#I3tuWI zL{Cmr+u}i8wb7#{503gMcHh{5?;wFJ@uuDgbD5o?}Ck&Y0pIlF>e9kjlkXqni>hC6B*2c+Y2Ru?kTBchw@dT{?kknUqopMT!4%G0xK<+q6cMPIgD($UNu9UJKy zgcbQ0swVX6MgmJvJ*7E9)#mqSZ4(I!W5KgJ1TMxb)dMexu`{^*A$ySHnzF!UTF2Jq z`r=3V;ldk;V3r1{>&P0u?_>eATchwa<22C))H91XJK=n1)~QKo-IrUR^5bvO zf@Z!>j!iq0=TitCfZmfXn-?6Z18+$X$xys(NYImV3vzu*B9kK%3$>XM($uMFyplyc zt0=ttkohu3bF+u*^A*}OZ#MAs*~y#X02`EKAQ*zagO(!?nA;ro(c1o1lNC^Ro9iFa zw`!a-e}{_Kj`%h^tP*BDDLU&5h}dNm8UA=c<9x$j<;QZ~LdWtqoH|r5fH=K{WkdR; zaJ#E$?1+|5sw&?iJp1SE)U1Jzf43@RBo`TSJVJsAxiB%rOf&uZ_m`bG2El}KvwF9SCe zmBPc&Qi>Lm(Ac+;=NZxmrAHdX7thRvfOGN_jc5bG|!=e1c z$+gVAVX%%G-NILJTyn)zlL?|$TfE;)05;IB;~Qi*FRHL)l<{UV64CBDBzbX}UXqO$ zu)OD`WQXNHm&8kfnuMML_8>Cv>%F>4zEkgHTRZy&kzWkq75N&Ma7ewy>Hbo-rcfuU z$!O@GwEkX42O&!awzBcy!w~F|+S_%XF&sRaXjFON${yJ9$;@cwgE-3y0kPLFTDYc1 z%Q`|l$$=z+KB;k)cjVI-@l!WWOSwV%`cJXm@F_khL-JtoU2>a!6xZ*ntx7K=9*g;G zdcnucL z;Cy>`@7Ta!xA>DSzbb~oummw3CKv}R2XNxWX)!OttA_1atZ!tdOB{fG3~2+9<$z0F z%aC+0ZgM|XHmE!BGas&GEgl6Hg7lb-52`dl*fxW2xyCr=1pL4# z$Hh1z(fUNEb2xQ4xL1})#8xir*8+EZylrxYr+f8XWm_ax;Q?G9A?ht3j+S!fi^FbT zqCGj$qD@5fo4y9{9%e`RHqK)9%P^{Kb7x*6&=M7(91=_SO+ov0TqD&hzK7#R*qA8^ zcJTQn2Y~e0pLT`I#0&l%#KFn{kbmyuPP(-52bs~AF3HQR1Wr6#@l}9dOEM&W&3}=v zj(=4R&WVYIOZs_kL$mq)+3~)xrAP9@g0Zzdc!0pkvb>i-l_YrH#|&CqR);X*>o6P2)J*; z#diCwe`V+K^{;B$q70J+3W<2rN4Pnkzg@ccc4@4_=MuA+e&k^1_D2=d4#cV_Jdk8{ z>%9*ju-0w+JiYOzvXud+FYc#U!~=&fBV)HQO&1&CRJ0hUTRAa%AAULfKr6tZbpWSY z8)au3=6`mrTQjy-N=lL#o7xre#f@9j(F z(p=p~IFn?BVD!tL`*uq=3BvD4`uA7~p!bN$h_uLQ-A&}Y+o9FbX>|>+jqArhZgRby zwX5N@RQC>Sqj)KU&`9y&6WDGt=JH8J#l||UJ0xB_V|n$a(*b$=u>oy7FHWXSW}jru z1xJGgzYoq%;q_;0sV_mKNS#dZpZC@xVva;o3rF=xcawf^7ttqPhfP47mhoQUPq z1cstlS46$YTrV?TE@mC2Q?S|GvKoS=DD1I=Vqf+m=0`Aq{IIl)Qn-WB`{K zC>$@=)wnx_QMOl;3IowfbB|0qVnJ_$nR za2f3j@*jPBKc?_y$bHNN>qOne`>3X^AdX=ILLau56C+IVCQKtoR~6~Wfn zT?03rY;>ke$9W&$VHIF0QE?Q+!ky&OxEC2BSxqH-0IbMX84<3I&$Z{X_Ja1pyV#7} zRCi&(^9zz;B%8pfi7q#XL4W)=MR$L9FZG?wsHW&gza>#E>x@5N|9~#$e<|=6u|4d# zBQ!OMJ!s4olUQTp7psId#&*OY5s?7Q&Ygn^e;wNwo7j6KBqz_VN2!Y!&Op~2FJ2N= zvU-+rq@MQCQp_ zw)&L>J2YW~5jV_>CCmvL`;8dLX0D`hV{7}uOZm}5LV{1m!$GVYzZ4V9O)xiLYnkG? zoa_paOkbRiRs`)K849>PSS7Y{mr;C*JR#aDt-GXD>ARs-hg$FN2%mXZR9#)MbwY&> z-l#SN0;B?LhdT%>@L=5f@_aTNdA_Ae?jUl5&=pWPF09%kypOBIkru3&tVt{SRg??u zNSN{7E5QUd^QnA#6C8vrk&e8qvi{@E|9G#AsKnk7?8LO-_0jdrUReU39TbhdBP@^|rYkOZpqFlfIVSSf*C)Y72w z+K*XLG^cMtk-3~up^Zk^;&0i_GF@JHz!Ynd2=S zSkG-bUsHVj&3{a0E`PVe z$sK>9+s_LCCm1a8e*K5&2nFljZ956bu+M$5L>wbV-Rlu z!VZjWHSO@9G6yg8jW6UFqY!ZWK_ReUQp&!bj6Z^HxFEeAq3q8-HK~6c`ec5R7Q`MC zOSAlt7vei;=x9e{3(~4(LO*Ug7lab2VQrP-V0uKltYyd+1D~ATpY=pw%*_xW-rNjC z=1>P~U8+LoKWiztY`ui=ZPZ%>h{WrwPVeMe&;LM;w=KmsP*w?+c?}h0( zWvI_EZPFYL+KBf*8Knu~WQBhMNFr%^SQo$}sZPfcFt z1yxtow7v4Rfer1y!WX$VGaMm9;HxwE@})uCqR=P8O7Sy-bS67Rv(9{| zun{*rC`IKT?8pBG)iHPO5Z;CVnC#TOe^St^Sf!Inn1|zvge){{rGvWuDPnda1@HFViS=|LYM%E z6tU0$r|DktPSexzN?&XPM9a?ShY^GfvCpV2628uchSu`zLWn>@o2W{4UQc~#nm)gsP1%mG{PJZ-OetBG+6?H8OVghZOII8#DH?Y^5)dLTc!NcEEr#;Jg%hGV*#b+*F6nMw zkvDW-6Dp|9obo&ezP*Eq^<&je3EP}%sFpu39Zl?|Sn#Z(g-Idklwc0(0~>F0V|Jr? znV(-(!zu4QI-00X_cd1fS}P#HS>X8)`NUgzvj=fjz1w#wf!HrxK$mFFp&2J^D|659 zZGD(K-%jqHRg4~Vj=)kHs)h9!v9AYNU~32U;H4^=fgbgH(Nr3x4X>(beSV+u|GG9@Otw|c?N1?UP=|i?13wplzM3wIPr?a1osU2*56BdLOC;I|cwX#`Z6#(%apc?z?UD@jg!x%t zCCwUD!I5anWPetTHkomDA&{O;@ccyR!2z5IHNa8B+K#7A{KsShJLD`GUFDI@=kGv;+mhbefQ62yjM)TY&hnvo4(xUbora zd)z0E-z`9yDycBK?QPC~VA?f(qY2n4t0f;A-)MN@@j~mO120Cj9csm^&9H42U)4R8 zwS6y$=q$H0^w#AennDJNfZ1m)uP_M>ay{di$H2R>#%{{Jw!|TkmF29PbX$u%Wr_w8 zz%G&`+#Y`{O5jWqe3P6?hDHmWg@R#+XZGF&510Nbbg-z)DFCW=I~l4`u8+UPQPL_> z*=j74vJzlS)vnFN!a4j)X7s+?_&BI&fx6rrx^F7BeLOPCD5(e(}v;H+NqUCm{sF^Y+2+ae6akfI< zUWwThDC9eo@ZHMJgRB@Tu3spLbr_os&d=X$()7IZZlKF*`=kjcvuB&DwQMl}$Hw z#FH>}5U?=UiNGll?PK~+;Tb>ARm`cTHseKj7ZKjb8vhMqk9pu%EZ~ks*s6oX(Ss3% z1JqYJ&wZ7(S(EUoBkve;k;sV7f|=5~tX^hf_ah5bS*)L6;vo8Q+h=FoA%|o`jcXVZ z!&6cgmix(PHKIE!$vyTt^3hQTS^f1QxK6HF(olF7|f<3U;SYY`qXO^n@bx`dE z4qXwrcZ6Dznk?mxEe${K#8>tr^7EIK6tL!$N62<^CIhHmytQZ!(eV$;LngLea$W5j z?(;5Mh+W9L!V{0ptZzvdA3XRWh{F(ry-MuJtgG3zFF zu0(MuLqw0)xqZy*Kye5}iC|zzSaeCQdW`3||B14cSwYgJC->Su;~y0WfruILMt=Rq zM15UVZc~FeB!w7a7n+k^f`}QFOHb%IYz9E@BL`UZ1hH$3T#DvcW#LN-gj+BD?5-xj zV2I0G&S+lD@;_}{+mjm8UQo*hkogX=TOB^&uIbO}R1+9BVl9yGf@{mq-gM)Yx1T>x z_uZ;sU}V?CE4N>G1GT8 zUxDQ(hQf>(4X5fW6-8Hq4#JYLA>pU%GvDxT(sz1m%Ai28NZJ{T|763aEutMPBCJ%C z*YFC(rk;MRxWOwv5)qRJ&u(KCGTjojgmAP5s@J^N5-%#!G=)Z)nmll*3BU`{z+^^; zZC_pLhMQkB>dd5%`lMrrI1#|mM(&kABp;gT|F^KQ%vC4tu+f=awG=N^Ac-=Js5u^= zw@gehE|#!1$~_O`o>Em_BFA7>%8vD!8)njTGR=gQ4X3C!B0Tn3tc?cE9Yr*F_u)7z zubQ*_?;@KC{An*?75(E!pU^Wh36dUg1v9}xcN~zd^*7$3zCwzT`xL(2;ot|B*DAWB z5RrMf1ZOhL8k_FL#MxG8c*wJTb6!^;4Ln#vC=|Wkyq(ov{fU0dtyoQ6izNQWxHA*l z6I~D8Ie(`7w9n~vVgV!GNl}18zXo3ei4r=gHPGQ^! z#i_i&gDo$yi^P!%J#Thvsv_29GOZu((0X+gG4>MJv~-VHxR_8JXBC#!A7o&Mo>LOx zh^||h0PS!KX!FP4`bPFxE{f(nIW-IxS{tij5i(1fZb8>DYCqRKF`3y(0symI0Ol(@ zPCh9>3_@krvnmi7@Er9ck}}`muTPX%PfbQHFJxod;gTW52GFQQ#_;LFh}5Uvfk8UA zpXPMt9zNZqSuHC8@Iog@fU&hyr8O=Bc*pG*4b5QZwLBvfl z;>_Q8pgGdNjp2|s;5*lCf6N8V2{t`26OW)n@g2|W6dqt~!^CadF_)54iH1)|m_>sL zzWMz{hOR6!n7t}USz8CZ!NbPT-;H%++4P|HQWzr7JG`oG;2QhD2WvwCqDucRPEB^A?{YfZS@WIRMEC#StHVb z^Mm9z!Up!Z|0%_iN=`*K&Q>X0B84W04=+9Z8=Cna{49UVQ2});RSgk}Aw*E89(+!_ z)7=XZWMMA_LMA>Pt-LnI_6OtJs!h6>DZ<}E?!=?#gf*=QX7o)zP}JRZi@pk<>5`#e3c-0XF zRgUNM!2GmNT%2d|WV*dmnp8T7}|iqIhZg6tLHCHucg1F78%YwR*f6N z)Gt}EEh6~obWBF6-yOf{$wpQ+6*`)ind5{;gqbY>Y%`%>xv7$@Z_p-+R}z_bxnM0O zyjI5MFW&I0Qhl0573TCyRX!~wSP_;9x3^v#-ab`QWKSnrj7F<&MsdC+mSE9w=HTUp6$xwDD ziM2&jFiSb1;p7B={`Tz`369nMjvlYF{O{sKsKPo^JP;0K+)unK*G}-)o%L^-WJsok z^^V)}QjvQ>6dL@i{jy-W0LQm!kg;_po1a%q%@}$kH`1@-PJzECPAbQScaL{XZ{(g_ z@0c!fdd5E4$Z`%&2Y!#N$_AP`;Eb{wS?@?d`~Fwzg<K$w2tjyKIHO%60N+jvT9f%92kTpvDYv0A@ zH2Eh{jeRH&%& zq!O!-Px;2DOEABo_+cR)SZryP%#V!xg z=$Ya8aq0uJwr6=OHSskM1DGjmz}`IjL+#I@0D$UGKAEVK9-cyjkV9`YD31rfQSBPY zliq~pNJ;D}RJWGL_0cu&{}P^Y>oJve(wc|Xi2%=oiEcGzhsDe7A=IW=QT?ltcZMJ0GI$GG_NNO+5Y)eE^ zDLWN+6E_G@ux#p~PXXYtS&+4p|9Mt*Go`<|~)k+TvRjpF&1_%%K=kZhErBvO0jkN=pC#i$}x_ zf8w?BmHU)X_v<{ZQtYI7g|)PZV8i}dz52Q-S;cUAXT0Y$UNQR}5eu->_#fNoVl`j8c7p0{!=GjPNv^Em;9=nc_l>maEQzsuXC~`i`H? z3u-$1$ZW&@4Q2Dsc!6trFr;I}E>J2g9ZRw9=?vt%amW1L3|3)al0wZnI1atm*;AQT zlEH*?#zX_`2!^p-d((K(Po4tZW|{%%!s0L^riKI@65%uDgO zurxKF)^@C^Hy(?L<1O_n>;-_u8fVRp4Zplu=o2tqL@QT~`yf{4&}YaWO#;bIP>TJutLlEd&f zGzU`TQCxR&r9B>v6d=)1v^18jwz8hoLen6K<#sNBtc3L(CgZcr!Nc1eoF@Jr9@fLG zLw+o-y$Zq2rHft$*D)%{t!h7Ay(6)iullVqt99)Kd45@YK!;KO`!OPxm%NP)7nb%0 zKe8}W#Mjh3>O^q=!ezf@x(vISA1s>gIiDZ_(q*LNFf;*7!kWtaWWe9l0>|4-YojUChiM%i<>XU)^ zVY@ttn*Q_TfxTE@c$D?Mu!DqCYojm9c`cN8^#;FyP8Z7!s-=_W;9i~kJkMe34v-A9 zCVk($eYfpUmJZ>Rx@~oeGQ6gIvU3n-MyPPpjumTYcMJ`KMxV%2RIj^#KvyrGW{-@iCI zSjm}K2Gm&df(78S`}to3obKH@ge`zdFvv+Lbzdt>S$}=w_U&TrbB}FKj?d)?_MvHq zEUp|0dALw(mLoWVLhHUPxTWKRw4fFLadpf$Y}KDQOG6(GXHKK^9T*M_e%&Z>@^Xq{ z6p7mOK0 zv>m&iqoQq8&~C%g1V?NG^1)-!dY5M=tfQ`ON7qE-EkJQ+9NRp{ITsOgNoZ~;5l!~( z7c8&Q(1V(cNO8@JGbOaKXu$AEZkcRz*!W(}Rlva2-SCVh+KQ%07eKpKrH>wiqawyw zZmNxQIJzreRx}D#{g>!S*HYj=L;#Y3&a*vyctdG^+t z^NDJWO_X2*|jQMTTOmX?{(=Gj6t2R7mbF!{&yo z$KV`5J!TvvbBgH5J;UaF*5wARAnb)(IQ4n z0V9Mh{b{RpONJn%KufGFifkev5CVA>HO7dMEeI$m`@RGSOOn&~I`qSQ>3}5XJj;Dw z_q8mgW(f;fFZ?1fQS)EQvM+PTB__J6p;&|X0{^&wqH(au_O&ppBmqbH85}C<;e;w^<+UZA|5U<3Y#KJ33rzxa=U|y>Cf3ybPP3B6l4S)YxF|j=P@ZD*x{8t5R3_M=; zV?e*GI`bdcFF`hJ9>z|P`pfj-zjxqrJ6L!40K6C4?Ddvj;InebZZOPg*)(`L&QC`cHR}gXLI^$|=R1X5|tFh^Y;dI^rY|_W~J`)??vF6d_ zFCUQZf^%8>Nmjx&3@LGlam;10lG&@4-AIoDdU zlutuj-=B-gDKxH?OLhei_Mh+otrvWPPoSJeX4%Rl_e_H7z_dc_7>+hjoe;~8%QVLu z?9xaDv>By*(4@Yb`C71f?RhHBac=}~fE#2#CO6)`Wf%f-xSO&>7b6fH@$gsI7@T*K zS>f(`N5>rkJJ3CzW?EmJYO9CR+I~KxI5U)E6SEP2Z_@IDH!|Q&HIempe;}MWaSL|0d?-nMVsD!5g)?u~F58?c;+eoR z9=3d(zp_$E!wR;FwNeGlW24435$*>@$)Rwfa(p8+<>a&pcst#k|Gg(?bxNX+Pj+Tm zS^KexZ_f>9%e0sPsjASnhZDsC0uH34|J9#df@Y9*8`>v{Otifu%#nYrD$Aq`fb}eX z7u2gC9X&o=xaZce8ZlzK#*$P#%kr$Cti$9;)Yc-q=Tz-Vr`rLFM7)oJ8LL~ch5=su zvW@N}&y9WOo_5cx(mS#=`fMz?qk7T{#fu7&VXAtx8I7+*O*BYRA=6a9jMMw;q0Kvj z5ufhL#*CekAhM?0!J~H_uF<(RjDxx7^1qT`qEF*brth`UxGVtfZrts+ntjITVh3Z=nlGgIp6O2G(y!s+lBxXCd4%jY2$N z2L5im?TQ%l52nwhuz9qp#Ef0CnxlgP^|;3YIpjuY>d&1e{>5~<3`(VvnkJW3>Q_$? ztwF4xzZVkgg||Sm>-2va2y_OexdyO-KHS&QY_Iprjt*sX^dHYd$iM2FXhX6w+`QKx z^Z6p2$Ie!mHPjYK+Dojm=(A)((ut!Klm9+D$v(eDIPL^W>>t(IB?`-UX7MXnu zJrXriMbc>Y|*nciv{|GTR zCvva?MW~HtRqoCkcSqG)*%{r}Z?sz2o4vj)7pa%NcGE=~Mdc4``|gnku2 z5+4NRPFN6;`LXkt8%QYV6;69!0Wk7GQ-;eqmayR97 z(AWG9XTkfEs})X&rOfLvW_sR&4aa%1{brIV5%?!UW2TjGr#F4PJG=^xz}SoZVnZ2- zc(Wx~=s)}BvMC+!dM74QJy^V!6&juIs{2D?zxEu>oSO5kB-!N@44IK2+O> z5pwvm)u{`d*J#;&Gp@uH^5ND&C{2@s{+fSbWjPj^yg+EcfO|v3Kt56e)}RDqV^kxm zwb;Gjo9~_bf*A6~cJ!B?0FJ=i4C`+Ud7ZZq=!LAyM9NsHDh#lc;gR9-$A8ymsnxjM;IAnL zCk*elKyqBfUi7G^W*sRp8vo*Rw|w!s$}zrxzGDoeLEIEpOEZUZT&pq%rpDNMSiHrFPK_ zcosxcvf}Lf@|9!XBVfHB`eL219NFqO@yGCt$`Q=8@h5tt#c7^JX(RqV0}O5#}|Gy`7rjN_hw#Hxb(s0ir-5nCaHP|J06ku zeq@~D{^;fS`;i?ZK7P-KA{iLnqUxZ8ppYrP*US=nNkvRp+5$Cp*hN|dJg#pkp;yWn zoSP$3aeLuxwMVlt9nBhfKtEe;RpP(ccr0sMmWzEkSjI*JAf%gxHSVk0e57Tir`<-! z|2;yV{bh2TXh4tzdj7q$YZ4mb>oXn{#-%n~idu{cRPrNXvW!zcSsBDKY?V)?#=e7; z<&vD~mB=+p+ZeMx(9N_d$YHH9bK^BqKks=tKdCM)oAK!6g!2KEVXa)@b$L~aA@3!}4`Sri zU0o}wk~Q@S)i@%`v0!F{qvEc#1u#0S_zv@9rAnx2Aw49gNQ=VK1R7@3u8n;7* z3!}d!%i=8GspqX6?RY;q>|bQtgKzwHB1Nt7Xs1txl1e`f*v2J`gTC}Twj9eU{lVWO zLUqA^r6o~u``>ZoZ{2J%)($A~BK$=RXgdxJV6lrQkV7Ft9w!`MyHuaA$}S8TT5>+u zTVd>f@RKAQJ0ojf(#$eXtl9O&xt{kk(k`3+&##k7mDh;YdJ1R%8Y20{z1=u0b&OWC zE!C4GQ0$)6IFK}PIaHNVZ`|mG)cT`;qp#}Gf7)MpXrW3U>*(x5Ix|~jx)zY%* z3oNVz)wS^<6|Q-IB%(HNVnmu%CR6#+w64NqA=`z16=kP2p6T{G;bDRh6cv$<`FYDx zV!M!~Ic~VPIdhc1!?-mT>qH|Ju8+0yIcx3G+f>0R!x$5HyuBTLG2HBjC6m+q*HpdP zTv1y(?>b`m4iP0|@8Ke%BT1+ENlC&~Qt5U6z48y&{tV!C>M&jYYdEBMk0qzJ*AmO{Oy54Kc0ph#YF=W`}PB4HDOS#p@6eCFf7moLPF-1vEU?RT0{Kco_lEr4?* zoKM>@P0C}@Ob;A9_v73zJ4a!sSTY-%EpXX#x*MDg^D{j2TK&bqg3OOBQ^p;311>O< zTW_3ZHr2C)l7EZ96Kn+1C8GD@yo9u{_y62Hf2xx)B@{YVjUgd7r_G~r!2hmJXct(r z6o&4PZEjLJ<^A*6Z0CoN|7MHaVkdArh?&2P#f|>_l-fCxU1nKvaD? z0a`135sPEDY2BU2Q*!v)_eFaqVCRx)t00=Mu2ZSc-I2Z>cVCONIK;n3has2~b~$hp zD?m6M+jz!IlghP9f1e)?5uoY)NwNacTauh) z`Xfbs^Y1c@P@7PekO{BnliST^=*18*rP9YLvsPO8a_6TpElQ>MyGhU8*`t~ zRGGVqdhb+{hZX9b==vDZ7gvYjxX|Ce7=_r2!~0NIR~H`L(G?u1TshwrHffh2oJ;0+ zx~@=Ud^l#P0w1$<`JT$kIu4NIAk?3HrWTd7AXoQZ4BT-)*n3|_tm8) zWg_$+-JmFt3arJ!fOkjVLp(#m`alNN2SL+h<|him{gc~D&$55nqW_Nw<@sXxM$*E} z`+nBn1)Kev8KTayNTx2`mWuN8Um93dx46#9k`m|UGhs%$`_B~~eE$x~D7Wbq{P?NCxzO8DdhXn8q9?PZV%;V+3}#V(CsdP#;{n5g zFsri$`^dzew|ehrk%f8Qqp|;ijR?*mq(OzvQ0+528IOH4IQHPkLobv4DdW!X!kZ5E z%t=9!*gu@wj!)cQ;awNxTpTNQ9-wkeN6MI2d1H!AUhX78R#W%G*ge$Q#0K z6@Jqe+a(W>jB9G1A%1DqTSF18`LlU(@Td`&q{{5c7va%-V z4&(vK6VMIk{E|LxE8+^@fq@8jAojGTSJe#YQpK_Z%JaEt_j#Ic;<~~p=BiAy-$-+! z01~1v5E_G-#!Sk28yei$yk|>b?n_MEnzZT52)tOdmp}F#Kje!~XDP*0Hc9Kclkx#G zyIYBs6?(m6F|r-prFqgp@2SMcXIW3h#3<av zBvMFb)yAN#BT-;jZEKAH!3w+s;S)JdC?9h4iG%Fq8=7ORWBB1R(vh$=TZ#ZIJLDqa z;vJI7Dl$gsw~2|akBJ73*^wdV?6M@t~Vykp@@kJdgawjf4vQ$!IuoZ z8Z+^|D<)~qz3a9ETDC=NAqc{Pv53`clenyx!kYJ7p~1M4g+ zbr9Ft)QdVsy4e*GBnL~wUuBhY-Osf{R*Rep+(oL-%}c}b!+knK0wBdpi-Y5t#Gu8e zHg5Dg^~1=wtVQVJG6w>bf3DafDd%5PQE|KNy`DX?&v?2gwpY-JS;YXq2%fn9E&ubP zHUoAyG^SUKx`fORYlAFDlhQJiz6K#*Q$Yg!^?T~=Z234D=wCf7TmriRN;^)^Ra3Ixk@RCK@EsA$c#0)j*VcXda5{`y!Q z+jnWwBBM74&W0ymetu)O5~ zXD1JLF?r8W3+Zdv&Fu@q@kVlSBfX<$CIP%>=zB_b=O<>d{eaGJt=fw*;Qk2q(A;&c zwU;6lX7YA0ROh)57E*Q-3a~t>~G29Ca(RwajLA9+rP5No)6Xjf15a@r^_QfR|2LV&8}nYc?kx%d(@}t^2q4?Uy8> zw|2*C;r)*Wn)-M1?igOvoBYDvPJOFyR<_|WPfWM zB>h>e=Z;#uXLD7%i1Tal<4tWcw`{sWPs1R9T+4NWX!f^G-3ivZ*l z1rQs!ZEodij#c zQqIaaVV12Wx>Wg6^%fD+qjh6CQA`Hwn4LRoSRH!xB@CW?7)2wJ>2WlyFY3j!|0-;GDe39q)Pd?!f$W3^xSAdu9|X9PTQ#sIXss5A@lMERrE^!g-|l#m6B z8v$x=L3pLPX%)!eO}#4Ye%vvTB9#!S&YkuZpNxezV}qGXV9eLO1H8hZ-zCYfVOH|T zi_T+d=0~3V;1{@<#lafYhg(fr+v}a;Re=}%<}MyZG%_7yb$yjgPR{?E_tv+-$6@Mt z)KP8Fi{bfz#QTwIz%jl)cyu$T2GW1OnAEEPj^mH9ZkDKNA$Zz!ZTc(oIZ$M+}g_)+#?X6xoisM<4LYX_* zD5DJqX78H0q3jBD6x6DV-YH|#2z!UjCg7WuN%op8%||Ll=sE4|akpE(K(wJ;_tdre zHEVtF(U6ZTwRSlv-niSp1~dTiByRV#C|{G35FV@~!?_VURf+u=pAFm>o50qhAj9P0 znpvdJ(%|+I$)xg!(g;>ZHtt~l93JQ8XREI!L`_$;)l*h2oF4Itu8%sF5%PzwbC(6u zAr6wxF8{&K&q*DO$w-~Qv~y@KaCCGuQ(d;06$Gs*x<=A%yp(V{>dLvyom|*MYY6c2 z86DI5zhDwK{jd%fBgrDd9@m^%f8Ub$XH>NRh2{H39XqYTJ+dW5leBLrb4>#qf)%3IU^8KeG8SFwvVnmod9FS-w`tW^2gcH$qalBa(k=L$5 z)ei^E+c?mrc)zE$;T|g8M~+oFdhaX=3?HCJIr+w@WWBw`xw{*PZKd#Yx&ofD>CzPp zbV=1w^{e!XkB{5VQAH02zmYcg>c5=gE7~A6mG2tM*^)=q)dGL&9h5LT zKq_n6Tm4$@5V883x~zB*ddhh)VB&-97)`u2O--`DrQbi@=|=`^-(8u+A}$t)|2H(l zdJ(+avRo|QM@&9JorU>fFqnE}G7;xaz{_BU@eeeIu+q?IDrnAm9rb|TSl^dw>~dfw z3b%V3E;z95l>z8)CDiForj4XVWb*QX4Jk%+MKoiI5AG&dwLl@%hcyLRi|dM6;aY!% zv+gHtz>97M8#)lO6)c1sDUosD@+$4a&6!Hu!__ICzyKrlay!2q45XG1-%-gF|3Xgl zap%Q(hmhb9UKC^X(#{sv43?b0*Z*zJrw=UW#Sv!r<^F?3K0WD5qQEJ}!`vv!y65$jjv`JG z929!1Wr7~SSjg+98#nr}H~ug7TZRbjHfmq2`fLRinhET4a6=nu6kcA}(e;eH?!=nM zRia&k_;#8_^4nEw@-p?Ke!T)79Z`*t#8xycyz{i}ep)3Ma_u0jk})ENI2DErWvBpC zyZjfnjc5aRjrWy`wAOqu4k0Q|C+CLdZ=KoQ)GPE6c}{NML!dBO_E_r$uW4jNVX(^D zBAcp1xYP%&a~!8G7u&gTjE#7}&S=tMj}O4Aqe$|3@}sxlU9b3SRDUP+g?~L|$(3oE zR{8*Tw*OS>J661Yy}${DG{pP#63;Fn-=Z4o^IiQ|^E*g*rP=k_g6HdCC!|{_KtH8+ z9l4Th%<|s;30MA4lDr_69_M!W|GDBs zYyn{@KOxxFQ&uVRWMYSiiRxB&&)r!%jpycJTFvE6pxwinMND*jvpRjm;=cMw+?F!zw~N*Fmae8%>jZ&TXqUpb{PKM zm{TEI%Z3wPZx?^Zd>t%GXw}>0KPP?IVfgGBKN3q1@S7-vm;4ntDJ`<*(3Wx-Qk%j^ z0VAWAJaE39y>3hO-*!A#M#8^ffH}bEXPWsTVQrh9_AXN=sX$B~)&d*&X#Z z{|nx&lQ_Xn#eqJ@szH+c-?Z_neEV$4cq7JbHQ0YZl&)nrf8Pvpc4I=d9ewyG+@gyo zzgazKp6fXEoVVc?BbxKR$x`O!NST!$aR2UUo0nXuhOEaW6agexlE)lJ=zy#SOjg`=SoY+BYReAwB2Ba=4CUnDc#b{-Ac(J^pqZ8((&fk^Dr7F zT1xECKK)Sko@Hxs?zY?;iPt>YD(^11gzZ2)^ysaH{MS{p?Z@W=KeFQ#+ZDmq9i>u@ zfQ|t&uD{-5etiu527jJ#Ox!fl8Q6lKsMc7ekVh!_gp2P^`?G(pcuu?{TH^H!y)OX9*@eRibukDZ64)Exn>d=2#k3G~fz?$h86ys} zW}8tp=B4KM6z$!Wu$psYCP*g%L829kvU7LW1XM7L<8ExnBRvD|mhgaK7(QmC&ChMCAKF=Yfs^kBd3f zF-7EKX(s#|dB)t5qpk@eZ)#dnm>7}~XjcG=mrYl+4n7|{QIt^Q9%kIuwTC>)u`!nC zr|X=3g!NIt{`FPhGA(r|%39>5vsDvyt+ovkLp(GN*^7|@%bibnK!*6|u$oL2+>}OR ziW zM=8u5P2qn(oF)y*7|6{r3U`a#!q@XWng{S&X_%YxSZ1T$>+LJ7J>6IifOVVtss!3Z zr1i-A|8MoZ(*Yk#DF>G8Hv>?bXg3Ow1F zZh(k`h>4zE21xTrNrZ8c+Y-Xw_-$;q(bbdV00(uqs$RsMKu;R7QNI=1#MqGDdeO1U zHuEi0gG*lbH=asjWn!A6h9L}PNxJm+yE}lhnT)F_W1U6cel6FzjX*^QI!7#bm=}{uGkMx`|6#G8g;uRZK-ujnm@1Y9Z)|G+y&| zBISC%pvPH1B{nY{8{OAd^s1#eJ7qsiEroR-taA>WF9p}6?XjB z#{0-|>@^iQf@jOvii+q_B#3cwt2X>DmS-XeF`*o~&{*AR8#}1R(mL zM9JlnI@JIJc?~etkYO-ffN~X5^#{JbDdADL;XfSbcr=*)4;Capy015LRyOsPqH0nb zeU~0rzoj?(DVjU3GS~{na$98; z($jjOd!G`@?h-y(3hL*BS}0 zOdDvo)nt>ONx!tydn>bV#zPISydYjAuF8iv*AKkTh;R6Qj{#-?hlS(#hYCP10p_)1 zHZhJ@oGsKlJ?!wr3QSf|GRat(lCeh4${%==;r0F?(c|I+05SY~=t9baZ^A2ygpAY* zb*5guHP#@nz+e{byUMjBpRKtyqyfWdfXVI0dt2<+gJXMj?YqDJOI{@kx0lHmbTCmu z$}@f(9WaUfDuyY?WEXnJbeMRDgiYc_r|oN1e{k%e$My?7>$ba%N^T?53$d=(~m?*47M?Y$rJg~ zr@ilOs?^QhN7HjV5dz{c3~%JRrW%uVqtCwRwT<=?JX})fSM3>>RgoV;gfBrco-95w zqsZd^x#Abwu-3yZ#dh-Er@PRe8Bf!9ph<)@+k}oG4pqADVdlzt*AsuhYyc!Hl{D?I z;}2zu_0xftoOAb25nJjiMDE`aJ)RLhE*3LeFTjl-Y+_=f#`{9n_KHlLGEgB_rMU^m z^i|`AN7t|=eJyz*!ncX-&q(h{qLB?PG97y{jqDd!JewX6VxVs~$`lP~p5F7k7;cM^ z>#0)AyFfF5!pZx-^td<;oH97PM>6%ep(B$JfzrmiI@)Ib!BkyoC^fC4qOJ_e7O*y! z^5HWtNnd2MaQq;#kd9=qJW7qDXDA} zhE%iwQ3r(G3xbiZHn25*9fK&6X^*i)hg!2OYv!Ksd&76g&e@i_9B2%3GKQxZH3$lH zB|CzocS>9A$1yENiNM%sEJIEB^*3+EtN)?Q9udFGn|Ibk1_F?d8CZ4~{LfJHp_V^H zy9iak5#KpMRU>p@;e~kH6%4sPtmeUsrHQHI+d}^->*!_==(G8UwoXTDR zeIn=S1rQKGA^hix4|zX4;IY|ePhyZTWRVOR$C0B)XPDVT-G}Q$T?6zvIPr0)k?`ol zR6i~CJF~6cEB{<^l*ys%u0-Jp;=vehIwpR%R+Uc`FoDS#w=vD>c0aC3pirHCMY{(A zNp7(MveHR#Pc#QTaUlA8k-QgPABOIsHrrn_D7Oi zOQa0IpPEb%SWX2pGuN2K&5UatzQQ<>2PP_x>?BgP9KO?S(Z0CwaH&?_SOgZuFxe4} zO2mG^a&_+Cj%!ksd5bJSF;(c;T%VnIlZVU?69;b# z!aaKiA*a6265^Ii^oJz2u%7fsFjI-VB>(Usrf$P+aEAze%;ioV*(e~<{Qz=W2pEV~i*@Y-3Ov(R< z9nDvLg32)6FYZCVATw9d7n?$?e+%KyqI+x5idY`%Vw;~5b98>acSM0aQmZ<~)QZ;Q2}+v0ToFT+)JpNpNaLjgiJ?9f=k93(6uF5R zE*#8!A+Yo%EndCr`NyVS_-_1$8A=r?bR7F9GnT*uMhw#Gu>61_kB?_2;RP;P4DZ_= zi4~5)U^p)I(W@DogPm9lOZuG;F5&KPr^#pWgy2Od;f3Xmc+iOY)`-yCRZ2l5be;&vMR>vRdLl_qGNL30*s%_42^yBqI9yE~yPyr^6>>JOL8s904?7tfk zYI=~sWE02-G744Li7Be;stRMDh{qq11Dc2=XjA-7nhTFM9}rQg+c~(To)i0`G9g(F zq-*Yt*?1S&EPo@dtJ8bK00m1++lIt$ZEunHhsXAf?BkiUIwFu0y$jD}e}yow-u%ps zKqZPLvlhsRNZB4bniZ!u#L;5n-uXipI2w+`B1D|DLm+~K%J_oJPsf(t?jFCpN>(9j z{jb0++%}=w0_=8JmMI`%y4uCOx+ppp!fNkNK`U+S&IHJrzw=^)XMNl{MSJL-I^;ko zbybj$(T&$B;(lO^A9&Hv&dTkW77#rngFf6+CPK!K;argXk|TT6-A=ot@Tn6q4{Ik# z1?~ZE@TxOC1qF{P)=*2OErkZ9Js&*vWfC3@gAH?9^U2NY?Ba(eA|mF}X;q}2yEO>C z0PgA!lQvJiNQsU9=FF2j)gqQ1lcaS3h7Rfz$oLO@#a$m<=o)!AG7xyNKxt7aXxi0x zp$*P)IA&^!XL>f2CSDzNP9D+*DQAHo`WH}U2GMgfr^ zl(159DPhruS*7=bvD@Rd*DKy=?bo)mrs&_S*`s|zPvPSW^d0k&p#Nuur3cjx~?3bn?u`wp8fFBN#-Rv`2gK>GFL$)6PQzxHE$YxdBb}09nVJ?YHmy%22Ua#-wWB*jA{1$ zG$|aMS8nuS@Yo!V8P>r~5Dv&{c`hk9awa5rp)-Wco0M$t@;us$l?UDg7bXP%FD8Tr zzc0aCju%}*5p1tAwn4K*O(BSbtP2&pU~b%FVsf{C_|$ufXgJcth?!(l4u8JUL%}xr zu~*{NQj~+3lqy&_sS5l^gd*MlR5UZ}CW8w?0b7|+)py#3&b23+3lk~3(O2!sFzJFM zei`?^4ZuWmzvFmXBm+4OMM9MjYJhA;Sw+nZH4Cn6X(%O=m9Q-!)qqmu8Q#y}{Lbb; zlre0F3AZE@hH0}+)%9k#nqP~U-2x9;1-174VnnqROA)o*Ek^b z)4;1!e@eo>j#MvFue-e6A7tg44dm-aS@u3SmISX+(IAuo|ECp{+iPacoZhNn+Le?P z)wOS2`p>DXFyc!bc6WRaIpu~H`gN)YUnqDVDByG}=zenHV-jUC8UWyit-I6_lArj8 zB1Zv#grgHEcggkZjnN1qG9(&~~LwtF4xN#Q_E$n5c)tYomkv|bF;S6jgEb3B*# z``{(3mZFH+#Ly>o0*%(i-VQ>eqPW%dFP**0E_5wO?W&cu82 z_k*^j<}#vhctvvhUx4yd0VLIzH$|zB9tjBIcYZdaX@nanVwhBV%p{j-RxRPuw>L$! zM0UJHzaOXq2c(T|-ySZc>m7%BuMblmASD-8RZ`*fuX!4cOejF(W>hwtB zT=+^!Z5nba58mIgtyFh;JG?cj>hK4!);u?oe*`x%KL2rn*|(3fG#IP~c$-Rw_rW`4 zXxmxu*~`#5w_elRVE;+9qg+Ud>0JjBzhh$^i$>^`%mIbv6%l$vC)8bVtS;C@)=RH{ z8IRyMg&m_QCd)61`^z?qR1sSN5WbzV9!C^1cb5lnWZM&CuShdpMlr&A?0Vsf6| zy#0Ag#rd&0H#kxZK9R#|q>HJ~=MU$GHN3qcDTqWOU^`-VG$e!{?is1e?@iIesJ~K6 zo{|Oln6E`M{@U@d3vOuOEJzv10TL*^iSX_jpyJ|>I-%oAwIAhw4D?`YBRMcx!VBye6i-~Wi{?mQDys8cdCAM7-AeVMv zG$&yE_JayGR$W2eQ2EJ{{eZdMSC>X@0s>xh)s` z?M@+Cl;42!7tx^qGDQ)pi;3HQEMPoCPt`pb5u5wdMyX6D^^t1yHYVKYVlP;Texoy(-_XP65+l>UyRg z*(kqp%`+Y6Z@+-kb=PpxVZeZZn)KlT=H4k>wDV}_L2);Z?SDf#!g~AoAq{t(r&{C= zR)y_2S=Q{G0Tv>W6>}bZd-t{1T1G2;9dwW6O{CTYld0dLu#ViVpY62WJGy&HEHRaw z5c#84jOl;L*GXsSNa&@1z%riQwbS||%Szb1&>h@cVEBkfuDM17Ry+s9rf9~Ywk5w=TsN`Fh9~1a}T&aQI^yQ!Ono9X<#wtTVqC-y`5+5 zKM9hRNj42x$z_GqNL*c`yAI zlOO1-$YBnk75dU(@JiR?Nt}*s;aC%yb4#9Uh=4Y(((-TlFG6`mJ^1H}u#_>#=0FO@ z<$3JjKxUr$oiiM&2#GYH-^uM8KoD(wRhGuwf=1FGuN%2C6_)LSqq1sP@8Iv_g4yov zrW8Lxfqwu_i0Af;T()4SVuhI5u%+&oi}08RyE7|aXXXTB+#T~NYGica$)qK*z)eir zp5bL47+T+C&47&gy(#(hV@H`X^P(23oO|8V!?drv3 z^*uzFyIZ|ZGTV%osl+uwzG7ua`_#No#ezw1yvz8>@qZqaRaB<@pG;8yjkdo6UZBRw4I0;hl6jcLBvNoOx!Z5Z|h75M$1M0vvHiIFDujL z9q`-w{peo{1aK7kdtKY5s9<#{MHeIijYY_#wTI$aaxDjH@iBjHtrK$Je3BLi6~-|U znCH3$oZ7$PNta!Hr-`x}G{cHAjs3EOPTu}XAGWlvRY8tGmbX>k-cd)&8zZ+Ka7<6# z`OJ8;r^oY`!AjWLgE@ID*7u%tf7g1rSxE0Z1EI346SH`oi9Nw{w{ZUQ8tkozEILQW4*7*EoslN@g^rhKRwDgBwNR@^IoA zj!STGZj)Z~!}j*Jhoo8vlkvfJgRP7sIx9GOiE}=JtG@yI&EVKHHVksX=j(!3#4apY z6@H*FfH$mo~`;5&y3v;jSWk7|zZ~teP!aiBIg!(db|M`b4^p}f#AklS7=&(f?Rt*4hjzhD0)KeV-Y zf+ipLgHKZZ9=7cDu`^>)ySWiuWGf~s^X_tpRQ9rw^LF{^ zlbdgF#s)PuC!f#i{h?x%7&%x|UZnZZ=Nq;)CrrK*sK`@e8(E0HgvOPe657IyG;Fk~ zqn;bac5jJs25p{610JN|7v5Z&KTj>Imd)f;-v%e2X**aBPav|0I*jv1wfD7wX)NgR z@`2mDv|8}z3Q$FWPANV&>+6`;xf_2o4~l6-U$cA**!{Tz;va0-=7fS+8!oF{X#;#3 zP#}uSsHL~NpKIVq%+yh~lgt7l0EaN$E{AA4y(ULmQ7Uo+GO!%<9nU3wXg=Tc&v4N6 ziXg#=z^M;m^x`N!5sbuIWK3>8+j+w+URo1WdAul~p4N5|dzU8t1$!oB)pl|o8Q*ZQ zKP!`+HypE}4qgI4m_dUpzPlE_5oEV_1pB`~_rw?RP$m~vzB|nN9&d4u zKT2j~4dzDw&=ve|Y6T9Q2GmtGNLa+oWN(U>Oz?EXdn+6pF|r|D8sH@UjDIgwrwaCP zvC^MR4uawFBm(NNn39eh3kg9=7p$2hdEMG(15Th~);<};5nC$sUh)aJG2*xi49Hm5 zl}TJ%T1%=O`l7t`s91AXeA~>mAA9Me-D4-Kh6sWLbRI=90x}{7iii^Z-iP#C(9l@G z(**-AO7A-JkZOzyuRkKy})*x=d0^uwlMLAYtvFN}>PnUmP9CP*wf^~u&`FMo^>gEb0 zS1C;;-wQnbN$7jHgglC^b25FK@@@&>zTS$!DXcWulB@Nmd=5@vK3S$oM%f-SEHX=d ze~PL?_ym@@e6jup`EI#Q{9=SrgN?fWF94Mc$g&yd{HqcVwJcGL%69)7=d#;JzFCaM z;N+{h&S-pbC2Pa`!f1yM?&23WLzca7;!TdCnE{(Y5wTD-A9*9+DU4mruRt| zfG6X48_B@L?7FnSOrWhdT0b_9Ve$XKAPR`;XRGo`d-5^qpj`|M4EJayE!772xj5oF zMx*~<9Bm$Uvj02QJ?&T}ddwWR>DAj$VopG+SsI+ZYNrN4!I%5yF0}(9^6XF10+0)v2H)n@VcQ8s0yI zpWPKO+A)X3&&c6=IU*uQWX+#FKhZ0E441oMGvCafmJR$N`H>mh9)V{#J%+dgn7dWs7D1B%u)@SrdQ-$@>3Cl8wiB$Xwv0@04sSy{a^8g{{CG_(wS2$mvjP zyn6hTk_oF)LFYWAeNh0`WH=^KC6AUr`!YHBtk<6yqKGe>GVD(rJS5^7V<2@cc$RA7g$*F_B9)6pg7}?2ZUo z+jN6@sj1Ib9I%K#9&umw*bT%VO&j4Mk1-WN;EOT=_yA@K$Bw#26mEJ)(iflzH7gOD zCf_9ro)kBSO#KJJ)4ETY*k&k)^7bvFAmRvK-ImKL^!B>c{kaBBKR@!ZsQlB(6#q#j`;qT09t6$XxJZ4bWzK>se1<>{FX7Fu3rQ&Oz!+n zC~|@icWrJx1UnpG#87jWY01cUQOTYq{3?$miZyhH9=`FT&p-NMxVt07bY@G6(Sb4- zsn*xzAI1lXVwk}{bnT<^g*j`#39fKK2TXaMb+qWbj2X&fQzb`411@|GGX~c&?C-Us znbVg2np*yCoO^f?+JpXH$k87#zwH95ZlaH!jPy?rFVOk3v9c8sl{8 z4%DqHvF+h6MY2}at4BM`OLIH#a{FJr2vF+M(ULEsHJ(pZEOb8HkhL97*?r*TfmG=| zM_vM1sOQVE2WL0>X1tvyJuo*{QEOBf;(`^FB^VtU!F1dVsBH#E>QA2EOc4d!ilT{h z9ouC~bMK60^RgkfhTrUBodd~UTm&I`b6(uvfdt;ukVAqDSIiH|dKhugYwyUeiHO++ zLycRty+=boeghNH6;W{$wXgU;$2ke}hUGH7jU=KHF-4#qV9zb@Nlns(40$>W`M9Xt z)-&F`6c^kg3NofVF}B+SyX!;Nhf}w zZv&mdHXE3v9b%hw*m6|`o5SJ8Xh&Nhnqf2odgSiuf6VM}{;qXP1q|2b#m;1xia2bw zJ}iXayVem^T?<(ICyHT$<($4!vS>mO(4=gaVQgG$zIJn9xSgeN+<|o$kRvLh;{0Lh z;r|)0LDa)91U}wh+qH*z9{yXdBA$EleY&Pk79(q&u{O@fv=OX z0LWMp3>6{UJSyN(h2ztUsJO^#NlgMiNNm_%m;3hA^TZmX6={OnL_1G|$PQ!)5`Nrs zn$3E_NDRdW&p{Q{x3{J2Y}?mOV}GvTM^iE(K@MVF(lWIcBfuk-__shK#f+-h{Au_$ZL{8KK>tkz2*vz@3!fm8=9vG2uOG}rRH2sh z6MfYwMhZ@?GZm~K<5#7RxcM}dpBxmY(l zn(@byHkO_7!^{ThG2uYJMJmg0jlX+*pq{p<2h*|t1p;~673N|Z2ziyhxCHiI@8zt% z`%3a&SGaFS>SZ);Nn_;*vJK!Dz#rh*>fNv0-lZ=`gv4aB|Dlq^fIWV^1~eG@=_9a7F zF>^dr&k*ylUiz=T(=+o>nHK;b#KefuNR0m$G5MdK1K%FsE(XCvsA|$u|CrKkmT!8o za82)5A#&R#t9A24Xm`z|9f+`uXGm+%+`BeEI4nG5`apw4sZh~{HXgw$uWV6B;U`N(K`>h5W_Hw zFa1^9p{e=%ck14%sA@ft12M!$0N-GgRk)9QfmG5R{Nx)$o8IpF0IbT;(``XO5}zRD z3=VeS1jO;6n7c9MeYr3p;wJz5KUZAmAL&-;Ykq;}+H&4ZhwthsKH_LvZNp%Z^GkNh zLBYgZ7TRkf-2PboHfUjR>6A{bW=QVcsf63Rnee=2v!Ui<>;yd?$yvor@?zr4kn^tI zU3yN{{NK}~Ud_;r_ldeD_Doo$*#n+eQd3|Zz4TbuuuAPURq}J6fL_VJU))7sj7^!Z zxI3gf1!?pvXO{^rft zga%J2C#lsK-j0l#vv+Ni%3#)k?bL_6rxy~-f~(CZ+ik#@zA6>yAH(4j9H1rXAhF$x zeT2)gl2}%vLBkl3Km_79tN0g6>~{WA*UGjm-)GD=n#}XDgy553d~}HxH(R+rcz1L( zB^|?EtgG&&aMXTkSM^lkXI=bjm^DV2mMZ!o+pJ{-TlP)YgYsq8dg)Y*4@BHrQukh; zfqY%LeqHgoh(utjb-;R5&>QU=3AmD>6W`urP^s3Qr#&L)23#`+FPw6AJXHVoQfkG0 zoKmP}L0>GxsiezA!+wv#4YN5_7bd>J4AcDzzOe>e9*e7Q*X}jj`&8q0-j@Fi4NNcK z31Z^1lh#vJaFMcgm*ECPzw$*k94^Dy*~E;WNDm@rKZe`Qu8NsgWsUL}c7# zeP05rcT~_nT9nC3n>oGU(U!tn=m~6$jU>smJ@}!KBQcbH7zmh7W2J({TJY z+R~IBq$Izor})^nOvi&n5MP#tA)o%{itB}5t1@jL6s9PEiQN35BT>+%t}*uC2l?=wZLu?*5Uwhv54)Xs{SM#-Nm>?uQkt{BNikB}OrHR)Ou2SliKtn~iQm&!;Yl|%<@ zhW?f%%pR_k$m$R#l;&;c2qI|l7jI96qlVnoL)v6(g3$}QTNHEdXUs+)OPf{#JrmP zNji}EIx+OX=@IXBikr;-7m4UtM#!KISk|EmcAZ-n|LEMem4ev5w5~w@jz^@o67*M|Hisa=u!dg4v$vc3D*SSp6?_Cn2tN7=6YN>W^tsl&mCG(;On8|`j5)xx_ z-hyZsyk##hJ7M3e=Sa-t;*IGcr6sZdpQSU8Yw}9lKYiP3oz~iFTRRG5MuJ$`T4YgK zQm5m@kTO)NvcpuM2#5?|gs`O_ty;AvLl826Cble!Y_cx``Kf56L8E{Hfi27_x;RY)6OV7&w1{1&V66k_lj@@ZE)t6Xv!uW6DW*F{S=Qdf5V|ym_E$;a|6Zs zdQAM;4KqQtRkdgepMz}yzYLxakxU3rztJ+;39CcWVOUB=;99cOV?uy_S^eSp>_^|M z{;drs9vkL=S8pRh5iM@lEqYZ_Z zOx96qJNqYT=y1PRS06+;E`zPgVidj%p&v*6eo0t$OuTYJ=7BykA`MrG?)ZtQp-7h! z!IWw_AJCnzE;W$a14|}!XyZ7I?3S$V+}bIstw&>XIsnl{vI;EYlcaNF5Y*krxbe6C zqaCGU7Eo(CJM1o3JA|7W`To|^ifjCEpX7^~lvF49@HuOIZa4G4ZGW;F`MaYG$CDh% zRzYMm5g?LVNNH#RCuqfes@lE`*L8{J?{P606TxG?IsT!?y}3A5vOGPxz}L4QOczja zEHR(;Yfp+S`iespL`zirg0x?U4dYPHhr9Zgok+3jz}6KSEw!GhG4_kah&(c!xi445 ztXVL-d}um|KZ={O)erl58z&gHhkM;4hF>ABZ&b~SiKI*=W3?+H;_)905_6HvUjLqWx)XSrK)0YU&hh0pJvqtS5lt zA=Gc4T`96-?lr4YPEB*-4E3QiB}hI*7Jb=kv9Vu#R6uR*i$Sgi)m#dha>Es!F+6~n z4Cs|j*H%2|cXSSXa`jek{JDn?ZSm)(<{#Y&KRiCKBS73hW@U8fqy~aM)%u4UkA<$08zurX*+Gulg;kTk6{vIn!59IbL~}R+gggTaadugtiR) zurR=B&W4rd-&L9ctyIWans3qHSWC_mUJHmn)u~BIaJ>sT*tRY**S|=IDtFvqsd*L*v)=Ys_T_OQ-gUp=65^uepfp&UdyKak83nNQ#GP7&B^7> zzU)A93&ivTC#9I@Z#%mxJKZ{S`0oZT?&Ay&yfcl;hVG9gWGL8Mn``bn%&XI0;T5N~ z3g6}RJ)%Hs-R@TL@-!j}S8TJRrZZupV^hm>UKp5sD@i7}Prfm>o-djgwV~}c9f7N)qe}dex*g?0Q zd8HK(f4Inl{C!;w?ZL>nR*kK;B-Npc;fVSx>h`R8jLfZ_Q$@wufGK?!7!)h_m8BBe zqi=iN@<1?r`LhQTQ`AA-tOhFyZ)DPnQguRa!+*>Soa(Rg*4>hw9c*1V1&0-fge)*+ zd+4}@sM>J7%3@dzuJwFn`$w0i6P#Tq4lVY&DjqAnO{)2N$M_)xMn+lj-s)95Eb{{o zB&*^<@?_&9P$ylC%2U zmr@T>Z~;zR3%-#|-R%va8>UKjUL8i9DIlF#AR>_l{LB8IU5r;K0aYC)a;UQ#z_Qf< z&-3dQeI`MupxHNk3%J&;^Pc}N+4HfnO$Bhb+V=$*+Hl6v6zUKo+awxK8a@l1UrI=# z*(E23h#cLoiKsv?aM3)3?1iE(6{i9P4(||F^Jlf$mJ0w7CgGGdoe!(7;H$SY3Wg3) zQ@er9L7Q5)J5V#~leMY)VDa_M&?Z|0XUovnNN;Z14*GIuN_PQPJ|#6p$*}v(sU?JH ztML_c3nz_}9FEN~jI)EHtutdWbt0~Hg)k&x{Uhd+=77JvL@s6>t|A;O2eiV8YY;b8y+Sm}ne1AN|Wg(D6d9 zW3!e~Q~W$|D*nx26gLjb)1P8d0q}Es5IHPJWJ zEYR6PV%uUdz~47h+|UARTd9=T)1M|v+oQ0gq4kDGOjklfXVWC(p;Y*8M3^bGm?Crx z5lbpiNoQBGlIu6!#s4U4A36JwZBQn+`^jRFV&fQSwLs+g_*;EZB20>}Jhkr#$515B zhQ2j35^rOsp$t}QUE4B$VE{aPp*@oqHszT74X5_!}sr~jp#u|khu4o_|Z z_9h5+z4hB0n%d!Jl$ZOd#l$$9akRiCZ(a6BX)O^uv!ktNEwQ&PiI<)gg$b#`2wz%_ zzn5MG9V39wHm+P@RRiHv9RcMz%r0}`6&nN;yoTZ7DP z!Ko*)zzXf(QkO?mOsxIapZ`8BpsjcmJysS~r26d#`@dEwS=l@*$@CXE^|YqSFRGmW zGf4|SE73uC2DeTvGEXu)I0{a8brZ4~g;8XLG@Tw0$(+v0tEVSZg-19I;F!v^%U^C6 zWHl$8gs3F^EWE8wVt@dSiVDYGJpGOu6Yl9iQ!Y?ohaBMMWV>lY&R^f0d`tHU1G#}} zen-|d8qgm_N?#lqt#Hmg%m6^@(RIG6075{?_V3${%)xKm~!*9awTq8}{W zis-i&=b44_6)}J81tVhwWR5v}?Ve&@Uh) zX!4V|fX`p#)bl5&IzRN)D>orff$%K6+M}rRM>_?>lGqV7KLtC&k*xF^Mf-0Fup1K? zI6EwgBKY{F!M69FCuj6A>;h|0jis3HId$ctVk7D4cWiMv{f5!6nEG;LSHhn@n#(d8 zz3P#?jam4q<*(Ye8$>~O|96s$EOPivVl|ShmBq~aTR)%&MVkFYV<#HMutg!47ob5S z+V1z=A00d6@+S1-iQ^{x33n?TI7GmNr$idRlhSP%X2k0zj-Ne5e8y>>l=41}P&k)% zN9H1CVb3@GHin536MdOh{fEKKB*kkbHCb0+C{P&Z;jQ`7-$p)A0J@C}yZ%S~xFxsY z`DwK+d-ZVg9iLZh(lPy9IiRJHf>kX7Ldn_nnVYdC<)H?uPaOD-Ml+3GeW<&oulK{d zt}@T<_>~`JBiAI(E(t?ZRq?=TU=McQExu&Na}zMr=)xX1tp$=~C7_|;GS!BAiyRdg z>tyUrtH>a)=cmM#4M&rI4UFd;sHrWl)uxDs`#ETa0DRXu_&Cr0YR-fYW%XN?9_IKD z4y?_5g#u&vRu{~R^iO$OkG=_Jp^@vrXC~zpyk)_QgJwD>`u`o|SJIkOR|+5dT)aq~ z(<4!EYd3~eI|ilP2YZLEv=aUcZW`#Hz`ZiRp666+=kKX?-J82W_CIw5EHot)ulAt1 zlE%F8a-fVb@eJcbb5hh-V!hIK*i$ zF%v$9?g<&kn04NPCIe^j3DNnaEd?iBOt>2P<58`Z@7}?N1*}qw8j5C*E))*8200e= zD&I$uoJH&NWl&~ePbDw=&1~JH;ZmHqfDQ(O5IhQYN7m`K`4eqdfXb(L!B5%B+pPi80k=mVn7KZ3kIe>?eP@Ox=qApFO zOc11+odOAQ#thn>M<78JCCB~#EDw#WKcCd~F5?$=OvGs>

8Q4&zW9;0tfQKj_x5 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

Vv_7|to$H8il8KpS81d1 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
BKxG_$m$BCSgzCmnCHGum0fvh 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 literal 0 HcmV?d00001 diff --git a/bg_img/9.jpg b/bg_img/9.jpg new file mode 100644 index 0000000000000000000000000000000000000000..103e60158e54fab7798e773f9f16ce381714a580 GIT binary patch literal 112154 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

pBCzsxxr>@)Tt=y33p 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

+hdzptf4T*(SGYT`C8f3pbQaokU#i!t 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 U>06j4>VA8@HB>;U{_o5G0bTDN4*&oF literal 0 HcmV?d00001 diff --git a/config/det_DB_mobilev3.yaml b/config/det_DB_mobilev3.yaml new file mode 100644 index 0000000..043d391 --- /dev/null +++ b/config/det_DB_mobilev3.yaml @@ -0,0 +1,87 @@ +base: + gpu_id: '1' + algorithm: DB + pretrained: True + in_channels: [24, 40, 48, 96] + inner_channels: 96 + k: 50 + adaptive: True + crop_shape: [640,640] + shrink_ratio: 0.4 + n_epoch: 1200 + start_val: 700 + show_step: 20 + checkpoints: ./checkpoint + save_epoch: 100 + restore: False + restore_file : ./checkpoint/ag_DB_bb_mobilenet_v3_small_he_DB_Head_bs_16_ep_1200_mobile_slim_all/DB_best.pth.tar + +backbone: + function: ptocr.model.backbone.det_mobilev3,mobilenet_v3_small + +head: +# function: ptocr.model.head.det_DBHead,DB_Head + function: ptocr.model.head.det_FPEM_FFM_Head,FPEM_FFM_Head +# function: ptocr.model.head.det_FPNHead,FPN_Head + +segout: + function: ptocr.model.segout.det_DB_segout,SegDetector + +architectures: + model_function: ptocr.model.architectures.det_model,DetModel + loss_function: ptocr.model.architectures.det_model,DetLoss + +loss: + function: ptocr.model.loss.db_loss,DBLoss + l1_scale: 10 + bce_scale: 1 + +#optimizer: +# function: ptocr.optimizer,AdamDecay +# base_lr: 0.002 +# beta1: 0.9 +# beta2: 0.999 + +optimizer: + function: ptocr.optimizer,SGDDecay + base_lr: 0.002 + momentum: 0.99 + weight_decay: 0.00005 + +optimizer_decay: + function: ptocr.optimizer,adjust_learning_rate_poly + factor: 0.9 + +#optimizer_decay: +# function: ptocr.optimizer,adjust_learning_rate +# schedule: [1,2] +# gama: 0.1 + +trainload: + function: ptocr.dataloader.DetLoad.DBProcess,DBProcessTrain + train_file: /src/notebooks/detect_text/icdar2015/train_list.txt + num_workers: 10 + batch_size: 16 + +testload: + function: ptocr.dataloader.DetLoad.DBProcess,DBProcessTest + test_file: /src/notebooks/detect_text/icdar2015/test_list.txt + test_gt_path: /src/notebooks/detect_text/icdar2015/ch4_test_gts/ + test_size: 736 + stride: 32 + num_workers: 5 + batch_size: 4 + +postprocess: + function: ptocr.postprocess.DBpostprocess,DBPostProcess + is_poly: False + thresh: 0.5 + box_thresh: 0.6 + max_candidates: 1000 + unclip_ratio: 2 + min_size: 3 + +infer: + model_path: './checkpoint/ag_DB_bb_mobilenet_v3_small_he_DB_Head_bs_16_ep_1200/DB_best.pth.tar' + path: '/src/notebooks/detect_text/icdar2015/ch4_test_images' + save_path: './result' diff --git a/config/det_DB_resnet50.yaml b/config/det_DB_resnet50.yaml new file mode 100644 index 0000000..a2b425f --- /dev/null +++ b/config/det_DB_resnet50.yaml @@ -0,0 +1,87 @@ +base: + gpu_id: '0' # 设置训练的gpu id,多卡训练设置为 '0,1,2' + algorithm: DB # 算法名称 + pretrained: True # 是否加载预训练 + in_channels: [256, 512, 1024, 2048] # + inner_channels: 256 # + k: 50 + adaptive: True + crop_shape: [640,640] #训练时crop图片的大小 + shrink_ratio: 0.4 # kernel向内收缩比率 + n_epoch: 1200 # 训练的epoch + start_val: 400 #开始验证的epoch,如果不想验证直接设置数值大于n_epoch + show_step: 20 #设置迭代多少次输出一次loss + checkpoints: ./checkpoint #保存模型地址 + save_epoch: 100 #设置每多少个epoch保存一次模型 + restore: False #是否恢复训练 + restore_file : ./DB.pth.tar #恢复训练所需加载模型的地址 + +backbone: + function: ptocr.model.backbone.det_resnet,resnet50 + +head: + function: ptocr.model.head.det_DBHead,DB_Head +# function: ptocr.model.head.det_FPEM_FFM_Head,FPEM_FFM_Head +# function: ptocr.model.head.det_FPNHead,FPN_Head + +segout: + function: ptocr.model.segout.det_DB_segout,SegDetector + +architectures: + model_function: ptocr.model.architectures.det_model,DetModel + loss_function: ptocr.model.architectures.det_model,DetLoss + +loss: + function: ptocr.model.loss.db_loss,DBLoss + l1_scale: 10 + bce_scale: 1 + +#optimizer: +# function: ptocr.optimizer,AdamDecay +# base_lr: 0.002 +# beta1: 0.9 +# beta2: 0.999 + +optimizer: + function: ptocr.optimizer,SGDDecay + base_lr: 0.002 + momentum: 0.99 + weight_decay: 0.0005 + +optimizer_decay: + function: ptocr.optimizer,adjust_learning_rate_poly + factor: 0.9 + +#optimizer_decay: +# function: ptocr.optimizer,adjust_learning_rate +# schedule: [1,2] +# gama: 0.1 + +trainload: + function: ptocr.dataloader.DetLoad.DBProcess,DBProcessTrain + train_file: /src/notebooks/detect_text/icdar2015/train_list.txt + num_workers: 10 + batch_size: 8 + +testload: + function: ptocr.dataloader.DetLoad.DBProcess,DBProcessTest + test_file: /src/notebooks/detect_text/icdar2015/test_list.txt + test_gt_path: /src/notebooks/detect_text/icdar2015/ch4_test_gts/ + test_size: 736 + stride: 32 + num_workers: 5 + batch_size: 4 + +postprocess: + function: ptocr.postprocess.DBpostprocess,DBPostProcess + is_poly: False #测试时,检测弯曲文本设置成 True,否则就是输出矩形框 + thresh: 0.5 + box_thresh: 0.6 + max_candidates: 1000 + unclip_ratio: 2 + min_size: 3 + +infer: + model_path: './checkpoint/ag_DB_bb_resnet50_he_DB_Head_bs_8_ep_1200_bk/DB_best.pth.tar' + path: '/src/notebooks/detect_text/icdar2015/ch4_test_images' + save_path: './result' diff --git a/config/det_DB_resnet50_3_3.yaml b/config/det_DB_resnet50_3_3.yaml new file mode 100644 index 0000000..ff52b4a --- /dev/null +++ b/config/det_DB_resnet50_3_3.yaml @@ -0,0 +1,87 @@ +base: + gpu_id: '0' + algorithm: DB + pretrained: False + in_channels: [256, 512, 1024, 2048] + inner_channels: 256 + k: 50 + adaptive: True + crop_shape: [640,640] + shrink_ratio: 0.4 + n_epoch: 600 + start_val: 6000 + show_step: 20 + checkpoints: ./checkpoint + save_epoch: 100 + restore: False + restore_file : ./DB.pth.tar + +backbone: + function: ptocr.model.backbone.det_resnet_3_3,resnet50 + +head: +# function: ptocr.model.head.det_DBHead,DB_Head + function: ptocr.model.head.det_FPEM_FFM_Head,FPEM_FFM_Head +# function: ptocr.model.head.det_FPNHead,FPN_Head + +segout: + function: ptocr.model.segout.det_DB_segout,SegDetector + +architectures: + model_function: ptocr.model.architectures.det_model,DetModel + loss_function: ptocr.model.architectures.det_model,DetLoss + +loss: + function: ptocr.model.loss.db_loss,DBLoss + l1_scale: 10 + bce_scale: 1 + +#optimizer: +# function: ptocr.optimizer,AdamDecay +# base_lr: 0.002 +# beta1: 0.9 +# beta2: 0.999 + +optimizer: + function: ptocr.optimizer,SGDDecay + base_lr: 0.002 + momentum: 0.99 + weight_decay: 0.0005 + +optimizer_decay: + function: ptocr.optimizer,adjust_learning_rate_poly + factor: 0.9 + +#optimizer_decay: +# function: ptocr.optimizer,adjust_learning_rate +# schedule: [1,2] +# gama: 0.1 + +trainload: + function: ptocr.dataloader.DetLoad.DBProcess,DBProcessTrain + train_file: /src/notebooks/MyworkData/huayandang/train_list.txt + num_workers: 10 + batch_size: 8 + +testload: + function: ptocr.dataloader.DetLoad.DBProcess,DBProcessTest + test_file: /src/notebooks/detect_text/icdar2015/test_list.txt + test_gt_path: /src/notebooks/detect_text/icdar2015/ch4_test_gts/ + test_size: 736 + stride: 32 + num_workers: 5 + batch_size: 4 + +postprocess: + function: ptocr.postprocess.DBpostprocess,DBPostProcess + is_poly: False + thresh: 0.2 + box_thresh: 0.3 + max_candidates: 1000 + unclip_ratio: 1.5 + min_size: 3 + +infer: + model_path: './checkpoint/ag_DB_bb_resnet50_he_DB_Head_bs_8_ep_1200_bk/DB_best.pth.tar' + path: '/src/notebooks/detect_text/icdar2015/ch4_test_images' + save_path: './result' diff --git a/config/det_PAN_mobilev3.yaml b/config/det_PAN_mobilev3.yaml new file mode 100644 index 0000000..186ef58 --- /dev/null +++ b/config/det_PAN_mobilev3.yaml @@ -0,0 +1,82 @@ +base: + gpu_id: '0' + algorithm: PAN + pretrained: False + in_channels: [24, 40, 48, 96] + inner_channels: 96 + classes: 6 + crop_shape: [640,640] + shrink_ratio: 0.4 + n_epoch: 600 + show_step: 20 + start_val: 300 + save_epoch: 100 + checkpoints: ./checkpoint + restore: False + restore_file: ./PAN.pth.tar + +backbone: + function: ptocr.model.backbone.det_mobilev3,mobilenet_v3_small + +head: +# function: ptocr.model.head.det_DBHead,DB_Head + function: ptocr.model.head.det_FPEM_FFM_Head,FPEM_FFM_Head +# function: ptocr.model.head.det_FPNHead,FPN_Head +segout: + function: ptocr.model.segout.det_PAN_segout,SegDetector + +architectures: + model_function: ptocr.model.architectures.det_model,DetModel + loss_function: ptocr.model.architectures.det_model,DetLoss + +loss: + function: ptocr.model.loss.pan_loss,PANLoss + kernel_rate: 0.5 + agg_dis_rate: 0.25 + +#optimizer: +# function: ptocr.optimizer,AdamDecay +# base_lr: 0.002 +# beta1: 0.9 +# beta2: 0.999 + +optimizer: + function: ptocr.optimizer,SGDDecay + base_lr: 0.001 + momentum: 0.99 + weight_decay: 0.00005 + +# optimizer_decay: +# function: ptocr.optimizer,adjust_learning_rate_poly +# factor: 0.9 + +optimizer_decay: + function: ptocr.optimizer,adjust_learning_rate + schedule: [200,400,500] + gama: 0.1 + + +trainload: + function: ptocr.dataloader.DetLoad.PANProcess,PANProcessTrain + train_file: /src/notebooks/detect_text/icdar2015/train_list.txt + num_workers: 10 + batch_size: 16 + +testload: + function: ptocr.dataloader.DetLoad.PANProcess,PANProcessTest + test_file: /src/notebooks/detect_text/icdar2015/test_list.txt + test_gt_path: /src/notebooks/detect_text/icdar2015/ch4_test_gts/ + test_size: 736 + stride: 32 + num_workers: 5 + batch_size: 2 + +postprocess: + function: ptocr.postprocess.PANpostprocess,PANPostProcess + is_poly: False + bin_th: 1 + scale: 1 + min_kernel_area: 8 + min_text_area: 50 + min_score: 0.93 + dis_thresh: 6 \ No newline at end of file diff --git a/config/det_PAN_resnet18.yaml b/config/det_PAN_resnet18.yaml new file mode 100644 index 0000000..de766cd --- /dev/null +++ b/config/det_PAN_resnet18.yaml @@ -0,0 +1,87 @@ +base: + gpu_id: '1' + algorithm: PAN + pretrained: True + in_channels: [64, 128, 256, 512] + inner_channels: 128 + classes: 6 + crop_shape: [640,640] + shrink_ratio: 0.5 + n_epoch: 600 + show_step: 20 + start_val: 300 + save_epoch: 50 + checkpoints: ./checkpoint + restore: False + restore_file: ./checkpoint/ag_PAN_bb_resnet18_he_FPEM_FFM_Head_bs_16_ep_600/PAN_200.pth.tar + +backbone: + function: ptocr.model.backbone.det_resnet,resnet18 + +head: +# function: ptocr.model.head.det_DBHead,DB_Head + function: ptocr.model.head.det_FPEM_FFM_Head,FPEM_FFM_Head +# function: ptocr.model.head.det_FPNHead,FPN_Head +segout: + function: ptocr.model.segout.det_PAN_segout,SegDetector + +architectures: + model_function: ptocr.model.architectures.det_model,DetModel + loss_function: ptocr.model.architectures.det_model,DetLoss + +loss: + function: ptocr.model.loss.pan_loss,PANLoss + kernel_rate: 0.5 + agg_dis_rate: 0.25 + +#optimizer: +# function: ptocr.optimizer,AdamDecay +# base_lr: 0.002 +# beta1: 0.9 +# beta2: 0.999 + +optimizer: + function: ptocr.optimizer,SGDDecay + base_lr: 0.001 + momentum: 0.99 + weight_decay: 0.00005 + +# optimizer_decay: +# function: ptocr.optimizer,adjust_learning_rate_poly +# factor: 0.9 + +optimizer_decay: + function: ptocr.optimizer,adjust_learning_rate + schedule: [200,400,500] + gama: 0.1 + + +trainload: + function: ptocr.dataloader.DetLoad.PANProcess,PANProcessTrain + train_file: /src/notebooks/detect_text/icdar2015/train_list.txt + num_workers: 10 + batch_size: 16 + +testload: + function: ptocr.dataloader.DetLoad.PANProcess,PANProcessTest + test_file: /src/notebooks/detect_text/icdar2015/test_list.txt + test_gt_path: /src/notebooks/detect_text/icdar2015/ch4_test_gts/ + test_size: 736 + stride: 32 + num_workers: 5 + batch_size: 2 + +postprocess: + function: ptocr.postprocess.PANpostprocess,PANPostProcess + is_poly: False + bin_th: 1 + scale: 1 + min_kernel_area: 4 + min_text_area: 300 + min_score: 0.90 + dis_thresh: 1 + +infer: + model_path: './checkpoint/ag_PAN_bb_resnet18_he_FPEM_FFM_Head_bs_16_ep_600/PAN_best.pth.tar' + path: '/src/notebooks/detect_text/icdar2015/ch4_test_images' + save_path: './result' \ No newline at end of file diff --git a/config/det_PAN_resnet18_3_3.yaml b/config/det_PAN_resnet18_3_3.yaml new file mode 100644 index 0000000..32d9ac0 --- /dev/null +++ b/config/det_PAN_resnet18_3_3.yaml @@ -0,0 +1,87 @@ +base: + gpu_id: '0' + algorithm: PAN + pretrained: True + in_channels: [64, 128, 256, 512] + inner_channels: 128 + classes: 6 + crop_shape: [640,640] + shrink_ratio: 0.5 + n_epoch: 601 + show_step: 20 + start_val: 200 + save_epoch: 100 + checkpoints: ./checkpoint + restore: True + restore_file: ./checkpoint/ag_PAN_bb_resnet18_he_FPEM_FFM_Head_bs_14_ep_601/PAN_best.pth.tar + +backbone: + function: ptocr.model.backbone.det_resnet_3*3,resnet18 + +head: +# function: ptocr.model.head.det_DBHead,DB_Head + function: ptocr.model.head.det_FPEM_FFM_Head,FPEM_FFM_Head +# function: ptocr.model.head.det_FPNHead,FPN_Head +segout: + function: ptocr.model.segout.det_PAN_segout,SegDetector + +architectures: + model_function: ptocr.model.architectures.det_model,DetModel + loss_function: ptocr.model.architectures.det_model,DetLoss + +loss: + function: ptocr.model.loss.pan_loss,PANLoss + kernel_rate: 0.5 + agg_dis_rate: 0.25 + +#optimizer: +# function: ptocr.optimizer,AdamDecay +# base_lr: 0.002 +# beta1: 0.9 +# beta2: 0.999 + +optimizer: + function: ptocr.optimizer,SGDDecay + base_lr: 0.001 + momentum: 0.99 + weight_decay: 0.00005 + +optimizer_decay: + function: ptocr.optimizer,adjust_learning_rate_poly + factor: 0.9 + +# optimizer_decay: +# function: ptocr.optimizer,adjust_learning_rate +# schedule: [200,400,500] +# gama: 0.1 + + +trainload: + function: ptocr.dataloader.DetLoad.PANProcess,PANProcessTrain + train_file: /src/notebooks/detect_text/icdar2015/train_list.txt + num_workers: 10 + batch_size: 14 + +testload: + function: ptocr.dataloader.DetLoad.PANProcess,PANProcessTest + test_file: /src/notebooks/detect_text/icdar2015/test_list.txt + test_gt_path: /src/notebooks/detect_text/icdar2015/ch4_test_gts/ + test_size: 736 + stride: 32 + num_workers: 5 + batch_size: 2 + +postprocess: + function: ptocr.postprocess.PANpostprocess,PANPostProcess + is_poly: False + bin_th: 1 + scale: 1 + min_kernel_area: 4 + min_text_area: 300 + min_score: 0.90 + dis_thresh: 1 + +infer: + model_path: './checkpoint/ag_PAN_bb_resnet18_he_FPEM_FFM_Head_bs_14_ep_601/PAN_best.pth.tar' + path: '/src/notebooks/detect_text/icdar2015/ch4_test_images' + save_path: './result' \ No newline at end of file diff --git a/config/det_PSE_mobilev3.yaml b/config/det_PSE_mobilev3.yaml new file mode 100644 index 0000000..fb6979a --- /dev/null +++ b/config/det_PSE_mobilev3.yaml @@ -0,0 +1,78 @@ +base: + gpu_id: '1' + algorithm: PSE + pretrained: True + in_channels: [24, 40, 48, 96] + inner_channels: 96 + classes: 7 + crop_shape: [640,640] + shrink_ratio: 0.4 + n_epoch: 1200 + show_step: 5 + checkpoints: ./checkpoint + restore: False + restore_file: ./PSE.pth.tar + +backbone: + function: ptocr.model.backbone.det_mobilev3,mobilenet_v3_small + +head: +# function: ptocr.model.head.det_DBHead,DB_Head +# function: ptocr.model.head.det_FPEM_FFM_Head,FPEM_FFM_Head + function: ptocr.model.head.det_FPNHead,FPN_Head + +segout: + function: ptocr.model.segout.det_PSE_segout,SegDetector + +architectures: + model_function: ptocr.model.architectures.det_model,DetModel + loss_function: ptocr.model.architectures.det_model,DetLoss + +loss: + function: ptocr.model.loss.pse_loss,PSELoss + text_tatio: 0.7 + +#optimizer: +# function: ptocr.optimizer,AdamDecay +# base_lr: 0.002 +# beta1: 0.9 +# beta2: 0.999 + +optimizer: + function: ptocr.optimizer,SGDDecay + base_lr: 0.002 + momentum: 0.99 + +optimizer_decay: + function: ptocr.optimizer,adjust_learning_rate_poly + factor: 0.9 + +trainload: + function: ptocr.dataloader.DetLoad.PSEProcess,PSEProcessTrain + train_file: /src/notebooks/detect_text/icdar2015/train_list.txt + num_workers: 0 + batch_size: 2 + +testload: + function: ptocr.dataloader.DetLoad.PSEProcess,PSEProcessTest + test_file: /src/notebooks/detect_text/icdar2015/test_list.txt + test_gt_path: /src/notebooks/detect_text/icdar2015/ch4_test_gts/ + test_size: 2240 + stride: 32 + num_workers: 5 + batch_size: 2 + +postprocess: + function: ptocr.postprocess.PSEpostprocess,PSEPostProcess + is_poly: True + binary_th: 1 + scale: 1 + min_kernel_area: 5 + min_text_area: 800 + min_score: 0.93 + +infer: + model_path: './checkpoint/ag_PSE_bb_mobilenet_v3_small_he_FPN_Head_bs_16_ep_1200/PSE_400.pth.tar' + path: '/src/notebooks/detect_text/icdar2015/ch4_test_images' + save_path: './result' + \ No newline at end of file diff --git a/config/det_PSE_resnet50.yaml b/config/det_PSE_resnet50.yaml new file mode 100644 index 0000000..ad8575f --- /dev/null +++ b/config/det_PSE_resnet50.yaml @@ -0,0 +1,87 @@ +base: + gpu_id: '0' + algorithm: PSE + pretrained: True + in_channels: [256, 512, 1024, 2048] + inner_channels: 256 + classes: 7 + crop_shape: [640,640] + shrink_ratio: 0.4 + n_epoch: 600 + show_step: 20 + start_val: 400 + save_epoch: 100 + checkpoints: ./checkpoint + restore: False + restore_file: ./checkpoint/ag_PSE_bb_resnet50_he_FPN_Head_bs_8_ep_600/PSE_400.pth.tar + +backbone: + function: ptocr.model.backbone.det_resnet,resnet50 + +head: +# function: ptocr.model.head.det_DBHead,DB_Head +# function: ptocr.model.head.det_FPEM_FFM_Head,FPEM_FFM_Head + function: ptocr.model.head.det_FPNHead,FPN_Head + +segout: + function: ptocr.model.segout.det_PSE_segout,SegDetector + +architectures: + model_function: ptocr.model.architectures.det_model,DetModel + loss_function: ptocr.model.architectures.det_model,DetLoss + +loss: + function: ptocr.model.loss.pse_loss,PSELoss + text_tatio: 0.7 + +#optimizer: +# function: ptocr.optimizer,AdamDecay +# base_lr: 0.002 +# beta1: 0.9 +# beta2: 0.999 + +optimizer: + function: ptocr.optimizer,SGDDecay + base_lr: 0.001 + momentum: 0.99 + weight_decay: 0.00005 + +# optimizer_decay: +# function: ptocr.optimizer,adjust_learning_rate_poly +# factor: 0.9 + +optimizer_decay: + function: ptocr.optimizer,adjust_learning_rate + schedule: [200,400,500] + gama: 0.1 + +trainload: + function: ptocr.dataloader.DetLoad.PSEProcess,PSEProcessTrain + train_file: /src/notebooks/detect_text/icdar2015/train_list.txt + num_workers: 12 + batch_size: 8 + +testload: + function: ptocr.dataloader.DetLoad.PSEProcess,PSEProcessTest + test_file: /src/notebooks/detect_text/icdar2015/test_list.txt + test_gt_path: /src/notebooks/detect_text/icdar2015/ch4_test_gts/ + test_size: 2240 + stride: 32 + num_workers: 5 + batch_size: 1 + +postprocess: + function: ptocr.postprocess.PSEpostprocess,PSEPostProcess + is_poly: False + binary_th: 1 + scale: 1 + min_kernel_area: 5 + min_text_area: 800 + min_score: 0.93 + +infer: + model_path: './checkpoint/ag_PSE_bb_resnet50_he_FPN_Head_bs_8_ep_600/PSE_best.pth.tar' + path: '/src/notebooks/detect_text/icdar2015/ch4_test_images' + save_path: './result' + + \ No newline at end of file diff --git a/config/det_PSE_resnet50_3_3.yaml b/config/det_PSE_resnet50_3_3.yaml new file mode 100644 index 0000000..aca9e57 --- /dev/null +++ b/config/det_PSE_resnet50_3_3.yaml @@ -0,0 +1,87 @@ +base: + gpu_id: '0' + algorithm: PSE + pretrained: True + in_channels: [256, 512, 1024, 2048] + inner_channels: 256 + classes: 7 + crop_shape: [640,640] + shrink_ratio: 0.4 + n_epoch: 601 + show_step: 20 + start_val: 400 + save_epoch: 100 + checkpoints: ./checkpoint + restore: False + restore_file: ./checkpoint/ag_PSE_bb_resnet50_he_FPN_Head_bs_8_ep_601/PSE_best.pth.tar + +backbone: + function: ptocr.model.backbone.det_resnet_3*3,resnet50 + +head: +# function: ptocr.model.head.det_DBHead,DB_Head +# function: ptocr.model.head.det_FPEM_FFM_Head,FPEM_FFM_Head + function: ptocr.model.head.det_FPNHead,FPN_Head + +segout: + function: ptocr.model.segout.det_PSE_segout,SegDetector + +architectures: + model_function: ptocr.model.architectures.det_model,DetModel + loss_function: ptocr.model.architectures.det_model,DetLoss + +loss: + function: ptocr.model.loss.pse_loss,PSELoss + text_tatio: 0.7 + +#optimizer: +# function: ptocr.optimizer,AdamDecay +# base_lr: 0.002 +# beta1: 0.9 +# beta2: 0.999 + +optimizer: + function: ptocr.optimizer,SGDDecay + base_lr: 0.001 + momentum: 0.99 + weight_decay: 0.00005 + +# optimizer_decay: +# function: ptocr.optimizer,adjust_learning_rate_poly +# factor: 0.9 + +optimizer_decay: + function: ptocr.optimizer,adjust_learning_rate + schedule: [200,400,500] + gama: 0.1 + +trainload: + function: ptocr.dataloader.DetLoad.PSEProcess,PSEProcessTrain + train_file: /src/notebooks/detect_text/icdar2015/train_list.txt + num_workers: 12 + batch_size: 8 + +testload: + function: ptocr.dataloader.DetLoad.PSEProcess,PSEProcessTest + test_file: /src/notebooks/detect_text/icdar2015/test_list.txt + test_gt_path: /src/notebooks/detect_text/icdar2015/ch4_test_gts/ + test_size: 2240 + stride: 32 + num_workers: 5 + batch_size: 1 + +postprocess: + function: ptocr.postprocess.PSEpostprocess,PSEPostProcess + is_poly: False + binary_th: 1 + scale: 1 + min_kernel_area: 5 + min_text_area: 800 + min_score: 0.93 + +infer: + model_path: './checkpoint/ag_PSE_bb_resnet50_he_FPN_Head_bs_8_ep_600/PSE_best.pth.tar' + path: '/src/notebooks/detect_text/icdar2015/ch4_test_images' + save_path: './result' + + \ No newline at end of file diff --git a/config/det_SAST_resnet50.yaml b/config/det_SAST_resnet50.yaml new file mode 100644 index 0000000..8318077 --- /dev/null +++ b/config/det_SAST_resnet50.yaml @@ -0,0 +1,100 @@ +base: + gpu_id: '0' + algorithm: SAST + pretrained: True + with_attention: True + crop_shape: [512,512] + n_epoch: 900 + start_val: 600 + show_step: 20 + checkpoints: ./checkpoint + save_epoch: 100 + restore: False + restore_file : ./checkpoint/ag_SAST_bb_resnet50_he_SASTHead_bs_12_ep_2000/SAST_best.pth.tar + +backbone: + function: ptocr.model.backbone.det_resnet_sast,resnet50 + +head: + function: ptocr.model.head.det_SASTHead,SASTHead +# function: ptocr.model.head.det_FPEM_FFM_Head,FPEM_FFM_Head +# function: ptocr.model.head.det_FPNHead,FPN_Head + +segout: + function: ptocr.model.segout.det_SAST_segout,SegDetector + +architectures: + model_function: ptocr.model.architectures.det_model,DetModel + loss_function: ptocr.model.architectures.det_model,DetLoss + +loss: + function: ptocr.model.loss.sast_loss,SASTLoss + tvo_lw: 1.5 + tco_lw: 1.5 + score_lw: 1.0 + border_lw: 1.0 + +# optimizer: +# function: ptocr.optimizer,AdamDecay +# base_lr: 0.001 +# beta1: 0.9 +# beta2: 0.999 +# weight_decay: 0.00005 + +optimizer: + function: ptocr.optimizer,RMSPropDecay + base_lr: 0.001 + momentum: 0 + alpha: 0.95 + weight_decay: 0.00005 + + +# optimizer: +# function: ptocr.optimizer,SGDDecay +# weight_decay: 0.00005 +# base_lr: 0.005 +# momentum: 0.95 + +# optimizer_decay: +# function: ptocr.optimizer,adjust_learning_rate_poly +# factor: 0.9 + +optimizer_decay: + function: ptocr.optimizer,adjust_learning_rate + schedule: [300,600,800,850] + gama: 0.3 + + +trainload: + function: ptocr.dataloader.DetLoad.SASTProcess,SASTProcessTrain + train_file: /src/notebooks/detect_text/icdar2015/train_list.txt + num_workers: 12 + batch_size: 8 + min_crop_side_ratio: 0.3 + min_crop_size: 24 + min_text_size: 4 + + +testload: + function: ptocr.dataloader.DetLoad.SASTProcess_ori,SASTProcessTest + test_file: /src/notebooks/detect_text/icdar2015/test_list.txt + test_gt_path: /src/notebooks/detect_text/icdar2015/ch4_test_gts/ + test_size: 1536 + stride: 128 + num_workers: 5 + batch_size: 4 + +postprocess: + function: ptocr.postprocess.SASTpostprocess,SASTPostProcess + is_poly: False + score_thresh: 0.5 + nms_thresh: 0.2 + sample_pts_num: 2 + shrink_ratio_of_width: 0.3 + expand_scale: 1.0 + tcl_map_thresh: 0.7 + +infer: + model_path: './checkpoint/ag_SAST_bb_resnet50_he_SASTHead_bs_8_ep_1000/SAST_best.pth.tar' + path: '/src/notebooks/detect_text/icdar2015/ch4_test_images' + save_path: './result' diff --git a/config/det_SAST_resnet50_3_3_ori_dataload.yaml b/config/det_SAST_resnet50_3_3_ori_dataload.yaml new file mode 100644 index 0000000..a784869 --- /dev/null +++ b/config/det_SAST_resnet50_3_3_ori_dataload.yaml @@ -0,0 +1,100 @@ +base: + gpu_id: '1' + algorithm: SAST + pretrained: True + with_attention: True + crop_shape: [512,512] + n_epoch: 901 + start_val: 5000 + show_step: 20 + checkpoints: ./checkpoint + save_epoch: 100 + restore: False + restore_file : ./checkpoint/ag_SAST_bb_resnet50_he_SASTHead_bs_12_ep_2000/SAST_best.pth.tar + +backbone: + function: ptocr.model.backbone.det_resnet_sast_3_3,resnet50 + +head: + function: ptocr.model.head.det_SASTHead,SASTHead +# function: ptocr.model.head.det_FPEM_FFM_Head,FPEM_FFM_Head +# function: ptocr.model.head.det_FPNHead,FPN_Head + +segout: + function: ptocr.model.segout.det_SAST_segout,SegDetector + +architectures: + model_function: ptocr.model.architectures.det_model,DetModel + loss_function: ptocr.model.architectures.det_model,DetLoss + +loss: + function: ptocr.model.loss.sast_loss,SASTLoss + tvo_lw: 1.5 + tco_lw: 1.5 + score_lw: 1.0 + border_lw: 1.0 + +# optimizer: +# function: ptocr.optimizer,AdamDecay +# base_lr: 0.001 +# beta1: 0.9 +# beta2: 0.999 +# weight_decay: 0.00005 + +optimizer: + function: ptocr.optimizer,RMSPropDecay + base_lr: 0.001 + momentum: 0 + alpha: 0.95 + weight_decay: 0.00005 + + +# optimizer: +# function: ptocr.optimizer,SGDDecay +# weight_decay: 0.00005 +# base_lr: 0.005 +# momentum: 0.95 + +# optimizer_decay: +# function: ptocr.optimizer,adjust_learning_rate_poly +# factor: 0.9 + +optimizer_decay: + function: ptocr.optimizer,adjust_learning_rate + schedule: [300,600,800,850] + gama: 0.3 + + +trainload: + function: ptocr.dataloader.DetLoad.SASTProcess_ori,SASTProcessTrain + train_file: /src/notebooks/MyworkData/huayandang/train_list.txt + num_workers: 12 + batch_size: 8 + min_crop_side_ratio: 0.3 + min_crop_size: 24 + min_text_size: 4 + + +testload: + function: ptocr.dataloader.DetLoad.SASTProcess_ori,SASTProcessTest + test_file: /src/notebooks/detect_text/icdar2015/test_list.txt + test_gt_path: /src/notebooks/detect_text/icdar2015/ch4_test_gts/ + test_size: 1536 + stride: 128 + num_workers: 5 + batch_size: 4 + +postprocess: + function: ptocr.postprocess.SASTpostprocess,SASTPostProcess + is_poly: False + score_thresh: 0.5 + nms_thresh: 0.2 + sample_pts_num: 2 + shrink_ratio_of_width: 0.3 + expand_scale: 1.0 + tcl_map_thresh: 0.7 + +infer: + model_path: './checkpoint/ag_SAST_bb_resnet50_he_SASTHead_bs_8_ep_901/SAST_400.pth.tar' + path: '/src/notebooks/MyworkData/huayandang/train' + save_path: './result' diff --git a/config/det_SAST_resnet50_ori_dataload.yaml b/config/det_SAST_resnet50_ori_dataload.yaml new file mode 100644 index 0000000..a5e1505 --- /dev/null +++ b/config/det_SAST_resnet50_ori_dataload.yaml @@ -0,0 +1,111 @@ +base: + gpu_id: '2' + algorithm: SAST + pretrained: True + with_attention: True + crop_shape: [512,512] + n_epoch: 1000 + start_val: 600 + show_step: 20 + checkpoints: ./checkpoint + save_epoch: 100 + restore: False + restore_file : ./checkpoint/ag_SAST_bb_resnet50_he_SASTHead_bs_12_ep_2000/SAST_best.pth.tar + +backbone: + function: ptocr.model.backbone.det_resnet_sast,resnet50 + +head: + function: ptocr.model.head.det_SASTHead,SASTHead +# function: ptocr.model.head.det_FPEM_FFM_Head,FPEM_FFM_Head +# function: ptocr.model.head.det_FPNHead,FPN_Head + +segout: + function: ptocr.model.segout.det_SAST_segout,SegDetector + +architectures: + model_function: ptocr.model.architectures.det_model,DetModel + loss_function: ptocr.model.architectures.det_model,DetLoss + +loss: + function: ptocr.model.loss.sast_loss,SASTLoss + tvo_lw: 1.5 + tco_lw: 1.5 + score_lw: 1.0 + border_lw: 1.0 + +# optimizer: +# function: ptocr.optimizer,AdamDecay +# base_lr: 0.001 +# beta1: 0.9 +# beta2: 0.999 +# weight_decay: 0.00005 + +optimizer: + function: ptocr.optimizer,RMSPropDecay + base_lr: 0.001 + momentum: 0 + alpha: 0.95 + weight_decay: 0.00005 + + +# optimizer: +# function: ptocr.optimizer,SGDDecay +# weight_decay: 0.00005 +# base_lr: 0.005 +# momentum: 0.95 + +# optimizer_decay: +# function: ptocr.optimizer,adjust_learning_rate_poly +# factor: 0.9 + +optimizer_decay: + function: ptocr.optimizer,adjust_learning_rate + schedule: [300,600,800,850] + gama: 0.3 + + +# trainload: +# function: ptocr.dataloader.DetLoad.SASTProcess_ori,SASTProcessTrain +# train_file: /src/notebooks/detect_text/icdar2015/train_list.txt +# num_workers: 12 +# batch_size: 8 +# min_crop_side_ratio: 0.3 +# min_crop_size: 24 +# min_text_size: 4 + +trainload: + function: ptocr.dataloader.DetLoad.SASTProcess_ori1,SASTProcessTrain + data_dir: /src/notebooks/dataset_for_sast + train_file_target: icdar2015/train_label_json.txt + train_file_extre: [icdar17_mlt_latin/train_label_json.txt,coco_text_icdar_4pts/train_label_json.txt,icdar2013/train_label_json.txt] + train_file_ratio: 0.5 + num_workers: 12 + batch_size: 8 + min_crop_side_ratio: 0.3 + min_crop_size: 24 + min_text_size: 4 + +testload: + function: ptocr.dataloader.DetLoad.SASTProcess_ori,SASTProcessTest + test_file: /src/notebooks/detect_text/icdar2015/test_list.txt + test_gt_path: /src/notebooks/detect_text/icdar2015/ch4_test_gts/ + test_size: 1536 + stride: 128 + num_workers: 5 + batch_size: 4 + +postprocess: + function: ptocr.postprocess.SASTpostprocess,SASTPostProcess + is_poly: False + score_thresh: 0.5 + nms_thresh: 0.2 + sample_pts_num: 2 + shrink_ratio_of_width: 0.3 + expand_scale: 1.0 + tcl_map_thresh: 0.7 + +infer: + model_path: './checkpoint/ag_SAST_bb_resnet50_he_SASTHead_bs_8_ep_1000/SAST_best.pth.tar' + path: '/src/notebooks/detect_text/icdar2015/ch4_test_images' + save_path: './result' diff --git a/config/rec_CRNN_mobilev3_large_english_all.yaml b/config/rec_CRNN_mobilev3_large_english_all.yaml new file mode 100644 index 0000000..7cab74a --- /dev/null +++ b/config/rec_CRNN_mobilev3_large_english_all.yaml @@ -0,0 +1,102 @@ +base: + gpu_id: '0,1' + algorithm: CRNN + pretrained: False + inchannel: 960 + hiddenchannel: 96 + img_shape: [32,100] + is_gray: True + use_conv: False + use_attention: False + use_lstm: True + lstm_num: 2 + classes: 1000 + max_iters: 300000 + eval_iter: 10000 + show_step: 100 + checkpoints: ./checkpoint + save_epoch: 1 + show_num: 10 + restore: False + finetune: False + restore_file : ./checkpoint/ag_CRNN_bb_rec_crnn_backbone_he_CRNN_Head_bs_256_ep_20_20210207English/CRNN_best.pth.tar + +backbone: + function: ptocr.model.backbone.rec_mobilev3_bd,mobilenet_v3_large + +head: + function: ptocr.model.head.rec_CRNNHead,CRNN_Head + +architectures: + model_function: ptocr.model.architectures.rec_model,RecModel + loss_function: ptocr.model.architectures.rec_model,RecLoss + +loss: + function: ptocr.model.loss.ctc_loss,CTCLoss + use_ctc_weight: False + reduction: 'mean' + center_function: ptocr.model.loss.centerloss,CenterLoss + use_center: False + center_lr: 0.5 + label_score: 0.95 +# min_score: 0.01 + weight_center: 0.000001 + + +optimizer: + function: ptocr.optimizer,AdamDecay + base_lr: 0.001 + beta1: 0.9 + beta2: 0.999 + weight_decay: 0.00005 + +# optimizer: +# function: ptocr.optimizer,SGDDecay +# base_lr: 0.002 +# momentum: 0.99 +# weight_decay: 0.00005 + +# optimizer_decay: +# function: ptocr.optimizer,adjust_learning_rate_poly +# factor: 0.9 + +optimizer_decay: + function: ptocr.optimizer,adjust_learning_rate + schedule: [100000,200000] + gama: 0.1 + +optimizer_decay_center: + function: ptocr.optimizer,adjust_learning_rate_center + schedule: [100000,200000] + gama: 0.1 + +trainload: + function: ptocr.dataloader.RecLoad.CRNNProcess1,GetDataLoad + train_file: ['/src/notebooks/MyworkData/EnglishCrnnData/train_lmdb/SynthText/','/src/notebooks/MyworkData/EnglishCrnnData/train_lmdb/MJSynth'] + batch_ratio: [0.5,0.5] + key_file: /src/notebooks/MyworkData/EnglishCrnnData/key_new.txt + bg_path: ./bg_img/ + num_workers: 16 + batch_size: 512 + +testload: + function: ptocr.dataloader.RecLoad.CRNNProcess1,CRNNProcessTest + test_file: /src/notebooks/MyworkData/EnglishCrnnData/val_new.txt + num_workers: 8 + batch_size: 256 + + +label_transform: + function: ptocr.utils.transform_label,strLabelConverter + +transform: + function: ptocr.dataloader.RecLoad.DataAgument,transform_label + t_type: lower + char_type: En + +infer: +# model_path: './checkpoint/ag_CRNN_bb_rec_crnn_backbone_he_CRNN_Head_bs_256_ep_10_synthtext/CRNN_best.pth.tar' + model_path: './checkpoint/ag_CRNN_bb_mobilenet_v3_large_he_CRNN_Head_bs_512_ep_300000_mobilev2_alldata/CRNN_210000.pth.tar' +# path: '/src/notebooks/MyworkData/EnglishCrnnData/image/2697/6/107_Ramification_62303.jpg' + path: './english_val_img/' + save_path: '' diff --git a/config/rec_CRNN_mobilev3_large_english_lmdb.yaml b/config/rec_CRNN_mobilev3_large_english_lmdb.yaml new file mode 100644 index 0000000..9479d4a --- /dev/null +++ b/config/rec_CRNN_mobilev3_large_english_lmdb.yaml @@ -0,0 +1,77 @@ +base: + gpu_id: '0' + algorithm: CRNN + pretrained: False + inchannel: 960 + hiddenchannel: 96 + img_shape: [32,100] + is_gray: True + use_attention: False + use_lstm: True + lstm_num: 2 + n_epoch: 8 + start_val: 0 + show_step: 50 + checkpoints: ./checkpoint + save_epoch: 1 + show_num: 10 + restore: False + finetune: False + restore_file : ./checkpoint/ + +backbone: + function: ptocr.model.backbone.rec_mobilev3_bd,mobilenet_v3_large + +head: + function: ptocr.model.head.rec_CRNNHead,CRNN_Head + +architectures: + model_function: ptocr.model.architectures.rec_model,RecModel + loss_function: ptocr.model.architectures.rec_model,RecLoss + +loss: + function: ptocr.model.loss.ctc_loss,CTCLoss + ctc_type: 'warpctc' # torchctc + use_ctc_weight: False + loss_title: ['ctc_loss'] + +optimizer: + function: ptocr.optimizer,AdamDecay + base_lr: 0.001 + beta1: 0.9 + beta2: 0.999 + weight_decay: 0.00005 + + +optimizer_decay: + function: ptocr.optimizer,adjust_learning_rate + schedule: [4,6] + gama: 0.1 + + +trainload: + function: ptocr.dataloader.RecLoad.CRNNProcess,CRNNProcessLmdbLoad + train_file: '/src/notebooks/MyworkData/EnglishCrnnData/train_lmdb/SynthText/' + key_file: /src/notebooks/MyworkData/EnglishCrnnData/key_new.txt + bg_path: ./bg_img/ + num_workers: 10 + batch_size: 512 + +valload: + function: ptocr.dataloader.RecLoad.CRNNProcess,CRNNProcessLmdbLoad + val_file: '/src/notebooks/IIIT5k_3000/lmdb/' + num_workers: 5 + batch_size: 256 + +label_transform: + function: ptocr.utils.transform_label,strLabelConverter + label_function: ptocr.dataloader.RecLoad.DataAgument,transform_label + t_type: lower + char_type: En + +infer: +# model_path: './checkpoint/ag_CRNN_bb_rec_crnn_backbone_he_CRNN_Head_bs_256_ep_10_synthtext/CRNN_best.pth.tar' + model_path: './checkpoint/ag_CRNN_bb_resnet34_he_CRNN_Head_bs_512_ep_8_center_loss/CRNN_best.pth.tar' +# path: '/src/notebooks/MyworkData/EnglishCrnnData/image/2697/6/107_Ramification_62303.jpg' + path: './english_val_img/SVT/image/' + save_path: '' diff --git a/config/rec_CRNN_mobilev3_small_english_all.yaml b/config/rec_CRNN_mobilev3_small_english_all.yaml new file mode 100644 index 0000000..4ec984f --- /dev/null +++ b/config/rec_CRNN_mobilev3_small_english_all.yaml @@ -0,0 +1,104 @@ +base: + gpu_id: '1' + algorithm: CRNN + pretrained: False + inchannel: 576 + hiddenchannel: 48 + img_shape: [32,100] + is_gray: True + use_conv: False + use_attention: False + use_lstm: True + lstm_num: 2 + classes: 1000 + max_iters: 300000 + eval_iter: 10000 + show_step: 100 + checkpoints: ./checkpoint + save_epoch: 1 + show_num: 10 + restore: False + finetune: False + restore_file : ./checkpoint/ag_CRNN_bb_rec_crnn_backbone_he_CRNN_Head_bs_256_ep_20_20210207English/CRNN_best.pth.tar + +backbone: + function: ptocr.model.backbone.rec_mobilev3_bd,mobilenet_v3_small + +head: + function: ptocr.model.head.rec_CRNNHead,CRNN_Head + +architectures: + model_function: ptocr.model.architectures.rec_model,RecModel + loss_function: ptocr.model.architectures.rec_model,RecLoss + +loss: + function: ptocr.model.loss.ctc_loss,CTCLoss + use_ctc_weight: False + reduction: 'mean' + center_function: ptocr.model.loss.centerloss,CenterLoss + use_center: False + center_lr: 0.5 + label_score: 0.95 +# min_score: 0.01 + weight_center: 0.000001 + + +optimizer: + function: ptocr.optimizer,AdamDecay + base_lr: 0.001 + beta1: 0.9 + beta2: 0.999 + weight_decay: 0.00005 + +# optimizer: +# function: ptocr.optimizer,SGDDecay +# base_lr: 0.002 +# momentum: 0.99 +# weight_decay: 0.00005 + +# optimizer_decay: +# function: ptocr.optimizer,adjust_learning_rate_poly +# factor: 0.9 + +optimizer_decay: + function: ptocr.optimizer,adjust_learning_rate + schedule: [100000,200000] + gama: 0.1 + +optimizer_decay_center: + function: ptocr.optimizer,adjust_learning_rate_center + schedule: [100000,200000] + gama: 0.1 + +trainload: + function: ptocr.dataloader.RecLoad.CRNNProcess1,GetDataLoad + train_file: ['/src/notebooks/MyworkData/EnglishCrnnData/train_lmdb/SynthText/','/src/notebooks/MyworkData/EnglishCrnnData/train_lmdb/MJSynth'] + batch_ratio: [0.5,0.5] + key_file: /src/notebooks/MyworkData/EnglishCrnnData/key_new.txt + bg_path: ./bg_img/ + num_workers: 16 + batch_size: 512 + +valload: + function: ptocr.dataloader.RecLoad.CRNNProcess1,GetValDataLoad + root: '/src/notebooks/pytorchOCR-master/english_val_img' + dir: ['CUTE80','IC03_867','IC13_1015','IC13_857','IC15_1811','IIIT5k_3000','SVT','SVTP','IC15_2077'] + test_file: /src/notebooks/MyworkData/EnglishCrnnData/val_new.txt + num_workers: 2 + batch_size: 1 + + +label_transform: + function: ptocr.utils.transform_label,strLabelConverter + +transform: + function: ptocr.dataloader.RecLoad.DataAgument,transform_label + t_type: lower + char_type: En + +infer: +# model_path: './checkpoint/ag_CRNN_bb_rec_crnn_backbone_he_CRNN_Head_bs_256_ep_10_synthtext/CRNN_best.pth.tar' + model_path: './checkpoint/ag_CRNN_bb_mobilenet_v3_small_he_CRNN_Head_bs_512_ep_300000_mobilev2_small_alldata/CRNN_210000.pth.tar' +# path: '/src/notebooks/MyworkData/EnglishCrnnData/image/2697/6/107_Ramification_62303.jpg' + path: './english_val_img/' + save_path: '' diff --git a/config/rec_CRNN_mobilev3_small_english_lmdb.yaml b/config/rec_CRNN_mobilev3_small_english_lmdb.yaml new file mode 100644 index 0000000..7cf838b --- /dev/null +++ b/config/rec_CRNN_mobilev3_small_english_lmdb.yaml @@ -0,0 +1,77 @@ +base: + gpu_id: '0' + algorithm: CRNN + pretrained: False + inchannel: 576 + hiddenchannel: 48 + img_shape: [32,100] + is_gray: True + use_attention: False + use_lstm: True + lstm_num: 2 + n_epoch: 8 + start_val: 0 + show_step: 50 + checkpoints: ./checkpoint + save_epoch: 1 + show_num: 10 + restore: False + finetune: False + restore_file : ./checkpoint/ + +backbone: + function: ptocr.model.backbone.rec_mobilev3_bd,mobilenet_v3_small + +head: + function: ptocr.model.head.rec_CRNNHead,CRNN_Head + +architectures: + model_function: ptocr.model.architectures.rec_model,RecModel + loss_function: ptocr.model.architectures.rec_model,RecLoss + +loss: + function: ptocr.model.loss.ctc_loss,CTCLoss + ctc_type: 'warpctc' # torchctc + use_ctc_weight: False + loss_title: ['ctc_loss'] + +optimizer: + function: ptocr.optimizer,AdamDecay + base_lr: 0.001 + beta1: 0.9 + beta2: 0.999 + weight_decay: 0.00005 + + +optimizer_decay: + function: ptocr.optimizer,adjust_learning_rate + schedule: [4,6] + gama: 0.1 + + +trainload: + function: ptocr.dataloader.RecLoad.CRNNProcess,CRNNProcessLmdbLoad + train_file: '/src/notebooks/MyworkData/EnglishCrnnData/train_lmdb/SynthText/' + key_file: /src/notebooks/MyworkData/EnglishCrnnData/key_new.txt + bg_path: ./bg_img/ + num_workers: 10 + batch_size: 512 + +valload: + function: ptocr.dataloader.RecLoad.CRNNProcess,CRNNProcessLmdbLoad + val_file: '/src/notebooks/IIIT5k_3000/lmdb/' + num_workers: 5 + batch_size: 256 + +label_transform: + function: ptocr.utils.transform_label,strLabelConverter + label_function: ptocr.dataloader.RecLoad.DataAgument,transform_label + t_type: lower + char_type: En + +infer: +# model_path: './checkpoint/ag_CRNN_bb_rec_crnn_backbone_he_CRNN_Head_bs_256_ep_10_synthtext/CRNN_best.pth.tar' + model_path: './checkpoint/ag_CRNN_bb_resnet34_he_CRNN_Head_bs_512_ep_8_center_loss/CRNN_best.pth.tar' +# path: '/src/notebooks/MyworkData/EnglishCrnnData/image/2697/6/107_Ramification_62303.jpg' + path: './english_val_img/SVT/image/' + save_path: '' diff --git a/config/rec_CRNN_resnet34_english_lmdb.yaml b/config/rec_CRNN_resnet34_english_lmdb.yaml new file mode 100644 index 0000000..54a56d8 --- /dev/null +++ b/config/rec_CRNN_resnet34_english_lmdb.yaml @@ -0,0 +1,77 @@ +base: + gpu_id: '1' + algorithm: CRNN + pretrained: False + inchannel: 512 + hiddenchannel: 128 + img_shape: [32,100] + is_gray: True + use_attention: False + use_lstm: True + lstm_num: 2 + n_epoch: 8 + start_val: 0 + show_step: 50 + checkpoints: ./checkpoint + save_epoch: 1 + show_num: 10 + restore: False + finetune: False + restore_file : ./checkpoint/ + +backbone: + function: ptocr.model.backbone.reg_resnet_bd,resnet34 + +head: + function: ptocr.model.head.rec_CRNNHead,CRNN_Head + +architectures: + model_function: ptocr.model.architectures.rec_model,RecModel + loss_function: ptocr.model.architectures.rec_model,RecLoss + +loss: + function: ptocr.model.loss.ctc_loss,CTCLoss + ctc_type: 'warpctc' # torchctc + use_ctc_weight: False + loss_title: ['ctc_loss'] + +optimizer: + function: ptocr.optimizer,AdamDecay + base_lr: 0.001 + beta1: 0.9 + beta2: 0.999 + weight_decay: 0.00005 + + +optimizer_decay: + function: ptocr.optimizer,adjust_learning_rate + schedule: [4,6] + gama: 0.1 + + +trainload: + function: ptocr.dataloader.RecLoad.CRNNProcess,CRNNProcessLmdbLoad + train_file: '/src/notebooks/MyworkData/EnglishCrnnData/train_lmdb/SynthText/' + key_file: /src/notebooks/MyworkData/EnglishCrnnData/key_new.txt + bg_path: ./bg_img/ + num_workers: 10 + batch_size: 512 + +valload: + function: ptocr.dataloader.RecLoad.CRNNProcess,CRNNProcessLmdbLoad + val_file: '/src/notebooks/IIIT5k_3000/lmdb/' + num_workers: 5 + batch_size: 256 + +label_transform: + function: ptocr.utils.transform_label,strLabelConverter + label_function: ptocr.dataloader.RecLoad.DataAgument,transform_label + t_type: lower + char_type: En + +infer: +# model_path: './checkpoint/ag_CRNN_bb_rec_crnn_backbone_he_CRNN_Head_bs_256_ep_10_synthtext/CRNN_best.pth.tar' + model_path: './checkpoint/ag_CRNN_bb_resnet34_he_CRNN_Head_bs_512_ep_8_center_loss/CRNN_best.pth.tar' +# path: '/src/notebooks/MyworkData/EnglishCrnnData/image/2697/6/107_Ramification_62303.jpg' + path: './english_val_img/SVT/image/' + save_path: '' diff --git a/config/rec_CRNN_resnet_english.yaml b/config/rec_CRNN_resnet_english.yaml new file mode 100644 index 0000000..fc51a28 --- /dev/null +++ b/config/rec_CRNN_resnet_english.yaml @@ -0,0 +1,96 @@ +base: + gpu_id: '0,1' + algorithm: CRNN + pretrained: False + inchannel: 512 + hiddenchannel: 128 + img_shape: [32,100] + is_gray: True + use_conv: False + use_attention: False + use_lstm: True + lstm_num: 2 + classes: 1000 + n_epoch: 8 + start_val: 0 + show_step: 100 + checkpoints: ./checkpoint + save_epoch: 1 + show_num: 10 + restore: True + finetune: True + restore_file : ./checkpoint/ag_CRNN_bb_resnet34_he_CRNN_Head_bs_256_ep_20_no_channel_timestep_rnn/CRNN_best.pth.tar + +backbone: + function: ptocr.model.backbone.reg_resnet_bd,resnet34 + +head: + function: ptocr.model.head.rec_CRNNHead,CRNN_Head + +architectures: + model_function: ptocr.model.architectures.rec_model,RecModel + loss_function: ptocr.model.architectures.rec_model,RecLoss + +loss: + function: ptocr.model.loss.ctc_loss,CTCLoss + use_ctc_weight: True + reduction: 'none' + center_function: ptocr.model.loss.centerloss,CenterLoss + use_center: True + center_lr: 0.5 + label_score: 0.95 +# min_score: 0.01 + weight_center: 0.001 + + +optimizer: + function: ptocr.optimizer,AdamDecay + base_lr: 0.001 + beta1: 0.9 + beta2: 0.999 + weight_decay: 0.00005 + +# optimizer: +# function: ptocr.optimizer,SGDDecay +# base_lr: 0.002 +# momentum: 0.99 +# weight_decay: 0.00005 + +# optimizer_decay: +# function: ptocr.optimizer,adjust_learning_rate_poly +# factor: 0.9 + +optimizer_decay: + function: ptocr.optimizer,adjust_learning_rate + schedule: [4,6] + gama: 0.1 + +optimizer_decay_center: + function: ptocr.optimizer,adjust_learning_rate_center + schedule: [4,6] + gama: 0.1 + +trainload: + function: ptocr.dataloader.RecLoad.CRNNProcess,CRNNProcessTrainLmdb + train_file: '/src/notebooks/MyworkData/EnglishCrnnData/train_lmdb/SynthText/' + key_file: /src/notebooks/MyworkData/EnglishCrnnData/key_new.txt + bg_path: ./bg_img/ + num_workers: 10 + batch_size: 512 + +testload: + function: ptocr.dataloader.RecLoad.CRNNProcess,CRNNProcessTest + test_file: /src/notebooks/MyworkData/EnglishCrnnData/val_new.txt + num_workers: 5 + batch_size: 256 + + +label_transform: + function: ptocr.utils.transform_label,strLabelConverter + +infer: +# model_path: './checkpoint/ag_CRNN_bb_rec_crnn_backbone_he_CRNN_Head_bs_256_ep_10_synthtext/CRNN_best.pth.tar' + model_path: './checkpoint/ag_CRNN_bb_resnet34_he_CRNN_Head_bs_512_ep_8_center_loss/CRNN_best.pth.tar' +# path: '/src/notebooks/MyworkData/EnglishCrnnData/image/2697/6/107_Ramification_62303.jpg' + path: './english_val_img/SVT/image/' + save_path: '' diff --git a/config/rec_CRNN_resnet_english_all.yaml b/config/rec_CRNN_resnet_english_all.yaml new file mode 100644 index 0000000..b0e3771 --- /dev/null +++ b/config/rec_CRNN_resnet_english_all.yaml @@ -0,0 +1,102 @@ +base: + gpu_id: '0,1' + algorithm: CRNN + pretrained: False + inchannel: 512 + hiddenchannel: 256 + img_shape: [32,100] + is_gray: True + use_conv: False + use_attention: False + use_lstm: True + lstm_num: 2 + classes: 1000 + max_iters: 200000 + eval_iter: 10000 + show_step: 100 + checkpoints: ./checkpoint + save_epoch: 1 + show_num: 10 + restore: False + finetune: False + restore_file : ./checkpoint/ag_CRNN_bb_rec_crnn_backbone_he_CRNN_Head_bs_256_ep_20_20210207English/CRNN_best.pth.tar + +backbone: + function: ptocr.model.backbone.reg_resnet_bd,resnet34 + +head: + function: ptocr.model.head.rec_CRNNHead,CRNN_Head + +architectures: + model_function: ptocr.model.architectures.rec_model,RecModel + loss_function: ptocr.model.architectures.rec_model,RecLoss + +loss: + function: ptocr.model.loss.ctc_loss,CTCLoss + use_ctc_weight: False + reduction: 'mean' + center_function: ptocr.model.loss.centerloss,CenterLoss + use_center: False + center_lr: 0.5 + label_score: 0.95 +# min_score: 0.01 + weight_center: 0.000001 + + +optimizer: + function: ptocr.optimizer,AdamDecay + base_lr: 0.001 + beta1: 0.9 + beta2: 0.999 + weight_decay: 0.00005 + +# optimizer: +# function: ptocr.optimizer,SGDDecay +# base_lr: 0.002 +# momentum: 0.99 +# weight_decay: 0.00005 + +# optimizer_decay: +# function: ptocr.optimizer,adjust_learning_rate_poly +# factor: 0.9 + +optimizer_decay: + function: ptocr.optimizer,adjust_learning_rate + schedule: [80000,160000] + gama: 0.1 + +optimizer_decay_center: + function: ptocr.optimizer,adjust_learning_rate_center + schedule: [80000,160000] + gama: 0.1 + +trainload: + function: ptocr.dataloader.RecLoad.CRNNProcess1,GetDataLoad + train_file: ['/src/notebooks/MyworkData/EnglishCrnnData/train_lmdb/SynthText/','/src/notebooks/MyworkData/EnglishCrnnData/train_lmdb/MJSynth'] + batch_ratio: [0.5,0.5] + key_file: /src/notebooks/MyworkData/EnglishCrnnData/key_new.txt + bg_path: ./bg_img/ + num_workers: 16 + batch_size: 512 + +testload: + function: ptocr.dataloader.RecLoad.CRNNProcess1,CRNNProcessTest + test_file: /src/notebooks/MyworkData/EnglishCrnnData/val_new.txt + num_workers: 8 + batch_size: 256 + + +label_transform: + function: ptocr.utils.transform_label,strLabelConverter + +transform: + function: ptocr.dataloader.RecLoad.DataAgument,transform_label + t_type: lower + char_type: En + +infer: +# model_path: './checkpoint/ag_CRNN_bb_rec_crnn_backbone_he_CRNN_Head_bs_256_ep_10_synthtext/CRNN_best.pth.tar' + model_path: './checkpoint/ag_CRNN_bb_resnet34_he_CRNN_Head_bs_512_ep_200000_alldata/CRNN_120000.pth.tar' +# path: '/src/notebooks/MyworkData/EnglishCrnnData/image/2697/6/107_Ramification_62303.jpg' + path: './english_val_img/' + save_path: '' diff --git a/config/rec_FC_resnet_english_all.yaml b/config/rec_FC_resnet_english_all.yaml new file mode 100644 index 0000000..2a769de --- /dev/null +++ b/config/rec_FC_resnet_english_all.yaml @@ -0,0 +1,107 @@ +base: + gpu_id: '0,1' + algorithm: FC + pretrained: False + in_channels: 2048 + out_channels: 1024 + ignore_index: 37 + max_length: 25 + img_shape: [32,100] + is_gray: True + use_conv: False + use_attention: False + use_lstm: True + lstm_num: 2 + num_class: 36 + start_iters: 0 + max_iters: 300000 + eval_iter: 10000 + show_step: 100 + checkpoints: ./checkpoint + save_epoch: 1 + show_num: 10 + restore: False + finetune: False + restore_file : ./checkpoint/ag_CRNN_bb_rec_crnn_backbone_he_CRNN_Head_bs_256_ep_20_20210207English/CRNN_best.pth.tar + +backbone: + function: ptocr.model.backbone.reg_resnet_bd,resnet50 + +head: + function: ptocr.model.head.rec_FCHead,FC_Head + +architectures: + model_function: ptocr.model.architectures.rec_model,RecModel + loss_function: ptocr.model.architectures.rec_model,RecLoss + +loss: + function: ptocr.model.loss.fc_loss,FCLoss + use_ctc_weight: False + reduction: 'mean' + center_function: ptocr.model.loss.centerloss,CenterLoss + use_center: False + center_lr: 0.5 + label_score: 0.95 +# min_score: 0.01 + weight_center: 0.000001 + + +optimizer: + function: ptocr.optimizer,AdamDecay + base_lr: 0.001 + beta1: 0.9 + beta2: 0.999 + weight_decay: 0.00005 + +# optimizer: +# function: ptocr.optimizer,SGDDecay +# base_lr: 0.002 +# momentum: 0.99 +# weight_decay: 0.00005 + +# optimizer_decay: +# function: ptocr.optimizer,adjust_learning_rate_poly +# factor: 0.9 + +optimizer_decay: + function: ptocr.optimizer,adjust_learning_rate + schedule: [100000,200000] + gama: 0.1 + +optimizer_decay_center: + function: ptocr.optimizer,adjust_learning_rate_center + schedule: [80000,160000] + gama: 0.1 + +trainload: + function: ptocr.dataloader.RecLoad.CRNNProcess1,GetDataLoad + train_file: ['/src/notebooks/MyworkData/EnglishCrnnData/train_lmdb/SynthText/','/src/notebooks/MyworkData/EnglishCrnnData/train_lmdb/MJSynth'] + batch_ratio: [0.5,0.5] + key_file: /src/notebooks/MyworkData/EnglishCrnnData/key_new.txt + bg_path: ./bg_img/ + num_workers: 16 + batch_size: 256 + +valload: + function: ptocr.dataloader.RecLoad.CRNNProcess1,GetValDataLoad + root: '/src/notebooks/pytorchOCR-master/english_val_img' + dir: ['CUTE80','IC03_867','IC13_1015','IC13_857','IC15_1811','IIIT5k_3000','SVT','SVTP','IC15_2077'] + test_file: /src/notebooks/MyworkData/EnglishCrnnData/val_new.txt + num_workers: 2 + batch_size: 1 + + +label_transform: + function: ptocr.utils.transform_label,FCConverter + +transform: + function: ptocr.dataloader.RecLoad.DataAgument,transform_label + t_type: lower + char_type: En + +infer: +# model_path: './checkpoint/ag_CRNN_bb_rec_crnn_backbone_he_CRNN_Head_bs_256_ep_10_synthtext/CRNN_best.pth.tar' + model_path: './checkpoint/ag_FC_bb_resnet34_he_FC_Head_bs_128_ep_200000_FC/FC_190000.pth.tar' +# path: '/src/notebooks/MyworkData/EnglishCrnnData/image/2697/6/107_Ramification_62303.jpg' + path: './english_val_img/' + save_path: '' diff --git a/doc/example/det_test_list.txt b/doc/example/det_test_list.txt new file mode 100644 index 0000000..3893474 --- /dev/null +++ b/doc/example/det_test_list.txt @@ -0,0 +1,9 @@ +/src/notebooks/detect_text/icdar2015/image/img_1000.jpg +/src/notebooks/detect_text/icdar2015/image/img_100.jpg +/src/notebooks/detect_text/icdar2015/image/img_101.jpg +/src/notebooks/detect_text/icdar2015/image/img_102.jpg +/src/notebooks/detect_text/icdar2015/image/img_103.jpg +/src/notebooks/detect_text/icdar2015/image/img_104.jpg +/src/notebooks/detect_text/icdar2015/image/img_105.jpg +/src/notebooks/detect_text/icdar2015/image/img_106.jpg +/src/notebooks/detect_text/icdar2015/image/img_107.jpg \ No newline at end of file diff --git a/doc/example/det_train_list.txt b/doc/example/det_train_list.txt new file mode 100644 index 0000000..1132395 --- /dev/null +++ b/doc/example/det_train_list.txt @@ -0,0 +1,9 @@ +/src/notebooks/detect_text/icdar2015/image/img_1000.jpg /src/notebooks/detect_text/icdar2015/label/gt_img_1000.txt +/src/notebooks/detect_text/icdar2015/image/img_100.jpg /src/notebooks/detect_text/icdar2015/label/gt_img_100.txt +/src/notebooks/detect_text/icdar2015/image/img_101.jpg /src/notebooks/detect_text/icdar2015/label/gt_img_101.txt +/src/notebooks/detect_text/icdar2015/image/img_102.jpg /src/notebooks/detect_text/icdar2015/label/gt_img_102.txt +/src/notebooks/detect_text/icdar2015/image/img_103.jpg /src/notebooks/detect_text/icdar2015/label/gt_img_103.txt +/src/notebooks/detect_text/icdar2015/image/img_104.jpg /src/notebooks/detect_text/icdar2015/label/gt_img_104.txt +/src/notebooks/detect_text/icdar2015/image/img_105.jpg /src/notebooks/detect_text/icdar2015/label/gt_img_105.txt +/src/notebooks/detect_text/icdar2015/image/img_106.jpg /src/notebooks/detect_text/icdar2015/label/gt_img_106.txt +/src/notebooks/detect_text/icdar2015/image/img_107.jpg /src/notebooks/detect_text/icdar2015/label/gt_img_107.txt \ No newline at end of file diff --git a/doc/example/label.txt b/doc/example/label.txt new file mode 100644 index 0000000..16a038d --- /dev/null +++ b/doc/example/label.txt @@ -0,0 +1,3 @@ +367,87,426,86,433,140,375,141,### +381,212,431,217,434,240,384,236,text +386,261,447,265,450,287,389,283,text diff --git a/doc/example/rec_test_list.txt b/doc/example/rec_test_list.txt new file mode 100644 index 0000000..fa73b1d --- /dev/null +++ b/doc/example/rec_test_list.txt @@ -0,0 +1,13 @@ +/src/notebooks/detect_text/icdar2015/recognize/test_img/word_1.png JOINT +/src/notebooks/detect_text/icdar2015/recognize/test_img/word_2.png yourself +/src/notebooks/detect_text/icdar2015/recognize/test_img/word_3.png 154 +/src/notebooks/detect_text/icdar2015/recognize/test_img/word_4.png 197 +/src/notebooks/detect_text/icdar2015/recognize/test_img/word_5.png 727 +/src/notebooks/detect_text/icdar2015/recognize/test_img/word_6.png 198 +/src/notebooks/detect_text/icdar2015/recognize/test_img/word_7.png 20029 +/src/notebooks/detect_text/icdar2015/recognize/test_img/word_8.png Free +/src/notebooks/detect_text/icdar2015/recognize/test_img/word_9.png from +/src/notebooks/detect_text/icdar2015/recognize/test_img/word_10.png PAIN +/src/notebooks/detect_text/icdar2015/recognize/test_img/word_11.png BLOCK +/src/notebooks/detect_text/icdar2015/recognize/test_img/word_12.png 441B +/src/notebooks/detect_text/icdar2015/recognize/test_img/word_13.png STOREY \ No newline at end of file diff --git a/doc/example/rec_train_list.txt b/doc/example/rec_train_list.txt new file mode 100644 index 0000000..ca6e8db --- /dev/null +++ b/doc/example/rec_train_list.txt @@ -0,0 +1,10 @@ +/src/notebooks/detect_text/icdar2015/recognize/image/word_1.png Genaxis Theatre +/src/notebooks/detect_text/icdar2015/recognize/image/word_2.png [06] +/src/notebooks/detect_text/icdar2015/recognize/image/word_3.png 62-03 +/src/notebooks/detect_text/icdar2015/recognize/image/word_4.png Carpark +/src/notebooks/detect_text/icdar2015/recognize/image/word_5.png EXIT +/src/notebooks/detect_text/icdar2015/recognize/image/word_6.png I2R +/src/notebooks/detect_text/icdar2015/recognize/image/word_7.png fusionopolis +/src/notebooks/detect_text/icdar2015/recognize/image/word_8.png fusionopolis +/src/notebooks/detect_text/icdar2015/recognize/image/word_9.png Reserve +/src/notebooks/detect_text/icdar2015/recognize/image/word_10.png CAUTION \ No newline at end of file diff --git a/doc/md/ocr.jpg b/doc/md/ocr.jpg new file mode 100644 index 0000000000000000000000000000000000000000..8cc1c92a5229c1d542c44fbd4e34953c36824df7 GIT binary patch literal 197278 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

Nj@&X@LwH9C*)fFWvbGQfv-RW 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

VGMlncoAm0P=5XuW{aPHhUP-V~gh!~|Fyx8n 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 GCjJ8lk--=M literal 0 HcmV?d00001 diff --git a/doc/md/onnx_to_tensorrt.md b/doc/md/onnx_to_tensorrt.md new file mode 100644 index 0000000..d8db60a --- /dev/null +++ b/doc/md/onnx_to_tensorrt.md @@ -0,0 +1,62 @@ +### onnx转tensorrt +本项目使用tensorrt版本:TensorRT-7.0.0.11 + + + +#### 参数解释 +pytorch_to_onnx.py 文件参数 +|参数|含义| +|-|-| +|config|算法的配置文件| +|model_path|训练好的模型文件| +|img_path|测试的图片| +|save_path|onnx保存文件| +|batch_size|设置测试batch| +|max_size|设置最长边| +|algorithm|算法名称| +|add_padding|是否将短边padding到和长边一样| + + + +onnx_to_tensorrt.py 文件参数 +|参数|含义| +|-|-| +|onnx_path|生成的onnx文件| +|trt_engine_path|保存的engine文件路径| +|img_path|测试的图片| +|batch_size|设置测试batch| +|max_size|设置最长边| +|algorithm|算法名称| +|add_padding|是否将短边padding到和长边一样| + +#### 单张调用 +1. 生成onnx文件 + + +- DB算法调用 + ``` + python3 ./script/pytorch_to_onnx.py --config ./config/det_DB_mobilev3.yaml --model_path ./checkpoint/DB_best.pth.tar --img_path /src/notebooks/detect_text/icdar2015/ch4_test_images/img_10.jpg --save_path ./onnx/DB.onnx --batch_size 1 --max_size 1536 --algorithm DB --add_padding + ``` +2. simple onnx文件 + + ``` + sh onnx-simple.sh DB.onnx DB-simple.onnx + ``` + +3. 生成tensorrt engine +- DB算法调用 + ``` + CUDA_VISIBLE_DEVICES=2 python3 ./script/onnx_to_tensorrt.py --onnx_path ./onnx/DB-simple.onnx --trt_engine_path ./onnx/DB.engine --img_path /src/notebooks/detect_text/icdar2015/ch4_test_images/img_10.jpg --batch_size 1 --algorithm DB --max_size 1536 --add_padding + ``` + +4. infer 调用 + +``` +python3 ./tools/det_infer.py --config ./config/det_DB_mobilev3.yaml --img_path /src/notebooks/detect_text/icdar2015/ch4_test_images --result_save_path ./result --trt_path ./onnx/DB.engine --batch_size 1 --max_size 1536 --add_padding +``` + +#### batch 调用 + +操作同上,和单张调用一样,只是要把batch_size设置大于1 + +- 提示:其余算法类似 diff --git a/doc/md/pytorch_to_onnx.md b/doc/md/pytorch_to_onnx.md new file mode 100644 index 0000000..d19e3d0 --- /dev/null +++ b/doc/md/pytorch_to_onnx.md @@ -0,0 +1,34 @@ +### pytorch 转onnx + +#### 1. 运行根目录to_onnx.sh文件: + +``` +sh to_onnx.sh +``` +里面有四个参数,需要对应修改,参数解释如下: +|参数|解释| +|-|-| +|config|对应算法的config文件| +|model_path|对应算法的模型文件| +|img_path|测试图片| +|save_path|保存onnx文件| +- 提示:这里onnx文件建议生成在项目中onnx文件夹下 +#### 2. 使用onnx文件夹下的 onnx-simple.sh对生成的onnx文件进行精简,运行: + +``` +sh onnx-simple.sh 生成的onnx文件 精简后的onnx文件 +``` +例如: + +``` +sh onnx-simple.sh DBnet.onnx DBnet-simple.onnx +``` +#### 3. onnx调用 +运行: + +``` +python3 ./tools/det_infer.py --config ./config/det_DB_mobilev3.yaml --model_path ./checkpoint/ag_DB_bb_mobilenet_v3_small_he_DB_Head_bs_16_ep_400/DB_64.pth.tar --img_path /src/notebooks/detect_text/icdar2015/ch4_test_images/img_10.jpg --result_save_path ./result --onnx_path ./onnx/DBnet-simple.onnx +``` +- 提示:这里如果加上--onnx_path就是onnx调用,否则是pytorch调用 + + diff --git "a/doc/md/\346\226\207\346\234\254\346\243\200\346\265\213\350\256\255\347\273\203\346\226\207\346\241\243.md" "b/doc/md/\346\226\207\346\234\254\346\243\200\346\265\213\350\256\255\347\273\203\346\226\207\346\241\243.md" new file mode 100644 index 0000000..931796e --- /dev/null +++ "b/doc/md/\346\226\207\346\234\254\346\243\200\346\265\213\350\256\255\347\273\203\346\226\207\346\241\243.md" @@ -0,0 +1,62 @@ +## 训练文档 +*** + +### step 1 环境安装和预训练模型 +1. 编译c++后处理文件 + +``` +sh make.sh +``` +2. 下载预训练模型 +预训练模型地址:[下载链接](https://pan.baidu.com/s/1zONYFPsS3szaf5BHeQh5ZA)(code:fxw6) + +3. 下载icdar2015测试模型(不做测试可跳过这一步) +测试模型地址:[下载链接](https://pan.baidu.com/s/1zONYFPsS3szaf5BHeQh5ZA)(code:fxw6) + +4. 将下载下来的pre_model和checkpoint文件夹分别替换项目中的同名文件夹 +*** + +### step 2 准备训练所需文件 +1. 准备一个train_list.txt[示例](https://github.com/BADBADBADBOY/pytorchOCR/blob/master/doc/example/det_train_list.txt),格式是:图片文件绝对位置+图片文件标注txt文件绝对位置,用 \t 分隔. 参照下面修改成你自己的地址。 + +``` +python3 ./script/get_train_list.py --img_path /src/train/image --label_path /src/train/label --save_path /src/train +``` +运行后在/src/train会生成一个train_list.txt。 +- 提示:你的图片和label文件要同名。如果你要在训练时做验证要生成一个test_list.txt[示例](https://github.com/BADBADBADBOY/pytorchOCR/blob/master/doc/example/det_test_list.txt),在yaml中start_val可设置多少epoch后开始做验证。 +- 制作label文件说明:照着icdar2015的格式, x1,y1,x2,y2,x3,y3,x4,y4,label,其中不参与训练文本(例如模糊文本),label设置为###,代表不参与训练,除此之外表示参与训练,可以像我这样用text,或者别的也行。[label格式参照这里](https://github.com/BADBADBADBOY/pytorchOCR/blob/master/doc/example/label.txt) +*** +### step 3 模型训练 +#### 正常模型训练 + + +1. 修改./config中对应算法的yaml中参数,基本上只需修改数据路径即可。 +2. 运行下面命令 + +``` +python3 tools/det_train.py --config ./config/det_DB_mobilev3.yaml --log_str train_log --n_epoch 1200 --start_val 600 --base_lr 0.002 --gpu_id 2 +``` + +- 提示:在./config/det_DB_resnet50.yaml里有[参数解释](https://github.com/BADBADBADBOY/pytorchOCR/blob/master/config/det_DB_resnet50.yaml),其他的yaml文件都是类似的,可以参照里面的。log_str是为了多次训练的时候结果可以保存在不同文件夹 + +#### 断点恢复训练 +将yaml文件中base下的restore置为True,restore_file填上恢复训练的模型地址,运行: +``` +python3 tools/det_train.py --config ./config/det_DB_mobilev3.yaml --log_str train_log --n_epoch 1200 --start_val 600 --base_lr 0.002 --gpu_id 2 +``` + + +*** + +### step 4 模型测试 +1. 修改infer.sh中的参数 +2. 运行下面命令 + +``` +sh infer.sh +``` +- 提示:测试时infer.sh文件中的 --img_path 既可以是文件夹也可以是文件 + + + + diff --git "a/doc/md/\346\226\207\346\234\254\350\257\206\345\210\253\350\256\255\347\273\203\346\226\207\346\241\243.md" "b/doc/md/\346\226\207\346\234\254\350\257\206\345\210\253\350\256\255\347\273\203\346\226\207\346\241\243.md" new file mode 100644 index 0000000..da5a0fc --- /dev/null +++ "b/doc/md/\346\226\207\346\234\254\350\257\206\345\210\253\350\256\255\347\273\203\346\226\207\346\241\243.md" @@ -0,0 +1,20 @@ +### 文本识别 +#### 数据准备 + +需要一个train_list.txt[示例](https://github.com/BADBADBADBOY/pytorchOCR/blob/master/doc/example/rec_train_list.txt) , 格式:图片绝对路径+\t+label。 具体可参照项目中data/example中例子。 +如果训练过程中需要做验证,需要制作相同的数据格式有一个test_list.txt[示例](https://github.com/BADBADBADBOY/pytorchOCR/blob/master/doc/example/rec_test_list.txt)。 + +#### 训练模型 +1. 修改./config中对应算法的yaml中参数,基本上只需修改数据路径即可。 +2. 在./tools/rec_train.py最下面打开不同的config中的yaml对应不同的算法 +3. 运行下面命令 + +``` +python3 ./tools/rec_train.py +``` +#### 测试模型 +1. 运行下面命令 + +``` +python3 ./tools/rec_infer.py +``` \ No newline at end of file diff --git "a/doc/md/\346\250\241\345\236\213\345\211\252\346\236\235.md" "b/doc/md/\346\250\241\345\236\213\345\211\252\346\236\235.md" new file mode 100644 index 0000000..f8b8174 --- /dev/null +++ "b/doc/md/\346\250\241\345\236\213\345\211\252\346\236\235.md" @@ -0,0 +1,34 @@ +### 模型剪枝 + + + +这里暂时支持对mobilev3 DBnet进行剪枝。尝试了对backbone和对整个模型两种方式压缩。 + +#### 参数解释 +prune_model_all.py 文件参数 +|参数|含义|额外说明| +|-|-|-| +|config|算法的配置文件|| +|cut_percent|剪枝比率|由于类似resnet的跨层连接,剪枝比率不完全等于这里设置的,可能偏小| +|base_num|保证剪完后的channel是base_num的倍数|除去剪完后为1的,其余是base_num的倍数| +|checkpoint|稀疏训练好的模型文件|| +|save_prune_model_path|剪完后保存的模型文件地址|这里会生成两个文件(其实可以合成一个保存)| +|img_file|测试的图片|| + +#### 如何操作 + +1. 稀疏训练 +``` +python3 tools/det_train.py --config ./config/det_DB_mobilev3.yaml --log_str train_pruned --sr_lr 0.00007 --n_epoch 1200 --start_val 600 --base_lr 0.001 --gpu_id 2 +``` + +2. 模型压缩 +``` +python3 tools/pruned/prune_model_all.py --config ./config/det_DB_mobilev3.yaml --base_num 2 --cut_percent 0.6 --checkpoint ./checkpoint/DB_best.pth.tar --save_prune_model_path ./checkpoint/pruned/ --img_file ./icdar2015/test/img_108.jpg +``` + +3. 剪枝后finetune +``` +python3 tools/det_train.py --config ./config/det_DB_mobilev3.yaml --log_str total_prune_finetune --pruned_model_dict_path ./checkpoint/pruned/pruned_dict.dict --prune_model_path ./checkpoint/pruned/pruned_dict.pth --prune_type total --n_epoch 200 --start_val 30 --base_lr 0.0008 --gpu_id 2 +``` + diff --git "a/doc/md/\346\250\241\345\236\213\350\222\270\351\246\217.md" "b/doc/md/\346\250\241\345\236\213\350\222\270\351\246\217.md" new file mode 100644 index 0000000..0b7943a --- /dev/null +++ "b/doc/md/\346\250\241\345\236\213\350\222\270\351\246\217.md" @@ -0,0 +1,25 @@ +### 模型蒸馏 + +这里使用的是通过大模型的前项输出作为soft label和生成的label同时监督训练,这里目前只对DBnet做了尝试,其余算法应该类似, +其中soft label监督只对binary和thresh_binary,忽略了thresh,测试下来这种方式效果最好。 + + + +#### 如何训练(对压缩后模型进行蒸馏) + + +``` +python3 tools/det_train.py +--config ./config/det_DB_mobilev3.yaml +--log_str total_prune_20201015_distil3 +--pruned_model_dict_path ./checkpoint/ag_DB_bb_mobilenet_v3_small_he_DB_Head_bs_16_ep_1200_mobile_slim_all/pruned/pruned_dict.dict +--prune_model_path ./checkpoint/ag_DB_bb_mobilenet_v3_small_he_DB_Head_bs_16_ep_1200_mobile_slim_all/pruned/pruned_dict.pth +--prune_type total +--n_epoch 200 +--start_val 30 +--base_lr 0.0008 +--gpu_id 2 +--t_ratio 0.1 +--t_model_path ./checkpoint/ag_DB_bb_resnet50_he_DB_Head_bs_8_ep_1201/DB_best.pth.tar +--t_config ./config/det_DB_resnet50_3_3.yaml +``` diff --git a/doc/show/ocr1.jpg b/doc/show/ocr1.jpg new file mode 100644 index 0000000000000000000000000000000000000000..397ff8cb29d0f13e8339626a535d2d9790385c1b GIT binary patch literal 237249 zcmbTdbx<5#^fovI0t5(>;GRH$puwF%f`tIV-7U!AZWAC#a3{dv9^Bn!@WI^&hrwN! zclWE`ezkw??&&^NUDfw?ou_X1?R%c*+~>LHH3056DOo815)u-?@Z|tJF9IY0uaJ=b zJO7sp<%qmz`(?MF|;?>IB&7A|GWP0A^&y% zuh+|og^q^)UyJ{5_S^x$Lq~r9Y7`miBj6Pt5;7jra}R(D06=>E677Eh|1U#&g^cnV z^(7<>%$EkWxG&)&BfokH_B9I1OKYE(`v4TY*Y7^Ci=n<(GD7?4fY0FB zIXSzyy19FJ2K){T3J&=b8XFg%keKv0IVC$MH!r`Su;^b^bxmzueM4hYS9ecuAGCj9 zaAI<5dS-TReqntBzPYu%v%9x{dUk$sd3Akrd-oqMBmnaN1?zv1{XcNwz2JKFG6pDU z|KUP<<@Vypcqp$wu%o^cQ$jOxc>j^Z4;^1TCabdZ4GpLA34yWWI0hjt*ZQZ^|DgS! z$o}5}^Z);a?04Lgr}zmZ_-;=QXcH?Rugzk)DsV*8iqN`##7z$dvi5C3|HIHHxmYRa(< zs;vFxdnRS<-J(kLre$ZBGc>_ynZvI(_|!>hi6YeA9k!#bl{GjsHk0yh>$u95y(&ULiL(}FzW%G+m!+8eqR1{y4sqR~n!PCRTfJej2&8=m z1U;O(#X4TAt8-L61Kf!#KePY>nj>zOG0ZBwL z-!=n2s1uVQbRQKqvo|c~hMYXClcq~cvS~z&#Z}Ip0m8*hYNy{5&%!ioJ!-#)*F>OW z07V$vs>>`94{ll=?B-N)dvR(Ph>r_*XZ+y~1Mkk1WHEH=z}jx=oSV7}@2o{WOw*q% z&5Y@F+rU1eCYbCu2TAg(w^}b+Xl^x#wZBq>9do8bF1VWd_AY5rSIAQ39NLs90nmIP zWBq;m2k%;?ZKRsOK7pCH`?j$Py>eP+lQuE77BMFM%z9PB?TUk?R-PF_$8;fLU`|D9 zl2@F5Y8K;ZxiPA#$*vIo`^u>{qLV?L<-*@S*`{Qnky1YU@oByEc@c^@d(odV7AASE zGy@f~99D|03D0$9CfZ|&m+(>7gMAY!X5ZxKpcCZCIDStRT|(?Zxns(D&b9q#x~6@v zk4=Xo_MoT`7oh{;L0A1N(y56@Y)Ml^JC0*t{)Qh24G>A)t z*3-x8#X?Y%nyi|-Bzw#`@}1h&lxbrFLq3-te8dct6jVnt-*z=*)CRZQ9J;#dVo;TSvA<#jwW<~vQ$}k_u3anD>zy)v8bOt zoiWL=W5pERS9j&fP&oFOs?I7|%{sy?z*m@F62XxRBgsLBiGn=5a56!aI$dBXhre`{gSrB;sp> zq>GRbW5Pq6zI9xGG}QP*^xqh|bJx#1B)d)sb<@Ujr~KHdFowgOu;EBb)#@PrV+fLJ zi%homdZE z&zPvPg-T0cZ_0wGk){JoP_PhJr7XNXi3^f&&VKCmdJ{I9Hn8GMiJ~Q4GPVCeDfBN- zpEmMmliQiAfLyWl5&oUo>?$8~yPFF?6D$*o2Z~D)rMxhVF(5qr8l{ zpmUSPN5<{MB+sCfuX!7xs;2x;NU-nEfOKm+{h4FXMGu}8sS8A@Qe7`AFFt+nA0P3P zOM)B7afQHd(@}k`0nL{08pf4&jrrzvO{KPW9DD40jDJioe+0;7_6TCD6&8p2O@Sg5 zUa+SvXq*U>9jq#!i)M!b)=ZxPjB%QE)fHzgDv;{yvK$P zV}_pUrQuKCyi;jXsXjxIEF-ua_vBj15~-1*r3<)r58H<9c`}m7{1b;T@M|l$7D+ZR zr(!?Fz=o1M};&%JLRT<&3+w zJlz;fDc8I(?ozLUp8@`v?h+mC*bm@!yWj=x>o6fTxV-qrweqQYmVGfvBZoOZsDsid z6cwYQ2*{XyKf9@MsCSwGwg4{*u1iO5yO&-pBllbxEJO%wzc-=@Df=3L_Ia!MJ=D&~ zzysW=@C^8wG1uPJ#(fGp{(ZUnM_c;OF}$Ivrh5DV*e*F;mY|rl*}p~d(Gq}0ZWs&b z^&&nlZ4n3}C^K2l&-};BIpVE&ueNmH%X!1?ahjvneCpQq3a#DY#dou*)TA`Zoe!uaSOk<{IC}^o(+ozdOEojG8 z%iy|_dSHk;{Jg16?J?jPV51mF`L_AA$Cu}1!CRyDCSv`-4#V<}G7UHJX_(J7$v#H( zdrM44yoEFM&Q=LAKTLfMuR6pjn30irsq#9)p8G;ZcnN!ArJ@r3& zaaGqK_HtJP9i?`GtK?*T&j8HzlZ2_p)7Rl2XFk^5YaBWhstsWysUjb&a>JXzfd#P> zEHP%Tg-iSxIAV~OS725zxKobp5J~p8>BPGkP*` zoxOBw6T4;75rkJB5XNr20b>QPyV+OaQ#z)7vqU`;3M^dX!WC+7(oP3){<{8p^T4FI ze$;AYw018j3tFK|QZj!h`}3G^LQy6CZ^>$C(^sAq=C<02a-*r5MOjKFufQ~P7nn)9 zr>A;U@THCqZx^Xg!c3MOUTx$T5!;A(mi}je^OH*rP2Q$$t+dWMg}`EuFMsT?qG_p@ zPsltdIf=>U7rjA~o~BcJ?N&P5X<`<<`65mQmBu#1!heD!_3&!lDY^)P3?aXt1)o$y z7tnsUeb~FcUVwbI1Ub(VVZ|iTf{b$jl=o6T2eGM&V}{bH_fq0!B1V$R_n1!p_GsVL zL(}tHS?RkJiRH4aF$?OG2$Q_^J^I6i{&l+3&;TsRHO!nginX{+ScjS;iq1?KhD^xA z9U!xKRyYwX@fjD^zns!D3CWchnVcmOVpiO1Uk3+V_^1rp zarV3$28|7KuxtqOR#0H_+1+nfeK=LcaUhNz<}>uL#C`*~pP_*DjEMYvNEkQ$*#+{; zgB;agLize+`L6%fi3rzLw#bqxK*HRxGQTw&%$ysldA(C z+p+x;wts7rIZ^RhFAWEQoGqL8ARo)Eh2m(r;5~qEa_#Vk#WED%_0VQrW6SNOKwPMt)QOJ z`Khvi=yw=Dn-luJLvx;m)qRq@j!c@b(|Nr?61T?CIjhd6dS2}c8qrq%e*S^Hp^Bs=JNV7~rs3 zaKL^Mzm@y0i}doXQziHcbRsp=z7jCi*bjanrMYjv`OF&;7akRq^bF_%?lR@M9cU?c zO{n1~?*40NiVFx&ek2~3B-l^4SCYgB{O_)eeE!2JMkeR265UaBkAB1@*D9N}#Z1P7 z0c<73WC474nI`YTxQ6=ED2PfSUfCAw=`tu0$!p@hpV*#K&3u2i^Qb&G;q+j|v_Yy! zf!`tl;k0GlFOSW8<=QzYW&`#muHo7HvtOQ@7X;J93w=f0#d!Jmd^VmZK2`*uGMMw? zie9A&1Q|ppN>a!ir&#`F))c=F>9!#5)e7HU95W#>v%w+{#}R`)1GN82tt*CS0!17U zqEREGB1*@yhoYa)WEa}m^~ox^Z$Cc+UiXhjKoJ84-jTl3>w?O;Y}Xb|R9RM!HVK1Y zkn@?&U{9DetSmrl#b7r=sPXcRlXV@3YiUn1v|$A(aL&o_w0Zf3?Rt;ut^Uu9lSpe+ zx_2X+eoYo#(y^S8Qt}oz-Mv*h^YL|22wu^RFGIC7&j7cXx(EBj>I0ms)Y}4k-~Bkh zLkBhHpagt`{ndo-QbJcXRg83ft9Nd_NL^yFN=TS&s#*h%my_$cEtUrJtBgG7N_P>= zTofHBSNNAVqfYzwKE+ZDC7q+1f}4QjN2}?PABJKR?dgcji%J23#+-{Q7E@|qsgJ(~Z zw!Zg_Yk`l#h5HtC<;Dde4}t_i1qC%65_Y!ZT%5A);E`w#Dp9NLjCGP^@OVcoEs5nP zpO`|C0^ndy`{ZFZu7rbE!9oqFRW(*dQr#S~SUT zMTw&20Trsi_?DkJ8-tYJU^g!0Qi@$Z0}j-m0b!(gHeE|mEGBPS^4E5dsVS$4s;8vW zgaZ`e)^ek2;Og7}YB)&@acF?%6iQN+)Z}6(iiIDIS$khCtF1|xdVQ2V+g6>}T{&=P zrm@v`YO!^q_ieM_HI0K>aqC#{P}ygnI;9M@U!+DukHW3{GZ`Q(?It+~(jks#004GY z;+%Adq%F%a#x}f^mhtQ0g20t6#8UzCyBk!ft5t53e`miKnu8Abn7bPV|QH=lj`cO z<&dR0Wb5;hI(HSVnhSNmVw9G~yIDznon)tJKk;YRZQduXxyD+ZrQfXVZG)>`-xUGC*XAPZ+4;l7{%?qlu5h^N@RvuN|fs9mmq7? zK;04(EF>x(zKgw!k|XV@<%*NMTvwxMl(!hkl*T#R`id6_-KQKl_Y{bF#1Fzc0 z;lCkN&5;)c#(R1+t&Pq1Hm}c69Lns;0*a5#o6@ot1lBG#_%a`=GN{q$(PP>GM%nC~ zJikXF@3yUVD&wCo!!1N}GgV z@jnADXQz$U@`^?1$%+LOv2LQ2PizY)o+1iY_OufkMf8?)^FQan(X6qhr_=~fkIMh8 zR<9fgMBF;n)DDk)rjI&)zbn#!FSV5oYB4#+u^RU8RF_)nDE##6f=YY;N_BCDp0r8O zkEL^b@{vv)5)+_xbv=vKBFA(@%&b`OZ6K^i8ET17X?57y*~d7Yo}d3Ak)Hy+553qq zZaO|UET4t}DU6+%*qqj(S3sD(jAcQ{yTraTN=bQcX_52QBsCw5uC~NQm}7-}z=81a z{M=Vne<;3T|AG@WKI~(JUl$unB+^x(`s8R#9Kdjgpm2qD@_Xt13eIUsu4-C5G5&&onw<%ZR0C&n7>e2Z}SNZ+9#V03V)c5;E>G|Wu7c>GVbqQq!Y(gB$Lgo9G=oaWmBW}5G*8Zwsi;33 zB;gnBe*Fv(sg1!;i5S)8C4Uv47zU=P>fe;T{zdyS-?%w?sV&bjH7klw>1mci`toC3@lXS!}&RibaU`!B>C{R1;UZGPl z$sbC>cW|qr0Xp%Yfr(FLPG2CzXVq#>S<~|3-G0s+r>w~8X@SxEh3&1Uxxn+2oiUG( zBo-L1#Uk&o6%??X4;Vpan?1VYC}eC%-Ndl~Ss0yozKeG9>zS=yVV4QZ@K;Hp$<%(Kn*h`1S%K`doypZ*C`0+rQ}IxS%vb-L zqnPvwTUsU`A44fVBhj>)A>Mf=7=1gk`QdSSk!!Iyzy(1*@+g5!|2;&3mvUny!MVz| z&?p-+@wQDCs(0CSj;6kzFYcvypLsaM?Hwq+!iQWOXsayrLXFu!#ybMgh~X2(dN?di zK1;sf9d4R{8pv7&Owp6Xjc_?KM@U2dg3Do2ym@p2>wV0eD}K5WJLrjghcdq)&^y_w z+P;$-o+tT1o&GO06E*F~c(0~RT1A%7)$g&R$D&m9flVbNkDnsnu64}hq-2YG#Mn4> zPK8G9I?1*}h7ulgH@l7YK#o+}yRNwKew{LVZ<}e4^tYGb7cC=T{{Eu~Q@yXw(VFS` zY}A2464i-AUko=av<-8qZ0RvWEtsFOu3+CObRA01_?Bk8V^53UO$2w^u2e-K6hVOA zketdYrAg^VU3;vAZ(~ab6 zVn1xhfP8VgHmW?JiYI@{WQxx8i|jTX!1xXFo$5ZTt#{5@m@~Zxi@#`@G6?vi)+xej z#LH0Elx@wjdU@qjHIp@|I(sxK@phz6)B<@PNM0vaiZkBrb^*%Ezml|vxzVa@PFXov z6zN&^r^vxv+YJ5!?P-4GEAZ1(wCl!b@p5Xn@ElDt;I*kZkig13H(IU^HiCmnjnEe^ zH?vULYjcS&$fuAuRs%AZ-j3$cTmriNqWX&G>$4zv=AIT0pLnj40=2hEG>*M6Hn=$D|XOa-RyL*kKr3d$uD#Z{)c^(P;XCIiHw0FvvfA=n^2;|!I_HiJ< z@$c~5ap$JjkBW)9&dUUAN|^Pjxu6`Gwc2DET5N-S)!X2M)w2?LYv?Q<)RDHXEf{8h zMgWd!3wXFF=WTh+K4Q&Ru~`2(macH?@q@1fVRuzL%QBv=h_h?pMwZuL69wKltD6f(qiG6otGmLE?5g$EEpi#hgBY?u4Axe#(Ujis{S!VG~^2&r^T*p(b{8S zi~1`GvfXjUbP`a|F_f;zC{>;FZbJ{pyRw2XwoP@t0@Ia#B5T_DCZdv0gXtOY{k*B= z;|@G2te`n)L*5mg3gsaN0Az+=6uRJqQ!>tMDY1o?bs_lhK4vOuTuNFa_g+btOXRGU zE=_6;IanriD0KlVvnP&wh)Aua?FY6$&xwi!>^d@aZJi#6u#&2!_Pz*-k>8!py7#m= z4+P@6^lCG3zblfQ;|vR*YLIhjsL=mOGTGH^VfV-zvJ<+_GV?%MBF7s$g#n-b?Na;0 zrJK%GG(zWBAvHM<*4u9hH$^9s%UF*o#{QMe;{5$X6V3VLq~Q!h@5yNOut;8dgr*Ik z?WIuA)|vgQvY0?@2qv3zSc%Zo+1O(Y<0D;C9B^FsTuqvR*T4KRiQbx#9S-%uX4=^k zVz(o|uV4D|L~EIWe3{!YZW8-3gkRWk8)8{(B@sW5;BEUnQPZg77ItXnkc< zi^$9HRRSTVpC7HC0k|tm1U3N}RTq~O?E36^H#{3J0u@o+Oob$K<=*(E~Altuf>0t5e5OGeL(+AP8#bLG29 zlzbx%T4I{0K0Cy?CH{e5&L>-%mr>vh)#~+0vOqvizsdJaEbl>vE9XPC{r<}|s;kVg znnyOXt7vWc9;VWczLh0B)nl*?+uOp&mAH`c3Le=3peTA7HWQyT{DznbAmH^BU+p``e&Dvs z8UwN!e`wRK26cPB6R917t{del~EM*zA`e%TC zOR@Bf&>teln7%R2sp&m#50VN^gm<`lF%AI}B(6su>Tvpvx=Fbe7ar5?p#QQ0LaBJ6 z*|~0DPZ+&L?i)B5527c*0of1U1b2GR0MxhxWq0t^(oL-q>B!~*%D#(rCiGHStZ65 zkp4k_Jx!v6^2BG75WTq`;uJ=IKcDZ}HBUDY=(P$~Ev;`-$93isKD_jBj5D z(Nz{&Hc4I;I2oA`PssZ393F-q?<9*-SNnC%9w{QkULsibCVqTTUhR@aSZ;##rfWdR zn*Pg|<>-43B}}FJ-<|>7J#p{8N$qMl{>IXTe4)wUf6Q#Rz_k07aiU;{d;{**AgX)9 zkhjp#(WBhU{yLc@&_pXoUPK%-Ao?p=YgF}o#7$k>tAL0lYG+|qYGopK(M=Tbo<~-< zh#Fq*!d;wqBLH$0uY}<%9}bR7uEagr5P5d3NqRC*9ng zc1a0q^}u+=%ygWQ`7CuCPc!C@oRx~Xh9zQQ!2cjmp4`Eqd-K!+o z;JUINk5uaixtUF z)Px}voA@(nv(fkt#)@% z)mbdtaay>lhN7@7%Nr^sKEy-{yn16Y(c-_+%kupy3vg!UjDa?YZp>Gn(Y(1G{QH1YgY_3{}ISI-{8N8Cguk8`N%uASI6$?%`Ay0VX)wX=C@5m z=3it%H;uEZW$|{C#VrP=wq;f1o5~WVQT_20<-qY&C;S+3t;{;g)Y52j_*5t?yO#n5VJ|MYKC|Pb|*nOi8@*`@pf3W06M;%^D;M$n+RJy znxOs?>W6JG?pI$^dAf$Cwe_;fRNWf|C{h+3S7Hy8hH{ z;V7$ZYd!3Jb%&{?F`K^R9Dg;WMByXXYl4K0dx9*qxI|OGi!wPWOp#`8C{TH5+5R%3--pB0gzQ*I!jJD7e z!87zk=;&Uj^QR^78O9MBUL0Zh%OerI4HUPImGp37pI|E*#s7^lBMdmiLAJ zrH*9svOm3fw;Iyyylx>fU@TryQB}b z23b0|xHvbPM6x%|!OPIvm@eQ?=vpUe>No87-yhq1i0pOtS7=WZ^5HVX=Or7V&j2~3 zrMt?wi;}Zj-zUlfU-R~Px>=J;*Oy$O3JAnt$o};|ZXMB0l_rq#K$_hAA&c|ua)j%N zD&jMLo>=0Y23zmPI#La{2ek55R{q=tq^wlC7sfbmzmLt~J9eW;Tg{0h=j7b9bK!E!Kxn~^Q;kCyYr1GR_8_3E+6*W7umEfb~|1)jU%3V~$U97k_ru08K3WWTu5 zp2jVt*2g+Yam@Gm)_l8S?D`i+_nFoBYmAzjy#gP_-X?hvW~|trTKanT5gJvXMQNFt zQt4l)E<4KOIuYMr#E}Q?`*CmMWNJu(YB^h%U)lk$MBU#7Sy)(zzpb?4h^*sY#bn7Gmo{l zBqCdZo=4rAgF6M=XuzF9z8@dMHZb%L^l_%lnR4Anr_)MvFCrZ>mPLTAvu7}f6)9e2 z?YZ{Os9z8-ly1wG^BA$3@$D(4#cpkcY$NzL+k@bS50S!b3-F(~QA|_@}vFcBQT`hTy09o`b2!@8PK}|_^>_ILPJAReqmbzvy#2dLB8RqiP6^w zB7uFA5o@OiZ34yN+1mfPzk#auifg0iOvZSysk4V ztdn$4FeQv(EXT#~d35zI`xxo&@N(*u0fXSgb1mt&!{R=9t}oJQdV8t+!SaN38GNKf zkwcDQt(EHM@0_>}7k-!(X!2F7ZC>tSMnO@JC@?4fNFZb+-9*Lno&Kkejl#Y|t z0G4u6_;7vj2&>5584%}uBFYk363ry&o?)sC#r{`n6SH zgOoT$B}1#&ozCclLHe7K5r>Mmd$Kd>odZSPOYRtGG2)s%M;czQX>J3Qd-F!HL^OkF zJ2r8FNP5%lK{Nfg4m!*_H*>kBQx)#O5jV?^J0y%A8(C9{+b4PNhwX$YXV2eg8S8}G z4^XboRuV(f$u;afd+lP1M-=N2Gl2y2X;>sTtXnv^`m4D@}0$HuICu%fVTgEcuBRKqmWCseg6$X-(WjTK(b%lAM9c{)qf+HND zrR*&)>3E&*=_uy=Cm*JXzVLca%Q#z74k>okS@~s99nVgYuEXsteT`d{KuM`nmr|x?eCRx$r>z+TjXkt5xc$C z5XzXWBT>~u2ho_hQQ?E+St4Xcrtvu9|e9mA04abQG77=)^+}h*;TND*j(;3T3 z@ktFM%nfdCSkA(y94yCIZOqPTDEdVbju;AxMa+-35jg`+F7=+LiNx?aV5x5axU;OO z+45!5QIlJge^T|V_jj>&z_zchlC|k{NcUtfAKe5WJ}P0hr#Qqw-gbvOKQJM1Zg>}a zPHu>vFeIM=Z_FnD(3}_qxFGaCz#e?QSNzKbhw(l9vj_=9_mZ`#yISy_ugz8f)4( z_bILNTK4YyGc@aIEJsBg#;$2Kh+a~WlN;k_Kn)lnv7s2E|G7R+ukwToO=n61x-C*% zIJC{hUMNH$8Bx(zN@X+K53XHB%$7fV3x2}eJF362kWR>uACHrlq3of}%<3nxRP%<1 zBUXz1^eKr#Ep*-@tfU5&uYK@FSfU1*`sO!;^;~Pfr<_e>I4Hi4K{#vKO~-+CR{|>m zWm>vAOfUy7yVw2n-Y9IMf}81=-9pW_1@>C?O9M={`y89+#{+b&8f$C;8b5BNPjPzb zF#qJ7wA3jHG8dEfoTdGn1A6bIwfdhMh()D3^N+gG)k-`OEfPO)mq|L&^+h6b$o^V5 zMoQDq-rlRT){#q}`Qt-)Yn^9+I7D!qo;N1`qwGHa5))}`M{sFB*qnH-QBHU63%u*Z zSukv9#H$}RNi@FQ^ao;T@K8EHxRwsH)N0M0(^346o<3<>93J__AbjGV{vTdfwp9>(*a>5a?pAF zxlpo#H1V3o@CsMNWS_t_dvsIjSU>dP(&~DIcSF9W^lC^)Sb8wjSviRTFGskFb24h8 z&2yG5Vm72!mh4#3N9ec7ukXKSrSQ0xhl^{iDe4bT6H>L!FrUh}`G0t2s2eC6bT!|W zXUmIAsk|Y31z|Ley5CJXI}T`SU5Rx8R)3LTi1L8u@9&z=xVXH-sfBH|i)Q+&{!?)* zoX=%sHIVSqOeY)4CGM2$M`48QOljbh8~D_#c31tbfs>G*YM4i!|A={jw#0{%SB|0` zs2Oy+@yf+lHYB-K^23}EU*AQR^eKZNdBtyz(waHbokC}tTOu*hkbYq_j;?w6y>MmB zSKc)a;;rEmAK!^2KLhywy}VB30;(%!E2+4BGS-1>*iKXT>JhvZjU^=mPb@T0xJ-f~ z)x5SPnvma>%mJ!S5myf{UR0X2{M^80SseV!SsQJQbwtdbr)M~i*sGZj(0YQQnz~7G z-(|stMda&oUh+HRnwicVZ4zABPs`n-XEZIt`igdYYGoJg0=rcw#5;V)dcrNFSQX}E ztx`V}i9@jNr;-Spxc`yUWwAT;$?PnK#`>gkl-Pc%{>51p0ar@et{CeZVb9yed#AzfeymCO3utZ)jRo2dd^;@Ql=9luw;CzHisV~Un$V0PBrBZ zoa%mmvi>;gXE=+;li5$IGy588KPD1wXZEBROv0k0aDo$HM9uW2DH`Bos=Yc{vzg}6 z)r(G#oDFClF?_I^&Y5YUSQ}V_#&Pj7r?1HXo}?Pr9^vipUz1wk5WJMye&R>N0Kg~O z@=r$nHob9vmzZ$eNOvjW3fA_uJ3c&$M-8^=NuC=)#z39B`~$(0qN#Ih7qP2DJnqF{ti7 zG11NFUQnCPi&wYy@S={@IV<1pD)ukJLjjSH(6_OV%`Ru0h*}?%+1=tL0XhLyM_e?UQW@4&$88PM5ILsduh$Bk{+o5Wd+KJ(rs z3crGM!)V{d-1RO^QR3=FBPg!}3jWXPpCP9d@WERJXVm?{Qp=~Pgja)k^UCal8wf^G zR77p3JMkN!R8PRGUa5_th(CN3_Xu$iFQVDrd3Xlg?dfbHFC;}HL)bhExH{_CJB041 zj#hsfZ0HpTJE(2{Afg5n@*it=YR+IbQXcQfrs(@eBAYCh+L@31&eOHX(AI>bn6>Rb zT4p8S{VfN0s)GW@O7;!NPU{n31jzLZpO7)3x^DPM8gCp2)x3OFqZ?VGY+e!NnT?c& z>&Va1tLdX@TfH6ZUgRpLR2)jAFA}K<^{(hhD-XX{T~r1YrjB;tSbaDDga5A(p6(>N z)$BKwBC9tamQZiV)QWz*xSA5#n?CwuF)A7Iy3gVGNq9ui*Zfw7DH6vs^59yOPq%`G zez8=S%hjxW%c7I&M2+o+W;UQrXpa=uYlk97zCEQ#xbJRVk^+nbRg1vMxa8bU84F5&cj^yqB>(!@|%Nr7kC)g*jFzznT z>>W$?H>^-x!}pT_4VZ=Mgk{d|8Gjwb)QC0koo-FU^%B;)LyXK5S&-bZ2s^ogG@pL1 zmvdPeT#D!$*-+dsRE;_&fgMG6t%kE}ysh)=KzEMXGOvgFxoxs;1M8Vt1WOagDi5ll zOL3C+rxxtWE;od*<|anHhgDNV;q9=XV;L;=%~ig{&%{%>>me5Ep*5?yh?+Gguq#Em zyF_1G<`5Z2_|*m9i{2c%-9|Xq%NtgEy?Ca5P?f0bbX^VGW-6>ycJb~XPFKeLc1^Ah z4k(?Kn{826Nla4XCB8)Eda7L(*9hP%mYVpwFd9XC3c4ToS&zy5yN@^RANoPG`#+*P za;HTO2~24&55`}cCX@6jt2)X_?JaR8-`whNAFNp1M0s(VJ_v>Fm-Df?nxYt0R83oyS0%Hhj) z++vT3-G)xfrLnG1-8DRY5qz&+!_OPd`~J;ff}x|+sOZa{3TsMr+k&Ou_DO>6*PZI(s<@t0 zZYgwJ^>DoU!6lq;E2pTz4j^X75YvniYTSRm2HmBk34_r50h<`WmVa8urW5Qu&pg>m zcHbeLc$_8YX0dkLr~M=niY-RWpWKgHf8@umlbLC(!>Z3)EJi`SRcopPQH9HfxS(?#I%$eTz}JP z*>;9^7Aa|~-Q#bm6>VC?=+FHOP90UjV}PT$TQ)$e{^KYE(s6>x7(s7aWYy- zP0fQ}J7hqd%hP>%q|kaIOd(r#v=0KIyB&F>o8b;kld|dDr;2isi;@+M3-?(GlfsVJ zZ=LcDxlYF0ble!qRC zEFs78r?sBgdre6vTt|nXb2N!3NiVyH_nb;k!;d8htv`}JXv(bk8EM{-wYxK+>y~3rq(fxJqt)I06FSwsfE86O+5^6mXecpwLu?(y2$Jg|lJ7BIo~QYG z*N^D9kSfz1!Z_u#RMJz6w2It0HaKLv=Nma8x)KXeOu5pd@1USu4UxErkk-PIM#SnC;W zo(zg524-0ym_Yamq?3KiZ{zX7iEn^J8Vy9W(x8W=X#cwt@m8rFM%DZXO^&Lw7t{UnFJGdXg% z*xw0}+M={HPE^VoLY^r{B3KXK!9j`I3DfzNK6!d00!+*pM`g{ZybP}u`Gh&6`OCu1 zcm=Q_3f7!^2EX+&C%u{0Ql9}N2aAUvP9EMt@uSnCTwK*+`Qkm(Ix@@%&Rq)wZE$72 zw7b3VERxQo8qlAY2B=o*qzTKzGuNKKn#Hd8lTm(g9NlXC3RVD1J z4B=QM1(QNA<^T4Eg`=@8ktuQsm?vkWI*RWNGL0HUONapqU5?;9TlclfF_smT1fr|a zTwKnelH*tVuAX*1OknPW4{ad}{k^9uJ(hC^^-Sm2VM>3sBZZ@jiCBZfA(;VVa3I4*n`i{?S2OKE zoU|R5&cSDQQr5Ib7dd~mSVU{;|5B-reFLj?S_K?CpF5O9$PCx5Cu#}R(EmcGh;q`d zl(RcU>*2Wr)r{*1AFh1oT$a6ZXHgVAYb0kl*40d@OO;9SWQwP+Z=T`059_IK7f?%= zT%KZ&BiE7A0g9?ZJX2C$>Ug?=7;RMGE{S5xsB-H&IC_m9n@R|^j(9saJl_`%38b}n zSvbr}pVC9Cy)jNAvS^pYtx3F4@bz;~@I{Hhxb=mW30jC@lmW>PdZKdRx=b3J0ibq~E) z;;GYiTTo2C669+sWa$8al=eC{p8=kY%QksiB@-yRO>1vk1FaVKjSvO03H8e+Muv{) z@FK*`%t+~ytNI(OxAndJNk|zeahc^^~5J@lZ3hFZ6 z9z3#ElCeXj7F^kb@L7}7HG-SoQfxj=4a_kd2C$N@3u0J3#e+55Y}vnLQFPQ(y~xr@ zFCl#HvfnvXGK$0-@GTskr%0K9mwV##hYpgA@-ye=F9d5EvpCgKs)vpcCiOiW+^QGV z0YjBH@nUVJ9lScNBs~4FoJ4c%>D>Qd%@`p`fVvWLWKkd0*+dmb+uYWln&*9{`3S_j zQ9H)m!^yYYoistz57Q<6ylFZ||3r_Gvky-2n256-7aqjjDi|KrXS5!=Z#KZPgb`gumTmHlE{96L zIT#hVS9j|v{>j|(=41kgp^x_DPBG#c-~u_agMDlQ`?cX_7_`<*mp=`;fJ;W~{>_h; z4TC=DcwPS-*u@6Hh!L6@6dz<@{w&ktk4fE$ok2&*x-(;yL+S;B{DKVL^QE&lnDF!) z6!=AXQ_4W9T_O6jB`UR#GFxw77~UHUVqz6bt`OQfW3DWZorzsinQH!^fD2@S;T)V^_HeL zS;x!-p78V)1KJm;A4VbL;L52Bp!RNd=)J_b{eQc0b4~o@WTY$w7e#SJe)dHFhhTpk^y8I%HXv!1;vtB>XMo=%l~`Beu%qQfL&4MTK^` zpV7OWAmmCcvx%8OZ(+96TH72u+%2Em@?*kU=hBUAb1!=7&NL_rO=NXISrvbGintEf zoM1+mx%8NimMH1Jon)6x+l)he9Th7OKNJOSfgiws&lC1Pv(hsH$UUcIgqHm0;6gJj z2ZQ0JcPOI$YT(~aq0vpEOUNmaJZDg}^yI?%IdZ=N;XQALkRh@A$O)%azI>VCeuwMI zS@N;r%pH1>zuELQM-*rzC6|Pm_{?IkQ{VVfI#~Rgsl7bF`){ABQ)tv12RdJ4(LVG& z)%L%I^CE^3Jiv9y4muI`Xh%*33_L$*<#y)OCnP1Yf&IR~k{Xni;b`! z92OI{>&W#Z$g}4N6#K;h%gRJK4@#QI0BgLN_AI$V$`_@#EyBYRME65jHX>Ld;o;HR zUXJ%qZCcFH=c48AKt!E6MXTWKpLF`48DcTDBtw5Am!F^oL)%P9)HF!JL$|X)_lnh~ z-fa&vcXi*|oQ=xIKa{srULn^ta_0m94Hl!~u;RYIv>)G34kt^kq_;9{KSGi(Ag4z< zUmk;*oSJ0dqZ0yDo<&#_OA8OF@0hpdXOAK?X2M{2B}rs&SCa5)f-IUv!Meydq=ToQ zM$%drv=^=KBzuw+Dod;#o0I>-k;J%_vRDg~)qi`&SPDRRiyghEp8-*SXq+05&mh>0 zRLBN>>7QmlA#K**+cqYah%y**PrU zh#)+=Sa#2mPOxJ|DGS5OdXM~*f^(88!4v<>L#0Ks0e&9S*Q||FeHNfngSr9ldwfxF z6JR^0#fWHLDDKRH7;2E`>;|m1Ep~JHkkM0zX5as6eiRM4;ZD^JTj!Io$o^Rb4eC2$SnLzOjs9&`(Ddd>$_XW7W(`p%tAUs=d-^(S@eFIFhjCmR^hYki8WrSlNrCihR*G~t? zhRz+_5SEei{{W8B^+?>$>rThd7QY;3&@SY7Zl2XP_#g}Z*Xxg>R zalZF>>}A8Ay@CBJ>1z)VUGKo0rx*bgFCT!ZCf4rcb#JstNIBh};*Vhj^)48R$saLF zz9c>>lJ?WWUM0Df>e@S4&-P@&5tW#n<#^8V&$VBVU%k-plS|fYq|&t&Rhmfd{z6f@ zWB&lIE9mpDU(W`{yt-sTou*bGSI!@`)}02W@s{Gw>wKwmq}q@HJF~h`!R&Y+l{ZE& zLVdjHoh`T9{8Mr*Vzxwz3>1}AoR6*!depkVfpxnJIPngAq6SQOW>R_QHRXQ|{tLwS zw^8c;An{z#jixAV^;xaLCO+xG9^aL9(*DkV3c8DXTWeP=-G0@r+|8e;GGe&u(}a4R zl;=`g8`0_FYkB z!uEI12mB+NGJ*2v?1;g;k<;7suN(2rlv-Df^$0DYOM89!jyRJFp1gLhoG&YtqH^L? z`EMhK@wS?c;kfrRu4ddyjGh4<^)!ee9g*Ee(pC8bJDy&$I|%^b}k42+`zLx4V>_4+XS70f^x6@hFWq+qD^{PAB4{{X>7Kj5SOB=9%HO<&=! z!)aSheFz9O&2l9lX?co_!xJ78Y2dN$wK=YBhRdjXdap!q%G;l4`0m43y7+J6xO8i% zw9R5o8tJTVt{^idysZoDcR)d5ka9Tb?TY->{ki-)*PpQuhV>5-cxKuguL@|oJ+wD> z8sp!`dvwv;$#Cft?b)#-0DkWSbb}{~_I(FQ@gKx*_$L>QZ2Up-TT}7BiFGYJPLk*v zlD4nmiHb`k7V7F$$_n|V_nd*hf~$e~SNl)=Zup)*46jrRWl z3~IZh-ty`T8V_xPymHaDLciYQ0FH6SKbfz4{gf2HhAxgW-`a$A?X^XE(vxsTQEEnR zi*CbD)nno>CsLj=2N=V*TEg)~qD=~tps+pr_3K?ujkkY^1e*!|)wvkYVIx)tiU^k8 zHVcdlZT0P3a=%llTE{)5D(YG;vgc1nNFF)Car2KWleCU-Mr!{6jC8hr7AfuaLln|R z`&LG9LC;We{OfDMe-17@eENr#cCcEhirq^PJjQI}KG>|g%^Ov3h1PfOW7}_c_9lQg zG0KI8KMkZ)hhsS-vhd6vLz6xwpU5hXu??gVpIq05t8d9Wer6{hi8bz8XN0a|k}2-5 zbotD17ciyRE&)^3an`&lAR5^?#_pqU9c!Zv9m1x^r2fuVx&_CICb98N<<*~t@-Ho(Almm2XnAn_KXPpRMV7G*@bXHMBlhMl6F0a1;@e zqt>ix-XMnW!a8firdsK?@vof^l{B+5!dR6@cl*Z~Ja9W=vb7Cv-@|%;h(EV&PPc#K zNM(-^#*Gz(LC2DWoB^C?>Z8)Wevh@4R^=@hKv8d1vB&t!#};~Ss+JmTOX52ZJjFJn zI-)}=@sNV$RD>V;PEhboRKF zI6GVWE5xtHIuDU}_OA&2qYA;}?Is-IW6^DqLHck%O8QsA^LZW}yt@jiExnD*Xh&c? zHR4Hnf7?~Bn!jk4Sw1do$^QUH)2&rUren|e*7#e>cW09=HaX7EuMe~ z-oDxK2C|wh{e<@~4YjqFzu=^jXU53IzlWyIbM>!_d=X@4@qLaPf9|druXBhbKj-UT zO!)7|5PVSh(+7jJi};sJ@eS>g$2fqrX6oBX+Ib@z2bCZmx#qX7lVYaqQ1~U|2|PjZ zPT#}dX@*;U7Q<1R$i|o=39k%{#Ga*^U&%##o}Fd(du8+oj(UpnPucw?d#{B8W{z9Y z@c#b*?NhEZF&fN>82a!y&q0COz0<>*4bO;Y`#qhzOEMvmlw;%rsXpKRYVdLD(mux} z_o(Vw@K1y5!n?<{oY4h^#_XZ`=1Eu9v|?QhoI@#ZT|oX4zo9yu&bEb;w+wI zAcM5>I6Z-{r~d%piC+mM_(l6sYr1njn31Q1t{OOPN!{~bTNY=@$t12%yLy_8vZIzJ zKBLZN-X3vXkJLISqQ2<-@F=2@vyMMn0ABQsobf^9o@pB=7$2QXwgCbXDKmpg$28n? zn&zF8_e(;PnlndHOlB&X>(5%xRxwdF{b?opzl}6WgULTZP+Z+-fKR<^7(3jx7TB{# zxef+<)Ch6>^G5Q+o`RcO!`;-~Ei4ooDH*n{GK9wBb4totPCAZhH*M^Fz@Xf18BoA< zIjLjRibmW3>z=<_a-ff|Omfu89|@1*Jhe6j9FJ_{)YdLa`V%J}WP$|+g7&7oe?H!R z_X9LVH_AR-AMaCL!ESly@~8>uQhhnctsZd)yrraTP;Na); zqp8i-z=zMzqVRpqIlfMxj!ib$uKl41!0YQxxs}n%pwAqS%CD2JLtaaJ*U)X$&mM4B z7~qPENdzmke)$`lBd=;=_50Zak`EiHvNBHP&h7n&U@J~u!1QXxy&6US(FgAEPhZBr zoga(e4!kR}`$2fpDN91Oz9PP0?SfbRCC`4mV!S-+PBD#-Ya~&XSsr00?CIgDnXV3=e#${o zC%c`?df>B~%-8-A_$J!aL8o|QPLk^*e2*6K*W8}f==T~@=?txWUUrqFl~|suk~((i zE1c8rjo*poL5P!XKnguN*UUQd*!q8E8)}MDd^hloww{Cj5&afTrx-S?z{gINmj;cZ z=+^>kZ39Sy$(1FEmIYmeFu@oe_0nqAQoF<@!9u(cG40y0JVz5n92Uv6?jXB+1_%09 z)N0AQ9Qc|sQMvSnxfbQg*@YmSS!+zth-m z*@%bCK7iLH<7v!39I*`FJi|F7JwXt(wDj#E zuC5jcA$ysee6gbLW9xu9tMGU;Qim)w>;6VZ`aAUezgp+7n&GKFT9ZloqszqRxI8?R zYgUY3oRT@1;PTm#zP&1F?YzUcJmWd7yPpPXv&*~tH&N-x`!j!qL8|DtnkSa`aLouj zD3T){;5y?V6O50}j6P|GsMR`k6Me3(O$_S+UZST?v}E5!B-{KeGb)4}9!UQH_1AloN1~$JWJc)E8P7Y&l3Z8O_Hccc zJ-yiE*Uo>n+~OCUw-Lf3Mevm~+btlFN3EcS&A_DULMcl*Tu0GwCPpBc2L9xv1cP5bD9W&rSv zIKcd?>Cf!7@F#$-nmN4NxhGL@s=@bG=(6ZktTE1LB@TE#eSlApW=J?kM=PAnfw`H zE47Y+@b=2;XCn(0%)yHdr-mUFFnRml=D9e7H9=3Gwis#vc^E zCExsb@o$f{O*%VAmN-MQ%_gy*m6f4#cETG96euKtjAJ9_Z}=!g*Pa0Vw!SFKdu0?_ zrOn(rtTwJpX?JNa%#W}fharyc{EqprNdEwWRrqWBSN7@9(#k2C*F@4UZ|@!4lPPG~ z5VGeP-oqSs&t7ZkulOpL>~g=fZ^DQ1_Joixk30uuB<43ve{9+&T-)cnukd#{#(A%o z!a{lH4>>!zU++Bq^=Ib)0E7NKYJU$dwci=|a`9~K^bHdF)n$2eZ!EK0I>@rb=beH@ zP)A4KitRt(yPh7?JZ<4mhF=k_be(2z3Tj>lT`Bbm>`Y46QP|BSE9O2A-F43!Nsp<; zcs{SA$>E=c{xR+sv`*U+n`k8tGTV#P?t00#hMV!HSrFRKc5Fx4)SYRT+N z*!%*vlQxktJD&jL9st0==f7I^583BX@j7^7@B0r<)8w~ZKr>ujxeK`CBZKYQyoLk* zhX-DR8%Og$m3;~OA(J1&$;Ll-CZyzU=t;-xU##7{9b9s-QeWl5N|*Mo#o5N8@e&w#s{~pVa}s=%w5T}{{S1?>bye(88-QD`*{tt z{NF=fQ5$TG5ssM29c$L^EK=6j?GZ?^!znPvaI5Dq3~-&wjCHRhjNy>6?zu+m?klea zQO+%p-*eD@4QWSG&@W6;DA_Z+%`1G0pbYfqy=rJ;FCBQ(!=4+x`zD`fsNSS=+^l)s z5p1(INaP@Gyw@|~LwkSl;#+G0JaT=s$vFTBKwH+-r z&k(JgG5B{*ahp>+#tFHQbY+bjp~&1%918PsJkG73Q%E&o_I@&##aA}krRRtIL#22s z31tc(wgy3JgKTPr#t*+8GtOz6PLXwKt|jynT$^@^G)pm!*OSY-L^5L=QBZd3SL<9a zj{Yn9R^`}`l)HQu4%awe%uOSXxoa4+H_9p<>r5H4k&Xx8zJ|&(102n-3_cQ&ibGR>T zKW^Ms1>X7Y;(-8>T*Z5Dv$))K~+o>3Z zu=pg0Zsxh)3FucEmE_vw)-t8G{{XYvG@zis`@{k>pL+B!4cdL0(G-qGLm#g-wtfrP8-Lo@Sa5u`fi%KNAN0oU$LgT|6?4NHDf>c5 z$^Zw0QG7L#t-oon7R)jGwA0Ba{hJ+0hyH{5R(N;sNgCsqEj4qfwYwJn5x-`^0>@GW z`!9uV(0}Oj`Pa_Bv>`G4X4E4*l(w~x>4-=8*T49JQ)A#gd4?DMC0eT?z~G%0IKTF4 z`SauCq5d`Nx%=6S4ts1+Kc#lzQ&K!SmPo?z*0MA&5!zfX?5083xoxCikUoaKvhWXt z55w;jX_{N!8}_mIk$ln5BqApYJT>QR0CWmpr%aRUUn%@IjDNyoF=fbIF6eSN=sSK@ z_D#l?(D>^9;y}tRylb!CLPtZhLbkU*sejJ2oA<6nJCr|X-wsPZj6NG!#};p8_;iSo zg~=}^^wT%fBbxgY;U9tI(0mVl7MBRT)#bN#xSmWZG=aWY)c!+?`M35)g>`S*Bf_w= zov{2ju_8r3G-2g+>-4X-(o0VT_(jPp6j4M~RYqOd^V^e?UniT_ny<=_(D-_kUK;Y) z`M>tC@n)g$^Y(1J*ED!tHSZQ%#iw3f?#%WONfLt`Bx7mdC}H28wdz0cZC`?~eiVPf zLAA{bQP4G;EB!;^jpnI7qp8OrYrN`uYNT+M;G%{(Jz0-VD}?d zD`|G$Vzx6~ys(YVL!aH-h{CC9Sz82+qdbqWzu=Dl00nRI4i1I3wxlO)Zi>wCrG0D+-co#URHgJbF=0bG1q4nDZ5J@6X{_^50ZiM7YgID*}vv zyF*RL+IsLR8Qi2qde)Jq?_sFBkz7h91cAUc9xy3Pg^xc|NLw|!<+0F}gbk0zgPd_n z$$_5TsVNKq$)o^)d-_q>@_PDIa<`Qq1oGLba!C(maV9Sl0(z0b^d6MhouqBOM|{)9 z4XQJ9xa9Lu$+bE_(xJ1P41c_RhqwO#UbKvEYw>DKSg6~_ z2d*h&8{40)I5(LmA$n(}8_@^^s{5KBHpqsq?1Y_yWsm48oRYMIIRmyTTgNhIXJql6)=90AOU2R1Q6oql8ZD}<F5F~-s0kk^AoTiG(@irfvAk@1mbbcp4{7k{o7IQ|>4hKR zS^oeMAb>5EupC>b%mZft<8SGT*09o~@ehT3Ho@Utki|dE&i??H2l{?>%=r6szAU^< z;ew#xaey&h)vmD9eoyi~KQ_||S+C`nK*3~H^A2(pmG-V|E?=>OE1nLNr-y@XG2SZsBZv6s`)TNZ0ri`GGsQFbs`^bz%-zKeviXrT zVX?_)9Ok=iD_Mucng*MxTZz`+RI`TmAt2h}WKh5y9A}#N@BRvzZ1&ze@Ka-Ho5S<3 zKPet;nsnll@<(Io4cswx{SycD`jw?J0>z@ zU+(%+%MOulpYi*tZ6EaFoc{piT1{1UX?jX8-CIWg0MYYRIACeP`NUB|rb(oJ7BmAS zvFtD_p9u^_dMd1|O78k49{&L0IP#1pFBOE2DtM{I%K9Y>Zq>DcWm8KIjT`EhCpq%I z&XD<_@s0c69dq|iagH;QD7n&M;OY8;vFHu*bM8NN{(Wobe}liZ1N?pXzws-og_LU_jyxRW&(s@zW{h0~1EJbRQf8ufcAB>@CB-g~>;Ac;EBW7MzfH)*^n)&|#_O)a`6EtO9=3P2D3lC7v5B`N; zKqY`LxJ?F^q6s)_2bo_Q15b200`dP!zH>%UAL=k zJBB^$tiG|jnq)DwKfM@5kc?zkAE9Y}T3ff;B#u42oaB8gsJ4a)u98sF&LZi!@t>u6 z6?Z-Q3h+jrhjS)}YREjZGkmf-xfuL?DY}=4fLIYrT--)*0R%>*_bhtT8qQ0|aL$u=ox$2E9dW%ZX39K*~Wl4VFBw=>_GhQL_JrZAtI=_|kMAH?=Pp`dn zP+HjKtE$xVuZwH_ouz%Ll>Y#u#3ecYCD;%0=kTx7&mQ~;*1iM$Gx$BH>lZfH!tcOZ zWY&_v?BSwl!vNVu!Q4o|CxE9s*XEbSxZ6_EB1q58Es#|Qra&NM^&D~auhAb5{6_d6 z;}6-J;l8EdJI@=~Yr0;Mr&;PcHlukarDYokHcYTc(nw(2BC#oi7;J>+kC)@BO0HQb zH)qiKjK@TMS^oe8Z_#frJWcUJ_f3oJ+KNSKVh__GR#o?MM4L_{wc>O0|o`dNzr7d!*}eMi%DIRE){X3~mfp7&)(*zu@8hMjPMw zCg+832bD9$eQqX>A_+#5L%SS+a=6{QpYGNN#Gm*tg@=y*AM08!i}0flt60sawZ@~Y zuaKrhDZlq&n6J&j1GgE^A0__)!A3veq}t!dZ-mi!IOx6<(X@!{q`17e(=4Ty@^?)d z_88rsby8PzfOzP0kU+1hp^wA4a>Z)MoTBzWBed_D#ueZVp-w#qAJV>v{f;7I;DDSE z*UsybPR0aa`gO&8<)@5V#s@h8j^GAzazC%>Uf26Pd^a9A@M8J8uCrqiyp*JNw*@20 zwnqc$Uad>Gqc}9q!${I(x$ty18)OM1%%JVS3?*!j*0_%x1&Kno;Q7G7$o#8gPxyUi zJ|9}#E&jW^=Lco`h26b|>}MT?aa#WXgc`!rc+@YXMv&x7DRYea_O2--tD)C;7f`p- zw5?J{v=f&=k;q>H*@Bb3qv}Uqgmukz9}+w_;$McJ0(7qy>Uu7n_8nVL`!%)fq`l0M z&u-|uBM-GusoV=3<#Ub=aUKx<&R+sw1F35;YRhzyh?2?fB9b#6L!2p40R(;Qox`<3 z@h9Rv*NVOz_*=oHQtrz`y?fT3mUuqUaC4PofI{#H>D$)4ntalz)7a*2^0G8OBWZEz z+Gm6Q%7_-?&RdBs<$dz$XZM3+=lI(Jx(p0;$gd!jkf7rSsLx)*kHWnXb$fkMZ9?@S zhSDj+#c5+~pSrV*4^zn}Jd%0lyx_T9XD!0@8TGGg0}e`d6t!sF(k9ezd^Y+;nzxsz zT&OJa0EC`7{7A0PQSe>Pwc;7hr!r|?6Y%B#0QP%d#60_h62B<~WPr|iz$~NG(|=~| zH^U9${{Rc$_}^7qUk9eF-)@4$q?fXY*UVKboTN^0ttLKE*w?Rq(OPeXm-b%KyhCHJ zPaeCe>*fnBF4ACQ0c9c9RmmYzG1QD_p{^`NR~p=je9K4A{x`DH=J7kp;iIj#jF74? z_Oww)a$mbRiQQBalZ>8(S3~1{KJQledGN;3wi3WEs{A0iBJ5qo=+UW?#wD^|3I=H2(k=b>C< z{{XLCLU<0_S5kL9z0PtZi?8Eeui&2!!=~6;THAuL#)FgmWb#R`3l&jX$J6E1>nCY! zbQ&I(O!2Foqj25cxz7l%m-|Qf^ZT(2QTPofRU`HOb?6=&(+W+zw(pya`u_l*=U#6H zv1{S4j{g7>ukN48wYI#nf=0n@yY$UYF~)zrA`ix%3Rjyo%_C|`=9i7NOM5>H+(mlq zu<9{fY3j+02_>}Lhkw>W;NB;m?y)ylej(k6<>KE|^iESlF zQC-oXxwmU5v$s9gQOWkNE%?PNUVLitM2)oLP-OW}U;zGLSG7TV$IVsPx9~>C`(NU4 zx8S4LOr#%nL0?+<&s0(43!@vH#jbd{*+Bl~q)YxEm3*Q5KdZOI%a={Lg2Z4R`>r4# z*1nAKR;DiXElT8kkLGyV^?*Dv3knbY2v$|^&0wT@7wm5s(*8ciBPG0#hZ-C)wlY|> zuGs#6IE3sfY@PEKp$7a9p2k7|}f7wyRd?wNEw9Qjeva`1T0EuO! z5!(x?A9K9hoXEbU<6-ybHTDPm6BkE@+xEA;nWQato({T?Vdo0hwnX#w0=`r5mx}EE zAKKhnUU`B^oZS}+K6AP72m4qf@D=nA{1NlU7d|fiv3yA^q1G?>MJ^gOb>&CfEb&_9 zP;VPfeq+xk6@jZCBkvD}U$q~AzA(B&p!lOqxVw~mwz5Bwb)QcvK&_vkHQ(f#{0H!# z{1ijM9wzdj@jr+4e-Fyo4RHo9Aw%oAKxG5{+WJS}hwY_(Z46)Vuf8rV$|~VB>x=Pl zh%!D=1}{%nuSB9nRCK9%ubg1_LWUMcYIBk^y9Z8byy z0dc0vZKYwxSjPEN_z)}Fz8n7E{{Rd=Aryu6rttOa5Wi>EBl|JQ^mCZc{q;k9!*iOir|M8^R!ehbaRt@1!!gSQf<$rb!-G%881aBT{{UXK9FF>wi9F4((W(|` z~gkC)v*8-k^4-DZtMkG4I7+H!3YIq;j{F%8}O~0a0$t5_8X`O*GzH0D2$N zp^%{at)6;gr7vo>X3-7XdU278Ya9w`eA1}TT3*unMAFs}%5jYT6a&AEao0Jd@_u~h z@Wm>Ovku)VIcA=t)1XL&N8cPCGHHsLJsMA9c&0tUW3=pDybSY6zjyq!{0DkEhRae$ zW;Ww*oRz za&R{iwNa98%)2%=LSZ}-7e6<8is1kNsobZ%Iy<5RGdDQE81GG2GJ}!4@qwIG^EltJ zq++dRGA<5E?f`N#igF=faJ_mTYHV_sfMm??V3ogxS0~KE0*G;sk+iO@y-urt;2k%B+5D;O7VM zuQL6uziVheXP*wd9vnJ1ihOZttJ!JNcxmKyx|m$;ASh5ulB@`38*mRabET<_`BCeU z$-3e)LHonoiu`T=0D_9xNB;l~{qZ^ml)%#JvC5>6m5Wd53PLR^^vv8FXGm=xNB!YX0G^YvG=~7-{Dn;gBGi+T>Yoay|TB3glNQ_ z&PQL%HPq^s)|WQ$FWFk{x!73>l^yGv@!hT0h3(DU2`o@a8DK{}k5GN_P9F2n(oQNT zyLeLIP4L&klA|-J^v z1}>$-Xja1^v&c=SuhPDTb>r-lNgXt>@^xBl@c#hVS-vAp@poRE#^3OnHH_LmA(K~@ zM~KQMP8WGUHs8=1`pVz^E5f%suzt&T5sUqwF&sbV<){7g?sJ@OWte>OJfBnjD?-J5 z&kA1^J#Ov)03VWS`MlDkX6Z?3@5IMBgN$(N{+`426J3a{hLXFvHl%gO{I{F@{uSb1 zwx5K)ApA%0PKn{K0>wPGy6wctth`bzJ0e63f#m9_NIAwc#(3hr#_G|p^c^{a4>1~R zWy5V77i@zkvCOTW*aE!&0L5Rl@54`q5xkn$jkF;dV;qp$%l4;`bU!S6hfh=2+|iRs zs!E$$#kBtNIDPV9ey=Uyi8(Z^N2|*LsELh_3C|OSQMPc12LC<(^%ufHsgwIqhAK z?3v-;j6NX!oxCIe00=*dyb0iq8hd96ELuXze|{EMQf{4w81aGaUq#Q$%`WGCwdGr% zTEl*IT`Jz~t>?9p>Lr>;ZU^t8W*I2ngc17Ec#=XjmFjld#MlF`?xU^);HI1LTf(>3 z_8$?o-wSx3OP1YU!urA=BJn2#2988k2v=@MJ$_$vUWcqFHg10V+JMK`YqAggbrgOd zTKSw>wl;_Tz4;tCyZ-=bVDJ6^0R1Hyk;yI8tI97q+n#Y=75jQdo*(#&;8r8|vgvcP zXB`B?{CZcv!x)BDkf?5X`d5H|!AhoP@kfB}BVF5ctBEkgjlfGDMnL1OdTQ@ReFVFl z--mo@Yoz&8NjLf=i@B0W&&qflHfyudyj5V23i+ZS=YHqpH}uVT?}aV?B;Dyyd0rmy zaz!d-7JJuYk5h#|@hc@uO4e9TAS6jzl~4(++@(!^R8vFWmS?kJaS zlt<4b{_%!?5soW2{t)}keh6llQ@k@gt+prJM{}HK+OhQi00(?li%5z80JJ;p0jaB`X; z3NzoA=bqK%{w3Xgx9A&--0#;U9=H zA=BZ<$`ap$>EDX|Q2m`TW()(~p$n9S?{{X>9z7^ekA@P5O z4fE`SQ_$6mwh5BtIWnS-pDyJC>z`VaT>YjsU)e|Ya{ZnBXW`vC@5Gv}oq2s}s_1iv zh68Ur43TBWQbR{4u^8a)%$UaKYpcE{ibIj@jE;GtgxJSFg(_F~ko{3$l;C)BO0yc$o%oJU))5aqble5WAMd&Df=B;eW&65qNxd+yP$pkb|2EdYPY%) z+d(PPzro8*c4sJBv!+z1vCy!ohZ%+8D;lc36#?7?>6!1i# zsf~{vGn`|!a$4_=H0!NA`B%|MLvJH#%LVqual1EWbtbuWEooi_e;Mm{`dy~4_B*R~ zS7;}cMzbgZiO&VPf!8&StOmUC#m1LturHj`S=mIbwI!ZFV?bNF?qwPG6lh)rhsAz7 z)E388x3-ehD<#B^jp(eS1E1l1Z{OkGIGzFd?ANASXucu6(rzyAw85y6W(zbd#g(Eh)8!zHj=Wcah1wM0 zZP*46RnB^Q*U>*1H9ryjL(sfWuj+aYj)0mZD{m#e*1VEj$qVJ9Ht{xOZMi#%8*)Gd z9M{iI%6$&i;3?$s$6s?@*e3Z}V{G)VgfAAC@N>l%<4(D`zkB;zVRB*p#k?ev{{TNw zasU7xq*qI?_zy|=NATND)V2Qr6uRr4DuHh8yf>kQmOEQk`J-#8xdGU7u>c?+T-8qw z`1;OI**i+`e(ksN>r!9blK%j^Sk+iA?^C&yf$dy`wiHs`oV3G8# z{{Rr`y0?cSxYHz-?CDqWrOZ&s%q`{Kx!$8YbIAuCNanb{tfMY%*`kWwoHe^x-$H)N zV>pi=A!D2$QQEis6`@<75!pvA+%IfBrF^I2>HgK@y?ODKYx#&j@FHv4pqhH0FHz`pU$fjz{{RV2s@ycI9hw3< z{P%H=pMbAu@xH7e)Ae!=(kwh~7+`*4zOwOe zj;y>HWUa%#-h|_Kve(IcUoLaccb0KAsIegT6jQxd2E{Nej$8V&^#w+ zYk%TD5^0)T7mwxHo2#$&zwRi@H%_=8TKMdIV(~9i^jxN=E)g9jz2aN16zT@*U=R>W z!n}yr{j7Ue)Zg$$eLGTr+lyAbO<6y&SHQQhKAGg;{iFLsPAfQnO`*m=+7-|1`Q6+jh8-U$S581yyQmEB@smvfIgtrvxwd)WN7wAaPh zV6PBhdhwH1W1kL>A!K#sUOaBkvFa<4YbLYayE|l&lTD8H;?gbX$7pYtdEg5BZgkJi zy_~jZxO_bQs(gL$%x%(qU4Cs>mbAVY}%ReI0HP_O}+qV!YSG+Usy(ETv@E&FzC{{RdB0N|uw5Ohs7Wxk8U-aOL+WiDZp z?9$3TeAv~K{{SJMudm?j7xvnWeC=?oFg*@Bir4U`j`d#zUHRG;uc_#B7W>wBFA|gf z>W~jX)KKFqS|iu7{T28h{tA8IO?by|;w?8@@TJ2Hypu!rn^pQr9$Oz&73}^A__yIt zijgB|9wX89f0K8YZFG$?{{Xz1+&>U2`OolI{1h+ZABG|ct2T||%T@VZFKzt0nTP#! zMae%ds%_BPHMP|G_>5DOmd8B*06O#Ug^4}+uG{kJ|M__M|OmHz;T^~=2r!d8<10HeVL zv`H*GA8FkqaOWT$zJ&T$yZ+2ywzZ#$ehFXA;@y77TJcYY(rM&dNQJ!Gowz@`Wkd4r zIofvmXV#Bsp5|WDT~DFnx6VqTZ%{cS{QYSX2@CBRZp4Kgdmr=OzdnCxzuQa1Ullbw zI1^sfd!(Z<8QXyu$IHlyu~SgZWZqBMhr_>FR2wHuOai<0{=c zQlw>6Gbug!9DQk62AFUF9PQ6qmdYbF&cu96AliqhPkLO9ZbYrhe6jxk1t0y2{6+EO zU+{;F`~`Cbr-*b}yq^%-$m|zc(<2QqiBMsSGoW9TgNJWQ_>1TiXPZv9jl-)*kw&VF%%gB#dUN&9!Y}wJp1a^*jGB*x{CA^k zvv@u@?ygf>y|!p?;#HlJIed}fcsKUP_!4z54z-TDZuKi| zcGmv@M7$Bnsn2dgDm0+{*g>}q&?Y$~)XP+G$zAAt)%zp<(ccU{Aw}aq6)vMTm9IpP z;%^>kcA8w9?yvq>l-o*Kl`Y;;Ht3jdCO~%uuY&&oYHcpV_JR1(bK-A@I<4o!tpe`K zD4#)!-Z_L52_-T&o1R#bG63TP@%sM&_MG?=@k_%$9;3Ff)VwF*d$})e<*=7q((Pg% zG*yfUZG6aqFC@5=AmD9WoF2a$_+rj|Z%{XPnq{_vZC#E2p9=i867D;ZMoA01q0bIW+k8OlXZJ;tE&7-*KQ&NKa=)gtDm4X%wGP=+D*QtaKG@A_?+!x z#3cS!WIFctHRH<@QpeWE z4Nr5Em-c7S=ZrME*NbM|g;1KS{HNcH^s4utvloND$s`kB_@ZSa1}Sw{d>*G6&o$kx zjFw^U0ZCF^5S_vGsvaPW+eWkh0B5=GWLI2|!!;D~5=@HJB9ZCe0lYtD;opLu8`AXn z=SvMb+}hiu@4eAcv4Yt4`qO?W$D6AO4+2Yb$EFQ0?Ee5Ko$$udF}Lj3Q>?ikcjn`t zu4%sz@4ne%VS^dr{^{odh#!S_LU(@g<4Gj!aQ^@u{AiyGJO`|Jo=Zm5qtLXwTbH$s z_B5l*g*x{8!oG5!{tAhvMvLV9KGdI&`D>~obB|;`we-)$uLx=<8qD%i%?kBzPxIm;SoRB%Z(RR?Uy> zukfnQA2og-cv+h~ES7=$b>}ti`+7W_mOT%vR_5lp^F^=x6$8cE--z_-X!IUe66Jk=-zNZC30&fUha%em64{yBbzxfLk?0JLBJe$VtWel^-QvG6;^ z*79vg{{XS4R74n9!lvvhjs`K>xj)!XOnX1r@8RpY^4@(qNx|eZ2e_Ay#B=!9s@&pBjlWjeD*ePuPFIs3k170LX~MeyI@qx?MhQ{k-w(m8IeG&>0Fl4L{k6#yUVYt@xh z;NZ`9*`+(0uxNtvHI;UZ4n}`LQ{SqKTNTIMwj<^|{{XI*ulj?JOm~zyAQFqb|}Wx_@A&`B#Gf z0Kr8z-1w*A&{35808v4%z6I<~qq>P;7_WY{k}GyDySd*}J}^a&<&8Zyacr(30~`DqAHq z{wTb@F2Ve?d=5=nn&Q^!WP*8iO59*6=kcs>va{cL5u+5`6tQlD*Zlg|H#?TB-rbFr zzq{4-1&ol;p%_xUbm};-1NgL?{vvBCh!e`XD}XVcd9Qr6)WjOeh(#}!7iuw6!Nqtl z#bLAJUbyPa2)ulC>JPnoIeqz>XP23O4Ks%Ey#D}e&@Kr%5k$$y7$9T{{f_;d?zEf# z0N97&#qPCn1*U~*;X4gN_Ic$I#{xo&A!1J*P;rBvfE?n#8hmv+TiZ>u_iJ+-5tGLM z08Dp1dROR&?JwdvygB~>1hM#K;Rx=M%T?F(T{UiW=3p)*u#P?K#ShJ}$>sBo;CHW6 z5ndGOImr18eNIJkZ`)7e=AZjwd?=qy@b84RX>EKkkKzQ^E)hiH1yZ|`KfIAUV}~H| z_eFf|;49w~e$jLMJos}2e{33Si!Hx#TuC!Lk`_Jut;A z8y!7mx6{|d+JBvEa(-oLmf}sY<7mR+v-5r<<`i)iDyp~N?nv=mIhvC$e$ihE{3j3W zukjB`k5SX^{Cn_~<+!)hq_>t!%bPjM9X>N2(egA5@sofH1I}y9zie-VvHr^6vaI*; zTIev}_>`SCYa4NMYPx#Mr|%=lEI}&C!Dc_}58=>$(x0|xg1kX_ugR%+W5)gh)O=H- zK9#B1c#BM1XVmSZU~OcD#!^orKvh6&kt1+$2*3XT1t0i%tjYTqczXW;#M+jltK6!! zz`mL>3fv#C6B%UcxCL?8fnJ1b*ZR~cMjq|=G;2nY`CnibD3L(hfB_&6pRYCaPwbPY zTS?#>JC=eso#&ZZ*?wjLo4tJNZcJ9FM&I>ex%v*Dm+N0g{>rkve+%r}0}+dv2|l5p z{{XFDpjDT-e&kYi(U!FBIyLxRqz@({8p&OA{Vp4;U=Dv)>?#w&$8?sYp@i{ax>r^KmmsQ7jn zE+k-(&t$g~y8^3%q~p}_TQ?sH?tkGw&>)ZPmp3z9!jdXC*{#LI(kr4UEW;jI83Pz5 zt9V!8UZvxY3t5qC9gU5<%Xu!Xbej^=IL7%Oar?t+^yk{Wo59{6hrrs$!s&In$A;lL zmBzDrZlY&4Qn+aq1Y_nUM#LV6AlHG4#`3>&y^d`zct^xLJ$u8xC-EkW;t4HhzR}~F z(NoMxW{MK~)D}{B8;QX=`Mm{sivIwqJParv-GKhJ>E9H*PvHGe#(pW6!|?cq{^s$X zN%fsxRa=Yc8G&+PLPSfP1q7Z4YV#X_s}mLe=LgjEucg4ZDk+hz&eOnpWS$|?)-5&x zc`R`jmBWRO5&h{KA1*<{HMv$4rFk7Z+&Sb+*wZ!>AF>CkJFp)v|c$>t2cCe+UVF z0sIcqtn~YP3Ge(Qx{c(Kz#%unYl(KvGk`ahSlgB-7#QnbVex;*Ryw8ShMlIXPo(K& z#TfvEl2783p~=8J*Nr;0Cx~?8w^LZoOh@>gsA}E{*Qd9R%v$R=QCJ|J%rvlwmwRj{ zgau?w;8jcQVrl#zqG`9yB)V>k14|T2+kDbk;4^xB*!*!@8gGI%j~f2Y^Xm4V9lnoA zwbt6+%TZVlk*M8yQJG>_BbHRz$M{q-`1eon=Y%v@(KKtz-xZ*C)O8#Ca$>QVNJ7N~ zeZr!nZaMjw`V4Cp)SN+In8(ueD~o>-xYTU*D?7W0#G(nU%uyn+`F7`$JNN0yt<5t_ zZChQvxR4(wNfJd7&NniH^B-Du*M@vs;+t!YN=UUWV_VgmW6wOLq)<=Z^zXHXkwoJR6**$V_;|-( zQ(L-Uiu9{1Hdyr=Yl$|4vf0*Vla4R}9sTQp_`UIa#-9+p8R5?eYxmY8!oD52x@|*R z8C(0SHIL8xD@oUD1Z5{V=rdhcg?=At-xahwDcebhMew*)l?}b8*{<~>I8eC5M;8Yf zJCAM+eCyG!5sZ- z&NNGJ5NRJ4d~0*7Xi!*7KA=|m1AdPsq`)XDxXQ2_y65n(uzzH)ffj!abn8^Nj^fhR zd&ju9f#fo{F2#@U3ElL`$gh^Xb9W+qX84;Upb!552?gwA4yWc!W}aPJnzK>Z$2*}? z6k_TXJVSmaV;XJ>Z!FXU{8-c=1m~wp`DXn2{vy>|ITBsUK_BlCUrS#~Wx2IupPKI4 zOnL>p&;H3@Gea?o_Ucc%4Duenr1!6-p}F$2JAc`_4?o5CN;eZGsiZ^)Zf;MuYt^8? zviMWsh`dMQRxquv_N9@qk|8JB66;YjWMJfGVpHrT=&3ZqMJ^M z_lh-5M)^E7;Y;>{+R8|!gx}pgvY26r1=v@1!rbTYan5U@{h53dr&)NHQj5U;7mMO= z#SKxj;(co7;jHw32|*(gV~XbGmZQEEYThLH zgKMf=d_#}oE~~8D+J9*29tyM7^!+AHRy5__S7{ZZjdRFk=j*}xoBj>L_|f2hj34k) zUmL;TuLWuz61DKpfG=&`=9Juhu+6FI(q>sO0?45iK*`({j&POpXN|w$lsYfN?-}Vn zH_^4NN5mS%qg`I>IyZtR_A$gqlUCWjKAs|;7Eq|ZXXF&R zQNIu`>*R{_pS((a&S;F3U zLN77=vnuWEIyF(b@TAUvgy+|cS9x({B$yss9essXlS6^q;m5_23x!uo9*L`!NhhLd7$5F*oeH9pV&N>$76{K;J zTb^}HxvzldBJni#@NJcTYyjJObgTBhGJ)gR`5icKN=uIiAtWP7yyt*;0_A<+8~&3Zq9ziaP^9}iw32Z&!) zw+Cv$sIS@=^FKdqaNWADMiPq`v7JQ8|UkL^3V0-J{Q$3klPVAatMGFzkVUkQJ~ zNA%4`J)SY}oyDw*etXRtUFwUMgU6EtT%K_7Vj6`v|h zSA9>_yN?2DR{Bh~tE%ddSxvZkab@G@lg~q*e@9C>mEU&s%wdzXU0 z;Hi4J3kQq5DW_`atUuE9w`+NRr4Hm{^v7yafSr${z83hYw7(j88nyhEHkUUZY?nsd zPcNLj#;vEae9}k`v9r1oF}MMqmFvH;pNYID@XPl909!3G?^L<|+H0#cyOwrHOlB;ob#OPI zIRb;69E?|od>i;>q3b$b=8XD|k!`C@DZ8|vQ?|p{ylSi$&0;r3>_VNqjARUq*CnKC zULf(0#AtL&y;8$azk7R&J9Fj95=av}6<8=JH(`;w3=VmsS}g_6?__?`Ux$hIFh46F z;>hoT`D68}`-?6z2Y#Jv!av}jKeeUr?Md*S8(nKpzyAP)pTnX{U0UnN5y!eW+JS+= z4p)*0?A7lj!sLtqMhNSQ%art~%IHxb5#yoy0u44lm=Vvg9<+^j6`h>7Gqg6=*73Bm z%PR#{RRoerAOTUueQSH+*!7EB>l>XSXvDEzMKF>nRFD*_E>z^4b)}+umgmr!A>A(4 z&+^42&9Tb;?l3ZXS2K6a9F{{Xi>o$&8X zpTd4K@x$x7d>Req#l8K`k!oZ~;+oFlFqa59Axyi$3Ro2%QS!%yK016v{{Vt={1nuG z;S|<m(lRhIGO;dpU>}uE;thP|@x%TO8Sw|ij|ju1 zYn~L)iMM~WxQ@}CFSleM#DK0sHUa0Jb6+|B(;x6tZxH^|UOCdWne@#%EBok-5-!z< zT7*7Y1GELs)BrIDjlG3(yhQEprgBQGcSqd1kL{hKd~DM^5ut1McDDMitEI)_DST_H z!RKDJ_`WQ*gvb8?9_i%AG9d@$Wsi&+@b8Xy_Fg#nm#kZOZJYZKP`n;p%zFg4q$mw0 zQiZ-=`lwE zAd(%(aoxG&Jl2KwuV(rkwXA+zWwIM1<|EfWpW|A$Fv}LTs9ZLX13j&~Qel?m-Hc4**hDHAA5;uRwx(%YWbyuu^6=ZzBjb<4yvxL2 zwI_l69q~#Z1Zp>0zx*Uq-dos51a?Hl=FU}P%V*WcHR!%BzqZjlH)DH$a|NB9je(lx z;$wt`MZ~#gBk-?({3h;eQ>!Ywb(mXYB>2 z_zE8k>6)Fstu2Dbs7ZRuA_>`5u&N53r1i~t7yK00#@Z*u&k6V|#9kQIA=WgrE&YkR zww6SX%?z!zxEzg~em?ce{@6Y_)05*@!~J7Sywmk>>`x8o@=lS@zA+?9jAM2j=Z@U^ z*Rw|k+3D5By`p{VtXMU!qc|#ma^*4yp&t74_SN8b$iSbwVk?_6Dn$5nKq~2E6EbwX^b~ z)}zS6#8pl(2=n5|{Kb6!b52xfs-MdxFUS4~=Vz4@!K_SorzpzZ?CtuLq=SEkESHS^ zu6?=x0CEh`4Xnvy9PXHcX$r>2NwS$ zK@rQjP+JNQ8;&^7dW^nK)mQiB`Wxg8{{XaKe``O`^KaUd;}3~HXTOD(zAe$LG>t<| zvA(f3Ed}^EGr^0^?c(X@_drdoCvlk0zDEX~$N-$%L z5*b)$p}?<*{{U@Y+tWw*zwplMz?w&fb*(mU?G(Dyu62MQo+B3V<@q@vmgP8PQg)Df17A0Zf9;f4_`f60$42><6aL@-0Mbt^mer%VXjNIZoN_vIULpHu zA2-C`4qJo13-tNDnji45qP08AXP5VKo$gDJ2SJ+o`}X0|zR|Dv07D|~`#tZ0Y{(<73^kTNvI1mMi@#SQx~TsotTwzVAxg((YDcI1Anvg8v1`yckfgWEf=T`w@vVMOF@o+YPoVB=jq!JhTf|qI zEv~CI=(5l)-onQ>GD@~l7Iz4Nn26LnyKPJWSn-GhsM=D)2iyFq`%ZP$%z?%?yK!M!OsWlReluz0Kq~$P2nE_zJqh| z2gGsP>QhLTcGni`_DwF|aYcoOGD8nTo=$q#%GOh@=C=A)pSt?sppY~Pl!`ORX6KIG z_~3V~%|pT$Hku{B_S8_=Su7FV!EtjoLx%$!LITWl$4)B`slk5ln#Yo-D_I`z<8Rw8 z> z9qZh_2y6Z#(f$&~x_yqHExSW=wdF6n1inTG*1RuolM3K}1{?4d^hd!~fA~`Npp%8M zoq+TtzH8a5bdofu@ajPf-Ys8-+FsZ2ZcCg!T8f$X=B)8F0=Smj0 z@)#q6SxA#-GCS?sM$iT_Ivje}SqH>@H{wsl-wHR6Z~RMlZ47gZne45^w{l12lC2qj z2rdC;#(3%Jk??l%_fnqP@(3>Du+a41u|mw5Mv>W?RtKQnf%NTHuDn@cqx@UZ?lmV# z>@76=nC{X)nVn^gl%{alIAS*pW7fWMq@f%`rx=`z;yORt55*q~Blroa>0SWRE^TL) zXx>}Pn{wAP5XW(8nC>8+*vB5#^L4;tq#ieQz|L#Zek5p?)99MUqoqeRhJYsWH0ycC z@0uI}uG|h8NF$DiHRjST@XgcE`ewe9E%&J0x1&9yz#kCc@rQx*O&`R%=Bwp+dqo;` zxVG3Ng56~-sCN!j7IDWtJ!`1=XYkwM9RC0kygL_(_04hoH#NcYAc|O{Nn2|e!^jsX zrSKR5oD+{x;X7lSOhkC2pAq3${pIP$t}92uHupX^*Yy7Y4ry9)Nvqvtf=CGxugqm^ z^aE)m`U70@qN%qy^&D@|`xEy5{k(2GPvPrNfFBJ!F`{U{4*V~36SHa7+wXOKK}KFk z*c`@z#@vIs!u-a(R^#D){{V!i`)`gfw7ml7#8U|qNAvvKO zC$rZYFAnJb3)5RgpTrOL&1c5irfp=tWAl}W6TT=EVL=KDG3rHmbsCz?sZ=cyl-LPe;Dczc(cXH;fou1i|Ns(p8A^H zl6Wa1IL3YWIW?oLXubi`{ut|8&9B69s_UA~l#gemy@`hJcdU!a0Q1!L70l}zc9-!Z z!g6XhCOfaODUIVR<-7p_B?w+e9FNkeU3gmB9}W19{@h0#wsz5mJxqvK1L>Uhr$$`U zS1MR}MmJ}gTA_jkQa)X~NbX7OJ8*IBUqpV+Ujh$`{{Uwwd{b-jQs=?ilj`>oCY^C) z-`UKOmED{Ul3@wcm7IEaH!JVBuN;NoT92K z7ILM6fOr_>*RpCl#-;E##l9WZd}pb6S5L8?^q&#jQs^v!H;7YB8j;nmn(8|zcaAy! z(QK}$TdI7-DLngE>34;6$h5o7E;!a3SS<4$t(~Mo2q02(wPPJRb^ieCSLe5eyd87m z--`bL5*cQO&dt0@dmG+dAKf8KyEk#3=W4zH>PCB3nN>(Sl5s=g2u)FsGdaCd-fOE^ zq_#<>Yg<*ilp<~@qG`@p^ju>c`q#|bY=-XFa3yy(;yp5RgI`qqTKI#f=(?_@;lBkj zmqmur?ez=kn-6@ZJDHY1!-i5rbm@xuUrPCOz>)l*uTkrq{{S&xQHR>-__=O(-vZ`) zJxQirE`3%10Jf~&+6ZRyyvfyMAsdcY3`zQRuUsD-G&%kWYkEDDtp%o~9D0TT*^uo|{SiDAV+d>n#?; zSijX$X!PALMZb+>fn)nUq}NjBEOPF0fPxRLd6VKq|F3( z0@BVg610Q>&fcxT<+IwnkNyeYe`%q9%i8X_CCqDKbK<>P@9hm4lXQM!0rG%bk}yCA z?{w!i*lB+nw9g3YR@RbR+-P1H)|!1<*Hzc|X^=%~qjdQ5KyQ(V2!L~+L0=n)l;t-b zi2WNV!%)NF)k$yM@{ihM_LaTy&&O*Igj!tfqWDivxLcna>hP;eG}i1q$!%iW$|shq z<}l~y$9&h-Kk!3u2l!&o{t8|3lV0#%oFBuwr-fmSZ@jpzdL5IeyHU~g#M9wsh( z!~xt$Bji|qHTVO^UlH}M68J95Qq(VRbmh`+4Wo+-oqJH0Vv)mdyC?Ukv4JKrfKMH3 z>wow+AK*rt;O~k)F7br1>h^l(vu~@~#ba}}K z)5+$(wN5@~25T>EgsUjZ_DAOIx_c{pr*QY}QQTTIT}I_Rao(>yVaU#U_B9j2=3>gg zpGN2_=oLneZd7JGQJ49Yf=0|^f+`s_q#*H*Ffm)L0<@cm<fKPX63R{XSF9MOQt^ zak4OEvUH4{hpsW#H3a%}gpb`J1an)>7J84SX}@P(-qlIW&z3K9m%7rh*-Vd7RVUKf zi)9G)IIgPiPm)$UNGptheQKnZOur*5bmVrXw0CARg6MJ@hKMHHh2E#-_Z6Fcq3nN^ zJ&sLw)9GNI3l4e?)mgM-gO~ipTB{`VIo=&hQ_nRo3vPh0V!7i8D=y>1@6WryV3eM=sn_G(VQmj|z9d?clZ}=zT z&xpPm$|msVh_ziE;~D<|Mz|{({{Vm-ujyD9+5^idASom8tQ}$tn@F&$BJ;}wn(C*? zC1ZxAIYU;Ds{9lG00lSkW`h(___ILKJX354#PJhjbB?(<1sL(@0ncAwa`;*Q00kQO zb>baX(()~0@4>p%47(!L?LTx*LC2hhmtn>~hQBo8)TWGeBzM8VH6(hhXNFw#%8}{b zpRrF<2QG~Mioa&>_$l4Dg>>uBw0K8G*Yxdj?X?TdPs4Z0gu8zcjD`+R&B>8^*VA7N z{{U`Jg`XCr5n1?%XgAEBV_Iub{gB3^lb0o<$mjUEujNO=UOv=33#qN{g{IAaS4iNA%Fph>M%`m#y5J3RU6dwXg)vc z)*4;J_u9;w!FMwtn&JGn`I`?R86=)aHI3qLiW=9AEaVpL7L^X7gk1=(7^I_)j(P)u z!2am;uOsm-{C7Gu7J7WlsS=phcF5j9I0M_QXb%{{dvR@Yg6m)bn?_FT z(D4P$vO=a)J45Ay$rmyaoCO5*>-h@Lmr-k}ZQ>9#F-;=oKQn7b7(Li~dQ#|@7gx5D z3yW8p?$L_x3I4f9KX-w^=RVbur|WTEc!oP=welytL*>mhR6K5NXZ#L=ZfOZ zC^%5L?&Mlxen-1pokzm@gqCm;Rt{$X0@6Q2)E+ACui^RH7P`87Nb!%Aur~~Pde=GQ zU2-wvJ!au9oT2a^o4!#g(R%khkQVzQ^7PoGP)mMPV+%^n$ZyB^hBQ2QOB zd8Uxa#C)4rjdhINRQ)PZBEltZ8$ya z+I^SFOPK-^5ElEmC$HgOq#h);^6fO%G6>^}$=*h1!lH@gVoB%#9G_ud27cbYD$=!& z+OtU4G<{mjTC=dzwDWO3n=qftk|~{;Hz6y(XkIk$-Hr_Yd##(2_y<+GXHgg@zf2`d3)KzyHng_XZa?CJ)1j7YXj)Tle$dBuK)@=vJevA%Xp~f5hf6-Xg!#}f!jl4_yUs?Ev;#Rn9w2N(b8h)+f8?{NiL?B{eiWOp{<#r#JjUYp=w16}GeJ9xWa)4tBSl=kReL%d|XTMFP4 zmn;Tq&+JE{SpL)BwLiqGss12p`cAKFX&P8Jhb`Aj{?pT9V1{j=I6yHO2u^wH*0z7( zsCHf+pTgb=YxVIf+$NoI9)oG7q-&z#M)noX4PIF z3J(i-z5>za?jx+JR%n!}1G~vA7!~QloN-@D^}~C+oqDR*k?9`=R=cm+{PBkOpwDTr|DlKe#Un* z{6zl%f{c86@ibVMNU(-I78%qMjNV`>pq}AlIsX8Aiu)4Lg{&c1ebggq74sR-EHN}@ zms@|}OLLPQcw#Wvo$Wqbe~nuI04p9=%3`?-lB~!G+%kIOALr7%Z}#ufq|)@?hMKFg zpSR0=p%fg2UoZLMz1zl9C7+0xp!<>X0QLU>>*BmS{t9Py+CRacgzh(Pj_&PA?uvFX zuKG&q$oeU}YR+3hMYFK8o@Ut@KuOQt-JAxaP0-Tm-sXAy!!*V@CW=qGihjItUUT7Z zjy@mNE))A-`&!~SV-c)SX%{Z6c=@ede7~bR`N)!f}i?IP^b- zc}V!9Kih8t{66vg$Qxy=E|_sLpJg?Hc)(;kFaC}1zuIX1=?v)I#ve;W{ zgHCx}Sb_V~9GLnAuSnKP!6mW5Brqp$PzU)H;hrkBI#-Ex8>Rrn*OFo4lXf!4t_KJ3 z?O#EKNv4mN%xz=Vbx(*oKB3^7uMPMI!&jabxYVvpw<~vj_S?xJPzZ?J1X1Mz3vh~l zQJnEf;zfT6=#pGdB-f+KYaw#-mX0|Qv+|eaQ*XD@x^MU=@56mJ;%~)9vGG2WHI9*a z`|FKM$YDH|*68y~3}=9%U7!}u>c;VZf~`Iz-D{S*U8sWaU&|~K5N2p%F2u6|*<&P} z1B`UVc=hYTjw_d^R(Si#MRhWM8hF=P)xWkO(=^y_;7egHp*(S}VOKi_7~|%4ausq3 z$F+36Fw->+6T;sS?6plY`%IE=4U5R*yF!y}`(=b*Fz5~d>HX^Z6T?3VBlrvOPsCmu z@IQpp$67VDoLBxP)eQMdtAXZua!AYbAV7{$2c~n*E6qMUc$>t382%La{{UOoC(EklE(59sKJ~wu6 zvwiW<*on1^R0etFYf;Uhxkul<-! zyhksYY0K~VgKl5z_5QWdYo81}Jz=0@?AJeQ^3d;gC*~?LI^+KU)m#sVye(y?L?naG zjl+$=%675z#(upkpOsrvX^;<{6R}&rZPU2k*<6=*wU$=jY7smep9sVI|x_ko9K(fAvPqnm<9#ROQjx`7e8$zJ`>*vX&+z-lF?aA$) zwcPlc;#crarS_|dWw^Pxf zMS1sw{1d8pqf7q)Lz3DU?!kXH@DgPJ^}q+IKc#Y4UM0?}V`Xk6QF80{gmLo;dK4qR zE7N`l-!-3$u5>Hyu`RW*xZmb)2wnc};lh0mahl@xPB6LOgM?=;S@kh|TcT>70PxYq zpQmZxZ;>Lp)NLd$6RzxTb#*N^;O(0}0zUldvC@s-jp86rvX8_60EiR*&psa3bojg+-W7M9m2PLA zJH}BdR$_J(UVlpY5wi`cKr_a1*Kj?5OxNg-z?+M&5B~tcD?BxS48q#!^n0|lZJFNk zUARC;eDJ=%H5t85T2VSO2r1T+Wlx0~G?o{3em!f;i2nfLBD;C)boNy#1XlQgWDYWS zwqzOO=SW4!%DfyRaaIR_dfODzYV?`>KZS@FNL}-{ls@y%co3| zJZxKgdArU*?g_`?Uq)-_JTc+hd9EE&D;XDS4sbabQQp2c5c%a~eu;;sp#yhLy0*}# zOPi?+MG*=zypw~FEAvaooRC3)(Bo3tG>DImo(Ckv?!&;a42B#_V@qjh2DhANk7xWdUMFPPwTy6byGDIB^Zje;aQeqX;hyKQ{>-{n$?(mF ztnmK;p&t@oD4)#RmWxeBBOQ7+bM9%@7VLZpY{&~p`cH$SU8fn}b0aoBsy?5tWc`;s zKc^?cty^2TYqr0%xYS~@j7r{96J&v(W_IoX9&!AIUboXc%?i>T$6#*-+2ML^Smk`@ z@vI%SxzP)w?qAsJ!^`2{fOa=p+>fYuFT_40)Rm)Fc#el~XJkY2kX{*Ca9bl`9Pn!s z{tAuz1-FjA7Fu|xz{_yA-V`n`v_o?=&|O=x1*U~vn`^1;I;lL@s(!=}eW&(B(d=H> zU0YvYYAtXjOm|Zc zmNNp9+nj*PqbE7f!p*mcYA5M%p-y-u3a;87FaH1p>HVK9wC{uw{9*AHmHz+}O+IVQ zPs6UO^TBOv0@|yDZxb*PkQ_7{tfeeb5ho|E58x=UR^I!Iz7~*Q2|yPgt)o2 zxhSJMx9)Si@(EGT09WOXk^574%fr92k?==`A-?f_+}ecNbP1yAgrK z(}9dveq9-TdYgUEGZi?0XV#|$YcIU~T(|h`0!XQSpz4;VIA)2~U?*d2ZS8cvGmP8j?49XjaYk7pKJ;wl_rAM-o(#K3Ol%%z1 zSXXyiNn#T(Bc~#;lj6UJt|bfh$t~TE-IKu4;Ql$S+j&y-{{W-S4a(y=jl+KEE$^7wJ*AN*K=G+HD3X(_VROGi<3C_&>6T91r&h|xV z(4uVwMDc=ikyJ12{J91LW36ha=aJ5N_pVpse~V9tZS7-$*X?q?KymWztBY?*7)_+L zEKR0v8OGnBsxPNBt(}~y>Oid@3Tq9oX#Rc5s$DY}-Z+8UqqkqBRMe$yK4mUIk2xne zCW~2FY-7r;a}M`OXy!j4BRR!m>RM=aa=1~95scSjLAnYXINrQtsI4JV z+Zav?S~Je|3lA;Z2&31E%evB%bA|amKDF8F7HPODk8zsBp4DNIf$5JW?nI zA6r=Er?Fsg+juzOb*)PsTIWsBRtP~=59WEG{J@O%5X)5ZZkK2kp-O>sUvv+{l=_>Lq(inr2WFC1?F0QJxP z7~|VL4N{DGR-3yz?NConX~s5sgmrcj-etPBW3f=i2uuw7Qz6tY^y~Qq5;#^>$UNZn z;;YLcicqV;UjG1{ODx76ME?NOR0WAOO<4`^V*L81qR~qvmq&HvZ5tK@~6m09dqBHKjZYTr$1mD z4-ub*9x8ti>bfK`zP_lomyk2KD{i~nEB)=!~cenTdd#~foBFN*XnH(ZKsW?eZXmu{VJwV$7v_{q(4 zz9I24U0z$e`Rf>vjAtcAPb7XKzA@q-W9?&wO>S}ae+Il?BmtoDtWd)V3gR6WBaHPq zB$`b};4YJ*^sd9= zd@`MLPxCh!645t~%Ea@E<^KR=Emk|9iC!qT0o1mc1;AGww`24*yW)$6xbb$T<(%x8 zr_9;F9S5+k$iA%cWe<36v1pfZ>Cw)i?d~mJ8IZ0+M$$^G2*DV~t$cIfZ}=sA3#?5q zh`d%toNi>2-p@$4Uol}W#xT-8aB+da{A=u8Wrfa{V9GMFE5Yb@D{uU*JsT1K34s?zh(~){3QLEt!MCFha?ud zPP&(Ryv;k@+&oVNk#Ay2&Iw)z6~KHy{{VuSc*Eh>!|fBpwmM&iE-kb>IP4&}k~xf$ zA}$vsI3IT$0ggH3*WYUw?j?j8N^yh;2xv9G!oL{t0?N8#5+Luq#bp1z9wPBep88SoP&GFYB`(jd>aVCNsfFMRxqr<8c)ULoKT*Kjr@b(vcJk z<@kE+f;qZ?aogmYvULp#)5Ja>zS4CYcy$doQi-9ux19qd$ao-+>)N)Vk9UUd_#g4} z2?y8Zm;7ruF-gOE|!&F_vQK>xfD}am;V4yXZjOIWodJv%cIJux^&iZSnO2E ziZ_VHcITXdj`{g|*Ogy>#99`u`zrW*z|!g$3*tWrSj`TL;wS@K+*{1@Ng&P+?=6eq zu5rdd2E7x-J}c6^6QDMstK7ACiL#Ev|mE<$pJQLf@yy1|H=Ldp%dRMPbq|*0wN3$qFy-EK7VNcoa%l2IOVxAqh zx*DdVe?RtyuYMG$zMYgVKsg~-1PuC|^{;rjZ#N7voDw~|e_G@I7{k6foj~2VmK}XL zuF@Dhm&j=Gj=ueC!_B7UDpHpG@jQIyDlo%FKlAn0TxO z00kY@*Tmloz8^#rWY%}rF*hG6%wQ5f3in?h>G9a=W<{6Hd7E^Kzja4y`PcUIn;r@9 zei=6%N^Fu&GE@22uR-Z^+o_{Dp9ae@wy@hUAGrvMdM+?4vU_>t(po7la>~G{3)h_U z?Ocb$g9Xl)Wf6hpYjx@dHuvjYxAv#m&z2%*Q-TKGmE__oNy_&^mQ zm1!`ccJ}`OIH)f*yMb)>!9v1v8FG2&H8q>=lsH)8h;VW_HB#zW+{+}4=%5zJ8OAyP z02=3aXzS5*GZj_j4&!uEHWf(YKjc@>-xO_G{CTW2U>TC-ht2#z9+mX|jcccPufjJn z+(7?s2nzPQwIJ$Zxfz&Vl z(|r()Sgdja=s?`3xcZ*e<=zqaji>kv#l9fFx$w{S^oH_Pg6Wi$w8O9@{G~%<8E)9) z8Lv>)qd#npX!rS>m@voR=lS}66>Ii=`1#}gE5cqRmfyzy3Gna3{{RhMCY^I_u4y*X zeW&d*IcUUjMrW9XS1frwv(0;}STv80%`Y8Ft?@$kUxx4Eom)-3(*FRqr}F$c9hKaV zd8o#pEbhz65ptkNKp9pTIpcr!R@Pa*G3axf*sW&NEuI#D$g{L7JVyddj2;+u9Zh=| z#_JD=9xiLI5@?!_!_Nm<=@wST-sWH|ueGZeA>&J1W4cJ=P+0AgX)M^rE6%@bFNU`N z0JGk+rdsJM_POtv$Ee>#0Jzj{BmV$c;eh?xBLaYb7Bkws8t|u5Hc_*GGs>XtH6|Ym zbxVKQzxJf@pM(5MscW-o!$!W+;Y(@cTW>5YGz}t#+7ys+oQ$_lYt4QYO1>oc3Hw3c zzwYIW!B9r1k?d4KtZA4`dvTm&pQ)|J@T@*FwU+NslT-UcskGFz_%}Ni`zQQJ(X|~a(?X5B6&x02N}HWRN7i)bXOx0lKPOimxW#(;dQs(T zbE^@Gl;g+JPx1!y81K06Ffh&q(wOL{5*O80Jc^0l*j~Z8$U9RrwR@` zob&isx7c{z%S!#8BDE1}5?!{Eu(1}hc|>3-JF$*==zEI#97JC#Ov)N+cb+R-cJR!t zB7Dbb4UFT8@=q288h#&@b~~_eab1qJ@gwazT$#~zm5tZS++fH6axgzC^N$hu!rJQ8 z1-H9aLxNp4;Gh1za;Ryh=9BnOW$-79F7z*jUL4hKZ?6rWi)L-$w9{?U6FRhq+Yj+9yJgT-jO3^EO0?BVwlnjtdNo)ff29YbU~+4-eamfpUrx;y?!~ z7?J_?&N}wZVQPAi*QIH1BDRX%V+kA(e5t`5ae_(5Jl8&5kj*(_BzcNn@z?xM_-%RN zpMiQ_xvAWXokLf9c8)7(zjI>??qm}`KRc$z03H_^h)jdwnSMeW#qq9qhY&4riGDoMx-#yzNS>!#=aez)a;=4}<{4MbAm+<>U)%5H8 z*43`ylwH9b#s0%AWGN>Y3%G(n$3iR2_S~tYZ-)2Xj9(sjBx;(>bHH3`zG#}|LHSBH zFxUfw$>Z9hm&0Bfm*NJU{iSoL==w|%AGi5ryXr2l@`T3&AoHG^YkJnp5Zr1PGfXXD zD{*x$@}6PihGEV)#s)h5Y0am%#LYr0ojgMG-%lgkY4+CBT+Je>+;A715yAfe>(q0~ zO3dg|lwIO?8n?y|3Tc|Bhh^~HlED|5D#~s$&W=}N7Gs^m3~`@YnlN9AuVe=i5JpZHA4ds8qzxqO&-u8_2bG8E=%ZZ|`x^zexNCYinWu00idv zBjNKK&vCCnmruBiMGQ|Y*(3e&ALn12ciY!bk~|hZRu|A@`&a0P!#{}L8`i&Pp9H|S za+o|B;af{9nIwVZcqVZ2+~5!iTzM*|9f|9l;hm?+7^C0exyBQhJLr1y_>&x+hd*2I z-^MAWMvbc4Ql-_JMNu=l%#xz^tWhKOo=H*EE^5@QT2&+Vme^XeW{4eoCuWf3`{eih z0ZGou>zs3+&vjqd^}c>|(dja1wSVtsOC+XdnA-e`w2fS)IOJ26jlReei|BINjxO=Ps1_0@0Hc^<-t- zU1=uL|D*Yxhwyaqdd*!jV4I;r(ANAXl3UiObiW}uKQN+C55_fVZ}r)V2Dr*to!e5E}g&*1<32_H}$-rH^)}3Dzf@ZsUi!&SyhEk zqEP+a4z`mvmv&G|;p4a-c>xl6!8Y8cLhLXX;eYak-kwD58^SgC+u;(;PTHjU(cFrg zDJ50LgpyOy;Fq}DB~JdPta;h?rN4LkkJTkSe;4gtw@^l;e3FsKv8V0``NwxG((l!! zLQnc1*sqX_%NTvvm#C9-Rze=kW=gPiiqt4Rn)Yj6qBE@GaO3suBy`W|IV+~JdC~F5 z)+d^Q4@>YSag(568#p+g zCDHYEsVz(F9O5XDrCa7>-ND{~uJYio@dMX%R&UeF*aGo zY08ygsoH!Hj#2%ARlLRrBgHtat~~KPGvGd5$@H3r zkMzXB(O<*Yt%gQWNf{%dTNIS1oA5S5Zpf2zFtE1aFcHo+fmeatx3UOLIzT3h2u>Cm z+=eQSGhNzlazCNhpx{&E{c!D0>^j^`v`$H128RH{`D8? zsQ-}oCtl4@#dwWFPpV%Yx#xgqG3o)!k~%FInU~9X9CZJcBrq@gpNCdwjWPY=zn>D8 z&wY<#0zr9}ln}SlL2Wo`xv53t^LD|6Ci=*gQ}XWX1$t5$`Xc{K6X1{#(K7bpC# zIwJC$1jr0^8xg6}4=0rFsinMc0p|jTSU8kjE+PJJe<|&iQ|N#JI-us##I2#<6JRqy zyzDY<#n#s^G?5oK+8$aPomIF(yW}JDQhnCmwFU6S#0@f7(u{J7Ix+ZTw~iR0;wYlRw>Ydda-ugkLRr>y5+)3i_nF^24cZ|Vmx z68vR*2r^oZmBqiae(w4SEfVDH)jfV818 z2#j0lfIIb$t9J>hz|}nlrZy;QGrd+Fl7GJWl~yNLLXFLUe|-u!aJR8rc2dmtYfjsn zr^!akRcae1mXEN?8CA|RNt<6L?n7r_d>zgJl=up$7dXe#XEh7GtH^~Gr`f&ZcKxhL z_g+1m?$ZeUURtsorQ|v{vX$5ez^J+>kWzyAJ#9c$iX#9`008=d_)`p zF!Eu=as{K6A8uD`>g!J70YMo{<^dM9**=fnbNms=p(hB-=_OpS^cpto2PQ_M6~MTx zr{fhhBPT>HKz**eI=KB0Pl_<-XdI!?gTlDUL0tlL%q&fYW*feTum2%ui{CyjAGDv zplOV5?97%bZ+lY;P^uxaYeQk(d!pv3Q%K$}?@j0!HkwGIu)@fsRt|^&*7JTxYiqrU z+-e!e8+5szwK|9Ea87;XIe7+~e~z$boJ~Df{${odq=Y=dgIc~w=Gyl8U3i%+*5Zr10b(L|*y&#? ztQcjGCTQ5cKbFU(f^u&%aruEzQxe-U&z6}#GWyL8TK)5fD_YZk<#aPX{1-orVthE5BqmC zH$=Q;<$7%r;xygB7@5y5qNuQvPrFN7S&EB&eArNIZzWxLLn=u$i26ett@qLCat{=D z|39+*vlR>fKDaD)GZjmB9z9*@K8&W5Q_U@y7w|Y5H8lPlcl=ajv}aSdJ|YVxZJi+% z#C=kz3>JW}GcuWU{ClNHv0*dkyTTjnFi~ou>wd2I(3Jawu{S;evX0B|&9A$cTij5R zaLpGY-xRBEd}(B=D5Yu4K%xFCXpMuC0N!v%ah*TKF<+|(IJvcpV@`;7#gp9KTn{S6 zDt$Sqq8|EDNTFI#)m2`j+3KvR{qOV|M&QNz>06sq$p>xbwwrQtl(dBvxN@Dk4vWd& zkxn5i5s6(O5CcPjC`ea)!+&IC#G5#ymn5P9=~*fZWfEvPwolcVJd3knQ=d^)UA?<8 z@iMyQPDG@VFv^Ft(2x_pxvr1H??6U@ls;NK6*ZvJT|>^klixYi-+xkpvVTcR2=ZJ_ zPxVeFb%fFvXll;^#y(+3a}~GqoC^sn?eW()RK_g(bAMa)3*6zjdui?9Z5wek3LKNZ zhGHGg<2wiig~UOuLnkl_E;wFAJ}kS$+n8La)ZV1O$?yHihzCr4ev71%8AAG&q^3eX ztce3~Zd$HJ+S8j^%UxNG9a1}2_P+<%M(9lpb2;XkZDtjHsx$-hcp9sG#zbL-vH$Q^ zR;;FGe>vEm@=`54-`XJt|4(SuT4rjO7w?Ur41bBdXPgYNWp`ZPNu=;h1fOQC@qWKn z*fB&JlFt4+oXP1*sfFe|d+c_X=zPwxNrIO3(XAE(U&}Q`JAW9LMmV39%R?w{rSVT}{|olPSYLy(iDo03DqTSJ!3 zv{b>oV8eOc$paC0p4%12QXd6fQr6f9YARu@G*uxcAAJ1Pyeat-aTl)C{G~e4XBA5R zgB`r);WK1&^FpdD`Fb}4XoLY;Y?ZNwDeaP~wMP;Qn=0PODT8UG=zd8k(J7L8_)`v% z_3xZ9_{-AUqtYR6yG(mS>vd=}srn2S`a{hJ?p;}Xld%0!#A-WeYS;0=wwaJ6eX9t! zTE+7_5Auccsr+XaF>?H#zoAuAz~C95_J|U45MCNj`uqm;4k@zdg6Vn`kBJdT z;1h&HkjBvR|GLKwUJ@~O1NAM0Z zwFzg#o4;!H1XoU^4PLE$v}Hc&f`3ve)?ke^e5%FYC-B6Z!-u}dU$~j#bR^VRT3T2Y}Spc-eSf|%fIZcb#!vT)dbMGQvxZQZAs zb$E8nshjh=-}L&Jz!*QY+uXxU3Wbc#5u=iNfHhr9|bzyS?029=4?p1(~j%$H{^?spi!T-2gB`$8V!`bCD? z0u2wq`1g|9^c&NDj}!(}U8|(5S*LRuITEL+XclHWQB>Y~xFP-N1}i{-gAp#m{kMfv zR*vBfvtOU$4*V?j?E3C^A07V{I1^!nfOj8;7v-kNLplrxP~*_9SMa;D+l_VRU{u1v zf?tjF)?9rV^V@myuVkIJo41!SFn63kS^>oAIKw%UXD`DE|1P?L$jY{|Rq4GUe3G;> z)(xTy2_V3zqkM*3^+06b-KsJ<=G?TiRLT6l;s-fZ^`u7G=}ADvq^Ko8Jw`R|!%yRi zpFQf69~&A?1jM>1C2d7OBmGpN<(tnFBZZ7j9tT1kH%FaEtBeeWt`S#y;^nqI$ot&) znm;=b$z^dyz)&BQTg0!W-z6>wf~~H-E9_I%V>JUCW_Ng&?ska&xqeN0Jn>{AhY=Xv z<%B$bjCc=$tJ{%F=0HC)I^R?Z@KpglOTMR!N3B zZ5xzU01RZ-A~L4Hy#`sjmS+MfA}juSbNh`5M8q>6M|8qua39cLq=yuEbVPSbobQc~ zIoFk@V_$pd`W=f-qCX-i->QlM+nKZ@(c?f^{36Gv>o&h>Q^D?k-BMwPjx|0CPl z2kJ-S!o0bO_r>B@>YX{%J9*PqteS5BXvc_u?glKCRN-x#zG(*|<9%;6&`=gny=@1m zSx%FVv61BWT`i5;q~>bK&Hm=V5&IH)%4}IITW~n0ACJi^^gI7ke2=}mifk`0ZPsi- zKK|mGQ7*KVc!r)y-412F*P=V| z=qD+p34hNWPHpN<61N>SC>8wGgn7o=XnzD{bvL$}$x!hLTZc}Ra@%cIjlh&fnlkTz zYVQruBn-q89!IssmhbzL><6M^ArcPmlshy{%?yc!8491=KrTjuh`S-p5J7M+01>c; z6qJ)b<=Vqq95Qu@V6BCofqQ|I(kBDG)BFRbW%l;aCUs+gzGWVxiPTW8?aptJBZj=Z-x>d>mtOCSA7e?xbb=O+Jx16E9mV7yj`-GWFo0mdT;laU%Rdz_2St+}pr{YZAQJmaqlYXGklFSX?LB#sE34y}yZL=#ar zvGKWC9sM9a{@eGrz_zDj&O5WlJ67aGi7wuf*2pRNo!a`)FZgp?pqB8WR`@^aX z44Y>8rRCpc6V_&Ryb=d~ftTxLlEva(R}zW4t6%KM11J)UkQun>-V3;pNbLYm zLsNFUYU$?loe^IYw2Ny?W*;^2X6d^`Ky7=>7>r)4Su9>f<^}^5MAIRi9NsIFrESMqpK+|lr@ zqD*cZaxqN4E4L<%9+YUS8P^JBw8jqfoKE9!y1Rdn6ic%X*M2|r)yzG8#cfyi$R2Pu ze6si530q4k9 zs$AYB52vCL;O+k@3;(jS87=<6Aq=;MhLodSot3n7XR4%m^a4qteU%6A$H;`rb_Rla z$IcPa;;^kYeUHhaM^YEz5xH~tn|@Gg5>HmP_a5pS@d2``@^p-G=i~brs`*?^k7o4( z5s%@~K%VeC;j#K5h0JHcb8h$5UnhpWQx_0E@e%M$UTVY4c-8!wYI-eFE)Xo~(c|Xj zA-{qw33*2NiGPDZYxrE#N&J4)!fomi<=KAY_n)>GN*DelP3FY>r`k!7b#Pv=9|aHURPqrq*l@PDl-JOeHc?t>^5AGL`kW4ql^p<3&*QF+-o|NVu}yD` zyV!qIw|HtBt4ii_IhgY5jznMRf3(`uyvK9x+t zYSrg3&91R;!VPfJXmOT+lU^cq4F(b8Ge!&TmRizQZOz%}yeCi{no$4G7?k zq^=D0V-~ju16X6e70W&vgW`;^;o`R-e=s}erY`7=)o?v zoglIbt_=cy4{2bb-6mDVFQk{_-CXDc6&A%@91{NUc#y9nJLTa14_mGpxPdKfN)`3W z9)wn)P5ur1Ypg}XL4Sc@c(z{alqj&VAgrp=(Aae?0Ds2u92}LbwZ*TTcGW~WoqX(O z1R9pWb}nz&Rt#oq6^|qiK^nadj~DG5IWF`^85Eeaa6-_}D2IL+Dx!x7KrRK(i&F&z zSur^qQfZG5c|xVg>KIffn41;Xe!z-=D~VP$_ON}DdE=%e78DaA0XZf;D0pwc!woT#bG2tNGK?t`wV6zR3@Lj<7tEMXn z>tGgSp6>jr8mAQ6_-+=I8Wt80QT9cPCn4$^%RTR2L3_;<9;3Y`gkK7rOht5Y?3|wN zSTbHyft`YFt4vcjh8v$MJd%i_pTi#uZ;Q$w9IRXJb#^JKT|P6clomG6x?6T^Igs?~ z+TGEThfrT}hG2D6<^8x%U4+TOY=YJ{=d~}U%RE=JCR*zZU4BjGHp~3kv zK(=;pm2%SBGOJS~r+FNG`mm_2d26&jM^gesg@FNW_I#@n;tQFUFUq?|Xn^yXzv0wu zls``nY}CCjW(3oat^G`otiuJ-UTv*7B%Lnv1k4XyT=_7YnT_~6)v_Zu;%|Ldvw8=7 zWx;*&{>VE&T?{6yW+dLr7=N(F1AGUHv*b6fh>>#Y|M=9BK9T#J2f=xHg3kEj1Es^i zgyYKBv}Joxd3B6m_}j)$6J!*$0w2yvBp2ryypoWR{~bP%ld}oWXq;`z-gpteVhdA* zn&Jt#2w$pl^brp(FiUz$2jTa{{LcK%o-}^U{p*VgM`UWolMY9({;jDA;v}0@<8tqT z@{wun)ah+C-MVH(EPB4zDC0J_=QH)+HEo&W8p)%qYWxeNa3&#B72%P*9FT9?TpeQ= zK;CZu{ULWmay$dn^n`Kl9^Vf<2=n=8K#^<^q}XPtZR>N%3>Un4ZO7{UvKp5eNiJOn zv8F$$PZg%S)$H7$$lr_L6oJ}{CmQ+_?_> z>XmK&HzU_4h!5WBo|*+`ipzDdsG^#iuB8bA@orx<4OFaY3;^A7D-6>;chcpj{k6YxH;|5B70*U-B0nE5V-}hfEz4H1eMCh%F7~HtIv*gh2z0 z0LWxx*2hZpG-fgJXLT!T=TEtV4Xs_EeUtKJ*Iy;=pr-w%V73DejM-Z(D3;kbR9`o1 z=g_V6a>=Hyl>y}P7+-$UGZ1@4toyM$zosvf`6)R+vx+Ur`b{WlD~EC{{~*8CFNIG0 z5C%gFQ3J4;ktBRj$6Gzifvl~x@R^3hx&i2o5P)EK!`yy7mx9y5>0Wl&Gu{0Ruh*lJ zL*aoWzrXV&&<*ej;jYL@ zT(D!EP_8C~w`)ir^}$$8)JLA#sx^`FlUp7cIS}8|;c70YmeD^Z0F5tmw9V$x8Lr_R+c2RLLEs) zb(|5pZ!GeZ3xQL$9v4GT=@eLxupV54m?TAN6E$7Dvc1C zZ?udXs)R!Q2hOUR=jTd>MHW>bvdl z1+!(*tog+OIBQ_D39XM!0f>60gKf-0b7^qZnAwxkJE`!#HPmXR9Z00*X9m{F)NdEh z>mV9hI=A>t>IT-#4J{{z5BznNoe95yPNd2#x2X_r-=Xs~p8#0pL5j?WyI_H#$MI^p zWuH_!xes6<(y916*X&x|#cOeg<;%NBR-Lp(rn*Bf%Gb@7@4iBd+N$X{w;MgsV69cY zs$Sbxft8U+Re8vmV&b@bJW%GZPaQ8;)kZE2LCP}0*fe46K^)YiJ_#niLSd*H12O3G zJ%~MlGPipKjB34ol2)2Fy4JtA|B~=@de48xiQ`1-q)Bi#FKrddr2n$ z(EalrQ~_44p9@NjtKzvq*^0!QK*1t2SNFbir0>3eBjSHB+I1LO{YJou~vjdoN>;w?I>2!^ps zHzsiXZRw<3mqQ7|@9ld2ZCkw8@PuT7-*kOQ(pzReGkom)iPD`IZBKpHjyl)ma_=MfVhnwnFLOY2+pDxTBV zWSuj{1ZkQD1c`p?zc7lN13j$H4mj_ZHwvXQ-AazZ#b$}vKy0N3s%RiFYs_G=tvYa@Mc4L#_$pF4T|V_k4kdNWA; zivacvpG9JQ2`)RD7D_zsv}AFVcQ7K=re3{}3er>gNU`s*3Y zn)!iVucNI-w>c%~>RcM*uwR3@*2W@9mfG!Sh?r$e>QJ%7jF_vK%i%B^^UIfCL(t@M zo{7Sz6mK3~Shjri&JHG_WQN5j|9O+JQkiHc+0SV+tq{i^Y0$M8jL=3Z2H>lK<*v@3 zg7V&^a14^9G1&q^tnV63dXog5CQ1j`o`+y=!(!WYi|6-TORbXj!3L4Hhd_xTO&%fD z)OrdMtUIRQK>3g5V_F6u!v64vm?w?4zgi>h4bG7qoXpsaZuz{9P-;9M3ELv#^UIv@ z^Xh0=AnKrwOA_QpZMrs?Wt{bII|~{_I+joV*7{MxtWk;O-?g@+{f{SKZ2>89)V;i- zgdgy`Auq6$MFB}@XiNi--qPTU`WIkIxyFT{0~-s^f4><`%hp5z=#r#s6H{D1T0Eiz zmr%By=T>zkv=9}$G$<=kcnkSuTtij2i8?;Qk`(N@Nr`>bgdyQ(Z!169Hk7HG4NzZj zS~Bal`xkHXYQw)2HwyEYX@I9`{bHFE%SWb z^@KZu+&JRvt#yhWt2Y6^J*gv$A`?Sm6iXJojBQCMAcFAbQQUIvQ54iMpLy2VD+#BD zH!E3C3wEfO?GAV1U{`)Z&&c^HnefGri{qh_$W8FG2na| zf_}EXqX`^MRGc0Owa_9$dd}p8+p3S-q)r9G4lFIMoxmx}c&>kUq{;b^x-2{~Q8v&t zKO{0dIQg%&{1&1k5OLrdln$hNc7ZmC?)A7J%Rdclb%;k6YWpdNyP>|rnc;TTO}c<1 zJ{7jok)_s9A@nAOM(k-(BNM~W6xLFkqynI4^z(9PW1 zyi`yYN1Hd7X0qxa1lB0wOM%|G`W3g5u~*UDGRo1ltheRse8HtbHC4c&_QwQGQK5~> zukDTNU+1<<@xpl-YibdCy+fYp1Fpe@FJ%8K%UPDI*d|G~Ffsw%E)ujrIf3#H_g{fJ zv9;4K>G#{98fkEslXqR?Kbs7F;=e}80hzHUe5$>m6b3wl)PUy#aumQ~mje7^x}AJH z6nx+1V%@093^IveIQ?sT9AZ&=>mp&aINv#9l#F+|GGL&W@eV2g*5A5W=peZ;Z)u@8 zt_9Fw5u~P0woGKm)-K~8?R_6-Diu8)Ci6>jIkfJ8_{I_UF%;B&4nt=bB^k;~ec=jG z+dr@V6JJPi#37NN89Etn=vKS?$Hpee4vQ}b^*8!oS*8`nh!r9xr1y*u3)4q;(maa4 zl~GgQFQfGNTh%%^u72qzXRO%L8IRf%))B~(k-WWorM!}KasAQrIYi7SbBAVF?O#4QYpV+#q121RkZXx!{^e~3b;@)69?}=U=zI>nRheUH zIskq!U#FVBCHQ>3Mk#P4kTGM6%24j0>l&fC-{8)<8x$S&qb`paXq_lv@a2OF>iOtD zDcnS9RIcBpMPiZ@Ys>QP(mmw*Kc4@^be(LKj7>axgL2L;U<%mB@J8M9gclE0)c@$_ zDb?gnClg@4o-X`Z|B>kiM5`00`E+p(tPKBbyX1J@r=|$4rVTmV&wD6sXVQYH97$_k zpvz{}`qi`gap-qmuLCgBXPHX*u4RY~Zf^x~%LJY*wU?S{VEnDghTJtJWhjylV%tm# zO&3J+um5zjO?f$;`OE>UNNO!^Qb!pstlD{UuBs{KJ!g$H|M~vMt{gWubyI1LNK4_# z4Pd%I?s`}$o!PD=(GdD_LgMzf+TW{%jq!J0Ya|rJDAeRB;E8^JMS{moOs1GtzLN7- zuM?Pr(wnJ@eO93Y>lllO{*iXbjPdkZwefdpx@5&uI(lFT4rSq#qNG( zQ_dgD1+dMVIS)2{xsZC{#i`}(E#VjYNM!d@YA1QLioB7_gT|NVP39v!uudo&sZ4gc zZwSN*ZWI@?oXb>fKCi4y>q+7CimnI_=rp*jF#75b)aG*q1|bH+x&|hxeO4=iOIneoiqJ zpGty0sYWA4yp0W0!z1K|T3+8fXyb3<(NPlKBaIW#T4Y!Qg3=17g^mhCk1+%snLdSD z?+U()j|2Og)9}A>O)0Da1o}VeJOr=v=sh#z$+XIh)E?LveX%wF0HL8wiR;n8Bz=-j zidw!CLR}Cbi&hOQ=qcX`%Fh)kM=Kb-hsvM=b80oI7JgRqcnHVI>g%@4^ET?flm>gh zT_NSjKIbXn3a?3!&K@YEn@BzIHqP&P{Xr;R?PF_V%EwPPUpBVzhU5a={K~n{VJ$}_ zOBD;2U(-Sg?cQs}joZ>ZZZGpOd$RWLw7--4Tm5_6Q-5p8=YJ>zVf&(irHhvHT@H`B zJSoTgpL`%@C{#Omc%S%EC{Le?_qxYea^l2S^ACV`tl}Qiehz0JuX3weunY8f`Kdi& zo9yfq7T0fMuUFa%iHzuanI_x;N`Xi~yTqTlM+EuRt-+$;5?I+tms75f@F_*0XTXHX zu1tAGI?vi>kF}kg94x(I>@ElvKx)(y7e(ip#n?!osGm?$_RmsGt&0zbf_DL^IwI|p zHGWK69}1FstsL^E33FIb+EJgi1ap5__lD(3FYK1~pXRMi7i6z89j>M*V7^NplFHm% zJzb#<-xj03T1@HZ`atB5f`-su(ck#BbGuo51r-67n4UAK83XFzT<9r>(E8 zaDC067xX4qQQWe^mQaZ~C%)%EaXBLI7XF%)KcA?XpThxP_3kO{XRyS5Df}kc**5$8 ztGqIU`UUHTeBRCYPuOPLQhg#cEtH3GHDCN;a9+deynpJdqV8`@h(d`<;I5THGw>JD zcftZ%xMvzayS%x8Ydi?X&`WsYl4fe_Q?Q-q+2i7G1zk;f?&?*@gVd({4k zx^3z5?g+eZFyvD!+xd?Sx!KNmo=T{|8)4l$^Jz*j7v17)+epr>_&5E@yfqI3gTBVf zz4%PoBet$^GBC*KY4tMX?zor(Cjaxy4ynasVt+i(T$w!5?|_1WU&+&W=`ohxbZbET z^HtB#GsCO*rW=Go%}kk*o2mR|17ua3c90bb;3 z(+}2;SjjPZfpDh#s;mo)YM_3vo05(T**3CtuMU)9sBzJzRyh5y^Ro zgaE|Bc*zjy-B>O1(62?tbjmT?h(od{IZ=buc-Zla^!68$3tG)t-hbu49%6*704=)8 z4$~?y8IinF@>e>tC+rM!110jv5kQSj93-EsN}P8FEvz*!+pH0zn5Elqe|vuB@?8BJbX6Lu!23SepO++f#`t zfImjRAp;OLce0QdNWQcSV^C4o19Pd!g;k^nATDdq{lZ`FCnD5)TaG4b#|-$`+K9hY z`mzig{Z1Z$zgyVbs3(=w;B2o#xhCqu?K$}iHhDA5-6Va+SjFYcqlpmv9oJjp+TpZyiY9+=Mpg9>H(6khP|Tr2!&Ae4kiJV;7$a(jPH zAyW%AU_GlqiqL-bGGC*-_ybt8V#kYNo1*K1F?oQgL%h~cwR2aEA1Mo#ssb?~O=tcMp*g8H!I-9gCaKczgscpwrYtK1{PdFb9M*H}Xc5Hq~MqMTr}z9k2Ac#9-ybzRyHEW~c2c*6R7)wUWB zl~GS`9$y3Q%FpDZ4CyHEBVEg+MN{oF-t1Zd zm*3-eb(rekL{x^bV&Zxsyc6v-rH&bFb%c)f z=JXjl@W<3gs$!-0pKkdvI$vF!-^6velBllYF+Mw|O3?ShRnMws(w7B9Y+gUsdMY<; zhb)WFIF!>}Uoj{@0a(v1f4?Y^DXLMA;5jaWN?D}4s=wRp10J5dwxinV;sGa-Woz?fZaF;)&ApacBBY-Gn`C98{4V~}JdbNQ zW)zqfn(OQQQ6DcLL6oVB5=S}t#mi{XJ#hR;|1nM--(Nlk$@VjUJic!tv);6 zr(7woW8xvcVb{;_Br9wpsT1NqGDZuhRzOtYq)#^A2j;BcsP8jOcUi&`r?-|d@7z&z z{k(KI1OJs!v+f|#UQ5}@6y{&1AfeEtV7q&_k>7R>%n0VPk-L<{wr|cz70k%b3(@b zi8R;>Q--Eei&wy?!eDSOxN?Z^+iPrVM9NcA!`Riuas+@$x{?cB8s$(Q-n(oy|93rR z{pOmvZk{XFDVDms6(vo8m~b(LlWT<0nOO#7Ux6DER-BMok}>a0JPH74 zpxI}q`|H!3H#ImLBOpQEp5$T_j24dvGM@sXTSvH@p?xvRh!$Q$ zpyJjTW8qK1tyKl(j#(g-sLz)l(fhCmtGWI#%Tiu=+6ktB1N7~@*kUqCUH}W|4R!B= zbJpWvs}`oAEWH;G>qYH*e=KSqtFkxhsE?&DtEftlHkX+fcR2_84x9tT`L%{RcpcKU zzKIF?Wl4GkCF*!v^8Cc>H)3Huo7Gcd_lC^w^m5m^YHeB!)?zz9q!jrpgbHtyn&C+0 z)&vs`6_g1BraV^!e=U<520eNG3GTM~^ zkikuTV#9uJp_0<45%V@24?5IvLfvdYnbSm4<`2s3K>sUb+YKte|Ks1;ZTZEfgsLc?bML!3LUQI^A#S_e&h`BMjzfirHPFpP6yr1Jdu)>RT$%UQa8Uj$qhw2SH9+qe zJ>z2<<(a*E@2FotR;Ae+rQWdYSOq`dmu_lbh!Y+h{*l}KQ89m_^~1B7fqY9b$%bK3 zS-r%gZmC0;(8R-sT>X?TdylXYNP;QRauaTg+riGSC6JaMt^oI*rQ5_QO#4p@Wt#p3 zsrts_&Vrgpcy1bYJK8IKy&SD#@Wi3TGHN`e2M0I7CXr?qp_y~Xg9U+v&zCiA8}W>p zHRoh@*R*3#eia<#!WR&)HY6j5Ye@D7lHobJ*Xm|HAkpe3UKY(G*aM+LBu@4*UCN>_ zw3qhVXzgk4iw}+vgFXh(k+SR!E5-Tyh}#7Sr|#7=1$8&Y3i^;AjZ{HvC^#9S9z4HbMEYS0anqND&(Y)+EK{*>ftuOnYnZH22+_A;aBtQC0N*u%j=hiCV__H7*>g}%T8kZkF2{Db85w8s){vRR&RpX zCAS2i^t+CU=B)Q^fsu}^$*MF$o(tTG@a35XJii2q=uob?&^No-O>=ZH)83A%Eqm!y zm2@mI?17DgAz^BOwC7j-62Z z-8Et^iN3NP0A|?v6u<6wif%VAg9oJ(jiEb&xjiXfWrm>3$N!P-PzDuwEbSZ{FT7zL zsmDq7Nm6Uvx?lBTWhGJh+sUu`CbNyIg}mjS;}2Mgv{g#u*CHxVpmj1cA*D;Ga3EZB z?P0Zd9yK>|ygU2bGgH7Dy`uL=3swdO0zX8)sw z{U*f4Rt_dkfce;iJ~h@3emX*;FN5uRWH0ex)R|Ln7T@Pz^nXOi2_`4WMq zA7P94^`PP>PPEuBPQOTQyEep8KNK7tb+NM@IxGr|j!|4xT5p6iH+tE$MgyNx;KGhh ztJVgVX}P+6ZB@exDo&j)EA!ab4s z?hs+E6{>vFlPE5wV>Sy~zk@WywinA54=@jGN(eFCynQ0E)+E4rx_hx5V(9>}xCM1$ zj9y|P$Br8?u{R3_7ey%2G|}d#W_{sF0AYe0TX3)-%M!*WDYXxgT)pV}F^6f^?dAEz`WJqGs$Pm=DdL+>8-) zqakZlsp2Bi_O0O*t*{!R`I5VaE)TNQZJ1F>;z7*pV~e+w#}Ji{I0vJtBRCZRC4AG~@42>l^B1&3)k9oQ=w+X~Fnl7nlELdFUcCyrKpl4c);O;f$avrCwBCF@ z>!qRmAK7XRQ=b?L583gf@pShecNjXr2v{)tAPvAa;){ji_0?sYne|Ar&{-bHm$+Bs0QJs#D;XXol0X|5*+ zuxdx=ihnu1>%i6iYeVD@X7;7C5<&eEtlhdD^MK!@n5 z4lDkj@ftsO^4JkmKE7I99b7LPA8e`vHWSf)9P~Om0c!x$lYE{K*_n1F^tEvV`?F;! zP@PjOO0$mt^=F|U_M;Zw%gLkQ%3#DwcSaTQz zd8Q9-Y2H$L4|;_Iuei$gE&=5&;cmAx=CZ(E&z%Ict3KuZkQD-T1XZQ2c7WF$_RC=b zekeLus!*VAsO9RR!{@lPNZKxYx1zvO{>_C7dG5`2-d*sb=;>ZtP4=wX;fw(RT_#7n zgaev?Y+If?>28I^!lmn-&_|2`vU(ZnStoBp^~gP`if{vSss5_`iuu_QvByPKEkFIn zQ!-jwyjK`M!yoSn0i~wWIq3%J2I;QRG1!PPVC?@s?-#zoaq#fmX^_I}AXU0#Fle0s@;wT}}Eko(7vf!gaq5u zqcf@9$ON5Ue*ey+$bv9-`iH0$}NOFgKH{nOdgS;$o@qNel%S*Ub}*!LgF zy*3Boc>vSujuA=jPPZ#Nia}QI=dV1QcRS|d2<02HIX=FllnFUUOO~{kx%@My7UJMN zcCwZB>?Kd$0?Tmw_i{)>dQ)S{34}C0#={D>_cht|j zp`_RhT-B6<5tixsShEdscpXN8*Eji-@f=H^?qy0E0;ko65D0@qak$X0QjFz}hs&>vy$c)2eiQHJD-17!uhLnDDig(>Ke_ zA%SN;j`V`+c^s!4$n^4t!ygQ7#F;SDsEgDuzR2n#4~NE&3Vk{8RB=%{Dgg@%2&5*S zs|$E|1Zu$Xbb#lgl2c7n#MPnbRca@Azkf-8_ZIFcLcB-8(j(1oG=ku4b8CxRsX0vd z$no>s{d)R^@#YCq$+rRr$=pk2O)B6!;yk)i2gC$7 zApaA)LhY+dwZlnK^Y6fS!M>`sscnjS@Xn$&q59n$8u1a`5dKXB8{n;>a0smy)LeL> z^`!JrEv4{1g{gX%!!uQ}f(ThFRC_7xrkOM6EoodD1~10b7}ufS#rR?EU^edd=QG1~ z@JQJ%jHL!a!&p1x4y$ff^nYO5?N{4GN7^W2DIUsBE8v-sa%BB+1kvQZCJNcB|Ka`4 z|AL<2i$dUESJ!WtJrOAX(y7?lHK!H!VsP~&xhEv70ji(D@&Y8xp~&xHdK~e?!PAAK zckameTatAI0(m656J*F)&N!pHsOtHZyr3mW;(FG&T}ye=XN7EZ{fE}wga3kcO(H>VQl~|-jqJ+zK z@5dJm0v~WJ)U#>@s@rFhbKh(%FwhKV&pLw^N z4ll~peB+f-HiZQC6H;WUpG1=I``ipASKJ2spL)}U2_8>dKgDda(y7-a}o5?g^W zm;Yr}FBkIWN&3E$jIwliGi^Zm%AWqNDQ9@W0!uYoXuq#%n)lK3=zL~7^9LRG5rOzB z+z=-3C|>C?LHysZ83lDaY+32o*GAs*))ul8y&{*-8pRO9`{ zlMoIa<(KR|dA~Q2ozOc3s@(omLmLCGB4(5=<41eoK@Dq<1x<|^46NL5CI-W8*k z=M+~t!ymw90SE#kxKm0F2>{8JavijV(Y0QSEwu_O!aU|r6&$F0TlwcSr^X#dhKZY1 zogueO%5y`gEu5K5zhJN&OHGy;B}MG&;}4Dc1ed0&to$KOI19|X372f0Xb(4OTyQ+q z^!i{DQ@dN=+`bh#GFHJ9xQ4AEsy&^Dt@gk5?B#AuiV`+y7Vzk`GP|PJkmkInyE%AfWV$E=t7z1RdAQW9~re##Aju;LTr4Of~ z=%fNow>?vmcC5l%p=RPF>RKBDxZ1Lx@HV7mE8qKCc=3Ikgo-B62u&Uf&;n|e*zvU- z4K@X%oPld;vg$^zd zR0mg)b%oRN*G>4^L#G7Yxm??jK|?7b2VM;`?O3;%7%)=Z$@&loDa(bkD9LL+0nesU(UO2 z*Tv|zHKAmKIi{pEu-%V`36ytW{@im9Q=z>-`&TEkDe+G+L4eBItmcIAN73Z04{`+Q ziaU!em2B%e#uigmi?SnRFw(SXNF0C~HtqXxW$3b&|`O9VWtqLH&g7&b9|-p!+qojY$u0651yqz@one zsJY9g0fGbKU0_Z@1-(4y%k8kU(Yy1e?RMWy_f~q14yoR1zDaUxstwt_`JT$Ch4@#z z-wx#wE9LOxMMVSiMQt*5Bcij+f4!BGYovWwJ_|{pH&cs!gXggRZeu401ZLD;CMgT) zULUVyg?!L1Zx6~@TKE%YR#s9m_(kScRqw=ccSt#Lq0wu9rHUuMOOvc0H|X7?sx-{2 z3dP;u9S#=8c>F(TeBNUfHNJ@d14&-X&dS?8nBFZYc1Fxk^lz5duC@l(^*IMtXn{;;n{Nar!Wy%w-Xv1r=}}cOzB)stTnJN#2o;^<59Y zCqws3MnROCwQlp|?x0BEj#KEE`0*$z8|kuoG%w!npPo0I*Akzf&uNke=B9Fu(;SB5 z!iWY&?b33=pjtFx`t5?2by2%QA6C5_{@LimopXkN!5^@Zgoog2uiYw$hZfc@zZ7Gt zoo}gPY!1uSWt@6ukkaVI?32G2A_b^A46rqh2?A{%MA=~6mm)M_io;UIbYO%2_Ui_= zD83}x!5#i8YKHy*_Onf#-q{S{LD;GG)#LWOs*uZtw=Gp4Gkmi%5YwL;%74CgY=7&-BU&f!Sh_Nrj&RIPCnxjWj0-MTP1exmi%n8 zLb=%nF_Ir{&>ogU!_qHjRxR^Sf(`Jpe%S9rUo%YBv!oi8x&+|a{$wmS1H<^`ajw_J z%^Fv7yCEFY&621=D3@uHSqX*U!->^&Wp^^+BU>LqriWeyiqF82oZQ(!cMjgIWKqYV zp=p}1ANxbeK4sCIk^6jOrs(bOEFt}{`!!k!AA(Bll~MZ<=w%3Mmg2^z_RjDNBfw+F zgC&n5XqB4nuo|pH9X%8U&;EX2`mgH_cXqqZ%1E1rZx?G?=L(0J_%&Jxd*|+RN){PV zUWntqhwpF5kx^4^TV1u|y{-|$ZR$~vlWxfDlRSgPQ>?~7y81D4J_DzV&$t3Ba6ZJQ zgSUH!}F?j@vqzuwpV9B55Bs&A=Wl3dMqtP zBBSt7QTE7!wi6Uv2c}dK+7019miUi^5#akuFPLe~JQ%HKpg4hrA@X%xoFCHVzNDNx z?Qey7`bC!_nkh-Ad|wqM-|9{K_?c)num4Dx6-T`BQ)O~^<5JZPf#nw+D$NfLbe-Eu zcPVp63-aQ`mn)a=bY==O~2%DC&i zzEQV4rv3KXXB8k}Xq;K->SrD1wb&oOOQE^ZBX2_%6J5Pq?WQEvsIrJHsj?~fkHj+1 z3?y!wqP7khBcHM-f?J)9MmkJE)Y!q!DSzFn+?BTg5u!(t2o1`YY zoqlX#LPzbQdWR%hc!EY%wx0{p(PE^Z$Tfa;+u!(492v-)us`iMgs(1ka%^6GaAZ+(h&Cgd zsuU$LpZ{g|v{c5}FMG@n;&xkRhSD&*8W3NtD*B{H=Ic7275b0~CQFL8 zv2D!tFW#PM)M#A-OOh)J)Mj2m;5^*3a)v4+iSXV4iB3}`3;%%6JpH;0=wT$ zlmH-0;8@@X-(GUmwR5BQm-~{M=xKN73Cv%hQUj6YRm`RsSp)69q6-d6!+pkku32cV zg3$5LE+m#Ty6oPf$C7Oa0`Come+>ZSR;doJH3u0#SYiSn-F8@M9-)!|e=z}UpKP7C z6`;{D+SBhYBSfj-w*c|G!$P-PS}GqWD5jp=Kf1r<^BA0?4#6Wor*qs#P=}L-e9BnM4o5KmT&%F zDlZ zyeB<}1N`Hf^*vUtVqx^Dd)hizhK;RFebPsmc2VTut`t+^7Y3lFkKhPj^XP&;JBA_3)!n4K}t37X?rEpaoadj;#`ynS+z} z+42oR@j9r@jViMruhSAuzupxX3j8w~@&oA{16>9+>c>HgN&h2B(6+j31>BF{671F% z3T+$k_S31FYlrcBOBW-E?wWxoBl2GZjpl(ectv;|k=@KC-8!)v?Py|U77+FJ1^d^5 z{jUK#YSvayv-Q8(>F8ddR2>Ydt%1j=Ykm3MbU6=q8$!|#?t=76Zi8z!tCX)~8w5vj z&E#bGXbt(9B7Cw=%-);D=s(h)-2ruIUlveR^Pb4dz1rOE`isc3vfY}XoLz^SQF(mu zu($OG+W`@aAEWv)XJE<&iPYzc6J5I_@7&1&W4{vn7`Ifd@`&S5A2;)vqRW_%{11VM zzJDp6sY?Jd%F<0sDu|vfkmGiq6ANTBfG2=CxYX#5`g+Xp*f)I8n$vIPz+ldWHm}~U>X3h1jt6sU9`Y(-qwg|MT$C4KxaP<`6mtugPkhlO>{l$(mk+Pr&hXuz=lJ#N0%s;)A(coE5e@??Q`6a2lrvt4ztvl3>%ZD{+mt%~nL-JtqknZ=;`m4Dkyix5I>WI<5L*ZYQLS(Pit(wuu zi6)ew{fK~qzu)Si-iW`DmuSLrO{5)aw0fSwYlPj6JhMFToalQ<6i^a~xSejIp!8GJ zK#v_>@lnwec$GVYgL2GG7O(SCX9Pm*Yv6-7vqR8+6TMGMw9V7B(%yNP1p8u3Ab}j5 zx{UkYPWN7Ibh@5`l#q@9HXO7 zlH>?KPvFk0!jIC_#&hbE9P{XeN1~Ig6;=19@Ma~8PMq=+HGQSk=oi%&WNcbHgoS{a zdf&rmSdd{VN0UkB7(k|lI)jCJZt7D$^-Dg7Ju7l$C*7S+`+FHDC!A1ic9rWS=yUy& zGWs7?+N+Q1y%$G@!Jg!E+jU=6YEj!hC`o1&o^g{k5k=f$=NAB6}=_lXb>~&K&BhlCU>i@0%Q^sCYaLxe>hZVt;hq zatuR>gH+A5*~WjkGK=7ln&_?D4{$=UaI)l02>EP$5cn>_D=JVt+gmTWxk9eQ3kp}F z#Io+D$Mu&(qF*{cf86Y?M&FdgEhA6TgYM*vr$X?mG`-qVI8f4hKo7SI|>EPT^D1&EXE{dt5tSfP}BOK2tPO524J zB1rSTTC9>wc86(`jGHEC1|-~L2EU1?T?0oRxcXW+Pqb%?BpqpGXeDr;aNi$ndLS#0 zARS#K^`r5+6@ZKmk=RXb9Lf3xF?u%{teCg|)70g%Y%1-Crw4uH_viNS7hc9Sevutz z*jizvjR*$)>a<>Mab7TFHO%3ea53D4Ie1r5#onitf8I&eRjEqrdhDVpVLShAI49wo z4_tqu6%?}AD)A5Ic;ykpDK;rAS*ViJX@7hjd~s0vIYH-0*!}#?MyBt40ICA)O0BCo z6KwL!az}sfe4gXLOEUi#%-!1Ug+ayE@73VUOpyBp!)7bbKM!|-^>OqWn-*Gh<4v96 zIUAa?zwRjeXPBz@uH~;Q>t*xZY;jCoL;;oq9{m^-5qmp>YFa;__79p_H0S+C5%3NE z+PKHX%l!J%hTizOO;4w?GK+cR(uxH|^pN4M#35G#um4@c7c%fzit^Vd|8Y@F0Uoaqe*CfQ<6JcCfk<`trMNc0qgM@UE4HZ0`{}y5o1; zS21rq>m_XcyvO59@}kyimeE={0vpuLH2!0m`SHE-^wK#_yLkp*g>3PDh3#KWm{^<( zmi7P&g`z+dHFaUtx|}9mMY$5kjG{74`j>fq;X5w5Jz&T zjH)vm()5$Bi@>YjSAd|8{mPuY8CEG_`C$u|HyeA$-S_GA-lO{Ch{rd+ie}#q*tepA zoUeJrNcN}qc5@2U1vyS?*!MfcAJBg+7Oe_X%L_rk+EtVuBA7z@(#zr0xgu;AZbK6% zf3g-5mUWqT&-Sh8A(5SOx?SViW8N#-Q?x&o}jbmR*EBWZO(@@Fpbj|Tm4 zn05Bq=XvF111!r~_p0{%8YR3AKy&}felsu&6yDiBzIMsN-nN_Y0)ZlRJQ35igjWTy z58e4)?r0=ewsexLHMEnoHWZ4#b`d)imRizxl#ep^=Tj`%<-&l#HY*ZOs1fn#);A22 z3oreBbosU7)xdAar*klZKO%ZxhE^MHp1Mg1R;rU9QLhgSp0PEY4trmc$4bVVE>EFq z#t%PyH;;-tmKBq@f8>hhB6q(H{qn)AI)u9^ywB?K_)h6siq}8#H=5xyB&$Zr(Z#<@ zeaC`p6VGOPtP(yF)USUj$gkB)%J>G!8^y@4NY=K~@4XNhcc5Z;F(PjdWDDj!{t_SK zhp+SrU`)r}r4YPuX-E3)>>i`ttjgY?fQqbZ5{nHcMyC(;+S zq4mC-ApFS&ycv;zrVl#PkZ;$dJ zIa;v#Hjf66#zt7ccDc(GY;~n=ZlB1~OT4RvYLOc_J0yTh@nml%eNIzO1RIEB!eYVoHLv8r>M;lQcF)>l*}1&9E6uL z1Fj2EM1JCuTn8jspE%PnM&#K0*y)3f+Dww3lE^3R2o)2ZB%N+i$PZ6=B#w|6eu5G? zWIRuhCQ_^;HLNjdJ*lC#Ci};q{%+cRCB2j>J;Do=rwhYuODIc4wEW{KdT(+UtAW4h z17Qp3(hpzVXgP0CiX@xH0+6LXE&GoI|1_PJ4j9GdL#BH@j}dTHiZ(>wbP5>meXEQT z#7YooGlS3hYkp&}&a({7k-bBAIv}EyG_$AHnWe5>w3EwV9biLMGJ5{|-RKtou{_Rm z%JK?ljD5B#ZdbT?FQ;(0tg47G-}Y9C(Ip{R7K8hbyvEgbE)Q*OZ_{5JAt9PDhr0xw%e$wIuf=NTW%j?-h2Lxb%T zWv^Yhh?If_t)H&m&JBHPw0Oxj%bZKLa%~Nep4S*IYJjHGk+l(;tdf`BndrHXKoX3P zVS?JRJ~h%VoT_*ZwmbfTCI6V?l{auALPdEl>dYM?CeG z1C7^<&`ceUG`m>#Y=Can{HQ_BCHDr{nYj8IyNBqVOKZ4$krPM|9c@NRRL(hi)tE0^ zFUyEO?4C)1Vb>SbEe~&ry~})X~`mrca>b98gCgjmHYRPaE8}khg0_hZDTi8=c;EJ!;uV^Y*&voa2^ZNCh^Bgb8G!lm5)Fp0Nv%Nlt z$APhLLi+3OQo?@j&4^|ulyK`mxrF{lGTkQHN(CwCFy+2P5_lQU#dni>rtfJ{$ksN| z0Bb>dcu(6K-`CEds?oTI5BuDX-1rv$Be7tH@9`f~8sM$`QPE*ll@FF9VQb6*~N6D2RL*3f!6 z_`J0r0o?~8oA6&%wOnV&fzdA^4*;>64QG!v3_Z7UfwGxIXFrEHF&cBf7oDYZIh`JS zlQ8oRqu2vU)~Q0s9@WwlBjP$eS9uyP_OC)#x&g7;RJI^gv(j0th~#~WRzLIlM1(?u z%cz7$)TM9?*m3xNb@!6s0ntppZ)oh}ZWZ@wI~l_s0%J%^N!d2fvBshivgB8B&ofQ@ z?@!o%d?QzNRTA&=*AC-I$ zyeW5CUtV+1t`T6_D6AyQw(4B zz*baBy6n^S8fC?`OY?OO)0G2`9pG%m>WSWi^z6gL5m!4#Y0-wofyO4%%K9fBT`(q} zm77k9%DfpiJQwxah7{((3*36&2z7w@>LvCRaRVY{$ltoqb-(rvhRG-N#YL4c4@Bgqkra$Zpx=R7+BU}c(xraa4#t;BUv!jS93!qN38*IFh-?-J*nOBo zFvp)Y``68iV!{Gp7N7#ios}s2HF3JJ62^^`4zc!40VPXYdIDpQR_->3f9=Y&!AdIB z>MKsgIpVJrR0a~0KGND3Gtu>W2DmiNTT_=V2wAz$So|1>3nN`k$SSd4LEQ{=8)P#x zSN2(`RNcLc>nFP~8kjqAhL?ZKu`A4e@=w@}pd`r@rcb5Q62K;&pjIGhq+PB4%R^8l zaj_L;j3K`QCldBN=m^gv^=e%Lw!SF~*iku4ktI$@#yX*PRt>Bw7eiL>e|bJw_fwYO z=;^j2@n1Xn%VSF@EElX2Mni8a$zQY4;nO;V9Y| zfu3l0M2oz~V+Rj`wRd&M8HODqSXWwk?8vLPpW;C!AdaJZ*DNOWYYS3h{8Pgv3gaxP zC!Y%(Q)U=F3Yaghdg@BMHV90LpYDe1R>d3|U1`7%v>)WHdfoVbH|grrp46et`X|F+ zDFrl>IX4w0gTgt;Idpxu8-hfaVI#nwfmgCzBEw`%63N0?sUkRJh&Y~q?Ev=QB)|S; zvRRQbIWx}u%H#syp!z^mU;_tn&a9+~ga}=3EoI@-B7VUfvP6AY|)&=G2 z@=NcojrL`KIiqi~zHIsE-2Sr9W~82NjvZiU18lazjkv`#PUY(ZdA+yAV6Ykb0!=WX4q0UXCWE zif{U@)A$cclSMMgh3O)zG!5-#*HXUqW0H~rw8=NUACa+X_7(mNx|JBpaXav%=#XP$ z_Q?A6PnXZvNNHH7>{8{eht5(%ftTq!j59=D+y0i?T8&ZD{_9dfz=Z11 z@`Chg#&1OD`9eQZm7*3(KOLDxke@~$l^)#Bi+`>6gsnB4K$P5JRq5xFHDT0{rn)&C zfcW&Ys$s0OO0ujnYLfp4gNDrck%fJRzg@xd?bx?9fKw}EyusSi4x*biixU}#3(idf z|9JEEgx#ty;wa>+;`G-m7^OUlwSqiolU_T=>5^r8E}oXE;j{9M6+CfdrTC!tH5QX-87`pJ$h+rLwl?YDU$OUQ1=apvDq9?rhOi!^dIJ zFTwrmu}9)Pmj{taGnKNJsl2VfJ)$`u7^$V1j=g{4HB{Fh-nZW`c^uhwZO8^^+V>Y3 zD07h{4AaMm#pNa3;i?HV3FgkffXjb@@Zus=b@G$jOw}^Ssk(51O##>*Vda zOu?@we5~+42_UY{pyEBI4|lfGh73fWv)wi%jh0uvJu%{`zc2S$Y%j6CA;zEk*|S3Xxmr6vww2&YZ%j{-){vu3py3a<-taT6n3X-RGU8s-qn=`G~&E z28A8g-U9~aPXy5t0<Nz{wGO-mnHhja2Y9 z^Jra+aq9`acp6FOe0o>T2YqBYcqP2JL*%?JCUWkY`UpB?6xkb>lQnx4eYr@~6`+tA zt#O34n{P8&(FF@tnwu_KLPIYi=KB^pStFo@KaV~4T;}_ZFAW?=AIQ2@%F5=ABp$!( z-mZJ{dE+aKftPw8c)Vr1yN%5U>gO|@UBD>pR?e~Xz&UEe(579mkGux*+6$U(=PqkU zkY!lhTc&M=zdlLg^Qm88WifNU?f!$eWLs?z$8qMkc|ibhiHx^oZX4&Wi`09N zxk>->FOn109NWV>88|7p`DbIpF5#%mM?d&}?U)+@t=z>L@NMI}Ti)CLOl)?_ASAU@ zb^L*7-CdgXCU2XY*=GYpq>i?lzTuvxT*?FENnh&6+S0EldegkJExL*rPlRF3fnMaA zJFTSnfHMxi!ycDGZB{+nBM)JUcb{*_#~nk96YA(&?~S7^=a1(~%jxM2(gHSMtO-HH zZwvI}k_*reL^mCNqlkg0In`X(uC4FQNf&Ll2)8^@qU`%g^UTNcduG`3{PgP7R*R0M z7tY)fvSIWlpiV&y(VHge)amjU zn-sSqWo{Opol1KAHS+VzmYmp{MCxl#{QBDO?e+IhG=qi$&>l~=7^Art-;`e1YUn2p z@s*$!g4(=`p|p^J_I=K<`^qSz!p#XC<>R>JyDW{R{c@yK0Un_aWk9H@+*5J$&SF zwmfWqH7$y8YYl3DuAe@zuRVqY%quy~F;-Ii5jLTPorKkx4vnK&`^x92`zQpJA8iLtXM zPQ;vg+}@P~auD)m#=EKW>>!6&^m${}oSuM4+_Roc&s6Gj&zbr9EN}Da>WgMY3AwV! zsQe_2$CVWkPnN!_G(*Qmf_oUMc}6Ry7}zY;v@xBle$i`Ckl7?5098nkws!L5$uu<7 z*j}_ykNlyT5S#MQ*7j^vbE8?S*eS4PQ8;ID$=s=UoTo|W6VE&ilh?*coHyFyyxnTv z!4%`)Y4MWyR6$zK>4AX$j%iB09;Ecz=BOorI(sS5Pz_D7&JT)$(X*LM1Pj}&?o;l3 z{^R7=4fCU+*`_>>;38)(?mP)n8;VU6OiA$myJ3Pq?>tGm(8~SbT}P&niQ9+wwARlk zaTDAzeno4sCDcq^mj#jT(TLYf)a-^XRPUb(9EdZ|(%-=EAG&PPMh2E%g1WM9SkuOQ#>@2|`&6YuJJua8FJh-_H$Fd+rW0vE#~-m;?IROd9N z3@y^Cp}&;JG~bU#olmMDlfDz0Sm_SSr}YEF97hKTOsz1*ej~U$>!ntUvO&uvaIviu zwNy!Kc=$`Zcj(ETUW;aq=8eGZZmUl~gOIa&hAx&ix&}f}7+H3e@+0Y>(1Q?aV}L>x0KJtxPz zRAj|y$P$7|5*~@D;O(xM5~yxr4WgElJ+xnXba#QoUS5liT;cs0+KQI&8aoodcB%z> zrfs@q{32;!IQaOdVjs`tDoGzS09m9Y15xYJxRUN%Kf0$@yttFrnAMV!m5(gxvqa}U zQt1%!tf63DltqH36nL!j7YC**1;0g5Q1&-ndoCJzTptI{RHezv89F3q>UbAt7^)35 zqH|fDDr+O=iG%%AtZb8W&*%bxJ4g1Nv*Oy?xBJpKLy;q~6`em1;~4$y-HJ)h`foIO z%qO8VS|t~USPD*lYlvxVKcxAOM035G5&(~B2fdyGLwa=^6LuNcoOq1yk)nYyG|sA0 zLGeMwo@rZ0krInGQ{=%}z+38Pf^UyCRNfB=F&NlKt%pfddG4BTWw|di{Cmq5tpKDO zjJb0JI1*M#K$d4sIrvLq28he(^FLN|POL-cBH^tn;)^V#d<~rr3fU=tU06%gJGFhi zwt`}(mk%KJ_J6q&zyeVIzOM0D%?-_~oL`u%oM4T(=jnR&>BlHVxxV&?be87o?JAD- zd`BYNiX{iSc<${EtD0b4d?IQebjO)f9_t@@*c1Enx)NfWeJ|>DvYgJ|&yNgjFaggM z_;(_8unDHE9UdQYE9^?Ub+WXY|NEgSC9}z9s`j2m$HRVpmPGt6-1%|_$8jB@z=plT z-N(YCJXD)<@v$fdT#mm@kPXxeUeJBB+--C&=VN)uTY%Y@3^+euH`okvt@QmFcK_Neb#SiJa)q}wL7Z6}P7)!GA${V#0T}&WskcfBz4}cg-rSGK_n8SLr){f;e}sXy zipZH9uWH6)Mx_WY#C4HX3aqkbpRe;MTI5vJ#ANXoA7W|nq0I&BwZL_nZ+JPeTZyCI zvd}I*FxyrH7H;IO7*xP{TkHEp6Or;)!3D6jkq@fA)sQU9(fNWBrVO}&5M6fw-EKP7 z;+tfCLmkp4fRgmW;RFe&S7AG|EItS_K#VG6l>K|~AyF5MA+_QoC}p-C6qUf?0jIm>0(SMt*$VLX&?z{+JOs_swf+ro)uw^HoVwJ#Vsq}a zyp9Q$7dxDD^W<0e9=%NZkEGwFO{6MTwsOXeWgUz$Uy{*4=~6l8KD+*f&L!s%cW(hc zE%Bo`rzXH_o{c$hRW~cBR}SyAXSs9jf(A6yIPJn<4vcEfmjxWPB3$;FrW<&ql>#ps zjBBgz4foPmfF!1pH*t{>09#x0gBA`d(ZTaeP%h|??bg=9I(0Vb{XN5~W6!Lf1@I+H z`95y5&cX#pFBrP4lAhAK-15-o3~i|mq*oTELHhUHb$bThhc_RA3nz&K8 zp_=)1j4!`(XW3G}UflN3r^%3-O%yENKCs?n)tvD4pCtCT`CL_GVotO_l%jnbi>wFR zwge4dH%iD*MPBoDZt?Qr>n_lQTx2NW#iQD>(nZk~?ze@PWZlq|t0&IcM(7F5)=p-5 z&RUar@)5li0jMaheN54@U$LQ=Qp~djt#Cx?MgTDz*M*x6m7w#3h|z7UZy)#mBbfzP zbLN{rpxdi7`=;<-{R)P2o!MJTPs|~_2=#tg9h9fouVM4r^9-C4q7fVvwcEz2U{g^W zU_{~d8A|4`kQea7xkVMB|8K}A#sP{EH&({vpsLe6uWRKk-l{py4M>El{OD&7PeEJd z88VR)C^5K=_f<}{8cB1bmnz{GsxHUKwcE|Q9lEjJ-F4U8xtnU|w$K8E`3*4ZwE{+} z=sM?0Ay};SP47@`bO>O&Yy?jl&V6P`i5^_8b?CRk7=-~l433{I<`Kl{2AkazZXwyr zvS36Zq;kC*R`bsEY|J2i-#lisqHir3)E8oaBPTGj(pKRIreTMBiR^(#fJ`IuyOn;z z{YzT{qmhhD(5Hv8S3g0Rvg3`Glmvx#uyaT<@$dY_BvCT>L#=YHy2NCBJ%>$X9E_Rg4eBE zv~~VSpTW6TB^vYZEm#Hj@>?Ek5l0AA=XKZlt-KW z5xvSnFU~#34y>T>E0y#8wN_9gamCR}x|Nv+MmTdytgI5K>bw)jahyxOc;d1Z4QQOK~b zL=te=Dna0zDig}9Njkh7__w4I;+QS}>F+2@XNbW@>rLa4s(ZbIH`WCfKdSLzkal>B zyqeUCHF9%tr&fuHS=He%l_XNU?I+7E-o-ivb9&jlfh&ZYvn6@MR|}hWSkk)mwLNFX;4G%Pwry-^d`~ZIApHBdbG^PH6h6W_V0h@#0>JQVB8W zU1jjCDv^HoU@O$-5~pU@pIeG}L*?+J^a=s3%^&s|;-UU2A99d+MiI79-Yt^j3nQAC z$0r&7mLI(n4fiCyt$47>u4<8aG4+59c z_p)z_Dhr*5-P!5^xKJ0wlmUWrZER;YN47?3+QD&Og#gV$tHnBEya`TGt*4w{JK2>E zA^&)jBNx7*wrN3M;A@loKwHg*wgaV{v+t^Fp`^ed`yWZ#ce$AiLeO<~v;5Udh&`K| z8+Cb+t!Gwm5A zSp)7-$UY zW5cDfZ@2o0M=va$Rzb7M?VKF{Uez|cE9ezc1drBISoTQ-lVmVl*&u^Y%z!3=@>4J? z!Aa>BTy@%d5>{3ls2>7Nx++sYLYgzl;V{ z2=Hr$`9b)4(xi?Z=MxIn%xr6qw;~?9t00q<-IF~}*^TNn9LG%CJ=j0TI&y_7M5Y2NV0{P>3gVqnz}R@@*| zz|3&QtSQ)i0#b`E>E}+o1ph)*7~2+Ij2wP8QeH#9%AGjtoTQXkKob+GAsz=cYok%i zH!$S|pf0z-6~iP#uVmKmgH#u?k+T4L(&XO35&k&|7DGi!OOG`ddf@L;_x)Bd5z1zr zpV}1Scw8UjKTr$?*2BB%v6^W?{q;$@!!a8`jw|3bg4CD6>U(U7cW7YzUzonBRA3x3 z_(laqn9Pwm5-lzc>xi8;-mKMfT?*K;V_2f*vDuZaR#;fuZb#nO5A<4g4wTAJ>`nW8 zlI;JzmqQ1FQ~`XlN0{a1zE|MS)lNcs$}CEKQXq5An*Lz1>`p^1+q+St?hH2HKHusQ z^MatqTR_UGZW7EOH2(%iH%9Ybko&HsTi}WQreZpMPQM>3{O^Ax5rNvFo@>I?0|#^M zRq;`)SMNj#BAD*1!Ykp(Qx(JZd4cubiGn;=nFqo@Hpw4)_F8o$>$KD~d_&EuJ?+~} zpaHWRvX3X#33jyVSiLUWwCv4(t)kOEuN1VXn5tAi zy&)-P5B;TfqxI)Mp=oXC!<3f;goB7m!U ztT_W5j8%nyxXkXPZv1gu)rjmOW=p{eWx+VpAs1|vE;uxw?kv`Fb}S8WEgc49c*$;o-rPN%fx4)ZeQ=FgJw-rXxCD%y7c4I`ed>ffv>2M%P+6)Y0Yn!Kr736Jko!Xo z=AOYYQmzE~t&BVl>Gox=n8UqpN%>nfbdu@!ne9H%$JGY1dnr0AlJ$7b?WWE1OvhUX zaSUmRnd>lXxW&QKoLFSf`c|=T2xe2M>%B>q^RWTH(`Pkq>cvqSUp>4_{>Za`dnVh3 zCnQKbtq={fs>L$R2_2l4STN2ze$UHmsuHjnvd*mFMg*^CrP$vm#+(Uqutn^LNK1N2jy*qlOYh z*uukPR&7?%E{Gn+JMJrqR-7OPa{z>R>Pbk9T?tG`E^xtqpViwd8B6 z1stN*Ma;R(2;Hn4y56`Ab{f^dz?ee<+nU9LE>ppRC<1UI_%n7;bjGb&A5#%_(KL)E zJkNUlU*BTk<2>7bS1p;^>D3U4qT~>oK;;tITc+}%O3Q1m9=_B{kB3(8anJv_z|6Im zBi4@MstxehMTonQptn=O(rZm@(LTdUA0mP@#OV}PIm=c&_^6& zahd*KFtr_S|Ek}^F6dfs_G@L|G2%xi^@7ct+ukFu0t9oG@s`IEh80OJS_@1My2XS{ zdD#zp?);CVtBz~xecLD^A|Q&=EeI;z%@hShM3nA{AT=6k*c7B=0umxH6cNeM-6;}7 zYIKc;!A1@i-}n69zqhl`KIc5=exCcfujoW6)5`yJDK~qQP?oRW36KK$Vqv$`Lau(# zBfJ$20T!tC(UW(cDp%_tQ8|9GUo)L&KijoHaJSutz%Ek+02Qm`s%nYeu;bXQYJ8En z9(%+y87eW_Hz8&XlmS4&-vap`v6lf*$Cl6ck{h{pq5Km+8>A)D7%K$+Mp9Mm9wsm( z=Ux>BT4fi5_-G8W-yPgb7gPkuMxWb7t)6!eh&g)|Cf%{Gz>ITGDY@42=;$BIWm(Gk za!LRw@wNU~w_3Jvi}aDl0?&S4+rJ8Z^-K`qz1WsOMyNdjxIni;t^EO6G5R@9C`;@t z^>Jxm(rlbCi#z6JJb#up{|CBTbq4ZleAS+v3*gg80&(TwinH%bZz%*Nqm$n!=XPhny{hLY1sOy0r2iZGzsM%l1 zE{80&ziFSb^qhIJ!dkn0g!u-@)Ldp;hf+IvhVyki?Qct>Os%TT3H9qybvnG8}Hdr-Y!PR=?(&Nxx7YhnJQx&$#(5s9VnKoB?m zlwThRM8C{cl1N!F+dTZif8N8uO`V#QcEld%H+$-pHmT>@g0fbvuM{66S2jJUzsXj6-~{W<0pr|Jw#GF*nrXUJnH| zH&9p>DS);YXVj{=y7=wqEYR!Tpa{AIhvm$HxBWRcGV8nsRWSS-lQJJ`9Q=<_@_o=b zKyz&nNl^*Z>AoiCrx<@L# ziYyM_YxtA%+SiKzGH=Z8D#Ud?yC>6guKvW^Iw3;p3P=Bo5Hmfz>kb!+v!v6*{Vws2 zfIw##gWqN@1Tnt1J|Pc*Kz!MA1MbR-Vzu7L(1{d2zvvyporNGYP8*j(#ac2}&>w1> z2SXZxc%(Uz#(MK8^1ZZ7Gw-11asQnc`2ic)?bFC zo*?sn;%k?>4Wi8~^%vnQ@d%bV%YM#XRzeee59K>5rD{?2tix7%K)F16S+R@f_G&*i zW7(zF>V7Q^Wog3Q1G||ZXErt3^PTze=;xtl9t}GNEGtKUrF_2DvEA4;Hbxu$13U%5 zl?xeNFnSk6&DF}%;egWlK#;{GR<1tshmGn`(ldLQ6t8YP^f6`=+0g!`IY>>K$U=G; zH#5X0L-3f|Q~6~zoc1K;((a%W@8kww-Ntv%Wy$HY)$#&Q2G(RNf_n`_$$dh|f7L>h z<@G3IX>f>{LGk~rrJISo1lmp*gS?GoS5x9rbSYL|^41Pb%oz5H>a#kND_;lZ(0L1g zl#n?+#znSt19L{l^SoDcIsb)Eb!#Y7x@=jayD6%8a$EDNoD~n@>1c?_d&~X2&VT2kxMwUX0qht#!1Q24T9ilU-N_{=LV!c<>0BW zfc-eozIIT3pk=|=_e&D)vGoo6E}zy(!kizpxED_M`3e4Xg})zNkONEoaY_#G@lKAr z7^Ga2Eg+z^fyG|FGX=Y~UlWhwG$#o(*0pB5NRq3Q7C(*QH@@~e@0vdY4Ye}TNf=Jl z^axOc3bjwJeG!9y{*US{7al?klCz4?DzccXs@#rFc*8VA@jKydz12nlW4^t36QnC4 zMJF_nfEh15m&e!def%aWbi7A3+e7-|Z*L6LQf9 z^4SzS_CC5JW5k4+$GxK08%nrfV`UUU3|oH+tke}QG$XOn<-SIju03GBInpLcuxL91 zICPU$2afd(braWPqUI4MR ziiqp)=cX%svb3gFRW=>~Y|t%}j+{!6Y9FTyfB+osz2kUxD%kcReAA%Cm`agP&gUL90Qy%YrjnfhK8C~R4CWCNY=`~z`eL>1 z1o*zGZI0P#0KP6K#i4#Ozbp8Xml3d^BF&`Yyqpjp+U28Qowj*a2QyCE4TcSMb#?50 zUaXG*iSA=i4%D5Xfq##PTV!n;igqk=o<9~GdZn`o{sqGsPHUGD87*JLH$qWRrutN$ z{d~}4a7I(X*)*G0!%I%CX)DTk+Dll@yL5>DkLqg_Jl}Vq=Dv)j3=r)?t*Vm!!+y=~ zXi*cpL_EnEpvW+B3_-husI;4$U0FAYeRY&@HSy#2zUVr9x_HYR;5|QSd3<$WzD!1! zEw=!B4}%v6n}B=jLDxub@XvK;>Gf7Rewydy+@bC!vdvcxo{|jEPFai^*NmP1WNB^Y z(t^j@n#f=GDJcBz@(8jyKIifkn|#&hHM;N-&{UfNA%f`?6k8h~R`}k`Fc17Bg&m40 z3sb~t5{W9C%ZUT-fJ!8Brp+oEK;^X1WvVK==e#EJ#HFtnf;812xSFrC3Mzl0nBfHE z%m6y(B&Ctd0=AcGkg*uiig_^yj9U3Wvd%8JtVxxX{N3-Hhqt%8?=}9gRXdmHBbIYT zqJRG>)2zaUt=a(gtq9K_F;FJU+WM?XwYu6MLHCOW4RA1z)WeHiYIGc1b-Oi6ciulP ze_@Z)F(Z2J>ESlkuUd+RL=GsgNX`65^{2fI>IEHPU1_04=9f=R%F%CnAcB>Blt?sT z=fr!vEb0A?>fDa|sd%>vm?)88hER?w zt5(l`!?vY)f}?K@d^N=!V+&h0eglg1k!5^1O;kPQYFq$bh3ivkR%=rv1vI}uc}+}b z2WEL;WC_DchPU4;JD(qP_W>>YT1$iew!I)nogfG!OVLL;OL!SvSv_xigBlEBc`?^@ z);_0khY-o2+w7m=-0a14mq`kEpN7nMRWTFTPF*EAs1bBD01}j6g|kFir=!(zoR2_S zCG8B=ik(Zb{`uYySPD?!{qua_t&o3$d2d(NqM3RA1Ft6WcM>Xvx=;aUm)+-M**?=S_R{9p#V%mHZd}P!G8Ng@N{^%5{+HH6 zQ%D_B{k%YD(<)cBI(%}usn#ak|9bD*Us4IWJg9@To0(Qa&aZUUphO9ck-j)skzMNmUwI5vb&w=^v(l&f)-ao^at;8zQk7*V@t0Z zayKx4>*x2@U)kLS!eOz!*3}>1)mnY2ahcBksgKAE{x~t_6Hxhn?`>eaZPbxgV{IIL ztL~SesO>00cEOJa@O2whxaTV3W!=Y*x@BT#z4J0k2gmZ!tKWTZBn5pu<LZpj$ zU#oMRQc#*s9R${GT4;*f8@rtMP1o)0WR<_33ZwQPRk0|xT@6DktJTb^k|6u_a*WW8 z;p7fl(rIZ$Fa`o9wkP8o)P1=v!N&*xQRzBu3`cz|C%q$}i!1^iSsSNLcr64OJ!bsOln4}?NLx+eTZi5EpS*B)*3gk$>H08Eqh=zr^|IyfiBa z?S&DILLL5L1#@?8&^66L4u_w{T#|btV?;O}paINs3Q^e~BYzOnUfmXpbUn-9EYA-T z|2rHY#g#2oT9TVX6ZAFVo|@W^^zHoTU&yuBzqstLj-8kA*1tC=y~1Cb|H(1|DBq|U680UaEhpm8QVi>Tkw68hdOtGB@K^iuY+Kl{LeZP+T^!P`;f zoLVZ@LpVH}V1S%;9vQhP*$oQMhcY-LdB>jlIh(*7LC1gJlu?kNI z)Fp7BeuB+w`8ez2eom64scz>;zG`!yVwMe?&fv1~(dBl!T-)xDNxDr`C&`YtfLGlu z#X(m1P+M1PeHRHaY=BHZXWiJ{40XM2%fM$eP=najsY+sY{1npB@SAmuio4Zm3>g`JR1m0PNm_t=U6*K8sav7RrIcr_Ds52**j^0=we%gWjMpgtVULR z%xdnP&((GxG!{wdqAk{|1Nvp9QkU*4yM-<&%VXZM9dM~PF?-Mn{z%_fO|*KWEXu2Q z_rnbhM5Io>OF*#AT83%+b`c#z-&1Ru`d8Yj-lk&tTDU*e5cdp5;bn7DcSLFf6d)eG zc$?w#H2}#c^yvYKmL<+#(Q8A_Zz5%NV^P$~d8q8+i+gF6>-Trqc!JMX+g=l*LQyf$ zd*0nSnuf|K9RO=nEpo-4HhWf;2*o}6ym=*r8Z1fDMR@iw`qET+ZZMPvhi<6jTcr++Yx z+H4dW%ivBnm3W;^LgIx>pv%?@LCY?pfP4VKk?6GBS~v)M1F*?bFt-4gL4DI>XN1O2Bh zgd=_vqK)8Zi9%WFS?PM8J3>Fc^U<+wKsq(oj2?Jml-j&0ysBNe0VBgOM-^!O^~ z|5h#+myc3u**EGaU!W61HE~02Y!mM2zs^{RmVvT(l{R~)dr-ShC8QO-p#kR(k&j+kUo{(Z|{>P9Y@4Ku?n5MT+vOkA41(XkV zgRYCI@!);Njqp=nz2t0EUvgqx0>F*#cAdOhO}XDW{(7i!J65z63NCKamRjk#i20Ad zb2#i4UK4G#+AyQ~;9r#{IMocA@qID|G$imQ^)}@1{(?C%**{eSKI!azEIo`IJ-hhYh;|=8E_6NuPy~BJfrrDa1MbR;Nj=P~;FnV+P0u#G*Bh^Tnes!j@ zAdinL?TjQHT-b0yu1v=GGJU2%`sSeb;nhq1f4TUN7F)*9#z&=Tg7h_;LTi|M z`E5{DG@Ku&*rWc=qb2rznUz*!Rb!1Gc(HBxU&8`*RP7wykX|W?Y#s@bN~=4RIdSN z7Ux!*nNW1c@Z4vn7#o@A!7jxYdJG7xPx4ns3V!y6_<0v}{bO^Vo1oEsm2{O>(ahwUa#0qv=s$f&3FK3I1c&%!dW#1YX z91v#3XR=PT&(t{z+zoQCuQS8<12#mxM5$1A&xE>*_UM{n4YU=nNL^h`ZTmEwFUrtN z+)oahP}0ZZ4+OBVsxULD5^V!To)X-NF9_A$6z&?LaP*G_dOh=jg)fh&l!3?Odtk9r z5M0JXZ=Aps0i;!}c*b+Sto-ppY zmhYl3@QUFrG!Wd_A-#ZoKw+1eQe`Xu) zr!OUK@dbnC~Wa4 z9yapQ$RL;zU+{5;#R*SHh@U9I)7_9(>9BoPiax4nGsWSe< z2%A|djNi{4@7g(zxuf?7aMU4WWwU=lXT`M8H^k*CJRl(KQp8AmonG|EfXkM`o=w4P zNsjRluN2rTs=;r_Es@yA!E#LH?PVB&UWXUSMtO}?4_h=&lU*_5M$HifFcaKw=v z?8K(x(Vw4Y((307%vfQaRuu6-PYg76MKW52S!dN9I3$|k-je6`^`c(!(@$AN(;)AzxW&5YfN%|>I329|>Ee0N^hho|y z%FZ*!mkXEFy>~69n#x8}cGYuGGZNQpAd=}-?pJOGzR>|vc@OsXP&r@H%KhnW9Ju#O#&p77QJC{RzEa|`iXiU!J8l=+rf`QD{*9y%m|Wi01%`ooEj zNN&}s{mtbydRIc?zcgjexnP+xR6PZrM?`^*4hC43F@8VB)Va(3%UGKc02i#&mSn{n zPoxQpe=$+!$47WkTY1H2&TchD&afvxJix9pwKoirPY~RLGXKM_v@~ngkFS`R)!TY= zBKf3_PmtZHxsd)@e!>s<@*EHN86kw`U&FuS!7}Y2_g`Nv0){?*lRgm48@YtxKpGHg ziAIYguP{k)L%cZ-9xMLZbZ1{3ie3FuoNz~%{S!6$gJ^e;ofEg(+vJmfD3`u!AEzC& zem=&)h*v;F86-^n*u_afCWA8@YtQ^^%shjGWBJCj-Kk2@ax}n@!Pk>mReMz;*U}A_ z^l|B%*sCLcS@TM5|BQ=eQm)Y$z`EKMH10tcY$bu9HT5T*mS?f>7&YnNSH9?nhR6F*q(ynjIpkBx`(i8Y}-v6Azqt~2E!Xg1>CWBK6l`BpkXEd zR`%q+TV**PRS!+&-i;QQT|Ss;?m;*ZQgJ%3-ERB+c-p8wM?5*dN2ZVIS z-x`xnfqA?Lp`c!fyfNXDUgj+7?L}i$UtCVV_&F+}tr=Z!NrEO(3z@!F zcs~Dd^CQ}eQakWvd-!(fFmV78O!=!4{}{lrZsT5H9R0+3N9*=K=`vFT3um>hqkeel zBZa@i{N4K{C905EFVOKrLj1CAja>Vj=yETmyGjod$j7%gc#WHU2R+Bjlu($^5>l9^ z>M*wXNhhgU$h3dkx-tIjUmKVt{FOi^KI*DlVj=1BrA1NZ8bv@0vVc%EGK4Ao09G9; zid)t32k({nhj{6joG-M0i#uGJ{ngWtM_Kbr$v`C5E{*Emx9cZeZ)JPd#7tmuI1hJO z6cbH&fAzi(jAwHDK$rKum5IfL5u_*{Hj8`_?XTbCu1vML?k)}cO7S}fo(PTTczY$t zIkAT+Yn=PW&c5`8+19)CAMOA@z>{jB(c0E>y1ONt^+;fbl+p$`D27+LwA&FbP=jyg z1u*4kK8&p`BZbmmlnK$??`wa7T?mF?sVEfiu1Sj$)h2+G$lNss%UDN!`r&T&o`v{>EMM5&agv!)gQ zQSGOOJcMCYP`pUdZ_7|tpn{oIK-yz89*&2n6JNF$y1gEt`GP$94E1B{h!0|01+4cE zucg@B-M`9<|5KI#1?GALV@P|we)?AdhWbUZe=Is@?PKC;)dUYxacWnS2D$l1| zrCvH=#dH0f33iY8na1U}UO{(`)n=FYWcR~BL`ASd8SyGX!O!!-@xpVk6nu5=GU!;o z8z>LYmLW*CuY>n3j@1>m?1J~}fWx5_`=0o%J5!0lo2o@_IoO5Sw>Z`2OLIyDcn zXSPzG{XKN$WvOozXOw& zu%Jq0RBgfgBQ@z&PTJaI_3BlUQVV-F=FhzoqV#)w zvo}d6Mfc#?%Zm@_Z;S3r_hhnTN$r-^_IWymPp4egG>GQ=hEFqwEvRp3hgCgvmo=`c z{XFPu&&YmVkL5h(R37hlf514HFNCwg%Vzjn82EYAZN`xtucLw6539YyX<6U}ggSAL zF4dfr8NwM-c%Ky}o(7mDNjS8M7=sw&%J-hn?n>!^|9s~uEpP;i=T1-NU zTy2>qZ+XgXvauct2Kn?^eLpLYKefvZYN7eLV@AW~k-?q8^k6RPA=A-tfE~sGrOLcN zg6g!A%Mo%p(yiO^tRJ}ZqQRbd?%B6?X2w7fY#JT~CL>4!-(A}{h(Z~hNqO#$aUrJJ zk<&xhX2$o?Re?r^fS*D{Z|c$?E7-ILj^69T+;i=vsj9*zX(u{fxQgqPqA?IggsC}XaEMDh`0lDExY}E6e(9iHdNJ%ka=vEUn4EI=C&kAgW*m% zUOSUNgY8Y?_QB!pmqPF5frHo-|3xKX(fdD3QG)i{pP6{JS96LAyzThO`A)X>Nd|Gj zGNGl>L=`rkEQR_=i)Z@tDffPm^%a{c}@isQ($Q>0F_fgr{sI)d0Z_X3N$)zQ9U z>!SAI1^4DX@52*$ys}1`y}Nop0LWFZH`u>;$`%aN<#lZ%U%{kH;^a|SMtgxFo;L3I z;PUKXNYP?Jwt{Q@?G&yv>ElHL-+k_rvvQ*zFeg^54deo2$odQLK5x!yU>_M&|6#h* za6RvK3(FNCR{2^hzC9kwO?rSg>sGh(^w1Cr%(v3#p*h!pm}mZDH5~3wAFR3jj5rb~ zD1Z9JYsB@yerIPj$mT~_D$kHvdd0jIif4jNx^&dKXD-FTZDq3QrjDlcVi`f=tvqo; zgqkP=zysi8<&s1K2-irt9m@5uMWLxBci{P{DtRRzjd5G;`q;OVp0Evxn$ZGB26gie$LV)k$(*oJq%WjO=vvTh^MHZ6kWP*|=PyMQ20P4pB*d>}vXWTI=S@uMRco7vQ^( zJ2E|#MRTuhG~=XF(z4WsLRa$|U;3Jz*G8rp*HArh7*R~ZG?0!=Ni;Obu7kv2aAJ$% zX$cRcGq|9w`InZTjNWzKD{ZcHR3`I{uWqOmDGwVqhz$^e20J71VYgR_3!+X9=vOO zH}qP}P9{g!C>Njt`;i-rFle}+{_Vb5)~_q=QMQbqc6hmPfk*B%Ysv1cyI!L?M+&3Q z5>yI>ve6GPDE2KV2l<_Muq?r!uP(6uY1E@z5|Y@}kpm0bW7MYYe^hv;PENn}xE2sy-BA{#{{~Vn zp*&m|o9)*dunHA7(2gHPVr*Vc){J$nPGnkb7|CQ)Q?!CalGfv#lF!7N|ID*xO9H(` zL9k~+3w8c>8(VS`0WBqm3gC0LZ{R!I9d1AeogPHcDlN6c2icI)@LkKH*3OB8?kE@Of zpcZ+E{+-1X@u||caU+4h;#SvsSbp3}EHevyc$dR=A;*#U;o~T8YrABqnPIzV+;lW+p^sIo%9%0#Wj>^^MiHY=HE+NG zH5SQJeSD@wk~Oj=Nj1bx%gz_(E<9fAPo9ojB=}swB0?FO%JYQaSi|R~M47{?o6kK2 zQ!ZJ&!pE%e?Tcm2%>!}iQb?ECj`bjm`0pRD= zrBCh+=d_mfL=8Ai3#PwR5@PJ|wa#WeyV%2Xeci~iioLEQ^dW6tIvYxxS6&b*V9$-% zhB-I3ep-3wV*Y_P55*$m?k0F3IYYfF@`D`Hho0=c1JFibQeE^LlfQ~d6)FV zMYT^#!=(RpZ>Sj_yvkdMw_nYzUd^$4V`I=G%QZ7bL(f^+PrdzFl&QCAzj~F2-^KnF zg2?+Iak^56JgEPHAt689OB!pqBii(4>Arxm1Mk&4W*W@lksD`DPi-b+X7{bQVhSU} ztsm{+DqdMQ(29O|%z-*8V~Vl-lq*N!=gKG}s>( z=wYr2Pa^l^w(Pu2P8b0mf#aW*xP&Cxkw?6>^UyJHbvb-l^xGhhqDgZ@?Ck8OUh1%g zyF^dq@Sh|T#~aBXS~3=npysPT$;A?CQ~pX!;pvonxP+#Rm=^W#fiRQ0o4N%6w0~=u zgPB1()+*orLw9mh+Xy)Ycy=DLBtq;+AB&=jtk|2WWCi7`dY6xs| zg5eqiK`t)bSEp#=P1!203O^DAwa|@*mNsCLPL9q?`~+Nd>?i*OjNr6E@Q$0ry=nBrgihm?NB8=?ly$|f(7s1egIRdX6(rrz5SX+YvH3>9V=n3Ia2Ox{+X`~;yl4hIhyl}p2ZS44>)Ftw z=UMNHpZ`^>HPL-))oFJY@Fr?F^b$V)R2{9sykOW}5ZfoRy5_yI?rFW#x9El-p)dIJ7@&-fcwVB`NqsZTl~DI(zl zt&0=I(7-#|Id@d1@(YH`=4@PK&Ew_Y6g~L-TYx}ppjG3oJ)Qi8dA?hTm-^)C;#?lg zw0BPAafQ{kcYfusnz9=O!8G#iPyZ~uO?7b1%UGy~xGSkAg^4EMGm<>ka2u&B3ocJja3oGhZfZ(j&0B4d_i?II7RQHyj$`E4 zFU5)nZvK-@cz4Km2QKN z)z+4!mq6UMim6O}xkK6vWow%bRgzBBXfYv64BJ`jWr>?Jg6Qu_q7^sL4aRPtL~<`} z<~DGY7wRokJ8|daWXUmg=a}{Ja!g7Z?Nyj>!`GsNr26z5-?U{iCJ?j80Oc*hk!|mP ztlu!pdE68bPq5A|p4*h3Hh#!*u2UTN7xUy_O||!p;>FuxFIgtb`D51-Z&qb+^R?UH&3-A*RlFaBKkm46VeX(C*rx=a%^6a)8E!(5%T&KVP z&f{^mBw#tL95;4v=L+ZEo;zSsB5Of^xLSUOV*f7lMnE1a2ojPrUlGt zQ`?dH8Q#GsMI{d==5&l4XX4pGysahNDk7gka@77{>WAKLQZ9;y&@ErL);+fKkE&1c z9|%UL>r*1DvYr7DKZJvW$O*CtWamg^!>(Y&)gAsW&b)}9Q^kC~1Xw(xM*y0tIvC^(XMAZP^?UD%fRuOAn4|xot4dg8Z)WT8J^cM~sCpnz(h`A^JJ~_q_6in_0T;J1`AWvN zNo|X@#It*xCZpYutS~viEHk8YL`@do{p^U!Y&_I3HfdV+H)twfGPrilC38jyH3|TW z35(!vP{Kvfeb$jv(KH?}Q|nE>GJg=~3eZ`WoYE#RmX4xeju@V6AKH)pAB0dY%Np|yx~ zo}j)sL`RTktVob!gH!rR!8&q-oZc!i)np7SMYsDI(0Xk>IYOGKv!zh(VH0bhNS|wqrpki=# zyUA^uo2Tn09hnjOYR8n0Hc|e*%=fYr-Eoai&$~%vNW3^N#V@{cB6C8)Sqth+3BhTL5l}-pZN!AwX^kRTtj-Rg%A>*W7%bUWwSYz~mRZwNUH2Laiy2DtdggNv8{U z;Gowv^bMQ-rc&O7jV7Uw*cOgeB5SAy0;#3yT@n8JW;Wk@^22^?nnRN!HUlESk>Hx{ zaYb$#$l5C`6GCYOn>rIAJ+mLekCqiG@{n@{ zW64LO4hwxi2VMvOkr@@70e$q4kALOrsSpQY<6~_pgpa@DVKYn1Cb)Mq?}FYA>3R7t z@dH4fJJY7^ZQzoQ%0o31o-x><=#QT_h~M7=j<&Y`oX_P`)z!3XQWul->Rujf8coi%@rhxv)uUe-NmS9q)(0ANWp5o3X^IW*@i7{rgVTY?uUw z0KB)d*Ul9(r2Qx>j{PfOe#)QV+kp}KO&{RWp7I&5etO50ioAtg?rLknL&}Q)7WHJs zT)IM5d85N%0>+U9Q#r>_=qeqv4&wJ}CQXjqJWk>p)7nl^6)Q|zIvDF|EoWVpgtJ1{ zB*^mBd@{lv=ZXG3cms7ZY<~R zI87aJH09=(+zg>I)U&h^IMVm>DLEs`mT>R#Gb4~6&(9vjXsSKQaZSpNK z>eg21S6qBIY!)cA0O{!Jb_C?oGKs}Y9H7YY6xJ8Nc?3p1Yo_Lv$qPMY zq{Rk^h3SS)7#C5L?^a;$$$^^rySMb#kLewbKf6&EE<@)2qk6ObV+yKl8|b+F)V;-X zrB&zYXQ}08tLh%e_8oz|SVDD%^!5#3?8*-$>* z`--J`+igqh(b@aSzbtY>TW~iPW<=yeY;hBdym+nC96Gqe?aUu=&%Y5#ncHLEXv5LZ zTVzl|%(0qJ@>lzB98X7b;&e#Bv^{8p7m`eQ#dXbU32C?oAH`K>kj zQL(OI7WY~$%}Yr=?u?%Ha1i5r$0m2x$K$qBQ=`vp<-TMm4>0`9Ro=D9=;je#(kAGJ zrgy8nt4*)Z;85g>*RoTn5V_g zy&OZd%>Vc=>U#3r2hhXqZvEB~F%x;7>l##F&CEVC*b@~F(zb#AwJK|{<%_Q5U~0e7 zXjVNi$#b6CH(@!YSH+r_pEet!TwkLsd92te>|pe%BrUotyW@!3$roAaq(o4lfb`bK$@q11c|w zf@RoHuvx!5o^XuZ7xl zo}yweQU|#d=s(eCCk2*UoPZSAC?Wbi%#3!R~`!z4r3Os^Yy9KgE(N?}qNJzCe88U5Xx}sN-9+c^{6G z^nV13Vqw=q`3GDI&3DqVo=yR(eKSGqc0c`-1F}~|3{@h1-=pqZ$m}N^A?*5)5r~OVyg`fK% z>esDlJCO=S@`p@OSGLxTkn^(P7_W;_aJsPjYiAL~C$zE46cx3|5qTma$io zmh5ud0@QPG<6&jz`6H{UrNxD0fb>%!n3Y<5vOR1Us>!f;ebS^2^GN$~KkrHL85Q-> z4W(TG3{R>AMbz?1ANH|PQD-y$YSr+A@aaHA*)*5}nYytnj7m(5Th-7tj(6$9JTq_+ zG3_wX_VG}@Jk0=D7bvf|FPNVF;(|PTgo-%xb(y7U&)iUI`FW$DYKZAkKDuz-HqG7Z zk{D=mdHv|HPQ4$Rc2v96a-zN`Q*)~jRkP#Rm;*!k`XI;F+b zQvY-E8DrgR0JG6J@mHQY(rids)HSzy;K<;xG z*nWUdiEDhYd-L{1wdkD7(=@);XSW%c)TOatMZ(}&7Nt6E+LU4#!A|9&1RSESU%Z^+ zX$lP&t1(~IW)}ov*}#f{d2}t{(su(o4R+Z?^!JEw&2C4Pf7OZ=lyeL@QYeehM&>dL z67x9v4=&ND;L$u`kU11q2jAaAk)TXWxvPPw#!q|0e9#JqU$N%JK|WW`D`c45+;>E~ zY8z~A-irE}e*Kvym?Wh$ve#+sgcbr!TcK3A1Q1=zCHx)2$Ux0pNnmK*xCHudV@uAL znB>RjaU~x9qMe5w>fSbKt}d0!COs+!DYu%PAb7=<0uuB_J>E2bd4X3O|I{5~$Gv-Z z!MvC}WBCFLuagRd9`6nB)`r2<3|y z+-{Er*OwLcA_j|o<-Z^dR-!Ezw=k zNO>*`*3kFSuPVM&tH@T|ikN=GdC0`6Oy?z&$KXvqiaQ3~+*uTCjeB%DD<{gl9F>-I z!@D2({BViIAb>qZnH2T)&nx3&kx}DE#ZCFL%k^EY2B#kg$e#p*=eM*%iu5bK@))^6 z25-_)Rak#kNsSx%rkU|{(odCR=IN9ov*keJbM6urHNH0*(HfJL_$M8w&`UtF4^84; zfZ46Nf|cglMhyoa?puE`c3{_`iE^Qdds&nVy>GFSg-0GdP?v&%!A6u(!c^kYQ=i%} zeH}+Dt9RaK;ut~dl;_vmmt#pH?yG|jNPh0W!30NH{-&zR&)vR08;a)Py5afEEcUrl zg?IolN>O|5C3ibePCrg8au%DdRbwAWIlYEzgFM8%Qkd16cp&=I%=_FYJg=mDdAd*i z(Y9sTU1mb^zilW-l?Gex4x8qw;M1f( zmj!s91N>v*eak0)@!wiC%mu7syj?_cZbb`1vYj*tEMvlTX9Auw1?zp@^Jk^%dYX=@ zE?gehI$s%-DSpCN>PqifIP6D%ZIgYr zg!Bjp@gRm~B*=X9&v(iLX9zc%B-pC-_0aVH{-fSmUW_+ic-FWfSWk_lxg)VX(@j@5wl@c5sY%ijbmAwNh^$FGZMQh6D>$@b*0pJpk`YAJ zIJSpzko5HF@%Z@Bi{+ZK(2YFD#wDit#opp;a@%EP9_1az4PQTW@?14IJS*}D^$iJG zU2r_F`84*fsxCp!D_#26*q!XuNjQj7yo?MmdSc#YVn<;gs|C~SUU<|7{F+}jKzpIyN^2@A5VINojQp&Y?}C_Z{<6-e4}c1m-|^}=TN>) zi{|qVz4xD-;XglC5Ao6WK}XcfKFL=oe7_eA-ur-l}WIxous32pzevQb}WK2%*Nk;Jw8FaVs88D7{2kxGHrw9_B)DarWEQ? zZ7|au*aFYiB@XE*$qNK`Yp~*Z8{L1;OXN7-T9^=XZlI+WFjIqFLo%0N_H*_1nc;g9 zF)Z*{_3z#h6Z_(?0z7q3#%rg6=1GyPn~%9=*WTP3znb^2#l&+({k3Ga_E?uRbJPB{ zdEcecNvrjnkf!kEh||LDxorF01gHt5o0p>&pF_qbeBMp94MV7eT3XYKr9OW~B_;Sv z5z22iHL16=Z2;k=(QO>Aw$~yQ;m&%p@Vv@6O7p2M{}+I$#iV=PCO#Y#@a1#yo%x`H zaTX*|n&ecA^XzXB8CR3!i!(B*r=Z}vQ`DFLtvmQ1y?ys^S({c<$~yPaNPRfWaS1?3 zZjmHQE!um;Jv%`+_x`=Btip@`u$;3|&=!=S>IgXKa++WmQ>bVOA3BDEH2S`zC68a1chP+*V)xsIFXI0wx(dIhzBdel zA|aqiNlZdeDFNvih!P@9xO#=4W(zyHc6eHGtD5NQ6w-RRWY(V?euNTelLV>-F3ei ziOhF&uTSR9AT;nrD!OTYDYzj!12X&M6v!7)HLwSv#XbSCYOa-8y}^4suTsadQA;MT z7B)A0l5T2U;@i}ydY#iq@{X=a40>ioyp!7~1;!YTg0_h!LC+c!cfmDYDFCcBr0iMX zS9hd=WsL96MLKxnWNcDzK%B+;Yr_-1Z-54o$XyWv8~@XG0^|Cc2j9L5W_ec;;{MZ- zhafQ9aWyT%k$96YMHejGc6%@}3?}#H1p3Vr%6K9cGaf=^6foWSK0W|MlPAZ)~l9*`D3>tzN)L#kUzs|xY(9~yQ>(?T$ zs|{_hS9r*Y_CU2CHt)C$Pi&F;iGj@y3cF96&fRfuC#z+yoF?8wKY1&3Fm6$opnYbg zy`Uvach1D94tCDN9VAQUT$vH{CQ*nw{Ofhst3i(ufC@)Q$A91MwBnS1aRni05>&Q| z{6N8wGl@%7NdwSagvIPJUGxDV(E*hK9Ca8iK#h&slsYggw??g4Y3*Ozrt0srg9D@B5Jo#9*D zGGENP<7t|N48h&f`HIwOZ4`Hk=_;$XZ!E0a9wnR_0f)pg{7TWdHU&2JKrVimWU`!s zrGvgI1)xO6;`d#nh^!X?&7aJ<6upoN1`;&^7K`XM2m{zN+LWpFUFRt>?wTbmWU=}A z9Yphxz}L}*L4nw6ZCTeY#Qlzx&)y~rWR)Jq0PJvDzh95MjmV~_s$aUr4L*e5v6w;)-1?eCVQW0aKn`^j5bBO(SA zJd3E^>9SY=j_6Qoffs-8gtoCwEm7h!)vUG=J?$cGa*jO<>!Z>1&Z7%V9~~&R3|zO( zr75l!{5fXP0Z(!x3I`tHUlqL0_cZYfRqoYkw)u}lOyHEi+w_O>)%ze0#AlSj} zmMjMT7X1hUR`w=G>^`Vw(bvqqYTZY4EVhn>$O=j|jqN<==; zd7MJ-c-w2#l6R{Hyi%GeH%IUkq%+CU508Tv*uj)b01)1(`KNG@(Tb*k^9#1-5-VEE z3+&I437%TAq(eH8slNU6;|y)e87X;x7&DFn@9*QWS7i0O4j#qqQz~<5({O|rD$hkl zL7H9Dx0dXJT!CF^(knWs(DvPKzO{X-oj5tVYS6w?kydPqw_3gG z`g0G6`)xe>%0l!bKyxK=F-s%Ae_in8&`9*uP@)OIkm76tFIUB_cF@%mSk8bs?;l2>mHy9 z44qwu1WKV6+uwktSQhGJtHqXhdvnzXi|9o^_}w##yCeF8Tv*9J^s^mqUE%Qg8{0t&`oBGaKqyUywL=jdy)xRqxPYwp7Th zclZ0bP?1vmNE;Zt{mB-?9Iq&(VjV18{4Pit`rCL>{Dxi3Iy6lEEoHA839>{{(mV?? zPl0ou()iQ;wZ&(NIZcD`b|&>qcQ`+zXGJ)9jxjMjsJs$ zD0j&KyX$NX!k)O+`{wH;q|kE4i#pGUx2He=S;y3v1+B25brvrSCM z;)UHR|0Jv09^&jU)_rKvP%`=h5EY^XUaoghq?{% zfJHm3zHvsj{`S<3fmE((1P)z+S6vg(YZOslR`jLPaE94#*vpVD?!iHaBD%Bk%qQ%7 z%+yf!V@(FG#m=Xn656_qNU!+uf9#*Wfm{wxXqiAB_#*@@I*KyEo@AQB!sbER`Mbe@rope~^EQE)r zO;(riWUAcAVv;z0|L(w?2Kl>?E*cjyg-etVqxewIhmpjN4@fL`H^o4Tlg$(Q1Xh|X zfWTK;7|PMH)mYLW5t$n7RZ)X}U!S(oRTj?IB^2UMl5M27x~U0|_2KYoE2POj>;GaT zUD505Ag}wo|NgkVOPo3_3%#nE0_Bh7Iz{y2H6$jzIp=xPL_6_K&^#KWYeDNQ;R{g9 z76-$?wRymlBM6>t6b&1LvkECn6V$Eln(RoKnhUtP-mT5uv~!d%_iP}cczxL;G6z}x z$W)sDCo@2n264L3lH0f^GSg;vT?+$!u%dzS?WDm#6L}LgSFOS_%eM=X!ex@Cl1-xj ziO#g;)BcljY@Gg#tBjx9Jpqo}>zbN&U&WhK-|ED`wfUSt*!|GuC8`NFfaY*+lMhmy zs?D-s3T6`EP~OjcBJyfb?xtSDGXt_CBmN@T5SqGE`5{K}%c$UEF%+{&KS@g0xAMJJ zWQ3P%R2{PqC+B$SDb&B5=H%6_<3}DOLg~CXj=d=Qotk~0)$6je*`wHRgus<|%DO?x zq#W@C^tz>)%n}R<;vpUux?8x<7VT(?$U~($IjSykTkNuRQSl$Q$ z4fuG;aWGsv1+(bRPGl%raqCmJPU6 z{HzaKfZVnc$fS_>uvI6$`72I@_v1Ko-VP~wZDsvz^1mDJHWCW%0+%C4n|$#Bk8?gj za>Bthpw+Mt~=p}`p2k)|hnarS^TvN}d zJT4>S-f1akinhM|X+aWXmzX}&HWA^IS~zxjSyMg|Ymx?yrYbo!o_fd`j=&t~2Fcm9 z@ic4zt*wCWI&snA=khan)F$p!KC>FL7)`La^J7KhSGPML2l&zNPW|Uo?!bdrydm18 zfB0jL#r1cs`O<)pK~Bis@vl|$NeRucJCbgt^8Kud>n6EeU6(ia?dTsclP8{QL6

zZ4Wm#V!E(TYXX7?X>HYcZ4_{sM|z$y?sK_?PkJ1+g?Hxi)mCY1{he1Oha}IP`m$Lf z@z|pTXyt_KY*(9JFOBkf%X>fF0Mtzk%%6WkD8p)SgNZDkH1pIeL8PXlZ?RMGX3+A} z-H(?o+?KGPpL^K-}?84nSU$Znvn;)Vg}b>->b_T(q(1+u~xU|HITaNA80DQ zUcPY?NAFpjN46;f&UsZ)FQ?_7tRU#;%Rd?J9vL@RiE0ITXs(Xv|Dtir|4G&w;Cmp2 zF*3vpNTy@OI21=;CK@)HDFnW*pF$Ya2Wprz8_>iGTEB3J3sbs?QW+wm;q(9rGiLc| z1tf~75Vof=v8D;$#~_(9d9|7MJxEkE0L1|5KXsm%t$11o|GVrT^P^IwO6(y$$c2%U z9#9psOM$OfJ(?f^FVkX~exGEhxwCC8u<-lw(k3V)R@`Bm#7+7lA6MYax4YemYEnpT z%dL+->4Y*pjDN{AL=RSd*qk8y4)uK*@A(>o`h#G?y98!<2M!*br(Fgarf9e;%!gDq zDC)TV&N&HhKz$1`#!~IV$=cX!|(uGc@FOi=7kx^!$izFHHo?pq73x@1Mwgv*2WmCiU!W1yy1ynK}|9#~5Vt zp;tTi#b7f@DkowCm4x3vGVwgwKDwn)G9j!$rMpEyIW76ad5<6A0KNWDDLc|-J#LPvNu`lW62)g=>xzE zL29@XxydyimwdG*&BV0DBhsX)kT^+BGmZcw?)l*ItN`sS`H_^+W zE+Wq_96}OLnN8UEkKSFv35-Sm$j z<`Avl4p!0hu6_*p!gS5N@^gOQqg{mDm5jhjjBKKp)&m~w5q zvsYE|OIO`)qwL4jVRQE=WpwGv)&4mR14ij0@+#hp-9x#yPd^FYf3lv(gIX@cyRZ4d zK=?WZGovwQc{K+r4bZG>P8z?1ZeE8j-C*|Ffxg$ogkv~t=;yvD3bAMQiqggD<;h-v zm)mpqgUQr^fuslzjwrOkm3R5?v!G$^)$nqQ{+odb=#j=z+QiyU#wM*z4f$X9km6=+AUdxGZv~(A(J;BsWqLz#fF1(lf4S9!;4toWz%;Gi z7g_KYNL19P*t+}cX>;XlHccRkb(M~77SCoL6Z~RodDh}}m|s_x|u~3s2$@hyKEqr0)V|$X4N~LcXZr*OGETV>ix@|OW5jpHAYKY9*6#3OQ+N+ z`@?^vTimv;4eK$d0-d?>2{F#9=DpK!HhE^up(`Eq+)^f`OOY2#rp3K8Ilnu|j#IxX zP%?;By-P|R-zbe_z)*C@8+AT-kBqvvBf-JsxYpv6<-zFncEQ;0*6rDcoU~f*K1QUg zHDR9YTD*Jgs8KCLbR2L-Uo0HfpWF|lNFV-H=K59ff|=feM@x6CF|{CX?Hr} z>xKglGHAv2FHT!b$Kl8B>mQ!6m5o;-#ECX(JZJ^Pah(f98(#+E(R#9B8VVtSHObEm z-X|!{CQg2VaL6F#u|@~VfW>7`&)P{Z=q>}KwNZ0H;}Gcr>+}{B=6w;T-Sc#XUe30d zaT~Js>yt}5$m<@9sJzr0qRdt^n#LG^h5J%5)^gjWJgc*fv_98^bLo@p%a2{jD>N;I z3Eh?&LZP9i1M!asbJd4!DkZEc;}Ab&0DmCA{4OwyqWJvY<=N?cWZrKldQEq?gP|dF z7k(C2o-sF)-q+W_=rY{0zyy$Tm%|z7*gO(R(2v)1C4?TsUlUfbppL}*1Umczz&WG# z>(vStF$fFPjO4u)<7a$JRh3omD9p`I8aWQeOGFdUcmcKUv~>iz(|6l3r+G_(iH?4m zWjS_FU5y?yL{@t>B#KYtthw;X#%{oyaHl#m0YU#idz0S5wfWlr2MQj!{HQF4b@&QA z*#n$cWkB&k3|AoY>*Dn|!?^v*Wyf^-h$X_C#u0_jfU>m}?x3{{NcO{^_<>&6qmPAe z^?k|39L~z+eg2(#oFAI60RO0>0{K~yi6AjkA7Pdwu9c;jYe!XlVH8W?oO%F{1koam zeD<7Gq4wJe?|pSHQRP5Iua3iK5h?dD2&0SMobQfh*yxqnQZ)F{l$d0p1WP&|(@#2WN)Bd*#=w=iKkDGM;Es5`aOdFn&MZN3QdE!= zq$gg%#kt)Hofx23euwy(?=8gY>6RV%J2C~ptJ)c|g3VUaeoT-)%JR2WV0N@3wg!i< zZd%9zk6de(;CZn9NyP5qmBj^Oz-p$dNBN`*GlB&KN7!QlsK~sWm1fo__3akTXm;H7 z04E|MptyrW^$Fq0iu3CyhJMc8gWm|shR zH6D(GVQShKG22}nijGVMQ>WjMT;3c8EQv$*ZLRo1|HN6_)%Z&+U;OV2Xwq2OW(h1m z6U97hY;-t#jK&KtWe4zg=kdhn+UN7~Jam(Ma*0De!V6*Vv{+-2sJO5YEaaAiH09AaJ0dTtjG{S1P_emPn2PLT|mN( z789-gc`7&YCehP{US>`N+8!GQkYRAXQS4Hv&CS8tv$+p7v=Y1@i8S=mVcKY z0Y~uz0i5Fx6uCBxwu`B}>_mf3g{S;mJ`w?tF+5Bpnz|T&Z)8C8sD>Ac^Q}yKSadg8 zDxXA5b^j#u3XP+lk|fT?@(X6xN7xuFf}_h8BY04o$S~{c%plFR(Qv>($>$prZlO!6 z-t;3Aw#QW6F7#V+pvP}WVK89A>GZU{CMxuhQ(O2H`cTm&is2X8DJUX9y8C|3;(Dj2 zWVsKb+U+G?1$*^|t+9o~r&-ec( zw;Rp5!spLZ*k;IH+BZ~)#bjsKN%ipM-H#^T_KUgW@JCUu?}-OavZ}%Gk;#PqMY@mn zZGO&F9OQtm{5cc4x~@Pas=jg3xt?KmX_5c<|8h%A{_4mt%r0-BdZ(c+%T#{ZGg~4V)l@Uk#G4l_c0_t&}v9F@rtK;7aJf1B` z!}YJnv@lo>v)HUnjh3|Ak#52!O|x%_{zt+fL?0(*{V1IDBI&K)Jstlm(V({~jR^=W zpBk?>=SQl5z_n7?kgq)o7mW{wF%wmmnQF3-dX@Romkco@_~5%KD@VP9j7>~vJFIHf zFILHo13$^i-gZ!`+nR2*nh)cdT5rHigS=u>;+Jy)pZV;thqsR{6m3^8S<2i3RDd_Jo)*MjPWGxOWt3go$lC6#MK8;T!fPy;Yb0HsTtn#&Dv~o$Vu@&)t)gU`V@Ysf;LE zwtz53r9%bBylA3Qw|*Uq*H3e7izyg%0JL)e_EOh$wVjB8nWUWGq0 z1tgtwzj5qkDE<3oAdy$Lc4f`+$yM<4_TVENM%BoP-DH&iJZ?SCA4Tq4Qj$6lQ#=? z;OvgPiCgjr;okTE{sbRRpUQ))nE%d=w2} z5LU8Dv9)F3^09s%MIc9Yv8B^cS0(Vh495%4%+H9fqFGp-S6{l_TYx@W`SK<|<_PRC zx**aCXKs6>wpu3s0(+-+Igfp!tb5Ase(d0jZ7yEhII{VQk8nGyh-1(J#kW#*b2Uu9#Py?zE1-w zQm9;RY`+cJB6#AX`WJX*hI`PL$0ro!@yDUwYpW#!tWcK~gjO*RYL`RyhKH;lp?ZD{Mg7NB_5zL~fY zhxk-4*v46RBHy6zQA(?MNjK99QL2#gWqD_S+b-7W_DkqLrxii z3St^>J!B}mqVt;%)iT&FzVrnt_G1Vsy!nwLtpF=7jj z7nP2CMR5ALrt5L_oDJo!);bDIQIMG@h0v^kn*jT`Sz^oFHJJ39WVXZ;X`x($_cCWgE_NCmYq7xPGmsJL%10 zx#^73X;|_TvN7{c*^(y-At~_LNioo*TC^5t;C8f2DLld^+2QLv(0CT1x7hW-%6dNY($w~Yvq+d4OK)? zgn?XBBrCb?f;3WvRCT;^m@oc6k|-vqJ62pNbX|ix^NcaQd^Pv%K=vp~(3$vX$EnFV z+sQg3=E4?wb}-rMxBV9Zn-=PVMHXWQvqe22kp#3hE zLtl|CqwUe8Aa`9-epT&55@W%ql5{^A*NJ@fF}z%d z2_z^)T@Ve_x**qxBGW8#PlwwaJB1AVC5Cw z`?5}*nPo3BCX#Xqgf+JnoNhHZJ3pj0@Eno28MxfY1rKee%^)~tYSm53iNomzD0hrn zbiCiMqVHOCI(k03>^=Xc*2!5)@*jzUz#{B6tf%chw)c;*B9|X(Z8Usc__q;<>2Q0R4g6GXK#ALmm3P;oEWn~_YbY&l*WXft<@tIl2z#S=}+hYM{c z8M5<_Ne>k|hpf}cOUB5lL#D4WK=@T_XsemANw z$u99cEvvJ?RLh*KR~EnlGEc3znW`(f5$QhWrjanr{%b#DyR|E7W^q4)+hZ z!?bikf)4VN$6>60E_#zQD*I?iIa;y)Pb> zrs)R~?ECI9-e`WcB6WSD-^PUx>|`Q}X|zbK^%L}5J&J6OO*LHQW%zGj^$Lo4hR;4| zUd`tisVx5Mz#XWsIIO8Gt_4c47Jc_T1V3e;|9r)2U<6uUD$0Pi_x|b*O?Dw2f)*1aCr&igN zCx7Onh-r1M7wq2ho=J$2s#6GsCOdsgx~dG9H7~S&gWTixL9Uj!?Dga>_tIfdsftU5 zy>yo~?y;qNk9u!ox1EVorPJNE?w4|Ye-s+ZDHW);+)W=?iq1G63fF&z4O4#}+7fMu z0QrZE*1@SR0dTiF*bFC&IwUxk!0ib@H}CxuR5ARXs~V_*3=`y)d0gJe>omx)UiG?> zmjxNX-Rvq-l`S^iD32@`N?`Lom#9A?Hwn_5wDpq2`!&(Si~BEA%x$lo_}n}^fkauT zLP;#MX66IVIy1|=E(J9XKVV~*$nK+PP%V&z2N;V`D=XSJJJ1&1DGLX-{3@uz=7rE- zk<0L;Y<1GJQCEQRGu8hJMPO7}i~EzZ<;SKkzn+NCa`XmbO56b!5#6*)5w<-nNYk04 ziztp6_;BgHal6Em`o)B1#^T8nSD7~3N!Qk3pt`&DEgp^0gM1!cY9ae2rJ(I?AtFsE zsTb0Pq-olSw)G<&DSSo;kLIj&=m`Os(wLW)9uTHA@tPXCVOThgSG~zY^48`4Bk@HMZ(0>vRgnwiLBUL?H%yi9%O>*$ z-=|dSwoS0ywH;hA_0RRaGElj02k?hvytjze{*vt0(pL}Vn5|daJ24l%IJ0K)AU^Ev zxbF|_^Ig6A0&l!ixfa-^i#7%0kc4~oP`$=S6W-32<2?a%wXc-`WJ4X9-fsO9UMJ6p zl^px%pRO8-CDqH0CgsOanyfPJ^eb+wN_e2u7U3+eOrSf&(WakVFJU~D(QO}B3fGE> zGssl%N=1Oo1l&o^#KbNAt+sw>-20C0MNcc$Ae(8$r7IRZn?dY$%F7v>>{O5^vtbn` z_-`gQ5ihiqaFp=i=E_yI3+U~*K;pKEb$N+u&un*)(1%(L390e~ov==^zdeRfbgu{o_GaMGd`} zsD^h-3i^oyZG+gJ?>5(^fHpD5e4V+{UssePXb>XkfK3EN$eW!nFqxHp9R1Y3@_jz= z1EOa5I4MQ)TVcH!g5enJfOOYAg;i??XdU zBFL<+zTejoMT{^;RQO-1g(?avH-9S?JR_mI7Hy1A+{xT61gc6meZ#Km17wWnb?h4R z{&ZO!Jb&3(QjHuWsa}b|-$o|ES4u8T{?9~ieAHo8qw<*i{9+c-hnQ(Az%Td#Y&CKs z{jSp-u^%P)HFvipsDSth6#_t7PG0T%NAvgCzVe@^m`D7@CVq`sEi5#!o$P*e2F-Q2 zk~GyU*C;VjInm&c5>e;f=9WIUy#RuI_~SQ;C}2H(J3N4nPG$PdH{!ozUF-iye*8!B z8rV&bUp3=9^Jdd}apeeKhyLC7p_P|q!PF~-L?C@5%+D`u=1q!&4?-kQ@+1$0|5t`h znE$r=?lfAHNG$`Jm%sDfN<_fZ_5HIH;O;AX;ePSp7&ND;R@Zi&Dj{UL^*k+@zdQ4# zTc5sbym3_p1==5J2R!yi-{sMM#q{%JjW^K;n~R4Rz~A8<>&P7SNAZCQFs06olrIbGp-VWo;1uH zuOww=CeRGap_2c$y%4k4#w-Tz0mnTEAdUT5(4=~HOHL;0SR%Z17VZozs?5D5ZFse} zkoGBBk&8O{+QZnnG)b7#)p7_vY{HyDk%59!6ka zUR)X7Y?FJw-`+}OUTAC?r}SdNFk`<;%HCDS1u7QSV=0#235eyVgD|!nFblbN`X#ddzDi4tIMSCX|&I-;pzwR2W=oLjVqQfZA+M zTF&KEnHxLR?a$2#UhzIw+u!tFgHsU~VL~Z?12P@!Q8UbY0!`uH^lU3wDU!aZO|*?+ z*jcaZ6raxXi3jH`(fyR-evP-8?1HSo7am-$fzGOen`T5OGaiy*H`O+cR~dgdwDrQ? z-~Uw<1GMIgDfoI^BtGiS5Yp4T^n+|;nvu=0U?@lZJ^zh3BIBu5SEE@MyaE(rr;@=v z&1o5Ye?bF)rc>~!QADLvdDfM$Vy?egM~MlTvvL5jy=V0bue~OK#`BLL1V4#8o>%|a z@LqCuAtg-Ib^WVR%Lz3~D+Z8`o4=!lfyW@f-Np4CaM*!5zOg3I&9u2(mr8&c@VVQa z4-m25vGInx+b`ngTI%_%??3%!K$1<1P7k;Qvf_3=db>upQ8ulVjUXKU>~8OGwRvA7 zkG@Y6xG;5VAYT96d!JoiUgTWR(v5{)Ot0{N;2qDkE7SA*cQkjTXWXK3CA+H?r?py? zpgJO>rL=y^T_;cXE(!V{Nlii~Rhaced0WVhsKno>T;k|uGbpN^#HF8NAYr#xAz`lT z6YNE+nbyQOOqw{RW9OgF$*QqV<+IW0&AomsLl8hPmCm8VG{0i#<~%H%w$~lHbEGt; zQZzMA$$UM!;zug8-`TO>(+cW!TR22U3wP`E3N=Oxdg4)IV2ZQ;HV`2Q|0(qD<`jWT z9whETt;JvmDgvb4yAEQVfept;J#57f5hq8M2_QQ^eA$KNHo6_7^^N>LlFGV}_eN>U z3wrHp3*(TSFERj$({Mg9%2rrUHBBu=fJg3z-RQ>~H~mBi44#&4vACC4x084(wR8{l zsc!9bTKOp=_sL{fVWZicO?B{icOz%bS=}uh|?zELL+m=jv z|NCgHQZVz5&t?qO2h;eR&6;!_HdKR}>N76ePd)xUvgf6J?at^Z zabeT#t{mDiuarTW1Q;xX++m{44dZ)22cRiNy;;c5MdvQpV}=T z(q(o`!S^JtssFY!)GqOBVUVHL)m7fP=mXb)i3TqdNf%}krfo8oJuArB2Pq%#K~~-x z1(DU!gI!nocl~QQVk4bOe!Ry;lg^uIaB+dn!>$!Zmz)kSmsfeE_G9XZmJ=k6 z94elpPNzq+J?k&AYQ1GIi{e-PT;vssYBzJ1Gf}TimeTnDBpZ@D`;fDrF)~)1Ubx!) zv~hUy7-5C~mxZAGAZ`tNmDzEuhSs2&Bb)rVGin|tx(KikS;o|`B3&%tK>en^%Qt^( z3DyFdnDbYz-yHPX{vuYdr;G8KJ@_s%f)4gE@vzv30(?4GvMTngFg`~yNlQfA70*A6 zxz5j+`d(L5$hb2KpUw>y?Gieb7}}vkBq}~6VQBBXgoK5UDlv^8{#;`IXNTxFJ7KlZAYEuN>SqL{@97eOKLr^ln= zJ_BuRmBy(wL}S56aw^%hpRNVQ%*@i{zD&M+@QUk&$`h&rFWyU|#F(Fp^NPJR>D5~K z-y2&RH2Tx#1vK?peGXSnrmh8yI+wYc9=PmL9zB>>Qc4A}sTEqAGS3X9KWJEYd&1yC z1^d?RE%l%*8&?_2T>tj_t$5loztdmEIt4PSM}1wBhg&lee0qV;-cPh`wd|@LF3Ob zERF!i8if%c7_fRZ=CIM$fA#aeY8FkhxxdTaz11Gnyiy-uv;ES3(1CG14{%4TsX^}e z<*T*axLiK6(vY~F*WL)tLRY3lq6e!()L6^$_8OKcYeRXNIgLJ! zS0a<)r^p3tk{587fJ_6R`_kN7*T|GRV!<0qW_BOY4+)org+Hp6jSo2>3C+zlSEZ{=t0y40^-7vJ z$}EfC$0Gd_4Y0RbSG%VKH=QL1mVKi6j|&%gJ<3T*6K;2G1Pg4*orO4wAAEi}$n#QP zPVq+F;s%fai$#{h9h~L5K7G3#Af!0_2?7H| zRwtCHYnLf%VqVj`4hp@fC!@CmD^5!f0CI!Z>nZ!14!)TBb)!E55Qbe@ zWH%o66e>Ga)v1uv8XdU~`LaT?FZFbrRF&$~_Q+E2${RL^v#Mk6Qs6Y4a3z3O(R92x zS$S1`UnO?y9Gw>>^s-dP!D?MXYyXklTQ=Xl_cKes37S!m@bKZy5ue{4`XWk-srMf| z&n#S@bG&putoPH1xfM3P(aCg?*q?;X`w?eT=-WuA>ug007yC&RA9Ke~w~83X%!8^I zO=H`i(UZ}9AJ)mWlD#w+c(SY_aHo_inf8@$`<=F%Rza`va%)FSbEDq(74J4h3bZCg z8YH|SbRksgPs*Fnl#1tiL*9Mqo%NhnX$Rbd-SBYGO!apxV@qj*$&U;_3ifcKmN^y~ zRWQ!yNSt-aONtuV=0C&zHid_uX`Y4;*1(@``tsD>NZQMEtF5_RT$x&Krtv&{*mk3@ z)2n?+x04sO)80)t2VEJd#DpuOqPog>v?ou?xNke(i8brI7ji#n{gx^}EAPfO;{^sm zK6{osP&-g^nZQwf(o_QbI=y9iut}Wh8SqWPIenpO!f% zk6J{2I~n2U7(UPTcYSEEqz%Z<+#$#~xC)ntNtaEGn-s%^MQcJwi(g|ERM)O7p~18h zCdHvyBa&nF%Rvl4j1C!Urn!u#=RcA;PTJ-)9+o8MZ_am;qgZ$_BLY!(nYQh8WnAge zJ1`!sYThr1OzZ)(bfk#L=2J27(z#2*XDjFMVcd2Qq6kUxoqOW=BvgyV`6L!0U{oM?t z6HU8VR38k(rZ~Dn`L>0^sMkM`{bCtakS=@jC|J!Qkg1IJN_otUjxVn%3szdU@l%7l z?_J=Aeq5fen3P8dT_?vY-I;^-8Xj)3SMwE5Z{7HK=y_H?KP+uR>S>v>-*iKLZ!3F! zP@jLwLE+|mAVtn*;(KHK*M-J+v!i{=XGLg<6OfS0GphY}@kt36!)8;p)Czn9k1dUu zhO%E~T+j95QOqjpos}8hrJYWFLE#>G;qq^^ACYV%$ZWNWuYnI96$fe5j^)S;(M7s? zmGD+OI%m-6kz4#$ouc5$EkG%(T(oc@XGdKhO**E>)8AdLJmHDP?>6*N6sd97ZsNs} zbEfL3x-?(h@xx5EW)rek+$moSN^;#JUJZ&{*&XD~1wFgERfyI5J6{8J5~h)QRIEfc zRKm;_DQEi&IflkRAAr{Y?egm&kN-%}c<*1&E;)=|cf&pqN7_KgFiF8emNH;!vLSj^ zP=1?eaH$0VYF{|e7@c;&QD3SPdQ7mamtk`yR9f{}4kv~BfuWsn@-R!{Md4<*eU8uiIM<_!(YnF=&)wU7+MUDOpEcMFej zh;*&=OWr@gd5xZMw*k;tn7dUpkq>$q)IF~N>8yV}c)hPIdT>rHd z8~nI``+zx%`9uO}2Rd}QiRkMjAgkQw19TQ8C>@MHMSIy(K4BPCszuvgp_Ny zN5-bX756mCB$O0h>h?_IToY>z5>8eOE2y>xwq_ox$;W1lo#C7Sk`5?MOXV+!^&_*7 z9j0(-h0fru+J+GjtE-76Gd+Pjz#~k5Y^W9QB{Ik;NNO;^50s$A&t7k_>S+~k_^T0^ z_%$CudaotlDVm6gFuM7@fA8~AZOYS+b>*wylyC1J0UTW1@cqq~r8OD~R+l5>Rs z+-1(_-Rn=TQH1A-oo^22y|et-6gtu=PWuSZ9l9{5gDU}oPF3o+B7{fTA9i@bFzEQ5 zz{cq8ps9t(x>figpiMBHE_p)S5r0gqJ%e}NwyVaB_6pH`;mLeOmUq)-9Uv9jOU%Xj zjBWl6(nx_V8X^{~Fp$I>i^p$Fg&O!1-YAKjm|lpl+BYW7`C3i@^NBC>klx_9s7nSb zbJ*CIBEbHKyn4)~EB@-UOP=MG&!VEhD)KfIF$b`j@KtYny(UEDlV6bHJ&jjVcQmKa z5AvyY5W$3}O?QbC%a1N1MF{fgJPMtTpVf%51^7V6>#{2feC`t?|LPfG4ZYdQQgozI zjH05SrHK-mDaIg@O+nLm6m&|4DFKqJH=)S6ly#;|v#m;$GFua2T*|e~!$wLSO-+#D zSsR^;+1}4&?i%!fnFxNjY=zkp83CagS#2I@zYz2HNFWx>3>A%US&^>h0hn`0GQ37f z4x1%5zM^MN$R{ONQ5QPXZr>k|+E>ZXCc}Z)%~eiM-syt%CH4&CS3UdVPt~uOS6g@f zZv}VP5pNECzcL?e@bB56Zh+gOKCp%&02ZSTq&MyAVdRpZ~9dt;Xbm%w}5@O zi7tJVm0zp(<*r`pFfyq+!x%@tQ3AUG|~Xo+XS8kLE!E_u4J7_ux5KJqX^P zHZEkj`mFq1>#jiy1gf)7N(!1tfW9VDZTeGKGj1W^Ds`t-2Nv|q6}H57I|fX3*v z>G)nf50adkHkDq`s(*6g^7K|0brL%BLJpTS%{@h=CUCjY!0j=FnR2&VTN)SJC>CTH z+%*e%0KxMocFKj6G(N9kQ`)QYvf8n_A7neNYZcZ`>wMDqy|($z=n|iMw{3_Kq(9T% zaLZRQ4lmB?I_J4^cItP0ioTDYQ}4tpN4XIV4w^Vc;7;wI>)LrZR63=8i^+xnEvcU6 z6HNNc7EJMeR)M3h*?cft&zgybMc%9Kv`vsXs?JX+KLxjx$u6$>+fRFSy;r4a!M!}l zdi;MJorPah|J%mli;4mQigXMKQBt~@h>`-L(xo&^K)N>s=@=oPfJ{k|k{I3HNKQIN z=f;Qu+kDRN`2z;8ZD;3v?(4p;_mvI_LhzU^eIn*ouzszSjVe#BB$&UEB@J1Dgm+1I zQ()qA&gGpEN@VqXhz+Z1EG_yBIjL};JVXj0 zO-f(Mi`Y8&N@6N0cC4jCRZAo0c!JW zkB#t8bh7>76y>yZP~>qyLArq2-tly>uy$Ypv1hc@hTlFoTQmO`l=07ER*35Rh?Y-G zvh?N$sX@#*^DDfxSon#KPIS1w@9*0ED5WIeyc-|l5@M`B$&GRsv7MUO+_6oVFm3pnq9^A$!P;xc{?*j|)Y(r?ZbQEThr*}G zj@L)MFt*bYEs=x$IFWj!{MCFNtFkP`4pFFAS|&_cWqyB*f$reZ@p$FrETGZaR!I}i z*L^uMHCp+|%)>(xOQHIljb8xS+n~4EGk_rr1{lNuwQ9}2+{@=5nM&IkZoLI;U4`)@ z+@rQ=n<{tp9ud8*ezBFZLMO;CE=y+1GWu8{bC6gP{Ol6m&3*>&Hn4II%$W6|)>Sf7 z@{e`mP39BX_=pn{V;_}APuh0CDjyVk6k53CfU#tgZzE;vbA;Sanu>jk73ZGn=`RY9 zWgb^B?jal|OHYEV0Tzd)=);fj^H*K>uDvsSlJuW38JUKS&|Y!P>5I&|#CXITtYy!6 zXsOilLD|!wAfa%8-(MSBJ2ULnkYW&~#iJFRF;7dRhE0%(o%XjBxLN<7bF`$ z&I{1^o+{bE8G~P7in=O9UH!TnEyMaLgmoULJ932AN?(lWyCHE8($=mJamzJyF~J6q z6(X!>LYq@6y7XBxu!z%Ue0A#^O40Y>a{QLaNJt_x&77MC3f29v={drF+e|7ZpX_zE z@A)24`~a0Xjdv`Rg53+2F*PACG0V{a@1-=rW^>VNdc~{2`oQE8`qfdoAwih0_Kj8n zyZ+Zr!4%1C&kq4Rmiw+g`PSyU^xzB+XPyy;wA;w3!`I{s zK1zh2jGT?9)GfRS=w?zb87F}AvMg(p^pZ>9U)n?vTUMqkitsASo2vmfOCPJspS-#u z&nZ3|R(AV-&Ni-kB)pWN(Qi6>dS5}JBFU@OQ59sO5Ox2(H>VgXoogCuXO08M3=9JHV;xkw}hsJkXFu57<%5Kr~W|+UR}e@!jhi?%(Wj%tu_F zawiqm$wNPINUNe+e}HYbsB@&;CnFsQ!-iXX`tyAQ$T!K0!oq zx6Edz7H76zsKUZiop&h8t5OBr`Wm@a=t_8=Jelex1IDO z6bk7N?xl-k2)4`Qe0tQMXf8)UJi29{#IUVz1qn3x`7L?N#=@?K4{wd-LASSpu#s`O z%Q`;H(ZaS!i3_!~1=BjFQTCj?99<7CaZEDCs$K-_!3WiDWYJrpAnW!Td;Qn9tm0;u zcPq2??|Cw%KBK>$rcbU_67+XWp(4|^Z78FqC9oj-&2BsRh11q`IPnxQ*z!X9;iQ{R z@urrdWhy}==VmLv)7XRUyu_!bVOya^@l+@J?D#VMXjfSE(do^YM0>kLd7dFgZby#f zu~iG%_S3rrt3OY!r2^oiJMfbVEBx2MD*t6|?+!a$l|SWmx~=)+;u0a9&pSo;(8bgE zA+&o}i`bbQCJ+ym7a^DyJh^nCidz*SO1&mnPppI+fgwLi>1Sq(&| zF0~%w-(e7)FdEW-kjomTm;PRW_j!o2A(Qk%Y7RN}EW*5Gh!?$mtB(xDT(e@lNWKbI<8DfhTtm7@Lhy9e^d0(L-U zdgm;M9Wzq&(Yr*1$FCu0ZWOHW()UM(gLzoMIw>s|nIWF)tE5L-(@8K}GtCO=8J^I6 z)NcK)N^1opp}%G69!lcgZRG-hOt)$0-^eto*V?8ZUl%T1=_4q8&;Wlg2fy@%>=k%r zYijT=AD2)(D!hJ?1=S);qSTw-HcD7YXw|E{Y!7!@wO7bFe*>*j(rpYUek_@Y};`+K@a;4-b29XIIYwWh@XVmT7E20hWuLI!soVM4$VQx!pj^kq&1!m4j{#GS}OvLj5Af0e@gW z(&!#XvApx$OnDMfW1ju!D7A*hNBMc6Z0DlNvJpO{cwYZe^dc*(@vrLZa-)zwf~2E6 zUGxFp6z`MTW7l-y19e_*E;>+zr_&}dx7|n=YkpkjafQ^Ht*jx;whtUih6y;MCX`+~rTfWv?wI9~)(q=o&@^)W_M@tOEM*C@1`}ncU$!!rIyaXzXP$Ta(#;w~7|MHgBC7~!mTdVY7*Bp2a_l8jmc~ z^Bhw28dm<1iIIN)s+=V@8Tz@4s=76InnPs&k(rh81(o4@y_TxV4kI$)ocXH{;6r>g zE7-)=c9fxexM~2y0Oahm(R1tMkKP7jRvJ47kXFw6$#xWfbxliEazE363YU1i7mBck zEa#?AL5D7SOwpMoc(YqtSZR3Bo*(;olZp=V%9Pd^g{C@cc zv)EVpU&C%mQ{GhP_$!M_M^#Mb+5AhaOkOef-+_FKl3zKhg5VsXqc*&h%_~5#g0m(R z7?T*0XXHS`V`17dRvJTCV#O5372u=HFo+l_-KVT4158IR>KbwU)fw)hvYbl>W7iV% z;o4U)(rsqlHjDv`)l&;d@Ipco_4N;w1)GjHLi8*QnYE5J3EB7^n;FpEVB7!f0{p5z zgwX24%0hN9%Vt4fzJ7gX|5!!-^>I12a;zqB|CU*jCKjIX)}6Dx zc^?YE$^fHlH>wjRNetyUfWK}!{~<}RQ0hjQ#6nKicjJ$p^Tc3?o=U&p!S+dR%Htb}(H z15~cL&1B>#w_M|oCoY=+T;E6kE--%ylb9_9Q|7$&UDkbW$VUPFOz@8fiQlk&Yh3YA zlS5ITd+*~ZX|9ivMlOvRu!ij2AdHrxMWsj{ge~5GFJ23QEj33z-*u-X4UVsZF}iK| zEP@p_@$&)Fr;T#i{H&=NtdSf=>uzwJq2@q4%Bc{vA7aKT#bSR2N842*e^ zg^W5I!;JiJEh(NKazaljy)OcPBtjNyoVc#e9$?*ap4>HUDRR&Hltu^i*IJs);~-P3 zgLN7}MB#dAEi+KRPN4ghyCM2sSTrrF_!HeOWOEGvV0u#EV0^_+uIJ>~oIa3#CHx}x zQlCT>6{z(3ii4myO)nnp#6+2MW&W-LI1}3_+g?$hHQ?Q0>D_G8l>^F**)$CJnXkZk z-_bcaQ^@8pBx2=Qw9VF&IrZC$HZ0B$Jb+A`BjGM;l?dnx6Yq0or;b(3zj^>j zGc$l-?lo?s6-!~?TIQgx2vU|O-VPbhofA#H(g=2$`~nx6Xt7~!v~*eP zIl>!HOU&wRyDm0LwaayH(!hf5a6$hLk%elv&2gL*h27F#P_a~>+b%w*X`87BeX6t3 zj{y8eXL-{M&zv~Ux%BdwZa@>PqA*N%=nfpQ)@m6KlwWl&IkaziHrZal2OGo{`nTI* zw`-x$Z3dVleydkAFb)#gtIUF`CJpMLtSfJ{{?PMAa5|60!^TY9Zf#XOV_duZbH&^P zX6rVs7HJuNSBw6(gxD$wlHHePo(J0<#rd{Z$LR)XHIrqdV_pMSK*g)T@kS-5x&z^{ zJl9iwp|f5*RfS=Ft1VF~Z|-fJToI|JxR$aQPX>mdJ=in-RMt*fOs_`a8%URJ*B#8oiwY#*$# zC+9vU&y?vOS*>rJe>a(uF1t_&ABY%u6{MyLpp4M`p9jagAiu07?Ht#cjX{4%$P3&` z9Pn>~-7y<8YkgNPx8@T+4U&_5E;UMbRQ!6y66lKuny!K#QOGIUWE2r=1hC}%maiH=A`$rGB^Brj7V`8#vLUz#(#GHjBv*?dmGzkKNOaLm8qN7Bp3P>JR85% z!Va~ck^XDISN@oL_G(!Z3yu;^qlsS=7k-$iCe9a0iHe^HmT$caT}05aR1AdV>O0rh z-6lmEBZ@0{fzDkK_iiw?ilR9_{AI&5(vk-t{7WoY$T_o$92zjBjxBP-+96XXH%P<8 zBGNTWIYUT8!N-r_AZi}+i;5kKzx`LETSg``TfJ>ucglj*-pS4u*7js4ZK@`0adQ}d zb$J=mWQy6yleymBb!W;g81#qg3^C7hqk3jPa{t$&NQ z8WWtEUin_cN5lc6`2Iq3Fp-RP{-h#nB#*AN31Rqo5JFSxqrSFuq|4;|j%!;_>5pXf zB!`hqo=ZuonrG~0kY;lcTy7<2`ZlWTwz+zaX4|SjOReteix>U-tqpRZF{FbPK_q1t zJ0r%q$MH7CruGww^(`bp{kEIWpx3^$rGKQNQ2)otqnk0)^CIVwK^`Lk5v8b1mOpo9-{YBWp}{qnpeHms+#7!>Hrx{ zNgiYfQBg{8#XU=E^Z-AlUDU&uIjc zVKmXqTxeptSBL8nn-@b2JLpS3kmkD^D>?napK*!s44;-8{Am3BDIL9~uDv77S2!=t zM)XH*^)aPf=X8_%J!i9wPkqrMZmp<1cHAk0hkMdWiPB-+j zTV}bmFkNS|Et50m)~xtsA9p%py1Bnplj(!weMjF{0@2xmzvlvJzV%Jbz}xj_qk0gO zByrYUY#gkUufxH^=}E@xkXg;mh$`k3sQVD(Hi#O^=LBZE>)b3Bc-}d~Co9gv$zLma zi)xl-KalNEDnw#)6w3#3pk7e;R*dLkT`MnqP*%n#_~zjfcH5VW_p=sev%9}Ia>|{> zRmrZ$`42Q;43AR83VOEk)`EpV`z=bVk#9B!xo#dRV9f&$cBWL=EIA^X0zY#Z3#?0Y z$^0WL4~}j;neUbG8YSrRH$Q*>n4QG0-~pJd{=6j>0cZ?|yGLw@^IfRsg)lQAepT!; z$owCfmc}3$$-IvP4#kmydX;Fo&6XP3LvG|a&r9yiQyea8mzUf!7G2C)C74)^eH%=J z2h1B?&^t5h%~;NC^y``T`!VU%F#;E@BfJziNxO%J9B)0WqOMGLtnOh)zZUtsn_#8k zQNj8*i26G9txNATNqeO|s?^OCi3&gaURxaL{ z`@V*4dEZL$iuanVugE(MvJ0xF=Sw1cr4CjiDl4Lf9Hs==3)H!R^L_P&53QXZq++*|J0GZS{+#TJ{LP&)zS{k>5J*jm-XV6CZI!LNX&IYlCb z-=!TZ{GN1o+SgXZWwr4TBqn!YnBg21l@H^oI!V8gLRyFM1-N&HxXI73XYh)!CO*r! zPmXR0e2{hakbjwf>in)|-g_C>WLo_e|FJGzcIyxxbtG04as5~A_0l=_>^DAq^+{Yy z-}a`0#q)Pl|8|L_Bb}-j)JrY?%6>K{`&j4eF;>R%O9@xym={&P(>WvKO3=;QRmk^h znBI)+|FW=8R{T0qIe-#ZmSEd=mSJ{p;GJ_>|11?>YU=sNb_0}LsWr7fg*lQz<6X#G za>oGEeOWgL{x8R~6aqzUW^;fnZ)^A`cbS;y&eXlweY^Grj<|nTZe4Qx&80d;aJy9q z(WAp(6UYBsqkEiuqjl*6t$0sx2cMwZX1x$Hc*f9xprQP~{I2ij@+-;b;Q5o&l=XcH z{vQ__#Y1){N7hoie%H*9#a%4roZ$~CxVIK^`f%mLG#2J^;uD*d{r-r-X{DzkoMC{; zxU&O3_#^sS>H!KNTx#4~r7Q=L;SbU(t3Z@RgKoW4dY4ai4rC{;r@P z`M~M_X`AB(w~Aw^W353#FjZK zXTDf#VWITZzVtroL?H~WyRoIWy13czuKJF>aQg33-|?r66m@onTT?muUY4vr&U(5p z(8JCTpUdr!z&_6F_mr_5Qf-aM-;PI!luRkPejym-{$y|N5{!p8=St3VpgEOG23zS( zVNStsCd#hC8TzYjG);u!)%)uETu$g%@V+Ph)#}l^jp6P%s(w5D#Wj_`peW$>)+m&} z^&qEp|fI3(5gn4rIa&*iXa3voAX|zA@S4$iLKQBTfO4nzMtTPI;5M}{|3tk{1_?O zt=@)z5aa47msQ<7S*#<(pN#};Ru&d!{e0@HC9LIfFXfrV<`WJMJC1maWDaS1Kri0b zm!YLbAldah{xh&$yECtQJ^wX-O_JtKnW6gNk?=9KWlqQxloOJEe&AKWeNnpjmGv{l zM$%uF%?9^5jg|BYuQg|a{wjjTKLvKnKL>;~SD4d%So83^4oBJ8qT5#do!E76>*(`F zS$J<{k(ugSY5L!~F2?6CqBtcaXw|*4XOiO`x$F+T^d$5R>iRp^Ec^Il2zr6ji+BCY z-OGf(5|YL5XXh}4#}oAgbMj>@>esW;9A5h&3Upo4v;K~&x<@gs6ni8QxJHVOvVm0X}^Byj>Hy*Ah_bQ_7)j!|SS@KiDS%1uJZHzRW``tet+0k_;e&w7|x#W5T zlME#bUm}<)ar02uSmTpclWW8K{*ms4oF|taroMiy7Cm05-#^vgQl(W;GFPj5eNBAK@=%KA-&kscSNaIf{F&KG~e+ zjpG){&N1PFgv#D7!{^A^#4|O1F4tGhDI#1>BkqZ%6?1-F_)Y;o{707h%AZZP4y#*o zv+h03u>RW{KuTCqL?ft)Hy>4_j;yVW6cfF+xSsQ5Nkb1t^`{ckQt(SJ=2{p*QuiLF z-!P6*@n!h&=f!wzj>3#}LJr!TDe13TOKFh~qE-Bo2q_Y&aym3f9FXJn_1#*=m8%bF z&u32`maW~EeU@`*F4qbq4mZCUH&N8>rO?XlLwHx$<9vcD?k!P2{q^SL;tM@YdkG)$ zW2oG8fgMsCzK?fI_)_|MC6to0*Z&uZKJLXN;f_KlA8EEG>AA;!?mi{E%BRYBZg_$B z%ap&=27MdHZM##)TI**dpFw;Wdf#jTWud_O!bvuCb{;WV;FCi3GjRWXtyiZ93fB{8 z-_|Zh>tC|6991rgrt_lDJab-YN|e7hfMZGYO1jy{t3(Si!)XOb=0*s-+DiJ1Pj=hC zr+qWv9>bB*+O*M()B19HR58cO<(o&jf6e?A9`VJwPWPThIzIq6&1r%jlKCE)SuxiF zlNK%ED*=Jr^(%Zz4`1xeo5Jl`1z8%Dae&(f2Ng!+RGOR}l^4d%`+jOuyXxo2h2QQL z^Qx0O?=eO3)ndM_)cRY3j~ zXLi%lbhlg6#G;X!37g-4Nd~zkP(4h1z0K}RhAs#bFT~YeP@Qj*xBeWeXQs!7y}(pmZdYiIbPHBQ>TRPsOm$Ax z0s3!*X*+UFJrH>Ea%K`$-FHAzfzHi!7y$SW?R>GN2ao&ykv$~}H4zg{rD1>5$8XVQ zx+h*!Q*t$R-2^z7+Gq$djZNdlo8&_TEfQ1(UE*R)H@pvQv?=R9h{v#LCjBFmv_>G9 zUHd-4Tt+O(j-MA+#Y-%z*i|%_4m!8hWb9x3dKczm=4smR=aI$(CT1{V1rC}D66nsl zu`jI5D$;&N`W=P-1^kkb)azxsL0suBN7}+yqwExP2kympvrxw3b)2k46UlGTivZVa zONDE)00`)TIfHB6yGmL*{9K;i?FM{w zpMkQhD@yJ0_@!I;l7h!%s{Hd+@KuxYLt|dj6(go?e<1i0)Q6y0qzOpB9QIea!V@7U zo|CExtzv{%6B&W*+dgkzC*FD>-wQrkEno0$u2mW?(gMF820NETVG)G8hz>v7)FS5r zX91-R`?M%9=FWw2PaF9uY~MO&iDgRVt%wh`QAeMFpz$2Gjld7aXuGOUf>e?{_@1SW z{wWcH8CX;L-`I9W&4u(W(l0!h<&7^5FSD}5jNI@jysr|qb23MlfgM|O%Z_b&&@iL@ zHsoljIUIMRxvdva);a;#;kEBm+?GpjAAc%0(Um6lT|kisjdnU7SFe}cxC@aV_*q#f z4wwWN0^>7}t*LenN=O|b<`XRZMA=1;QqiP5;A|8!n%x$FQx=2}zaq$IEkZInB>eiLjnMi=eT>tgb}Dym>vGTpGQlV=+e9i8<9unfW(bg zhIaVsu6`4*gQ0}Ft=D^7lstorb5t1CF4Dmcaq^-B3M~4$XM_2CLm1a=sHg|tcq?QP zaME?&FE@XrNE)|iVIgg0--adylTEgET|FU$jC;k5V{Y=Kv;zs_OAA#)qW7J|#e zUnnV~-h!V(IOOu6!zJh+iE^6U6A|M;mnMlih3y4P?jE^NMuDYf!_3IoH2%5*7by zKo@7-+_krk4G&d`*FDjOg%b)A2hIq@GUv-}>l(^9o(qkm^C^vC+Zq%*CEE8Sa7B9U(N%t3kz!&IBwvi znjArcZGzGb&s2m>8U17Z$0iFQr?3u_dSa21$~G{gLbfx#9P363N&^7uw$BcFG50Il z9?`9&zdBBu1derngr~LMtTj`GuP8AIrPrZQmxmM1cLbK~6W~RYr#;XJ0t*rcwrfk2 z3B|e>%UL_%v|+EU5weK_9H5LZxSd4sH`_FKp)AL`*BXCel@ZGXJ}51|OKgs(0YHH= zR!y_6vfO?sE)^O+YFLd3Mit_JtvHjU@eIGhKiaR|5}Bg}LPWl&8dt%$sKASKNHs+C2Yu4MpJJ2+I@`D)V|msDCkaB=h5#edwTec;C6zF~ zfc$7PO>%h({?F?<3&yCkvnn&ZYms>!GR0J7*Hc3+DbQ9L3n$!7$7b{(=uN%OYzxh` zdp=U%$7!R%7~YB2`RVdy108C}9_;truU{JXJaD0MA~}#PT&&KX((vVr-0I85*oSJh zm*X3j^A7}qQ^XY)d; z?I;C=ulrA=)hc(#{_b*hm{2)=vM+m|18^*J8zZ`$-jS9aKy6vZzVGSIo{-WOzf1&u zi>SBqNNhdr3s}vc$%|(CWK~j9E8%r_Yx$@2WN6xK*Oy>^^K?Jz1@e!51+D~b-6ifi zrFCZockaNy<};6y4ygaqUiSvkOSB5r&e2!x1Uf(Wir_Qph|j4X6)L&|D}M=FBf!Il z+t%$r{UfVKTxzSR6B~myfWmEVykAGH{^pQFn5m6gil}2}cMF}OCv3%V7$0|5)!K~b z^jVP_7q0O)>g5zK&ub7~V@c#6Opb58wJY?6d9IN({&LYjCKy3aN8gDWA_y+I#6Kb( ziW@2MmoDPHtg-qTt2%Uy!hV=EhSJ3Mg$(^R_HVdr(aAN~KJ6=(r-GS`W+^`A62bhE zs)a7)IeaaD`0EnvbWH3r#cJvG2W38J4(uh~s(=wcQbs$@_#EBcRVXj>$$d^X`8{!W zHDBlJIdAn(8_l`zwBVMRW=PLWV?`|VfikMw(gmeDbBg||W_|;r(L2SU#xm^O8UL2c zD?Hp16-Cg%=ABtKO%NV_R~vUHQtOX>V0f56%h>m^JNlVSG5%W8 ziq(g=%A3a`8o$mp!D@t~&xefzO5J0;G{mCcy$s#bp48s(r`&&OSWqvp>X2U*0CQqX z2pi-P7+hRmVyIL8mC+s3t-@1>M(meKiPc@wFkXx@O2-?K0ud=n@zkyFC;_Gz1h(@K z3yTKCbeSwH8143TEoiU(?9Q`rgGmQg+#L5aemXn8Z_C4^vyxF3v|D^zmwUH1<=;J0YP|E0kWbv(c@ai6G zoY3CEJzumF%aEt?t>;5P8?Oq0FnrqLXn%XGI!z&{x;C{vkh!~`Ple}CsdRK%xe-Jo zM`?h%#O<0~2l@x=7RP3juF-3$;qKs13H3^}LScATY*Z(h`6MQz%j_WMhxE@yVVXkf z0;@wC_pI`uKx13Us$S-)Efm6i5?29|^08#?Epoh|1qW zm$Duj0`m@lBX&&K7U2M3Or6hjIKArbNC&U;5g^|&BfrIjx`bp{QM{L0=l_c#^+g$vT}XL73PtP zHBM-!CAmx{3d%I6;rM>~96t@>kE4zc(7)WuNy4guvX5IF1uu3@A;%$iH*1`!#*7pJ?rT z_<{8r=jF+sk!Z7W!47H-^gwIfXLxk|=TnV0M{h(=S!#65OCV09;d5<9JBe&GA$H66 z#jKwc`mP>tp2FBJWV_?SCHqnoqXf!@c3Z^sg7&WC%3W>hW5%Z= z+J=Y1=#)o-t1B9Zf)efMq4Xh9P{U^ zQ8;r7ZNnLb&Y9UCgdYJq~O-|CTbp7(6Y0ngRM+bw$t;t||9J&NYe01JDf zC#6|KLC+cdYwr!un=C!PlEL82?%&5Bo}hu-^eCk157@!{3Ql`5!8p=9r&m`zc3qO1IVwuX1F711?4r2ZWCFNb_{6$lO%rK@ z;!(HOSh=fa4h}_M2>=nsTMvvC4aeW*%-z4O z7A>XTSC$O#u(hH;@rPbp@g1?f_t|pf_4I>~rVLJ52WnU8nxnoG<%V|r<} z@I7qvmW|-O?AlJ2@cQIz(cBL%@p2yj< zv;rn|8(R%IV1J;pdvfw?>~Y3s2Yb~AzyYKHw>sTH&=BqfVcFY#Z@v4`S}d9&j9bX^ zxAq8i73TM7?^7Y1Ruq)T^`^YXq*~J5X|DU~=#=k#&(?G6Er=&i?y zJ*&X?{P+Y01KlVVi4wY`bIv`9LsnO5*&NX4fG8>rYICN>Kxb?Z@9pYEPfE*Q4;!lL zl2ZBpeqPVDr9LGYy6OB>pP35pVhgi`D5^1VCq&N8*Cf}bv2xzDHXFi3B)86pJ}@x? z|86(^>&Hp~{#o|0)tT3DF$H?+tYfke==~6Kw&v=BZ6>Cu$Bsu@*`IP4ZFQo;)~#-F zNOzI_2Wq}|+6$)IJpGa%c@`A9ks~wPzbj;I(o~Bv+s2v9S057hi<@&||2`Hw7Gz2%2x4|xbm z^A^AFT?Nc-FBR$}r&gYX0_z1G2BFWE^Li6cR4y3fZHJK$52T3h&nJDb59&BjnIXSL z?|<5GO#Q90=<-NO?HxJ@=<42`VSA<^5|5&nVoHXe>cmLmvyC~6lj<&_f74u{jSg>( zor1pkLOFtrF`WC@@5U+qHuektY+waNI4nUL4R?}XTJX&rAnRzpYHpL2?LEb4 z29JuVdG+S7>4Mr7ge!G8|Hf$RNO^b<-KBWU$1w994|8`&<@58D5KKXn)qPRF`CoOg zPzbT5B*3#5W6lFgYFp+_PTnIi{@H1SBA9a7cGV-7lS*k9qc~vzLgKhlm-CH_{2N^^ zB&M!o2E^EGO$%RZlURP<6c&yK?@NCxb&vJBx-Erv9rF+$yR z5DmIxTPctNjckOG<8^v+SZ5AIy>Hv(?;0w3nyzL%nYLax{n-56W2m?NADOIGC~ZK6{M6@w&%TZm>YoIE4*r_v1RwaJ zEfoP4sF(daA5OH1@MC>Gi+oYdVbvzI^NKMFgmI=y17?s!{3Q597f86A>=YOS)&XQ> zvipzV`!!Y7NF7BdF2*;89UZ|bK`EN@d?-NCz&H_F9SQfmdh6vWSCQu||Hyt8W?1#U zK~zm0dqpnYuSHe@Isg+Bo!D7CW~Q+Jp7r+c!JCIlH6^g$h5%U>W)F&H3b1T6j`bcI z$t&jzDFU>bl4Xo>)ANR?RrQ|@LO!eWPD6Ar$&{cJ$E9?50g=9W73N^a-($nIXR-sy z@ikt(`rEfXI*KA6|c4zA=;O*r)>*SOE zk{$|vG<)k{nK!*mfb4-{WPT#9LqS#_H)^BMUwJ6Y4K~!4iGZWJ)em?{zcY0a>9281 zLQmZiMVMsUC5q`fi8M5PA}K|xVAuv9@Z_-L^-FWh$5$6+o-VaMpB*6Fi``XX$Pa;^ zH4$^Y7w(GH#JG)%PlevxJ1M8^LV@O%EBiVR)SS_3fM~9K%`=Z~?*dh^-3Ul$QE_-A zW0jKH3igBeq{tn@B}(N!(%_q%>{Z@zb*CPQ6JPuu&+Qzz;1&|5M^4hfRZMXzjOZU| zz-p@dN_r>C>8!ze@$hK~PM}A-5!5}#5Hcg9kk-K+C_M5%bV2XuPVgM4Ll!2{=i`_! z_f`c1kp)hh+hoSRvUm@r{Z`U=h2#*Fb{^&g?SL_zbY%Z^c^#_E%ufacD+6|_L*RPO zl?a{}=%W8#0Lv<(AQ5q*3c&9fS zOz+C=Sc;|vBWBxfB2xz!oEkYsHC$6lpOFv+x+@2F%mHn$<}7V@NPzS4(z~703YCi9 z@g3ls_PYROc&csqGnttBe(CQQ#Zo+CRZ{aSr{|*7KN3S)OS#Xuj^r4_r>s z_;1N7A^M$_3pe|>Up)Co#v_E|MNh{}LKXafuy25!uSY&6|9gw%Gm^GECt0UxY><3{ z+LxC-gYPQ`#)Ww=GgJ119^*s`;io-8!IKBXDiWQQC%7h9xy$w{8^&wJ2@J?{Da!h1 z=Gf^NbU$jpP{}K7n35EAiK-?7(cir*CCluaBWcrL`EW_=RAY~gN=M@>%w$!-#I{m! zH5dYC&^Y(2X@@?e+p}~h-0h^3>?H|Q4bYYPYrNzP`q57^%mD0f$`hoKEAu_QF26a) z&+)Wzo3bY(5I_cg$jWdQKKb2o$a=dmt$g`BNt}e7XD0Q3U8@J~Ce^c0nLB=0%nL8t zaJ1Nn>`|?4{@T$0RfYn8GpyOid`6gj( z{nFL|F)Dsx#7tnU;;+ZJhn6#M8cp7Re=>I1Um+My+&xuZFxRJohn=1PqSV-bAM!7W zld(ioyuCkVkNzjNbcxKwsQmL$nf&n_lH)&OGFB-Zz7+gvRUPJ<5iuaXJZ1z}30`Cys@Jh`&w#kgsOnv81$d71} z;YFQ?n+@}|mHo!gT(J3y$>lJkiJ_mQ@mf!sfwL{>w{S)HALGrC-Y;8MP@DV?d;e{7 zjz_*W;56H`7CZMGhyxr7dzT{9U%6y7 z##g?KFIjR7!q~3f16LUzP_G@`+*#uP(;VRF@#7}&BR^^K4VxT`+%S6fQcug6B#bIC zwX#~+^;{Gw=*D&Cs;Br4R_^q|GJr_NehJpN3qjv1@1o!rwU2Zz+2HLjx4On2?`Za0ON zpqA&FBYaPl%W59VWJp+u^sKv6&C=3+?d}dy41uKA)`%}aJO@7E;=Rc0{#NW+McA)i zNSAmJfB0Pg#T&_;qV{pFAw=KMVTSO!sC-rgKAZWMQ9j{m(uc*Hd~Ew2y~DW5QLOy8 z`J~JEt)02LR$kFyEjq=I6E#aM|H%HlEq!EjrP#0Nh;TPs#>ZzVJ%2CXcczy$Su4z4 zo~=_W7)5Nk0Nwrq`MN3ss1D$h&%rTQZmOI`eTbVDJnE4_nwiF+M@gltS}H}Hduafp znpz8M-7}sjjG#o=AvN1`Inv$Dnmg0bl=Z`fer)MSgYc9t zmK6YeEzP?>)0o&I@72WH6HG4Daz5Kkox8N#*c@6j*E1pN>OZ8#hT|~d$nZ4rt$0h! zYLzo3NX+nJ7)cE!_-{y4sA(ySg{kwoFmykF%a%N4c;#R(R48X0dXr#+9|s;xY0X^s zFiRtCj{domD?Zmgyp4{^U}3>Y`0rU^F+yjKn4|VA$DIDwqKdIQiI#d>i@`Dtn)@pDL6RWv!jef^>H9j#t+AI=`dJ|pxMU6<_bhK&pz z;`%2qZk^(B&!DttZ@6M(Dankt@P0$_Iu%O)$oTx__}>rS3A!>{zSNex?br9%33QJj zu-ZVyM-qrV8d@Bk$d6q7Aj z3dt;o;&$J)Q%_}~pLpDwK_O|X4UO0Tj zYTI_WLW4`T77flXSkn%o3?)cB0S|4%bqcH8E zx^Y<53%|44vnqW%6NyG~Q-(ycQPuNHl$tG!E;?8uub=7M==ej4!0@m3Y`TdCt|A+D zl?48E>;b~2Dm+b2P)`p=Lv>-c`$ksQ{-ZZeOE@sw&hdl@*$Gj5nsf7M58rs<&9`1n zlohMiV#2!08{IH*YF@Y}pkP`xVg_0ggJ1s{#%!j7qoMbmYSU|hZ>@F8J+BTK6Sc&L z5nnC{Q7(rLay^{v4kz6)uRw+w#`b+7o`rc$(Vng#Xrzt~=;k2pe<;(hWtk#}lVvmb zgtD`_dfVO*p08Mf)#@8$=8B?ijK6HEPm%LpJE!14xSVkD&TjkTx&*Tj*MJ^nR06?z zjh$L6SOm7QFxvV~$DW^eRBIRfEkt*9c{bb#zHj!vm8pL6z_DJFNAdn$iAUEk7CDr- zJ!?BsC#?Ep`7XOTs@&b@+iUgYS2F|ajp}bcb2J#Bz}_K}dzME4hw~1eYD{|)*y_Pw zWWB^vhI7Ddtk`GO<`rvM7}bzb!+I=t+It`7@DEtL?0h)a7qd_k`TSX|syo{k#`X>} zJ6kerUz-cT2zcKA+y?z!u6Ypd5g};}gy{G~>H}Ea%6SLQyWc_?uXNVL*mjmU>#|-2 z?R~FFaT)!K z({kRGYvc2TmsRP`+zGsi@0(Qqg9|5mYLo$6ar#poMOSmpG|~65{3{oqzQ?~;`GmDP z)g9x?=u|CXv8(JwElAE6Nq69LABobXf<}Z>dMp3C!8#P0H=jDwxXj`||C~=d0~?S+ z#58ntNcTbldywlx50mo~9>m0a#6F-1^UdavQSd-9W=%X8l>Kue28Z1R45mQQWDSUV zmqg=Atuv{aEILw`r94y^AX*eLr@6D#!ro!cb||c5z6yC)uww)qjXK*h1`yi1s26s% zb7lu{zN+k`K&CUs4*xu(-`MEi2<;iDKzV&qf3f{9Iw=jdpYKZ}jFo9s)h72W4(h_2 z)e3@jrhY`~+K8Ab^Nvh)g_=7OY7FIG(%XLL^srF+Y?qc^R3xXJUe+Y}v5vTIBFN0Q z?z;=?Y}q^W$HxR=x>kE4%T^EEZmV24G*nNQl_TkY?=nqfryYOr^oZy1-Ls;PuheQW zkvR>;yHH_#+K-*U!nBZ;ES;aoZx2?F-X9*JO$qZ& z|Hsi)$2IwWZB!H$1QF?&5~3g=-OQpyK%{d@N_WGCAl)Dx6G^4JL2@+GFuElM8!-lp z@B94TzuAX9yYKrs_c_-HVI%6rn1pzP{)$s3baW z^ve#v%XHAnwNfvRGG`sXe_*2)a+s+;a$V&|!o8lImzp1yo5~Ju_n1Bk2pT2G+qY7} zY<^|u4I!W6!d8iVO0O3a!aq;l`fc0z%=Wq4?TwaLjlIWd3dXrjkM0NB|3kF6pCFd(ib=4-)*IQQ3|NB zdFyhVjP#I((BeqI2-Ycr+{-U8E&C_eR{n$E@{~#bUZt7V$?RFTH(6h24=nUJl|_Vn zxoWfi67}vDs^%Ga$lsXrt9YaNv|g`NhUeP|Bvv zaQWdqxBiWb95OBi__`_Y*Td1qZ$grMq!Ujk*a8K?K6a{NJ=%2DZ3`hRJHU255U_^* zxS-#K8T#gs>)mthU#T&|U8i@hD=$C0uRm*wFUnTKW{YZ%%3 zDKy{f3l;Cbc;%+&AK{o-A(6o|@b+d9H<90{rSxZJQ?l{0F+e_+7n7?Q=wfQ_PV(7~ z*EpXgyh58bug^;?7nY@3Zd=8@;-uTl6>%l}YSBw?NMrmk>~15990L10zes(?1@`Qr z@+9(C&B(D@Z16uVoZf9}ZVZKaQGI*LBu^%XrQe>((iml#)UwyNU|d5)RXQ>4NxH4> z%TOmf^K1{j;@EAB=WlW!e3dg#fl~~m|GfwV2-`K4WThm8R>K!AfBFB&TvI59zY+w2 zPW`(RNLb@6oN`XLy>6l}va<2mc=AF&^@5EBo4h_U>F&r7E#yO&vzMlZQ@1vqj(hLg{%R zIAI2)G2})6yD}2y%CBR7`52v00lXmx@Jy($fpHuEi7ZCKV0!##2PeD%I(%{D#;A!Y zEZF3Tc?=%Wj|IN#3yOju2|u%MmqF<_C`p0{ zUQJ+}z|BgeBOtxlGu$^D>(raP)DOlbo1`msRJ(U_e6Pap$E9HdlJWk4;p95FV|C$z zCAJ9G+6=?9%>nqw^sMvZ-1^RsN_@HhpU0=n{ZSR8{0TWVja}tViAG zzQbij@~Koui%&q1LRkL}t@?^H<8q1j94;9qP{>$5GsAB?IU2MR)B`#xp}u|`w<22) z>hYE|lQ!41BhpTIdL^sj%@XFXXIIZ935}B^Cn569T(R`^~@lZ#@-z zHWAFUsaoc~er{nhaZ-3mYO0WX3#s>#K~H|nw>Vks^*X*)xxthkpQe@QBJ*hoRv0*r zf1ZlbB+wlLqvs?!&**{lE8o;3irudeTEa*0CtUc3zhf)8cbF2HMH6^r4hctVlvd(Y zz-#b1FH{q|WUos?mV2uUA51Ru3?s9iEN3Fu37t3u-xG&OVvo^%lO)W!^NrWsUHB1! z4|JDvyAagZFCLvp$-aT*Bh4F)*I-y9{ONj1R}Hb0L~ZgJHrXaPTj4%F^cy}!j~+)| zI%8qT@ZR|Yu?in{ui;1Fp=LM07lfZ9%#nZcJyrq7GLLYq?Oz~- z`t!!H*+OVZH%HnEjHa;&ilZYJ<#WP855U|@_U11+=%jO`AT<6>Oy5mw-sdzVu#3*8LWa<7)o|&H^EjZJpTTR?-479Lfrb z&zVxv&JM|Gq`iyTt4C>{%E!FzT)5OskaP6B*?2g3FSc1Pi}c`WaliECk0r!na(s6O z38(5N^dFfOEJT05AdBDk@gwR3&(;&xP^gVh>qnn9nOWZvCCer`4)h1KZ}v?e zs9Gp!ny~V2g-mp!ZR;4P-xxm`xw(COQKoR45fa;3r&}y&CS7zx&MgV{3X+*Cx>{(* z@vM$l!4YT6_~ViW2ONz(<*rZ}2r3birJfwt{&y>#F6O@Z7(he2E`HgCfpRhS!WqbV z?Dp@)w$t4B-`Zr@5jW3d0R11GcwJ)fOaK?&4JWAk5o7XaDyV6g;am3ID+_E)wWH|T zT#RY|xQv0I1k8gqqXi#tcd3Szo*C^#Iittipa#VZ<7a$2G}{m-p#Q+Gj8ojekv@!k zO=EviQ)}@b8Ao9Tj8>$kGj#s)f^_$0v638*e3t>zIEP>L3;fIAtrfY`cwK-N@zy43 zM>I$p`QpVj4tP#rDBjjE2ePgoUXiNVxo*!~wajYw-FaFcaq))4JIV^mx-;hU=&Nb7 zL-{TEn`E*0W5GaSAfzv19_qnZ1&(%tcl?cysnndtnY6bDLVaZ;mkA(PGDxxY~DB z9PCq-^m#lhJi3c>spRg{Q1NwZAgD}VxS83!S$?+H=nY>uF=+^Ksd}F{$o5m0L&<%A z1z=PuteUpwYPn?!92BTWvge8`fj;!wV9H~)Hy$z?iB(mk7wult6Zbq>WJYl0%a^9s zVJb=-7sSOI(p;DRGQIx&^)FB>ZM!Sl`3vu+#^i4bvAwS{S?v0GaPeU1O>Zg|@6=xP)L8>{vmNMjgds#k|%&flbm5^7ROQTWL*rONi zC5_SHPFWbKdt70oBx|WG#eFpkXm^*Tg6`oAXp@QGH*YlI9cL_8`eHwRQejF6NxUd^ z@4E-5_W886W$PRV-oqYTO`AKPw}7x0TLDfA!=l|VAYZw9Ck#bqa^!frigz0=7{8v< zBqxV`yvo7T?%R5B7f6vW=F3tfkG zGZveaWg;TpwO@Q+9F6^%&}YofK5)ai$o`K43(g*b9uE(l3-92;X?KTcb67UO(aGi6 zYtJ6t`CxIcFHn(ePldUzBempswCw&h=2XhnA*J>*JCleMEz)X(R?KUNE zO%*x>y5Ngp%TC>jOb;I4#(XE z?sF4Hu?)NaCzx99u!;iTdv)J$u5{H0{4pmZb}X_v(ww@{>g!&}&Ka4$Aivz7cXCyw zpmtbBJ==d|{L!twZ-7{Mu@1i_r(N_@4@vJ;%R?LtyEn2Dj$*u>CP)&s2bUh!{EVfTA)<~L_#T#TN%)h15kYYPM(3~QOdlpBR z93MI+2@F+3w_kPbRu#;Yg|^p)-c5R;a$5#!qR&;Qu^ku*`e0k`HSE`y|5~l7n@IES zGH#+*B6+Ool*9K0#iL)FoE_PJ&+29!3sWUxCpfwD@2g6k^#!!MteL6MJnoinaa*LqZKv`0MzaHy2@e*qTCqXoEy6bDan0~$r&B1c9-)if695mu!} z`_+a<;~cnw&y;~Rdr@>tgjJ&7nTRL;{GYF3cRjaH@gMU>Fs-9S5_igbZ3e2KU-w;_ol8CSE7mjA8Cbg3LN-KI=_)(V2*xdNDG6!lEurq|Bwu_3Pf%98bB1gM?}CH~ExaI!Dmcz@&U?e~*gs zJ_?7{=p%EN`!p@`uoTy;NwMVkXUtotKPj1H17=s6=(97c+c=766%=qNintC+#IHtd zU+DHMr^HKGbus2eb*+I?`0e6qWd7WJbg>wOK{G^O?xnXYc4A%`Av zF?>kX#6p^)-5WxoAb|{chKVgd?u`$-U>SMwMDjQ-F>xwxK|O>>5B zK^}deX2RWFp%bcr`To}@8p2b6%;g0rNOZ5oxqJRev}S!;@7#qFtMLY9eNOezV7s=U zn+|n2v-rB3;6DkD*d*01e)#E?{`_hc?;iw8=)(MA@`ibVe|({M%i{Pk7oZ<8|P~7X>&w6kBbyD7ZaOJeSkfN7tsJnLjM9cw#i%YG=|kLaL4OT z{Vi3&PEkov;CXilQhfnC#_1X@9xFr|_5SRFv^NL^ZXY!r8O8c#0?Z~CmzWHpZX*qBn zu;sF)wo?M_J>r}1TFQ8;YB;NPv>cs_=Yh9-=x1vGZBR}i z18s9prt&@ikB%=-9o5vDCdB1m%N9qd%q})4j{P-tq1o24XcF$wckz^Frnj5y-q@fd ze)PRD?9HUFJ;(Z$0*yG(i7B;wA3GETqZT%+;3$gCzE?C#q=B@dZ6Y54jcQ}P+$BZs zXiz#M)!kGxH<_s84j!L!sDyokJ1$<|_?gGQUViU;`64P=@z= zy_$L)QYzQLP0d^=&gdvs0)8{6z*|xu`|Y{DP~!Gm(IEF>n>cWHliIx8pj?AeTwq>? zfujTMKYx9*4wlAdT%1Wl84yO#|lY@%E&nj5U+blK=#goB}N@g{hE)AcJ4_Q3%47rnir%Iz4%t}%S zO%49^<|rKw9Xd0U#akf3(G`d^3fYW$(>upq?pK;=2UrJ%txu3xjXyL#5Sz30&32QDPR=pC1SuPtGEQx`>DD`Y5KtHE|o~>=P>vyl-ZM%k1{v*6r|`B)WHVf=U*pb}d(>vh63@uEIg_34uM%xrmGurmAEVta_#HXX&;DDHqa(@m zp%+h=T3h0z$f2HBjQ^W=?Ar#e8YP1LAQ$+f8qsy1O2JaLPK zux>zGiac)OUCm{t#;@np29~AD(hkr!XO$0uWX#YsOK6d?CnSU;-^$y;_upNMtGgEh zb~EQ-+>?u5A}_@V`Z%&;>nV6q-x*z*0U!~%e&bj$DtwJ`<&XFxBG~8JMCoV`|Hn*O z;iPvh$qKEw#3BGJylbYYk6$Opj{2Y+=n2M2zlCbOIt{t@BP9AnO`o~tld z$$}6ly)wVec})lfqaeJlv)&c zU`KtO%Ls?HH!nm%S}qwn*OHG~IuAQH@lV(ke=UyW%j?h*RV&pnV&y<~2EKL-HR!8o zcDKm>kBllF(2}~|R!h3QULvp?F6UYlTXR|U=4tejYCM`yx&U@imlgwG_o2{HAvtpO zGps!jCRIAcL?X_8D4Y2-;!B*@F5GH6B z=|P%VO`JJeA2Kls=}e0 zRwc`~v=^WEfKohWgRg!|qmkwr(*Xi-P@`m|!@y;ICB?J%ptiR!L4TtN%fy26oAC(v z1OBJivV1m^s5NnOdyiBIKqY>Y8t3axYZ-5X-by7{+zi_W4=O?fp-V^$TS%QJZeUsb zHv`$H_zrO4^ZJ#aXZ+%@*10?^gh1Clqrge}V`L(tGG9L{l)SyxZ;${k-Zn)9f9n7; z?cg9EYh=LTEP+P+2L-O7;4|!lJ%IHD3wF269p;oRfZ83)M|wIWsp9`;qU7EnQ1$31 z82#%*kwZC}bmtsNvd>Prw1; z{ku#WpPPz<2?UOQ35G zNEUUf*dEVlVdHvyH_wUuHRUVRyBXo?=B)A{{aWW+q)FKI3b&ukHksqDq&@4+Gx`$> z!o%oyrd-&DPW7hK%(Vs?y4^8=YlqcD-Mc>(V2HN+M}I2N_F?uL%%fJA)L9--3M{U} zRDTBk{pch9Hq&uBo2lb?B6Sm*dghVm4&xSpoCeDcIV*_zO!meE*1k@N@qfW}IXBVS za7I}iz&NqpW8Kr6I&~T3memBhx)%VzdgMW^46F6qsD>k_vuC#v_{SRHcUO(1yZ7bb z3q8hZZ*hL#W7dv8h+TH9Xp4P6^ z5pR#9l9Dne25sg6!2AKp^Q@S_AUN&>UvCcAteIC?I6Dc3Z$o&XBaNK^G+S>I;v_3r z&8(1JMZX+EqLdP5F{iyM#cO_{hTjdaOQs^`E4Iz2|6^DKGU30W0o8bA5DJcaQrWZM z!kky#9FZX0#IN)p*$4~%4K}vXc=hi7LO=Ip<;?fE&enSXJ8Z6h^}(k7IF6?=Bt4sH ztdNP@#g!67Ol^AyVH`QrcvTfZHimISt2g>vST))j2a&vPSIY`|<@I&m-&?Moj;G@ok1- zBIOu`fM9j=F~)>~xf9)RdP3aB^MxSrGnX?MET{&MDgS|F=4wBsaAM-{xuCxiMdKOa zXdQog2Ur1)!+|5?u`a9}8bF$2pmD_5Al`vvEdmk&b-3z8fImx#%k+{&tR`#0YoGjf zRFK7SxS!O?w6`?o$%aCc0MWEi60^^*WV=?|3&DIRUsjdPeSng=WqB6?PXoZzs5Fsqd$_prkuU-q#FEJAnA$kR;i5y@w%iZbFjW z8y9Y>m~#L&nCpgA=f-gpD+yHlg~=Z$os#2ljheH1bqn!5`G8XYlhAzr8|jsU#mycWx0VLPt{CuF!QazDL@pFE!I3E& z;P50nJ-S!6zZ{wq+4AoM5$uY;9hr^yBfZiuQ1bf*WmEl8MqFVo1*%JHH=lNpn1>!O;|(rAw8xiCfs)Y_f_OhL+8G?L7;*`#!Pb<#^}iv8j}zwfiRP zRXyA8mF0Kb}b={x%1+=zi;!W5J_S{vn9dzuL&Zg0d>4p%}ebCoQ2PN*} z$m2l*1=4S-a}Az&38|B)tdTNb&NI1Q9WW3+9-gDh5i?vkJ8pu0<#)S=HE7e)jZ3xK8Sq@|1|~l+R(@ zl`q{9vz2s0uJdD2?Qa7^)&-PD(Tx!sE@1D%$ICJN|B)3L&B=XUdeL(;%)k!_5}~LF z^hIQt4{7*44L+d$qU@Onmn;-3RTg>mHO45Eg`2P_3xYwP!bk{|ud8MjgJBHMlTiVc4k=+9S_;2sqK5?Qxzm#cK}e zT&V}Q!T9gxQ_SY4f@??S4_&{meE%*`lJZnAVtCrXS@-A;Z(Is}=22VB*RjJXIZg09 zhL2ygSq=dAXcMixX2}UdS*hEvv zqMG$UpO^9@n4pL^XY$fUZLclY&lvcVa94-U#fCj^n?M<3#hA-Yz>P}tgRtH?4b|!r zvhKLdQ1;!oZC2}T_UeHr)o1vp^^`Zi?tbOubrH$cWG{*8-`VV5wfn}2EQ_VJabUwD zY2;&F$Mt{BF`#zzLQIUwK^BY8y&}VJ-|muk^S`Lfs`x%yVuA?Dn{Qb=16pxP|5k40 zA~gNGgwp74`O;##NY)*$vuTGcG4K~wa$)Qz)b_=9ib{ssGs^KIh~L0G2^uSK?-5)- z%^=dzJiR1#sg(b^$|EcHQRp0L)X1h6Mu}@QsLtxyT3D`m1xj(@5KE`++>zJu*q7$) zqy^rjQQtO7yhZ?cHuKq*@ja(IWU&p+V>&=q@u%}05krjY;92q6?aAeB?x@e)RZG(n zj#f3cUI0|z%cuZ-B@czVYSw*sMMh75Kuh95^^8qaKmMbm?C@&ww+JIOhBs{V0om71 zEjjAfk*TGnc7UaZLWY&ylf;4-Jw8;fa79)ZSwI4@0~3mcqX8%RigxjsC&(T9XK%Gj zhb3k``;CRp_8>eRMvoR)BW^}8Eg_Ozd_mIz{P;R;&0SGEFSg~g(;e2S#?M_CO( zu8?>@RJQbCJ+bU&VVOOZgqPWS*XLSA({yU_O+Pr=H5JbBqIc59Ey&c@DW7zL7BV|}S!C&*w4_2b$*mhR>OwgLaddYBh!tX3a*b}pd7K8Upd{-;TwQ$t~mandCN=SZ^dN;Q%>-6{f6!D zF>Dm{XLPvnoY-a$Y1n-q>rZ9JWu|o_e;iNpnQnj&iTtV4b(|Y-lQs-HJg5!6rYwn~ zvibc>09)ef{6VcELS54e7XWCV(?kFQk=yMN z(<=tLmeXuYfe{VhfOWsGF;^a3g?^g7#4oEW1jX3h>elHEv~GSr3NsR3 zE8Dvf-e8=24FSv`?GMs9>M_pqUVqpM^LP2!0Yl@!vK-a#A)%jKyVwu5vaPi5Z^dQz zYvq(TqaSWwJo=hgb+3sct_e{ME)3)a_`X@_v>wR9u-e2CL3)!pKplkSv~Ts#W6x_$ zdOf23Wr*mGLd?+vDuRjZ`Ej}FHFs2D2(cwa@c*E-jz=pww zPF%j4aPv}^e?#60zcsF{v0PEDh_#3A`!ZW$v`cMeSr}R5>r(xMA|6kB8a@}7^-b-^ zZpWMer zwk;{KZT=?V? zz{&&Au&>J>$B!~>*(7?7!rzMJ%JM0x>)XMHGwvNnSX?d{T#UZpM# z<1TbMr*T60*ZHNVa4%nW@^eUE%?T7A)c6=J&(18c=-wWOR&e7ZSX1j)JA@5$D^D)wQ!{KAYhm-lnIvsh5UbHZ3Wb zQJC9D)pC*U3(%9X8tvHLIbYI`pUfj6o74q9Pw!@~KhS;_r(qzm5y?dBV{V&oUl&QWvuF+(QYvTOCA>BID zEy*`AXNVO3*Sy8RW5TP)ZGGF^OrvnzA#%JV<6 z92@uT;L+QH+poG~Y?$}V9A*|Kq*M!H+x6du&8>T8Nw2aKH$|;{E?-d+VPkxiJrEX? z06`2JBeraqCbMdl7d_6fo_#UEel@4IUIe0p-q*CTbwV%i#aJ{hkL`dYU;^DuIs841 zxqP!fCVmF1*)J}nUa!3#D*1vuJHFhl)9*t5&Q#8`*DlJXq&TxX0~Y4O z-yhM7Uw9K(z|UZ~r>n6(|B+=n`l+JefD(MiHDYO314bkEOFqYYH%pH4kh*LyHdhGX ztkhLN4(sVB*j)oYZtv#`u+!89+hZO&lDk{5eqXdnU-$xdz=7~Odt~=!kr&AOG)IHi z$n?9wHBJQq()jm2)DG*MGaF32`@#of_GjMf6=BZeaHP@b_cre|FSo1fRK}etQ1bwZ zW=H-_nouW7#}m&*PjeSx{2I-HnG(gSwz7tK+4T4V4b$FW(Xv@zuu`7A@0}YzMjRH1 z_FJ8?e$H4xTE)00OPfoL@o((v;mRYD8pPbE>sv#MNpJNn#rQaqd4lEvL_FXXx)Q)1 z$SeJy-P(KHHqFSJgFrnl!t_=(mQ!ng)tYmt6mA<-o9Dc8_I31$kp2W+|E zal0!a(%pyUwrDP0$9d8ih9Sp|S|(sA@$&8^gmil_U=~&n&395#CnX#TBksZw^jKch zYJNp6+NO`T#p%txGSs>8Hxm-soQA^CZ)*d7JS9jF)@3iYD!Qqc>|c9*XNBjZENRd9 zGV?4R$61t*g)eOON;K`^A^cZO;5|L&v69dO!5jj~%QgRx8|)l3tcS&(2WDwYZCelkOVl z!>mb@f=b>FsjKqRs;r2WZugRuu}bjRkR*E)gtNd zi!lRpE63uO!y zh`8?8lm`GP5&2rSEvQi`bcgPHQ|1GTs?UTzXcQFbXSWZ!AqSyet08_HcMy9u&2DIewJ0D@=1);gdGuU_&!R6lO#E3uQ>C){WQwM)bY) zKMrVci!Bt^rT%2iQ=N0(Mf%bSK$*)S*9er8AP8=}xWgbYRAI^IA8i6mf_^S_I5S#W zxC7-8zpxtTwW;;jNuO=_vm3=kxmm|sj9?gSeU8Ku_OgJoT`Ndl=^t-jVEiV(b{WoW zE|_v5ShKqEGcejru-}0(eJKH`b^wLc*!5)D#74fSM`ok#?SEvx#qP7EtW%eujFy$8 zIS$IG69vWkLl@ayHW@+z-tsO{0eE8V*B;}cB&o^BuBkR9OsM?#{&WCzQiodXy%^kJ zZKqRypZIG(_3sM=d4^Y*T=L-ewDic-HY%PP(Au7wM)B1j@TuXp1;{pkH=Pq9BGL_r_B95ENCgN z=5|*51lW8aYs!BHNS-#6TbHXWEjjStmCYT-c zsulQx7b?M@;ddiAIV>}CEA5utf+ZsXcrwzL6_$s+9T(1(^tK%0fb`K2!db|3vUbWk zc1C%|Kkx1HaTZW=fh|-_#5`lOz%=IrRI|7$r_j}3fnvShMQ7NjciHl{DaTXr%mR#m5^$T5)qETI-~ zM>7G9)f3+EVxQBgG9(t-Y4ZOHwRSO}&1P@E>BhJsGV>ExzMf%K6V`Xci>FHD3!$q} zx_u_^=RVu|blr;GurIQ=-(~y#Vr!$-v|tM%iS~Fp{`9;eTK}7wk8;n2bnpEh`DChq zzDeus%|*v<8j~>guhLiB;lPmdQz!C~jC1BL`eJ|PMx1{^Kv(C6mCr3r&1IvB*nrwX zZT#$|_%Dp%w5iOz!&|7JC+K^f%?POe0;Mn1Jx;%b1NAE0MejxtHxmuMbAfwBL}weJ51=bBbfMgxTOl@U zj&uWc(zz&E*RT$627a?REC zNYe3^@1=QzRWzV+1^`L#zVrxdkBWTxmT-vrL$vdLlnd9+B!{LS%(mpn8!Mz<*_opI zY6Ekw8b`k(Uh-`<3?UM3_-$swLXEBR!uw4|AIfmyWCfQv(uMl z`GHE=SyQqRS<$zocYb30HS`gDtQ~M#S4yk2*Rs7p8a900WJ+4GIAsg?t)yIRR=)|k zdp;lre_;R1<(N&xj_$ezGlDnjNL+|+&shSeFvaydgkv5(91pD*KiaVkSN(I?rh%35 z*u(nY>BS&gp27>F=filJuQ+B>95)vGQ&%R94YnF$hnI?PUhY+e1XK7Wt78NzBgmUI@4OBuPuGagYswW;2g?vYL-|yQB#GS5(C8 zx|`5IXRR1&+a6Md!b&U z=+2|$?6XbwQJ*(u)!pt^M66-`moJr<*3`agA{#WWLt65?p4K;^#l zhctmF%)JiO3Krw^h>wHpVw3D*jB$wJR3DAWhe50qKcub_FZDMX3G|gOle;?`ipuRy zM#U$>q@U#a227#`xF1lkOCiZB7rsvlZ8TqJ?R!sXer_Oqc$>hFStU_EavDdiRtA=* zjj8!49nxfNbHilLPLxL>Z$`#8@!!WE65-;iXPuxPnMHN)=fcDFT0sMRul8S7)NbfU zTTVN$$^egN9XV4~%>6>4<0oLCnYjVR8NE~;BBok*F*76Wf8&yUQm?G`Rv4Zx-@Wb{ z#6(Nv-?IWicgGNG!snyTA3I{L0y)=2s(YAebujS|yPfml<+nXtOnM{z%~WNSN^UP) z?u=K+o>k20`y8RP?bqV7$T!7@a47*OK}<>&=?&)STAz(pL0Rn6;t^;F0fgSEUdMCi z0IGvg*o{DW`k%EEKd1Cmu(yQe@+fc0-(ebOn?r!>BzwyRg<`JCE0XP5-*~TM3e5^9 zleyjdyM}+sB{w~wR@VLs(e$C~l>tS|(Xvh0aqu@ZwM~7zrSPuJwJ|L|ZLw2h<^<7q z&&^S-|0<^*TP_rt{C=`{YWC`A+;X<>8vmGxCevs@|8>b;we%~8Q~rFG`=$5BIml3# z-?Q1`)5zUi$Jh90p!Wk5A(S{twW*_mX+aPEFzZxG*GKn-y|}j5jT8L*=8~6R1W6-g z-4skLV|L-rV;uv;9FKAvZDJNd*w#6JKdB6RsJHj&$rIupm+fNQJ^zf}pTCUW?eE4X zt<7@QM*s6g1S^Qy`E~Xhr%kSBsl+h~4?UTlzTWmJeN{@~4^(`4U%dd=inw?mKmbW*aeE%7SORu%PdGavRH$O_eqvi!lmokaTtw-TJ-_~0q zY5t!?^|2MfXH zY_~c2`Lwv{I3?}^hQhqzm14l8C(iq0>s(!qp8977>-yRVx?%>3`?X)gkYw!sXO%f! zv}0~wK}+!W8y$dDQE)QOY^q0r9Nj#1#{NdNzCA;bYEJU?XSsN0TPbGBQx5r{^U7rI zPk&xOUK~S`@k>!xWj_l#evMp!rTe>aafS>Pa^kQx=LZqa<%Nwl0R=HJPVCI-HI@OcG9f z5JUB%UvsvlTHD3;79&;hF_+AE7t2ciPlO#rJ*-bhOjz9wK44v4zgYgJ`1}b*wDcHc~3oN6%K8V;-?L+bk53$>wTXBE;F!&~n4EEy<1Pf|;wa z(3fywQ?3VZwxC&GjrP!5uSYO*auROZ#*6H85!Kw~w9ifxSob zXn~}2WG?l3b-yrtDAL)b=TzLb)_ULL z77hwR&MX~Bp6lC2Ln>#YibuV~+}3!!ad*qZueXe34PQzj_&N!?KC6*puHZwO4K)Un zEK{|Q?*ZY@#wYFm>*uRjcuytucpdv})o#-V;iyePkF9nfw8DT+(X+-Gr`1O=7yyqg zjPIK1>dmOj=C+yVfD$k9K;`~(2B;7_9ogxP!FzsU)z_t#)cRrlor+*vBDvk_tpL-; zTbfmLjT}i_|8r5h{2xel*&!o@%2=C(+~Zna`1J=rm;^p!A%4J(e?q49I6pL2<~_Ea zvU=4ojf7S3*NV2-nJEjJw2$d8U3!x!EsAP!jNSTWzY=Emi@_@&3$8q`H#y@t6*1Lw zq{j>QmIfJ_kA}#AI@x{#U>Q{=BFad^Ew}OrE`c#M=ZpBmUGN(-V5i7EW>XQHyRia{ z;I@W3SA^?MFf7IMvv{|JyZ(i1_yU}z4ZItmi~H=8EoR1(n7$d3r78>b&0z0vc+y>b z(IuGPf%|ii;I@vr0`}jB-LuNZ4!%n_2u|>zB;NG3FTS8p1ln2+;evHph@Hq8=V{=O z$ifC660t7ivfOvUqZ69^&&y(?qv8{+>+bPrWj{>>!>>tWmDK~_j2%?gLPZUHSDvA% zh;KQb#inm&*F6vfMy~Dx=a0@4QcaqM@_fzc3@mcG!45U&Kdy}7`yAd=dm15rU})NY zi^=Acv(;S|Z=apvj4m4w{~zB<#ybVGGP$jv(ffzJ9jfL^RCilNE6SUzIV1gzLwGG) zT(&&-mCygD;f*#tpmW4-XC5VgAGoxOgMKDB-m}`5I|U@pXc*oPbpTsYwMgU(T@S z9ub*tcJPrT(95ji^|3BBPn%r}&KAc9`6SN(n`QWFt@>US?&}6?-6d&!efC-e>AbH6 z{#P6t32crcD+*S_k>W;oU7gQ)V+`imdc{H@5Wk-6=6{QD$wUuNt7apKA2>_mV%N%csIzk!Ay=@Zh7dkk` zvB#lMwu~|2)q8U6_z?m(myS!Nk;EEwM3*$oalVINT_H##_5;cFYQDK`nI3hAP$SYv z<_ioXN)e6x6L0{IL|O33ledu!i2P^vSh48=VhQ*QzZGSjPp-`;zs)bYB&Cjr#IGKe zU@QjB`AAO&`b+*urJk)!mbR`i;D*wFVe-JlodRUg^+Iedw4&?*>Oz z{^guZ|8W%U-NN$&tALdsygnX4W8{_1-`sc5es9$ znJ>}vfBwmRS6+flm{5v>gru!H;I!6QLlMVkQWEZ6mlCs0jXv(ai6J#5bBXI5qEpma zcyl?qVI9@QV5_376kx`a}cLO52jQ4=`0rt@~Yi)@hee`tSTL8=&J0Ef>*|d zZ^CZE4Y6#}|FLuyeoemb+Xq1rK@g-<1x4v@n92t!Y3Ud#B_Lgc35ZBeKtQBaT7*eA zqZ>xY=x*4EF<_g&`}@3}|6s4(*L`2-d7Q_2yni7SAwG6##0Qi2H%$`HemoDd8DPJh z+BCSxxBZUlubTfyVlzLeq_B?1){V9Mxj+lFC}SO$*(-}}>jOHeXJ*6baUnn5ahwgg^_C zEn+;VM~c03qU5BEUpK}$0@+g+@}l#%==MCGXk>j^v2s_`UT#t(@SHGQLQGAxt>MOW z;g%jQ(*n$0+pAojuqkeaRxyYWE_3a!u4dD8HT-7WH=CM9H&ZT>k$N)WKVcW=zqxNI zOF;Z^No(%p!^$-!fhJ~YZUN2O@=H$(NEDqeio;XrsXfSj%Ksw72aGX6xq5p2Ram*TPhE=|*?`7nQ z(|d*&B@o+5BF_Sji}rqCk0;}w6qMBGnF10GdWpWK!SyHuH@7uJH-aXx3Oic`tcIQ< zK1~?>&C!akv(bN)C~0RxdR6-Vrq(JvHiu&zU%N@SOSw%*Vo{?k?x#(+bI7DiLb$v5&`pY9dXz5ZbUQaEJ6i68i>qY@M+9khjEH5mT9iMd}nR7tdJ; zcri3)*6SvP-U+U^CKb@PaU%Irk&L@n%;8dHFKLzM&1&Lr{HRWgN&QFiAwpbW2qNOe zjTaKR*X2N%M8*Fm;k73>DGPl2f^pV3?G@kVe0_YGQ(VcQ08bHxwk-VugugYZd2rFS z!v}whN-Cye;ht%-d_W0km9b^wVdO}2&Al7`LB>OOa+(ts<)?;U(()gCG_m@4PsLYC zk}VM~w3Z6JiSMpYSa?D(wZl6CE`q~0%4roEnsXPlL>v-8PxE|MDR&Z=@-^lv|QidX+I5J1V@5Hf9XifJZTc0apHP1 zXgCVB6JnxefVQg;sa0W@;%V zr24|?*cWjqdva=Zb`HJM7aJ`(e32_#P}zf@c&-)`G( z2})&ucMu`>U0i{ik zr5eWjQjE8}i_-+4ki8?v2L_66SV)FY>x0mr8>UI_8x&sqN%3JuJGuAsDq3-VrTyQi z!x>YJ^-S@db=%f{==-`>y1nBT(jA2(X3CCz=?bV;!+>*;7$wK}f_Hzn}Y<$}h_S%!eB1TgTlg zb%m!F{YhA33G2&OMpV~S}wl%n#orZP67p_iM(FTlEc_teNdUzi(wfF?$)F%!031^gjgu z(*uC3DEj8T?~M&PFm%pBsxE;}vc35wCdpxSBowg!*)HJwTU7JCXS6zUf00v(MSUfMSKmEL9H~<&2S<+Zr%meED=>XcVSP> zK}iFOx#IG&%X(dWAPU5e|p6SJx*N8sW_ZybFzJMU0beY!bYt*aG(_h1-JPGpRUH?ac$~YdnR+g ztmEuI(Jnqne|+h0v`Jj6(BkXSVYk)Ck%kkSf3;%Z_O>2y*fMhmeuX;ZMqqjLFk4J@ zsDs!@D`yQw2scOAbRE~z7Bp8j-{5fZXLkCOo4??op{*nBbVvYu0 z`f2zL4KEv>uQ%#1a{*Tvyd{XQ)02GfFP=A{psK#9y0t3j!GlJNT!DUL*pL?=zXk=V zR}^3sbj6h~cPWc&16RmumTG>6X2Dc-#l3i+$cHRDLBoN3@%Ct^PY`JNTxf1rqjbJs z%tob_R?!Uwvy=<3R7LJA5T|BN&8cK*s}pU;MSZcM9!$mt2ju>`nIDIrSvi#d+}!)j zCap%te~x?Y`7AKxB!NZUR~A8FM?89%Z{ZSulJ)GBu!6J&&do`= z6eIFqUCr|gaZU5b_VHh$Dy~dgiXzUxRx>C1mKun*gdyCltFs{(?60YtNE{+B3>;Pb2jXEd+XBd?#~`}+WoYaf7BbNOs2Bpwp?v$ z<+^O2@>Sy}g-iQ`TVNZg=Ki_vEe~AKZ!lnMrAnRf-4NB57MdA)PyWqY{KUP#$3LD+ zV3H^pn9>EU0El7De4$<|wixFBYUBHhsRG)~LOd~n0A_Se zY54e)yA+rj_v)z~6N+a@^M$n7!*?7K^dZ+6GlK;41m*bco~bw6{hTAw*ysQS%14IaYBs&v{3|=7QC>MKwyz~^dPUua=u-Y4#xmTB} zS7ohS3V`#t(av>1wileWR$~!84FtF5Ud6f?odrIvpU}v$eq!LQ#MhoARMZ2I33Imp zOMcYlq+t1v1OtS+_le904*_gJKG+KXa!ZoOUp1ec&=58P)L0DSj2_zYr0t`3ng$-hNrp!Y}uH?rXE|soDfd|&1aP1JN=;WCKfQ#Pp z5z5v9HKP&t8S3mDFj!L|lowB$1!;iy!tYvgQ-{r<+_H@8nZuz{C-eg5q={QeGBXGQ3u?Lr5#z^jDvP zfjy4)C+DB5mr)3#NbdbJ(`=I&8Yr#;Y(kv-4#gty_ahBY$&ME(#GE`&mt@V&JNSN6^cee>w{qKl-=!w`Hj(6OEVzXdRO zE2#z8e&gP}rLY327Yp#M+aK)MwE{_0vB6bTwsmp5j2Bq7!@2-6e4!R4oy4*mH<2Hq zt}NfigSOOV=jbecZZR8#SOtGyM(a>J11C`LTFYk0AfT~MC9&l;&{h}xBVow^uTD2F zpAP>wc%%|guK2vEu2{83{M`sBo^Ja+pXKw~)F2$O{abymcit!3=j zh{M0Xv~sI_cQb1-^HZ{iYsU}pinoV`3(Y-kbOvjwOpA*Wfu_s?^8 zeq%C4PPm8K-qfby_^)#%HcShrZXDFH__Z}jW0MVOba$}t$E%y)mw;pNlUQ?=TRvZ& zw=%4=5PoWUAL&zz5CvqseXzs|KsaHRb*=%`a`>`KQ5k?9a)$kEICb|9+nA12zJy85 z!_sO++l6Rv5h_Tgpmy0bdQ{Uj`TOSi*@QO3gd-zx3Z`@Daub>mGFyb)S zFKkqdQw7U8PNMkB@}<}QX5X(?RYJr8`7+)4TKyxf{*CtvpDL+ACM9i)hTl(__`my2 zfK-%kEfeZipb?K{AGxzlgFRJZD%Y~wTVW&I zv@tvhPafkFmv>FMo_vZ4>q7*30uI1ZO!4-6fmRvdiu0y%=jo&0Kiu+uTgv~ zM(B2IMT_ zoQQXp-(7)v7nvYmuvg#SFN2>Ue!wMe`k5<7TZ?FFh#}BMnFdS3;~eW3X}VnM94ql> z@IS!A4EY&wErS=z4ZycQ(({?1R)y36C;W83VS2D49xNQ`>4am`zshJPe!AGQGz4i3(f?}2;1-)AhSIe4gkUop=W z^AM2Yjpe2qCKS9y!jlvDG%HecBW5j_QK#}^mrTYF{%)7cmI(+!O)8`8D{VanAMjBk z6wk<~JnuoBTC-u5?vBc?195{KK&&rYY6S4*2Hl+$31Mt5Fg9b}&VlhhA3my=o$3$M@x1}EKF={vX1ou-G{JL1GIe` zn=Qx}yu`%$nGS`~!3_7jt*j;4S`r^wlzYcv58r-W{dFObW(PEkRnnxRz2@>Y$WU%- z{@l3D9@wK}FN#XXtwEso%tFD$*3NTfUMHpN>%bt+#!c|K--luO+0NOa1c-}PBa%r8P!vf8sX#K$G}dP0Tnhh_!g zVj`RTq`dJ(8NJKccu*>=@hO`Y1Ph{2C~L{ND>y=)0E z@w+C)-+R3HAo@kzv>`7PlC`T4z>hhNI(*V{PA)nu06 zG){XNvngY_5h^$A3#R=HqL=iecqaXmL0sf*LWcfW_#Ug)4f%WO<=!qYFC#f`ne2nv zss42Q3h93qilU-U6&(K~Kl#z|A|&tjz*@$}5y$t?myVw1=K*T<do4gSvXs zcD?@K>TTS|hcQA~XI!a5jRFH7PZqdpzK>2g=SuIUX^C_zK}v5rOy)bh^XlgaGdgHx zi4MAD+Ln|_A=k#Z4s~!*77C1zo$sg6v(Yu|x!t>O-LzV=RTJ0$aPBQ>W#^ZW^FvGA zzK`jC8z{i=FF6V<&+C6w{xebIKrJ|>Lr=}U+{zLxz-z+$<0O+{o+AqqKa z&<{$>rEZ+GkzF+OZn;VbR=N97Z_vmj7op)oduIyYT^QGFovO{cIpPCvK6>!%iu5}k zsLUf;0}B)lfvbQ7W3^V=r5L9k+x#xWF7dc>_E~I2WjFgZg{5WpSx9~w8;f7ED z?#YbjT4{`KwBL;Z8uwYpPx3R_5V}|mA`d84!W=Qn_pswrFIh$3u~lx|!^y4jzCbIgBZ4>BBJT3E(_6%%|AKTcfAJlr>t*)DX4_VUg8E+sCngMflPN zNY|a0bNif7%~e<=Ms=i^$oht@r6$j8fxb>(=hu5nx=%?AqTC1DWx?DB6>Fdys9j*J zX*20jJvf-@o^18v-IiPQTpX9&?|vH%RJU)No?%M(Uz?@gskc^Q?Xf= zcbhzs!X_+rKev`I)(>&tJTT14dvEW*fb7FEkNfoI!mk*T5`pl|9z5q3jLqc+XKT>p zgEyxnbM<{cJdp2_!0tc=;vE2z9y#k>lD8yCHR39PhDPe~BwnEmm#%~6+}CielmF_{ zdg9BH`Y;R~KG3w1bq41m{POEU&%}<@(UcUo!r=4e+3&6reyd97S1>)@$rCiv3O!+d z;I!0ox-OLaNg425hvOwm{)gwVn^$VgC|feNXudHK&mo!)@K42#!B4`*TO~2lDS0@) z|5{to+1Rq17%gK9N!?0{F4aQYtQ(xB+t&^7l(uJsbi&rrdFIu7wusD7mUH*_Do;!`5jbRg_2$WCpoUN(wozfhmGx6!K#p>$@F z`z}U61eVZ-6J7sPR3b-&sID9%_8&<%H!5V+^B@Ur(wRe_*;6Z@R+aCv>*$cf`bqAw z2}-(eurFS0<#;3BpF{q{t1MW{aJ)lDuM_TZ(8wjXqB~o+%KhQMJ+B&VRSpmEkkNPo z_X44wu=Z@PZ-PE>ih*;%)!fmC&CHl4%W-#lmUr*UpTsWR&KFE7`u6@uj*j>(?Xb@f zazeTCV~8*MdVh&Naay*TpbA7shQ2&vaWFPA==#BOhfFwB$L%z8{USyfi1w)KVk6WnY<%{+O_sNKpzC!vixpPrElHJ0s7YmI(RCAV>+ zQrK`X^dK8^|5~IQ_N2k}`f>H-r;Umh!SGs4#O?Nh94Ph7E9qZFF_7<7{RNK|xm*8~ zzt62S>44u3oL@64+m46mx)ivWKi-hhcH#B7yCy)vTYDplrM-PyIRjpWa1O#hUJ?0h z4nE{YA6>KuM}PjANMt=IkI}LyE8%bv9c}znbYkgWb+Wc&l z@JvcBf;Yk{R*11)m_*J|FZg_S-v|&>aePR;u;+Z5=f@m&Ou-oXC_G;csx)UTT5J+` z=!Hs3ib~VYcZw+5Q*^X0FfTV^*I-}S^X^TK_dsSNyW-$1fjt+NjgvK0R#3ynWDODW zgE)mc1;+3;l6#_*hn9&=fuBx16?rUK{j207o72crV20p7+)N1I4yJTQRDnJ!bL|r! zjQ>%j@LZ#O{1>KG7crxskkI@~IzTRnOG6sS^bh%trsWK41K-ATVql%q`WAr*^K0#o zGt9LqRuVzX9Y&%--Ci|pqP=9CrNKch>?ff;Q25ASEhU;sW%S<&siT0o7$eyhb zH$gfzSNi|U=6V2p!@z9!!j3!Lrg#?ig7x7*f16NVhKcN=;4S)=L=sJMQ?EG;To48< zEVchko8ZPhHR*aX*LYi*%Tl1eovvxmJd7dNfCm`ik;36W(P~5Jz2%I&mYMP{E_t-# zg00`Hs7fe;Jr+L+lpCcxJ-yi4<}ewn*+CYHAGc=#X{Yqer)P#;7%0t`gwAIvf&42b z@Sea52q##W;|omGb=8}DjE@A0Gi_L3Yj8>6*e}Cmyr)-|~#qHLVi>O2w#i4@Ge}1b={HUGX7=8dkg%uvFTr3Qjqc4hY5Aem?fl@gp54=d)Hbcn z(X2gWo^T`x$M?1XD>BdCCfowWdArulhM#NjS(FvD5;gsPicSJ41u1qd* zVfg1#N!uXg?=g2;r!|D}H{5Wv3rb<#+Y9}>P}qqjpxtrEvnq@JX2SAQZY<#5is?z*Re1kPS5 zUcD})<-S>@GjH!BAWWfm`_jbUF{Ct0&ZA=V5=Ol9BL;dc87RN|El^7TZ!S5GBWoEzEz%d14{B z{43fn@NMTWs15{5o=`m#5CoQ1*-H38FH4s5?eP1&2$4Jq8CTi$B&-pl4(qR&dE;^O zSCW6u2hvzHX#V`}@qyQ>lbsc!%Hz!nO;Z{2gz-$KOF?P3q{Ms6H|%ozRx<^*<>NBY z#xlJRGHQxe675ht{YMgZWs-_LI|}xzF7VmNoio(R)Wca|9#bm7;yOCQUtt&$*rU~K%*5Q$5Uc%rRNK%dX{TTxSD+V<9ed@ zi2w4x`gadWt3I&HK0v#jN3LduHmwFkI=qBJ^DJtQery6T7tUKLDMyK~sk9|wQn)1U zd8j>0FIoTOkla;YXKvd}9~7-UNa2~#NJv^r#L|t(j>uU2=p!0onPc6<318t4dEQ5N zeNN099bzD|*n_jcHxSO;ba;v>ylud0@u7)9OM=k%D+|B+vzrXg32iI>YIFGSUkX zIyiy=y*WAYL+AB7yk<4G1l0b^8|r@~rA@s6scSOq7DTij_dg$k;^YyR^U*uJ+v*&A z)xl5v<@^U~a+|PYo z3%G|db$BMoSB#))??9#5Po)u@@c;a>Ue-D^8q+b0@h%1pGI)kMwZ{s=2eBs27$B~U71|CSqz*5W?Oe%3;B5jd)C$6q_n5AVVm!{NzD{}+N#}F(npMi~P*OFWV~euKjSU(KRfR1|Ek`GOcYM#8 z={kAg8;6N<$ie#@4j-WKGDC^y!isV82`Ayrk6Nb;b26c9&N#0>%fjt{EJ+3^>CHuF zgX8hb1==oXlWzS!$F$SV)0cSxnVfk&w-2Xc;Mvwi;ze?!vV``2Uu?47w+n3pFyiQ zFUiO4efV0vVIr$8MFn1|bOh>5ivr@RGyQ~aEbEPypTC*;k>;0-+B6)P9gzN_))E8< zT)Qg;Tr2=yuEvdBo2NTiI?vCIS9<2Xr112K_?Lu!*2@Y!C@SPkG?go;2QswiwTjPThtfR{7F9Yx%m|WE*AXv(z2$Fi4Zx z|KPA^JV73;Uc0y=Xz-q;OMz?GRv78*Qd2<%;}-($lAaBmZ&LN7t3P~@Uu&`3Cf)&| ziD1zuu(fGBO3+Tlt@Y8MPmKjprK}+>>WTbcr!vl*I^%jW*F3_TSlu-jauhC0O-<-- z7ux6?B#O-%AwOIZ;0aQ?H}Pzx@`q@*8a-MPe;0X5ou z?zPtzbZBdz7z#jkNH2X77x`}ll`J1|7rcC>8a!yfsFFlAk{LKiZM+jRyMkp*a?UJ& zzPcQ@&jnmc%+$rABmV*FQ3myCXeoqz4i zcqgUbmVYh{`1?z%w#$^g+%d~tnET;`A2`q=Tzg{Ud&u9C_w5r$zn=t z06A+zSe%lnhP6{261;hA+j+k88|hL4OD*YiJG(LS9SLX8P_Ucu z-Z^gTUO4p{j|*m9V*L!PmeD@^&LZ}Eeof;d$n|w3dqqD7b_?O4Oufj`$}rndQCFLG zwH1>2vAQoIGwAuc+-igqr)roIUPt$tEzn1=C!!L_Af`=-pEQxS}t>oE!;SW7p;spE~>OzC{1+F{^YIE%l$&{ABnr_ zowcX)MSsgFI-u2D8Eb!px_hyXeGgc+>Vznf1j$=XT9IE(eCwXB3-D_s@m;HMmJVmj zaE_c!rjF&V9p77;cQ!FZbpg;3h(_{3XauYW@c^{)=jzK0$SwM-Ypd$a-Qz@?Un;h(+4eboMMv%_;aPuZ3DwrX^X%G^ zg`jmzd2mxxHiP4Dr|D%{gllO#8s7Qc9A`L?sZ(L$`{iYM@pL2sA&g z6@6KouqpFMB6-2ae!6uCM|G`f(Y+#a+c}I0rxsLV?NU3|=~pK8N-%IVq@-3~=%PnajvSTT3k(#SN|p?s6)Gbon&`+2Cstruku$?^jc z9r`asy4*u0Z!d;T&pe_mu+bg3F;sN4{Y6wqep&@L)7#3~kT!*FYjU90`vGtII2KEk7~zh9=- zjb6Y>Ac{CInNIf*vu`*j(>Prqu`)upQseEAchGnGAsFHgEmuY0DZAWOc zOhvK{9^c+_OCYV~&ev_{J@1h5#aWyh@s8U14DRO|h|0|bm6+>SX4xseFu#j^=5FtH z=kLjXuYa)8PH?}Zy?B`1nWyEheE&yW8-WauhNHbce@90Drm zFy2X#^M54KIoz$)lak1L6!ltd96di@M*Fdl&Zlzt_L`rFioUVguCg2ER&43@^+i;H z4-I`VJgXakj?wHCn%|kflO|yqXti0Dz%ulq5tUuqk}o_VMjwC@Sy|HOMv# zG0ifJM_m1z2CvhcaIB$#V(qV(E5Yw8_7y3DNtSrP+AL;fs z9+)dm!ORL(P>G-qjhMWrCU^4YbRySUN#HMf`Jf*|)(xd{z@^GWN;NItKy^zNy{Xj| zGvc{qvle|xmejoVnjQL-8_ar04R7cwEq89ttqXJ|`zKEK}i_Re&8um7AC% z*m;Xx=bKm;3(JPAdG?rOi3bLbv>&?t!+U`peG!#OMODesU333}Kb9|8($xQySqJgF zj#@AxmA(1ZX)?B=6o5f>I$Nk=n3AA*R+dlGrlbT+qNX&?90!XZIB~388?8O?k<0s7+dQuWbU; zpA#|^Z?1Y$2i9I5;^jCH&W$gyL8zd|L2S|jpA?(8biFK14%N`&gDWlN-=uV7|B=uD z8Y3Xi7F5Rl_sXRiYrFO0$?(cb`sG}u|H<#$jGIf$qXihk@9B~6 zF~jnj`zsXp*Kgzr1MZG1iCx$B$n#^|Q|>O>&pFTj#*X>Is0#CZ+^EX4>FBjwZ{ z(wu}QbIm;u3$7pGjlA8bWb|I3h!t_I)heWcY?hP z!T^6CUku1OL@A)xe`~3?eTgP%K*GGUBH(Q#JO@ylO7=q6UU+l6gC@w`J0JC=H8OV+*Mg5& zde_`ekn2jw`7p7-?`yhWhoMi7fy$F|BRsLi?VOKDeKl62@sGqKH#Pv>f5RCYc8lo( zH*4>8cB@BVYg&ZdPW}3Rw~+au3;PeoMnL=! zu82ql&6m!`(00^$eYiUrrK@% zbOMe?Z#Dh!{WIAEn=gL&n~rcc@#L2?dHu!nc`P zAB&12S!n?4PFUB#-KA2D^u#7uLgSNB{~n~->68(-yY>S`aY$CIOvO&i+^Wt(!k&XD zV|{yK{UXEdX+L+3{Fy?T@XVdCF%K=WWp8hR4ycP3^bj^j_FrwA;4aidDWi;!DZjYhm*n`8BOol4fMGj~~OhacH>QJPMC|<#yqJ0^*0h zojYP*KaOyUaS(7TZTFF8cQ*Yvm~sUhLA=Mi(4zdRPR(1@ubX(m=%TOtI_iUxbjD-4R&enb}$D!YbwR3>G5Ht#HXXuw{gD511j zhs{cB5btGK_#>TIPYPQF<_?uo2f?s|Of>3(!9#$%o>ws~3I*r* zo1;?R>V9570RbLTrmlY^C%bDbl(I)2_reJRCq6y;;2mp;8JqCJI<_c62T5ei=<5}F z3cl=4G<@nG3Fm#kYY@aR$}DtiCNnV(Saf$i`4a?lt=;~9y+ zL9AP^vH(PXjK~Z82&J>M36AcdACD7ZrEPumfsU#q3N|1Il-_MqDyzUc&fEYh8 zLo|O;&a*+=@@)e2lW}jofbXT&&}v)9)J(=WQ&x65#}lc$ivLcihN;sg)?X7`jgo2L%~f(ZJBO1~0_YN<=YV!rSij>reV zXUep?>w;hX>D6gQrBn!=d|F9XGg;XaqpB(^c076}p&g7rLS@R_@s%LiAAcBaic z-(r5IiIT^&kkz`(et)#;~-jwckr_7tC!{BmGRG zcE-x4cmZW(C#=w#Tkr*6_NRoYPL5zHH6QOO z0lvus8FI9B{GH2;eYoXoWn%l7ki&NwM|xc`{-l)+#Do&eu z3jea_+`O}K^_BA<37JL_2afEGN4yl3B3c=|Xn9e}WD26m`_G4uX#;c-qe4)6Hf_8G zzTxU6OkEP>_Dp~99hLkMzZ{C{!HE3dGR%n%!~CvnE?HN|Xv(fJ?WwXap{|vD4sSUo z=^kQ$$;n($_^#ukj;pX*o`NLBulv zs>5=@h^A;ihN05iVK;Gc+7pTjzk+gvYC>4y$M3R>BA621^xjYFNNk~a{K*GwpFZi4 z`%HGHKw%cUj4H*xm^_0;%PSYHf~f+fcO^@WJn!lLkO&1ZkScxmk_#UuN}s4Bb<~mq z{kf?i0b^$(G^V|)@Ef?oh05Ae-;;vEs&*sTCbfO)=bbj{GfCv!GB#@p`j&4BQ6VVk zr~jKOaMSHrYMNFgkliaW%qyvvU+&O)>RJ#-_7b?cp{0=5)4tjYy3G^t|EPY=@=&n~ zN8Do8`IwtY4#|r@O{^K;J82(y#ZQ+t9&_e&Ft)M}j~;_4jEXFwLRJ0?5s)jWh9Ei=pO+l^nJuE9Tq-ilgJcytord{}n;BO|LsfzE+ihk}o6)aT8)1?8FB zoM3rGuT*sc!*BM-W*P>v>k+r%3B}9UbP1v^p}tWMJkRuxqzHT+^sRGKvYl0Iy-3Pq zO*+}JZ8^1Vou$~!zm`_tI^W69Aw*m1N9~-&XXw(6M)k9clw^N^ULWbOwM=jSo<_mH z{5SKd-Q6!L)M)uP+BFqkFz=}I^z_Ji7G%p?p)CIkfrtH_G?Zm!1~2bB19Iem=X_3> zZEo1|bcwb8r2OpVeD_5{0p1Do+D$AVZGU8LPuWiV~ipS%Z@A>-|1!_4C0SIC9SWuDK#&-)`lt&fVjk_Vn@o zQf%_pT81v6;YglnbW&Io>J69f*1yK8k;VIRu!R52*LGA{1d{<2fViTk4? zfttG1#D2|t&q1FV&qWT*)&U3}l&V>!e0gYMj}0EXDcl%H zFp<&bhZQGrGAZSoB2nC~kCR68$-ArfBr7qj7osVgiRt7rqjRt zsU0ttbkvFQ-RA4M=SH$FtMs5~Wn>&&bg___N#DC_ z*pRM&&`o;WzAos;{MM2u)PK75p!I2juAsj}pZwQCR+lBQaopx3{!s9zS1(w=WjH#3m zbE`a|x|Y@vBQ(cm?L+p)uu!zTont`2GxrmhS$^sU4@fkmZY=Y9K5kZ>1{$aP-bLRz zd?iC%aHyitS5tog7azchl)U&M$FO)EcIWpCvUVl0P&aeU#;qiBdO`6g$8{?=PnUOL z&>F}xC{99>!fj*o4=T#5)njUXU!$;cPP|Vuoq9N)-z;kt<;qKYYK)&wko;Spj&f`H zG`te2C;Un1<_BBHQh3ACva8=KskVH(xa{-XujhQSh^r*l8_l!vH^RP)?=OBpu~9@} zkBRcjK1@()9mq}Hiwv#`H_IWkX+_1|I7Ybb;NNWiyJG6%OAHw#bg8+8YFUT-N&L6f zAoy8>$@*b_E-1KPWhZv9e8w$SJIADu9%@Q#o`E8f|>j7J^lX~jkvVs@?(*4MRC0VGbJFu{?Qg%@xi!92~vFl%t^WYSzskqyNs72R34&b-NF4IamexAWIxJAZGYpS&vr(En7|9VhHGz zxYJ>lED+?A8q6USpF@9;KRer%r-Y`6a}SS#cKP^f8*aEo7++j86uGx}elp>$ZN{N_ zBVntGyA>6IB76UiWhL(anrIg){|ld5WNPOoAeZyf5K9~N396_|M$aMZZw_5_CnzQSIqWv;SMHexI!#RHqfp4)l%vm^JjON2bpCW57E6e>mX3{cAHGbRHc=$`G-KRxo zIf{v8=>m%Mh&y>l%m84mJCSNHPcZFpzQ4kqlgIat^q!ci$+U61dds#!pN^Ol4$y<= z#b=YtOsD$l*EX#?If!j

_%?ai}- zY29A-I%K}6*4lXq{aoPwP1eHC_CRKSL`_x|C3-0wh}b@S1h?h! zdu-ttflh_wE%H$KnWF$xD2swe^fR01MU}3Kw9J=Y5pu$VE?1Q*?}_`I_~%W7;xTl& z9p142sODP_$EK;MDbQl8YX(Kqylq^6Mm$W*hmM^gAJO(cJ<>PW%pF7vWQ1Qj`KZ4i z7xMNv9;Yzwh7(Zk((#J)W+Zj8JdIzOTBhaN!<4rvzf>pf91%c3nQT9C97m&8Ru(8iy-nh@siGHd@$U`V?QP9PApb{>UkOYc^HF9EFk2SJctLZj^ z!Q>kxQ{Q01!>(DHB1L}4XGTieiEfObqDtw9VvE@muUE3;O0|~F_g>q?`W+kFl=Jl|0vLTcX+%|UHUT)p6{(&I5HISC(!i_S;Q_9 zVP3+4LQnu=;{~~*A=k;lgjiovhabatjVfN$(fx~W_>ouAA8XgSohO}>@=s&T+@W zunL=~i@am#KZRj_w#$flk9SlT+JPV%@4f~S2+HDrf8UF5hI0C*@H?Q1y!o#WbxqBM zeT2q3-b$BmqR{22h7}t-}asU1LW*@l*)T_=M^ID<`1=9`oA#% zh(Wba`HTs12BG=?X6@=fdAZ8lX)65O!OsI6G)?3{vjnQ)_2lw$ ztkR>9UDdk$36zV5gxfF3%ywToDaL79%jx<9+%N?U4CXo3f`0FN8^Pp1Pf&qh6}>n6 ztZ|a2w&R_f_M?bmbMcL+UEs-6L#q%$dw~-!RMTy|84q0z=2ttpgAyv`xSz$c3s&*fxPz6agTfxZa-Q@@LM)diP}(TRC-bEH#1NXx zGR|f03MBKFR8o5VD_4=Dphz>sOZZ%?qu7ua%wvps{uxXh@Q6{&QT%xi3(gaVu)04J zqR#(3@*E*+U*dr8IsDFLcbyOkuze!M3CYT5e?&QsFgX-WluA8@qmxn~@j;06LkObaJ9gzTL9zx44YoNnikgW1IieMNWsz1JW;>bErc1A zrkG93>zOCnHYw7?Qsy3M;@zNMJx#6){LGse?Y5zWPlswZhs8iUnF=`$O{#~1zbg=;_PP6I$W92hxfDkL%9s=n}O5VbPA{_xS zFQ#;P#hQPo9G}{6Z6;YkEbN|cz1~LS@uAh#(K6~S|H9eZbtKI}D*zkAmswM#5zAmVN?`>twr{UY8N^!^i25;y%jgLhdT;0Q=P;Ks;qQO%ISjRI`0 zyZKCG3pQk?P$=p&SaI$Ef6yqX5j~;uw>%D~9&veiZIj({d(qdK$EwGkK2cKlWDn1j zwn8h<^ZBa7PSZgYvjfi(VZ{>L{B=kbbA|15q8E>Py+AW0 z=eATX5tx6|lTk!eiEa@YLP%@q>;=Nx0SkT8H=2e?D;`H#=inwNycdRgxTYuch4Esi zOsQJ7t*kdW*?lE0D=F^VWI(sCqa(MW@B$=T_Q&N*TNT$8`9eef#*IGYudmK4@N8mN z)Ec9M3JT4@n}KWyf79C37ZYA5f|_{4<~{xgH2f=w+yKQq!KqzIFQ>R}jMwn8)vmpV zDhr(@&r59QD^sx6mYa5QxxM7&6Y&*88&|JAZ#@f^2bXjnjfIKl5eL7yhgc+xZ8o}$Lt1O!(5-po5R;clXKK>r7&v8k%!0*B$;CDv z)?cc$V)19yC<{;%Y*^d!JL0crcwXiH6rg7es;^_5=Z_MeVgG!-Cw@;nO;`F zyH_mKOVNP;#nA1{msw-nfp|G-Ko#g#&9xwR`;_-FoG$`pTzN z_uEra>-oPn`I21M9_!7SGow?MqgU)XF8}CBy4dJnyVN*Pl)#^M@axv}T(5wpbV3Xx z+&n^AApXWxF#)bAd{E?3(k0jYnLPdpi6dh6{yN4lE&+{|Kc}+slQ%{lfZ%!>s zy^JI(46O=qi(C{o8`yPV6^B9New`#S_5YJ27G>=gayDnyI7;D~2gLw+f3^lY{Lcfj z$VGO$-EuZ}f@yzmNME@}aOtQ!mEKK*OpX>Iet(XxoLWRExG&7P4z8_mpJn{@Wt62wtIA6DO@MukJJrp>dO`{bqnR-^Ji|>S#dzjh8b7I>@+}$Ge9VP4-o z^q9qcrP1q?l`kE~Q`JyP+8@_cPS0D43-MI_(=FKw2ck+A=UPQQQ>S#Fq&;xz7 zOa0p>9?z=dTFV6geqnCCc8*r(s~Y#^id+V00xGl9Qtz5PdH->DR5E~@X6~l_U`<4D zmAUcidjjIV{=0)DO=zy2q3jUHrR{pF&;qy9lXBjX2^)P!Mye}GF&P|V+Q9&AyEz_I z5BZ1+u_(+FW7K_W?!vYi!d7Tkn=4|eZB%i+RB24xB@TW(`oOI@%aW@evs3-XTKnML z0gjqG>~Px*vT>5qb6_kvF{HD~K#vmRJ-6^+@{$-D*p+X8x%=hr*z!?L)Lv+4E}v?p zSHcZd#p^f6Ydm7w^1?g&)SKHTrLc|q`8#dXn%TZ>IH3?I8;WEuq7iV)R#kO8o1eZhMohnlIjS z03W-@u;EI_a0ok$!BmMV5h4b zA=6?0SSIn_#aqad-!QpW^%A6CA^1;qoMF1s&u8N!3lWJO;^RvRM+}yvJLw7uM(^2^ z>yI(Y>zwCUlsU+mnJC)dl{V3?Opxy8TKotz_1R4c1DUqB#jq^ALV@-g6KYE1Ww!GT z0&QQ_jK5A(jE#-`QG2$EMbUatuHC5VSeEoR&>8D%tNVQa{kRy-^}@p{NgXCti@MuQ z+pgabSt6+yuTq#tuC2Hrmm#H)`X0D&8_^e={u>FOSu$3@1WdPU3HCV)FsfksNFTEG zyWW)MP^qF<*?Ym!w-@O(?keQ^m)mby_6>1=tdW)+F_Z8scOC#(l@3Q0Kb!U2)SuRV zk#-R8nwup$E)kaYdDUviniSAnu;fIzld}CnZgAy-=)~81(wQ`l_kTX%@sk~_0>5ke zSaLt7r3vJu!(^5lcW#d_ zw(38Y6k@R(_XFFaZ{aK~{UFz7>9~2(T_{?2J}IJeXmE*J>xc6%;#X=T6@$MZzUXv3 zuL+7%{d>rQzc%^DWoVx9Xb^m0w{3~Vx^u4<7#lhJn*s>9^pBU!e6 zE?F8S_eABD_2~Ndj&#k$ndd_#nxLWXl;PBgDwrR!fFOh&|26u!-7VDjL{jj%cZ%vj zHLf|uQc&%c2;+mI6q^>d54X}Bry^aB6*>9UZ85D~f;@M9GJ*n6k)BiJ;6f{pD0HhvS>}2?X{>Tr?`I zn5uoRU--p9g;dKO@R*ADFpmBf zD<30cdd+g&G56|@dFoP%Z>?k+__4lIS1npx!)s;~s=m@Li5yGcPR%(pB7LNxjwOu8`oZ{!EjU$0%53K`>misn&yC)G-Atz#nUIQvo8EdqnhRE1YcD)8 zYEwWou)kZEnKSslrFD-nHszf~)MIa@F^@K2Pfj3TPCaoZt~xJg9Pm0$ng&8^K9HL9 z1-SoRx%54AHt_>o&1$FUB*bNxZE1U6XL^k?Bkgzcdr2){h>A|AQ#JPdOLNurqyjvP z$&1+5#xj!4NfL3tl!*8#y57g7jdjypv9TV!uNx>a@zVJ%&XwvWdXa6#o{bo}jEeWQ z`A76I4P)*JVi8Mr0hHzbevnJyLQy(OoSUEMtdAGtPT<@?hov7`&tls!OqVjfp}*2| zmDHt2!j<^J_aXK|%cx&TjO3eyfKF+=bA0-FLvh@%s4aqUlv+||;ev*aSLb>)Y=P5s z6etZj8unl7Aydm9;{Njk2E6`Ey?O1C?D|7-lvKgmY$ZX!Lf zam^ab)TphvDD9@W#p=-%T_iZou|-gy(!>|xn(WiI7nwSRVCEO)Rqa|>b!)lFfV+WL zzZpi>yQS$dmOSW+J$v~wz2A1NX%BWuBe!t;+v8h#k_4SXckSwUYaQaqLmBQdPR*Bo z3~Ymf)#m5zXFio`ito!mCaK{zlCl~g(HOcY4Hjbf?DLpvHLF59okk|6C5DhxrGG?$ zy`LfmKp!Lifv|bIQ+E0i=d~cg_dn^Wa}d$hZ}Wj^i#vpta;K|+*f{xoGSEfF{)Gsh z(fg&8g3_HYj!3GU8^JZq{f9Dyy@vPS<7VDLG&x%nkB^o8tk%H(E54srLn=9&%>K>0 zrMR7%YWb+I{?fm7kRy0yY$AkO~^f8ftCFjmFt4?o^y|0Cjh zyEK5AoGbPk-^Q$WQG_O5YyT~MCo?Hk5M`CXa6|9mrSC5znvwEr2nV9B+mf-#FKt3i zEmrs4xO3UeT(6IV_s<-gGo!?VyELm!fxRBeAh%)1{28$;s(HhJ^lkQ{pxtZ<%xb@x z$Mr{AO)Id}#`xswlX9;s6+=&cr01r+doAn`x)fps2z8r+b}N~G=FcE@tmU&_Kqq?+ z8WVrFyAqz?v7OW7_U;#Z)6da0CcZQtncuKhiZ_!|1wpV#vW%l&F+}et;iH^ZN z_3hnUv8B=vFyl(h*249PfMIS`o9DYsC;6}OVU;^W1APw7%drt%_I@SpZG8x)cCjUZ zPw<0sH{hc^?pdf8XZ4@>lGUWlyCX4cDYa2_Ykih2Lb(PO9VwPpDyUlg(Pm3i@N@DB zs@c(34S8^us;YpS!=gMuOJh#0fFmXv>$K>s{XGxk%Tq;6b9y16bq~Dv>G1$AmYhkH z#UD&tW|*SuM<0#ybV)8}NtUs?@;T5&Nv0n4!aEUN-_a%D+0zV|tTyzCyX5j(?~pgut6J?_PC_mgWAB_lx(LU>79VMz;-6 z#zG+B`SMj(yv&6AvMvB~3;ES+Z<^ArpMAeGFy~=v;-iDl=v2x?>q|7s0m*8UTO<*5 zwVg$0V+FG1F=X0+$lM<$np`AB0n)43kgpKT^vTp7zZ;$;z)6~Yp1%$GMv?r8aM%}< z$hc99Yx?pPXsO9Rf&W$C%AWx>?nkG!ZgF|_h;1L_6OdB~F6JLlzvJiBKc0VQz6jL1 z5Jv$VyCdZv{Ul^2L5$3V)~)zrHU=leACM8v3$NwcK`g~_H30%*epc)7#SP4C zEyt4gS~;T2x85vx3Cw7sBJwvK8{*gW0#prLVb@J0m<-6dS>+msg00>Rz2hV45}Ai8 zGCIP44_P~c8`ab@32X7pFC^D2m3GI8Xxu*eR;50PeHVlk=Im+QjQ4GGy^AXZ@d)1uofiK1iZSB6_sxZ7&j5h1CBt{Qr{x5W{dEcK{+md|!W` zt?XL8aGQVruE8-`d`kW<->Dv9hv8?w{-^7pk6m|athduE?OmMNNvLGvUy|YGgd2*J za>qtyWt*MVDd5g*5TplgLApY^2iRSD!23~5Qce4GT2T)}SMDJP5g5wi5D~DbG@+H! z7+2ZlS;meZ2%^kTFC{Gklm00#<7%)1^$+}1cEy3T1SEuOKg#IfA*td}XFaJpeS2v= z=_@3gBC1fG4?c?X+4xIrsdjq}`Dh)LD~Le;6^@`rj>`I;y+V1&XPbrLaaduGz8My0 zz%89S2uPcSB=S9oBeCN;8&}^MO1c$k{DYmIn>%wEXs^|x#6fl_=W087=^5_joHJF& zKg%5aJu+Ffgu8P2mniq72|YCV$sg7aYHUt!p(wvEK2Z2T_-?09b#FqbP&m8NkCO0t zJQ7s`pdl3aJ6Fst3MP{DzT^+A zb^b>MJb+ik2nq{$=DJ&qPd21YSu8nSEPtqzchV8qy~toc;-eFl2$!jbcn(nFIf;&P zjap@tB)a`uoZyA!d{da?o4@5T)%)U)>49!`WNX=I2@i}y|ChyN**$5Z@ID}0bcDKKDL|g(B>71O6<_yY4C2K4 zT)f+W8>e0-kOLqfiWf&$VDEvA;bAiiQ4AZHo$}mXR{#U$zYD-n=cnCbKK)E;Yyyor zL$o23vqzM`;Uq}Ai}!WgsZ@|C%Hz37uKzcwhmt+DwGLf9iKyRbI+D_sbP4>=0%P+l zjWBDTT4k{{oG>7+2GD%&w`%9Q%7nxHn5J zX$svI5!9UzvL@8P7swE0Jf*HFVY7|y`vMaz9~dfY$MIi3%DIB;4ie++dkgkloajUn zJ3DF5>UAgWML{69m)J=rzIUvoPhHxW0Q0xgpY#-e!*B#t5iZgOD1#9CtAAuB z-67tEs0|N=FHD0yaPy{uniX3UH$d(5>HO$JB;mtVF?+QE)>eIvC3Bor;OYa^r834M4*%xob;zHn?*Kk3oVz}>I&*rjdzT2#@ z|KV)jgdNPttBNxzLRqq$DTxN*2BRd3- zAC~qba~}j7Hd?ja$@wlpe{{Li!+0kQBn$Tc(oU=_p3gJ@(EdlU*Ef#JwG{L?Y#CEW zUqZM0DXh`mXy2`20^jU#Lem9Or}bu8AN=*b_?LX84I#Zru_6v6inu>^&bT zmMh6;N>@Je#*j4{TpkmoqiY(T7Xl4LQg_X32TrFc2Vv?y|JF_fP^+>q5~#X`Xc-F; zm^SxitGy&a-}jVIbOMYE_%dwkKPv7{?oB?SNwQhNKPIP)cKxlV?#)V$xU4!k{o<;L6HF2ea0Rok78x0X7Wa2XCL;}w)WW!pY`+QWSxK2 zWi4fYYuCblRd3v$f{KKDL%Eka0K5y8fWp7Cww=LjxdjHlZesDix%W;3OEtK1k3S)R zpC9G8$O<2$F#nit@Uk&!Yv}AC(Mx6zenv>5?lJ7IWc-j z7h;q?fzpA#BQH?h5!1s|EMOB7wzlqr)Goagr~!NCVQPF zNI8ePetRa%ELqk!M9IItm<|s4-{-n+;%v(fYq0Kj!2_lLwuHd#;0!+vD{q9gTQoEI z`3YxPjeu)oBd2D5h7el?T1$8?;Pvci8xpWsu#_MDg&8ntkqHzMV|%sdCJ?Ex&KS|; z|H3+%iIXPn%zrZ6ZPr--Hy-ja7cF=Weh=n^_S^bJ$`L1+M ze~5?;q4(gg_x>uDu2-mcC-v&;Lj_!!P@;AaS5RdA@C&uCr!t0j&Ys$LF66CDLz|0^ zRUSyeIye6`)>{UzQifX6r*f*2m1d9EzWsq|RTy6x;d-4XQs$eO56Nfsh+7*s}WW z3em{DGq8=M%YiFlJ8dSaEkn11CqVOa;CAsNNDjFO4#^FBR;?{$@_p7lXwr#erd!;L z#!*Qi$^Fdb=MRi)>t2zRlt^e?Xp6nL+pSj?d;Z&>R6?hxI&UW*%o%E$eRxrhL)_F| zMqHn>Yx*oL^`*E<>8!}}05NYHc-$Mjm&IyR4{fkKu+#GFV8~#~W9x^dC|4(Of|=vh z91HBXSQYXMyM38D$d&P!*+CT{l$w(`>%i`=1x!;-wE(6+T^}ZE9%eGu{WNdl*B&He z_fJtZm)`ZuIlDVDn-d-Ga-g7b$CcHn&qBehgWvQ^-M>rZng8-IWlZigcQDy6=zY6* zT|*5zzO49R9}skHTTP#fHbzPZ?Dovs_Wh)#ZcVwwX{rp+wY4Cwd%NH@-!Hn#_ zLzuvk(HotQmRc1iIA}S|!T|&(Fi)D$n1-DW595ARi=S&^jY@ND&(hQ6qrb1JFy~m- z>ujnp)hG3g$a%#43xLKCa6J&FV+ai~5wp-G6#%Y%t}nIrBLT~)|DeKVRQ!|vaw7#H zPFdjRz!7%|*+$cMTJnxwY@~<4Qb+01N4Bb4^l@*le5B*O5~7f?1g76!WY7==mwQ)VC%k+y!hasQQzHGMF6UN=EeSseoKlhFz(}XTe^D zW-IlXjvU^;5fOdju-16TXM=oA@*&?%Y=jt20<@$P_sESh`8zGj9K#M74R5Uv=N9f+ z_@;B~Id7Z)N98acWnJ2bNUUDoWvUAg&>+m7C|CN&dp@Z^yc2RBGuyz0ob;&kd1xnrbnrcwoY=3=MFr`ucoyr6OiP zZ|YNFXDD2bWX#3+@_iOq)}Q~(Eq8eLU-E@`DL_=UK}KnaP<}TyP9Afd)cf1m>j)>B zdlzo^zJjJ&{b){qQMKEb?>h7ONv6Yf@E!kuuiJrU+Vmup`|hIS9BL_Rk;w-Tg)qm8 zp34Y!ACNluog^B)|as9NmY!LEJjpWGaHbJ~ac^_49s7 zpz#pUe)Vtb4KrT@U`0eg#lWb6DK+Pd8RA1;W-|g^6t*epSwpa(L(2iIN37Mqzu31^ zk?4r!r=bkziFFVp+h*-Vj;o7*oMCl9?N@RVv$-4mOjFVYPu2B*`F30Pk6V}ANOD!(jFRV6+@9mlS{G6(~la_5yNNS-k2!z>1ljxzlD(-i zbM|9F_qWs>u!sJIFWh`Y+RG!;;*$j{LG%HI3V{`4gCbn|S{( z_BWZ=r*wY!L;4nu{h=QDVp`bi+{&gBD&CI)j@hSKk*)Jzf=0bhsjr>69MlN?%4Z|n zXnbu9QU4rL^!5IeL+!OMI**oEV^4i9ZSg7mX&2*m&uX_+$w^fpev9O_Ktu~m(W=wo z>|ZO7-E56jA89c!+r15#;&CZ7ITngk&i?eyVU@#Eb@h8Xy(Nn?KO?3AJVvSZMS?{0 zGnORLZE%asP+ZjvYP*H>^91apUG8&mWaN{{t5}J7~^&d27eJga2D805@5t7g4K>|gqFb{$y&=k z?El>PZiX4F86;vy+_tP&hlObAsl@j7kX3Q<_}FI}7FHQeYoT-J2pwW7A^#Wt>r}gt zZz!;VicIG=F7cd(A)St*KCXeW&$P-M8%c0|tiS~Oz z9?KnfK2-9gk08}_?{hfTyp6~~u)*GnW(TI-ogFRQ@p!fREL3!6ogLC(5`**NHp8z) zV!iyApM)d~qA&gnLzMph`I9fsu?c<%?)5@D&O!VY7gi< zE{phW3&Pjr5owPsHdObJ5uO@KFa=^EG8@9_TB@IC6%MF98tT(KX=hQYmpcFr4Gb!v2G{retKUkdfzwZB$v z06+t%rB?b>j`}MWr89^~;FTf^ z&q%72hrWGJ_-HQ5VA6u_3V9Ml-(u*3Yy~*D)HqZ+S-svg=L?^CPa@lw-EPLqVfRmW z=JEFYw>KB=5cZ=QLIi zBShn-3mN0enVDU=+#pO~s9GaRwcFNqrR@61`=1)QD(Et@Gr{6CBESQ3Nu#&~;MEXv zD~z!WW#L_-xelMSvmVCZ&bSY0rsHe)3G*T%nPcFrHk-FkFi#syUwzPJDgTkEWc%(5 zUo#(IwFqlur4@VB!6Dr@2DN%)zE<8>Y zDU8Qc@^*?RNJ3)nMQ-rzh%%z=(~j6(+(l2FZkb#1=a{yadivDmLKB@OoPl>uJ{Puz z=8IMw-s45R3%pYeSc*O`HYGeC|NhOVyBzb6Kui40D+c&D5RG{;lWvJJ@ zV3OmB5&p_Awq$PISeY#<&I|F1j!E)jl)b1CE2&nI_xoeeP`k=vU+Z3&1p8jY4qO}2 zB`+^c)CGF;MVej6VpvJ2Un#K-A%Wh`i1$qR+pr=)H=pBJ^LT*#U7WCe^7MbusqV=p zKEvu0}FJ#~NkL_p(spmZ=VfX+Ms;zUvn6%lpQ8E_ zf*%2h`!sSCOq8fe!T9XTyY37&Zi;<8|IXswzbb&7Af9TKYhSE<@!wGNKet*3c$WK` z^Mw5FLq_Md>@_N1Qub@g>lVM>VLR^E%%^dH!ALf6;mg%_Q}y4aELR#JLrU z_*%#pn{-oiyT#kfk%PMIU>>~Tu?8UuHO#bJqz2x7`1SeL;a937)wuttcGeG(743I! z^4)vr-)R1HDTo(Tt)oh;LNkzs;mUA~<$`Yi1sL>jVe|IG%k2S8&wx-_AIi1B@&v1s z%lItX`4hvoGXEn7fQqyPnhotPnZ>PENgtdo@KGzRW<4-xCco{(0yj`|%dm{nJ?u%G zcdMLZyk7944#0qF_9NodMl`-;^3D4@jV>ejrJ&g+d7dG!c=#`R7!ceH|Yr=d8>8K#NK?yeMs% zk8+#PU9sx1Gd=<(w6;fFc&!%axXS!TrS9plfZ&%vqZjeIoFfowHD9NS9_#Iua=H?8 z|1+y0{+j_`94`u-Y?52=l>Qpx$g@{v_!>2dUS(nROPcRt--W2x0?jjt-x? zhgDOb%B!<~2w8~o%#f#=Z_&Vzmilq|_HR04RB;#>0Qi_|58&eNpW+33aZDDRN$pj* zGxV_aUI7hURNp8fKry}VMt)%Uk9Ze6C@oYu2ydz;@jA^LzDgVd?Lz2M{GPN17(oANxkIt;YEG|P+5k>{djYkSf*$uFIa>cJ+H0PC9jDsd zKTLd!{MG}&toG4k4w#e8vwZkM9dk@4X9j3%!6Zz>9+UMoo5ZBop^;&g6 z7_EAP-r2ToZ6f&H+lj9r*TxQUzo4+cpJb(UOU$h{q`TB@7hzZMd_v}li`~c~h5!54!3cl$UqeCjK#SMGJ{XKhqvxTF}>?bn4;`oO~NtVBr=mUnnMJbMf zpe4pD{bv^dc`zy*NN^8n_f^)M-oZZpb2&K+on_VVv0sM1?QQwSHC#(oKD+kGBCT!T zo3=m6CwaW?ZcU+C7KZ8l&iEF~*Sr> z@SR*7oZW3x!_o0Cr)Q-QLHY^8*Q>rp3GIha8akE-nzm0}m3q?-aFqGt(k#VeWf)2h zCe0?kdJNiKytv2z5Yc70poDmBqwz>)`()i;ap-IG7=rl_(+ML=0#YP1q78oCa6(P8 zxSA4KIw(_@LE^26xSdw?iSF4LZxWC}4|rECkBawVH2-aLIpj?g&n+DSay%;lE}?{)>0% z9rf>8=N@2Ot zI3VB1XyIpwRU`C5ZJfo}e~fe#b@G@jsE^gpVSYLGhFQQ-I0r4igo?)Kc1zQ1e}f{x zcCz}7E-80!TfX6(_4n(^)8F$bCHy#38F}Gz7X5-sX7YmN{uwM_5nVn6^2pvZSW_w^ zN%S)e|1}25rA?5IAC&Xl=HW#}O1VFYYav$_)?G0_yAJS#rCIXj4=4mMP1dBGa^X0H2uYs`NS<%UbgfFt#w)2OD#XoEsV8n zQ?S=~r>dY9&L-6UXH$+QLCLw`k?V7~YOA)12wU-?Nj*n_Du<7@By?8$xJA8wu~;_z z!7ZkL0#<`R=4}9Z1smJm3&(g{m0HQ4lAo?2tB#_QNOtriPM@3h+05us>#=97&nBZ| zP_E{aX}7hr@P$H0cl5=M*fw6W;e^LCvC7sdD$MNFIq5&NBmiUCgJQFUA4!ysr=944 z@eh356So0&EuHFgG;;SZIPgyIuklPS%58!p zhmO%+iu zK+QI>FgFKX@M4jE(0`Hkve6*#dD5`C=lf5zJL}5IYG`#r;@Gee;e((1+pLITkt%1) zdxi0gE;+lP^!s=}gUZ@Tow|5sj`5ipLBz+y;POKtz^Z!=Hau{oW))nskvdwYV5pb{ z-WMKpSvjp?Sn7&1C!l8nR5@9nHNT%=^$l$cA_-ivZ7S^ujcQm<2-R!?oLUU!U7D`w zeO6Ai{e+vU38q^}cZQ@`scw|3s10BRh{1t@e5PAh#%XvCcg@9!keaQ=A2zerlEcjH zLU)2LZRY>#Pz8WWMrq7UQR10My21rkkM_@EB52H`BtpA=vMMvh<6017$|X(aQZ@9L z8peF}WrCF>pAV+LpZ|uu>+YUBq84KPe0Ju&NUL?-&d{1>y9~9OW3qi1!I8pZxOjOh z=)7YsBQOT=Je2}Xc;5mIy_M#M?$6Qh@&BB*nygzWZu-*iKiHnzye_<691Q8%lswwD z88&1&v@zJS_;+)_VJL#E5!Jy{Q?1e#^#G-pzn_`a=EVppawbX8hIAePqCeHDq#PTy!TuKqYMC|~7*S>v7B^UBENei7PNMBl842bAa-tX67KJ^Z- z8zbun48MI)&6ZG1e{@Os(rm-ii)8prF&TkhY4qQE+hlr1D9ZA`KhEK57j(~?R`%Q+ z(vZ```mS2biQIjbf}Oe;lJJiaaVo3?55XP8)-GQ zG?pIsDQ_5u(YT3ySb2ZI_G&rCr~KT7P>7SNteD+wICV|^MBbf2B0`yrlINwkxd$X?k```!iXb91wmlIC1E zaAl#Gt4>;IG_a@_id3k*aZB zzki;S2R+a)k*nNrEl_<#%2)WznYElaQ?eDZS6E|5Twh*`zDWJr#W=el6Y5`6Ilva# z66fFlu4f&(soTXoQ@6C(hoJY+VE+Ml;K8n=(bo_fslFADTbyF_bPjUZg)1MNjKYrD zA^S13{U&&w9*^hZcjk^Ba7CBgbUB8_ z%T?yKW=-OPPPNC82`X)~oP(^YFNj~W-1$(%MA~pP(?WOZ?#@q}BINSS--jTDv=Y+Q zxI#!|sH^I!_BAgKF$C1!i!$kZM>`_uIH*vBUHs8E8it4zwwb}^VPup+|PQIYAnN9y# zO^?yMGvyj#)<8oMqN{z;-c^&N%$HfV>pGEhDYap?EB)&DbE~IqkqxuCrlrVB_3uIY zR-Ic(0ZdGPeK*>GsA7=r{LT(Wq>msqbC^wi8%L~&H!exW%Myw?P7ZS1@#UR<^CT{} zh|YWcT-SxkmZ4sS>XRR<;@(Xm3za|Bro~b8=MtfbU}<9KKxiFW=tbI*&4d%n=QQ$i z=)&Xmdg|Zt9G&|vH#(-e>n!}rlOzUPIfEtlp8Cp|qBUqr!rHFbO;Foy7(iYZ(eJV5i>pVV8fVJt{vT)+toAkC!YW@?A_hH5P=TM z)0y^xgKq6REjLIZsUM_Dt2`}x(623)!XKx-%?tUAU=<5m>ddW~iJd!rm~67_gUaSV zmmp9wAJ<2K#en_OZA?~MiL)nV2%>vg@u?fON%$d+cyT`cW4XvxVIHRK7Ny?RYNaS3 zjO%zD7P&dzGE8@TaQL%M?=Cv%qm=&kt62~~)mMwi8(>fzMC@fYCV$fi)=2RfDfDXB zJm$h04%2pxt9fl)_22M4b7EroNZ~xKho7De7`6F;)cBsffBNkFb5@ErAd2F6%Byc0 z@g%rg{rUn6;;3@QVAFbLffXtYGkxEIoGB^%J7Nb4cnUgs#YBF=y3&P5ddqry$VL8; zX~4%?{Xsts{Yw<4%|Sj!yxOs-Pvv%IHwW<-Zp?1+xbgLd$!=&pjU0J70`*@`;)UJ; zWB;x#R!tjXo&6tt>lbp<7SoM#tMr0nYcY4a(ryL!RGOHBh4w<=o3>a!-*}-EbG`?) zYj1sSmLm82mb+d^IZ3O&2a6EMt@A0>u;8f=?rJR2AitjoJkJi40XRyw^C^G{Bw7Cy zuMy0b#FNL*&3`1kSpQZ6@YXt%Fr(zu=g@>!ZpWMCb{W7Mm|E|QgJY+T9flvZ)TA$; zuJW<8PtZcS1s@Epn+au zeEOzC%cnjyAcka!!xQf56_6Ysg_c(~3){_~Lt5VdzxdG`Hc>yk+bd)v4_}L6&I1LT z3RgXd8TwTdeQWeZIR8GJVq|M?<{FfGyYpZap6@3Gh#6+Bb1;s1w|N0q=eeZ*UA;48 zgQy~JS)aeOj=B8-d8f<-1gMQJ5|1mm_MYSd@dL9)G$}qsi+T(C0p3X`rtOb^ic9`a zs&>|xcCYbaBv9SUXPQuX?L9sm$;35vE_s(N*jO2b=+OgW)}1t5c;PPL@FtIgJ^J!0 zWYNtW<8`m6s$M7ONFL#AtfocKfoF=HN(o^vAkY5S7PE*gt8v6x+k1ZTxCUq-@|;t^He< z%KTICweY4Qc2QhBt;7{haL%f62|#wtQo=ch3ND@L{Wbr9?)|2Y+0R6@h@&OZamUCZ z>cxr4-#FBIXC^;%HyIr}RJ3#e^k0;m3oCqLh_EA@BU$sA-JZRENUIjPh~lCc55hL} zRgBkZ0)wJSroevqc@lcfa_GaE^SDFWEWDGYoGS|Z4fhq0UrRXt%RG6jgt(!!J<_HZ zd%7gvRp>c&3cUmG1pQ~^Mc|K__t)~bkZ6$qLJHBcJrd>_obF1KL($y2`Uc6ePKDE> zI*?0wr}Y5)KmR5U1qx4%TShQ0gE~f#Nri$=ZT+{o87ikGFX% zZT~Q*P1t^Wz##yG>7u@?J~~*FO2&2m8%PuH4MH5e2(d%f53_j%-%&95^=?x`pZ_SenW_z@j!PrJT@F&2HY76@gA zcWRbeLFoMCpVxf4S61TP+jJcqH+hDvZgFPmmpj*NbWiYq$@xDjA|LSQwTa{eW+~+F zF1Tk#1x{{>o!~&IzWxc6-XV3&*|PplwiN9!MTDor@!%mjsumVS7-^#n+FOGJ-)_?x zU|eemIo3|3x1C(iZ(zc;=I~7JwL{KfL~SJbY)^?a@c=(m&FHw zccG(aZ^2PyVBF7S#+GmfmRI=sU2i&37*9B#V$Q%XPeuJTAz$|$sG#Ur$=3uWPj{hs z?}u_QF2zqLnTgkXNtl@L4_TPZU66|LVkft68HyA7$rR;)b1-N|2OIJFE}Au~P~O4W|Ddnzk4Tl~e+a%R&- z%3pjxaEA~hXqi#+>B-6UIA-bX>U_&d!6}WW2PgG%Cut#dMQ*zU6 zL$3S|gNvCT4|(X8T0$(WV4k4$b6}4Mso^dBkEOH(H|k8fKLmyg zKKg#bts%KHh|Kj9+lUd~0ELv+9?+l29PnsIt2sW7ei6IdEkz-H-^YAOPsh+*>ci%l z)Q+U9q`b@g$KTXQI@f*`v~5NFWrn0> zAY3RBR<7ew+bM*JE+~8jzQZj%k99>1e~7#1-q|NjH8n9+;1Ur%>zir+JI>0#Puh`A zK-uzAuUxW-0wwwv-R(Su(I%6usNQ{p8geOpw(P7NMj=-#cIsDz_jIm*oI&yA`$u}& z=g~8nO-qxxr@{yP991{P7V|C7Cl6S*jxMTi_=^EM*q$zg_&b(P_0s0 zy-ZDsV#i)wMxl*5Y2WyY>ZU5V$T?uBrVuwC`5w^{^UQVYLi1fsgF)H+G}FM=cXkDF z$ znngTi4=O8m*c+^v`B7IB(^91T`AMVTN}D3i%wsWGr5*+7y zt|oi+#E*9voDP%J&<}zKCBcM(2E>BTg1_2Y=HM5ca@%jFdoK5@c+pdbkSxd>TqxM9 z-7fy4(N>IkgFeAhJJtvz*B+vPf4Rz{8GMfhcH*ybH98?CD?vt_?L$S=;wQ!{|KMR< zxM6d)U~$C6+(Cr-oA)&o^h9p`TURp@!2pH#*Pbg>g>?S)LA zPi6j_4(ul{B*rVHk5W}osW%=NM8-4JD8$2CP+J3IfAk8IdQVT4C&=+p$HBVNplJFn z^HZkB0!*62VL?p(ef*1pZTx@rOsX3o9$4`^u2o07@6tvzCCZGy*MN>ghaGjwW( z(q9?N8%}edT=(=i->eEcC#YPLqK%|cf3vx#y1v$ZS8nJurH!__gt<$OS!uPm{*wO0 z`^rF;DuH?-vAgc#>6I)Z$}x{mUUlZ_1ET_xSqRjD-=}}W1QB`Wb!1RbMM}^g{i&>v z&XJUjDECyI;i#El(Fhe3zo+^1^$=xM{~vRiFeynUp0ZCiS8oWy%8-h(^X4+2@rzVJ zz!RmUtamFb?tIPFc-l$Q20>xx^Ib>k^@+HjD=JGN&+DvV*>1EuAuvDFYWpwj{X)x8 zpU>FBj|NMFd99c7%J}Q5=O@%J`5Q6q9qr+8YS9v31dVuxIkx6z_<`w@o=O^#h;b<< zw&%N2akcb<#7SJ*DxfQCro!&Mq41w{{0z^aB-06f5yImg=D5tf3DLhzpfm0@!f$plehyN z*Vhbvu)CnyJw|Pe^O#w&*VFRmOyX&-_Hxo^T@%0Cw?3-rb5_YP&WRw;=dV#O_0x*$ z76;8sD^yG-Tpfxy4c}fBeiRt1;UcZ)iqyAGW?(kfm5!Jy7wW(IB)(1Zj`S@nZD+q` zUjy~s>ld?fkG+5|j|IW97Eb-9smHOrx3kTSJ+zhyNC|7$9uy)h+=L zt0cJY4S|!J1f!t_to4GS74;T;%o?&_7Z}73+;lJ*rMOOYuA{hjKll}4pbt%V7iwM@ z{cWOOlu({gabWB-V*R97xTBuZn?8f^eJHWbPjNP~GLg#ma^N2kH`2?qU+%Gqyw)Vp z{>o~YoWVWg^^-(Qiu222CptcICoB-QxRwzEh1OR2+G{9IPXGLN^JBRx8hMh5EaapM zijlTCsVuYm9m{gFqL<>@0srFoa1}SZ;fPJn_r^9duSiZnMKzi~+$Y&bXm^&6VYw!) zW`N-`8=ati&0eCtrLPRjS}2Q!KFdWRoXIrj#GfC8Z8^?PI!-}Rj)k=F78&iPKPfAv z8T8(anHYI|Xek&ViOONKg)Rg!)~Om#*@oJtC!Ek^-IF;xxh2H!R>PAv+U0R} z;kqQ1rkGuSI!xRKEW>?$Q1pljha%7jYWb!Wrj-lKpUts+xqPHWLS`2o(odR>5+bmg z*!YMg=l&cK#wJZYR*R7>QwBHjnXI}Xmf5hx%4AnkUO?KVARC6L5b&Yed?e6LuELyq(*R`>~uc?)-nQb;&@nT2!Thh0M%4JeC7x& zI)(HvbJ1#iDgn>?jAC0|O(W%GY};5>HfyVNVii8i=rozfio{LUI;{xZlsWiGkzOfh zWnY2|WA6-*^?4G{W;&Y`>)8BJ%GZ^&Xx;vTgRf<`uVuY*AMqK*5|%ld0v+ERJXYK&rjkY#+PBjxEQC-Svygz6wp6T|TS zxILiUjbBvd8lP5B8@u#kl!vdusUxn5p%Ara0_{Fx^FpB9^J6nm&AA)uJc=GEV;R;d zlB0c=gD6s&a2{e={#Ym2@<`UZGU|_cY{N(YSyx`uk6U4CdtTf75QaN{C_KGfe=U{P z$QqA3@wZLIK`b{Qw78jpn;#qU#l~XY;3B`Ak?#9*sKm)7{LIblS3g(0fuBKJN=N#W z>!#1y+qYfFdx>=Vz@vIQafTO;#IP=AM%6+74;x?9mjd-6?{3X85afL&R*R})&R;Z%raJ%8^7&jZXa zYjeqKxZ%Z%r2@CTu2Ot#LSlkHH8=3c7rS9vH;J`Fa++9=dg(i$F2+Mb?fKZ(jZ>s4 z7_nmXyIsCwwrBPDO{?+X@#F?~riD(z>W5SDP4u=7Fb!CM-S?2bX}E;hl{kPOMC#-5 z9{z2#hAN0yc-?(w_<_hqD4(-rbkh%^!vWQ%* zbx)gW|2)(XfIoSgc_C=))Lw3TN{uCNQ-ds2cUFTE^c|Tw{z`0`pU_>!<6pw?Yf;@D z%f$;hTyCdifcGR5^QRF`=H|=c=)W=kxBQ zmW)z6aZMx&e))h(msla<$2soc+b0;onIK-**9m)_)UKGvjcR)Wc;B$;#3BRw%oa52c)Olj?+_q?=Y?96K@v$OEa@w z)4Y5APd`aT;@A~uI#G2BFei8?`+05kO|IC0G4lGY=yx4f^yW9}n~Zbh_@WbKcL|*8 zm0V%{Nm=E>+p$|^LguGHW5J}Epd7!o1kV?JzgN+0%z}F_Jn>w|LbaTIDqGC*J%+5amO1cK)evf zhioM$rv!3;yGJ)UrN%MsJJQi6QeA3HfZ~@ZNO(S-n!itN0-T4u&z5i5~yUv{*u6;BDa0c zL{IN2wuS|I=DD6UV{dBk)V(I}=W=Lb3~8J+)m1bgm@DATh3gH1`l%Tf>Spi?k>X@~ zb+1LPW~i?ps#pYXuw6)wHkKKNnzrxRTN0nRUh2f*IO(p04;0FJ173|+{B)<_LaIJ* zqntYxN`4}8*44Mf<}+NuDrdwVFpJi_emJ_g53JI|)Mrp!mW{F(;UeK5(Ujib-nPGD zEGEM)CwCZ$TVX!%&JXMNz0e<$G#_|Hx^2S8)QofqwWzi8()eBT_7IoCYZ_vsRgA6r zWcM}ppt#vdB<$JgWifJGExdE8%`r7T69wu`zy2QsQDbjHpD;h8TksOR$e*%C)o2`f z^PznRIyla_<==0(GeWCFaWc=VHJN+Na81WfYmJfV9c4H)u|74QbkAO1Ekee$2}{hk zF?hXR-Rzl7&qozW&h_oAOI0n<=?&{Q@9%ev}x^_ae~# z&n8)RPa58^h2m2ZnipXQJo~@lwQ!3uB5}+%cOntz(j$^exJ{uh zj`vF`l^KRUyMOUT2%**%%htkNxbM3*6uVdWBXO@+9G+Z{cCI(%?05Ot<3}GXv>C+PGo7)P11E-a{jeu+7$w|;~|ckCF%QOVARr<~CoA})13+(IT z&v`b94&oP2hgm}T!P%L!9pd5#u3+8y?%%%xK+Zs!PG|grDPf@P4vj;?I{UdUL}utE z8ayuLd#*zKndA^hE6Ej-oa=~!7^I)XTIrtBvD-V;>Q^xD} zp02h$kZ$~>{xI|4-tJt#Y}3_@V+4Cww@%PK;x@_8W*eN}8V|D1>fd!2CoOWgGIo3> z_MF^-g80VF_fT&W-?fj|0@4m}D^=IE&O7WB?p1wN>|j$vgM zCz-Dcx*qHCDGhs^co$pLrLSy;co{T1>}pU+mHNx~85WfJ!+)w)ye_fE2Sqhzq9|UK z>&5%@>}h96mE$v(a|NgF3tfirYxrah z;>KqUZe{zq8fPJ}JRVciz%4gY=|a68H`+?w>5+<&jZ7X$zT)EZc_DM#T`o89QS91A z&F~|1NV-4r!p1M<{@g?~6OpD0R!Q$@XH+!N_ZU^4Ci`N{U2N+uFzDfsinE!-BfJ(QBT4KI#Z-2%~ zflf~E1oL{A8wTfVO220OGcPu`)ww8CShxix+Q!G%x^6SJvMYB!4Boa0@339@J;nD< z2aPTLh;b>q`=Rhhg2u`(biN;T_9^!GCe=?nAGdqRvWJ&7*q9VX)hYj+T>cc3l=Oj$ z%KWpRa_Qj zEcnv5MUC#s>h`-O+9^h}brSJrhXqDnj0j=jxBOHvh0znE_NM#?%##Yq{a*Uq>Z7A$ zH>l8ixb)*PdJ~8`uGBt9Wi$}(aVMtEBelB@b&r0q3r&Ou?u;kc7d~8ZaW|?Ng1WP` zHdy;N?rteaJ{TB__867C;Ztd0E3v+KySfOpPu-*YvR*8qko@-=s8)c7lxA?~aKn|D7uEi8TzAuN8}Yx-^`_uJLP{2gj4nkowQ<`xnS5S7&=aZd(`1=S?x#B!D%{Y` z&`@i3B)7SD8{VSwF-j0*x8_ca)R@NfM;$Wa60qvSLU)!DvbtIHE5Wg~yIX`&)%Q_g zyIoqhgmhW=Yjxk_Ng!aQcDH3k}gYR~WW~Ur*YxOlnU)(UWYJ2_L%bkl4A)UtVo3}1~XUExZ9hb-R zgs?BbU)+<#hI&m7e~V9eMs^Tvoc2|hnRn2}FN>KzxsczsV98qAZE|Trw9@_`k$%hm z*;uVgp;rQUlZ%c9y;?^M}xa&xn#$uQ9`xmNus<6ZKXi#s@_m{m9FU()Rma zB*64}g|DU2TX$x3Izh}^Pv)3JJaAtc*lW1ydFV;QepQATm}jYN3g@d+75Z`qOMI(r z$vIdQE-c(Jy&=yyN##Zrw|=6rg|XJw+o?S)r&n)ZW^wXh?aO1Bz0xwLi%y>nj=!ou@>8J9KAoDopdO!soS z;a_t7A98s#5xHH~%=uyAVlwh@PJC(^g7rGn$xqP&iD)zr6nqGa<|)bm6WkOGz3#m$jK4 zXgQkx{rTC(kDFW|XORU*6?V;oc}4SUel|z0RYXiZSG;s$Oboo?D+~vF7W>IZj)=Xo zQ7AEEoB)?T!dG(rBjR&ozEc({Q2+Y(f`G%y`osIT_Lim&iUg;=ScI@Q=+nL8ozx{#)T`Kn&E zQpbP*M7XcX1lmh@IN2iQS|pcJY~q!09Py-8Lh!E2J(s`nbi}OLNx}HHf19kj9V0xL zVPUS0@B$?Z3kI9s%>-@HxGMPzwI5AEN+cCEGjL|FM2&!ObgXyE4U^S_%U@Kghhw*k zj{e$?AxU{pWn;eceX?oNuC}7Yyg(gfOSLpgyfE<&Z*NU}^0R}8-QW32XYA`$UH$sO zXPFUl8S5jC(ASvJ=5M)-V4t&{BQ?y*!s=UGWr^XUyeNI8-2(-F5-*pW15pGCSfv1G zyTKhMURAmPu7Auj5mTEWeyK(rJPGg$74t`e^^C|7T)PW~oKtRbQF0|yo}6qpDLsJ&UdhWxH6~_Q1M_ck!YQNa%g@?9 zbRK19Vo~hp+g(~}%SwMH@Crr8UY^=GSL!s6^t1BSrd7J`BrPA_z1i&cEVpcW z9JMEg8S^9?`_OCJS86BLPGh^^rY)d43%3+zRcH`E z$yAQLgD^L*tlN7rHWTIF384a(m$D6)$k}KW*@0liyzmqG{1Y6NfYizvyEcUMX?Uqta^1P`; z!b_yQV0Bc2r^(pkGF~O6;t4NB$bH+Lo0wb^|BQo^yQw4`xUF#6*Rj{9eYOi_!lLc} z`H4+)L~1oTWN-gwdU!pdHa_ZmqN$2@Yb6H-!r0>!5$^p_;MdgHpV(g2zwr+O|9j4O zO7hBFe}xY-n&zGJkLaVkt?Zeh?`;)UzRqk~=#G@(Hi^!-AcD?Z1$D-!=An zO#8ZhXdQy37DD}nrrM#V)_cbG;V22Qq%Ih_e2()AioZnH{C$Jb?1~?iv6m|8Ue8ta+f!jQd+r(Y zW9$L0^;n_{LAg^ zt304M*$?Mu4Xq!GEU;a)woF;i`iqy2TX_wsIcq^TpzB-xP3cP#LYB`(x<70yjjkRm z2x?nJ%JJ$a4wu+F<*}(*Se+r;z)u&&QZ@1NOiY8wQEJ@d`tXp#gIgPmx|gAOd5d-X z?tC}^x)cM8+qo@Y!b!dE;%v%pQKq|(r?_`BeklfQ4n-zO;xXyGtX9KS$?T-^Y{Qjt zfAszq?g#tR=i#4vG5rwOkmX}MomXb;^`itH|Gvrs)BC+B@ZQR9MP@e1;-Cd6j~p*g zAZtWs`C_GxJMA)GlgK_(MNH}G*n$P@KD}Cez&R-=_o{v^X6Jw^uTU-1e;nKJ3KB1d zyPyP2pQ|-bGC8+m$9l$9=lAdQ)8QZB-x${-I~-|B%E7D%Bu-xSdp7fJPRL8!-vD143tE7Z3d%UvOh4+AWQqjDtOc_M zvv%)7*K$3q;~$(AflRO5KMtEyuN{cCt|tQh90zSdivojpS=ztWZ4c~H!jyqDWo_~# zpnZ-}6@qqVQj{ByzLoXgoI=ar|As5)6{E837%nc#~|Dx#md$F3!(7|A;n9koT;D6Qpc+qI4l2YCdB2jwP3S z0uDi;qOJ+5T760Vho{R3q@Vz3sT9v=m;5$qO$mL`?BPf3KNE66$u zt8Pj|baD~1(y9P6gLl#+C%UoUFecTZ-2T$^jAt}})8Vhqm1p7GKf5Z?=0Icv@ZtOV z`d%_^wRb`U+t+@0Ozgl!rr)546Q~)XC!}{QmBRsZkf8qluTncDBmLV`KPC zGW_L{xbz+1P>NpGM_!xWP^LtGW5vYGbj)S|{uJT!=LLiXL!aP4XG^C>()JagK!a7N zLCJh1IrV4D*sSdaXKO^$!^}I}?Q-z7k|Ox}V+=o>g8gZ|)WNBK2Lx+)b7#uv=CMdU z{D{O~sTJl2C|+5FWeoN964sPs(%AWC54QRZ$#SR6-F&W3cxed($8jrc%)?fEct~k_ zy>ehIVJFC0e+R(}-zL@)+Dlq!45H%Bc*RX~3&*L%(PpCtVA9KzBG~X_wLehYwv#g( z=V!gxXs*sb>b+!5lfPg(cD$x*e!jaRN@Uqy=@v#jdGRah@*Fh?UZq_z=Xv2Bklk6~ ze5v;ra!qG1H@|^keEf|WN@X3FNElgq3onEDdF_YJAc{%u%KKgi=$Y!zR#o<<`C09Q zu4&cYCu-kB`#tL*96TadbPXZ{P1M6}H^%VdxKhBV^$ncIv_UGTz5H(|1VhESgV zHV)_|x@0U!#&zkFULu*}#{|T|qyd zoa@qUltPk=jQuxYbK=V#=8&G8BJ_{wdHriPgNSbPXjpr`SN-+F&ng+6?DtQS($veA z-f}WMmEi5fZqEMJE=aOSJ5PFoN5miNeH8>sgzDn$g553>dubfd0!Cjz1A5EtRT^mw zUm;fNa%xLmO@iC`7K=*5`}K)L5Z&vNbzyR-P$CLJy-Khyyr49_UkVi|6!j7pLsCUPpWpE`{}D|?*>o|!AWPMy`Zo);Ev|3hgPvzrI-rir-56@87a-zfkBLI!^=zr6 zL9p7kQ=`EL@r=r7PA4a+(g-(^($OROPPX>L<#7D4H}P577ll3n((ONW=STH0xzC@8HR@dOWOyRQPqb;S0 zW=G0hlsrZ(Gw{u-Wg0iJhr|I#9U7s!-_!g1D~fHw10R<;jN(f*tl254XL_=|?*e%U zWAw5`F$6;@S~M*j_wzrF$M+;xeF})DQW};34bLKQ-RlZ*U7B+xy(`oc6!p)o37}^E z?04YBLbF4ae(mIg>ym{-#~R6w(Trc$ZB5_(PZtM@;B{_{Wplvg(#A1qPN$afchec4 z-aseaSkSQZhvL^6d4e>1AcFFxAG-(xg(&YBsn#y9qvntLQv_nQ((1r{y?uVKbAf;; z+et6a5%Uc%FWaj468?_XmULy;wc{_p(TAH-F?bB5PBY}tkJ7XJ%fjcMSZSHLk;h3b zU!_FSIB)0Pxn~tSeJ+H=OI)+Gw0bA5J4AnWO))?_3ZN5SepMJFZj(Oti4hK6C55qG z%V6$Li-N-B7$<9=GsOrIFDnlemP-sf`S(9=waPZtP@|TGcmE?Aq&;79xqy|(_yAu< zn7OrTz$Sn`T)a6B_Q{c3o*vnJxs$)yz+0o&YZ<0ZNgkac~kmQ#>m20J~kP)|fX)jFgnx&dH1u*lzvu z#s9;>NUn)eDoQR7d|u3svOQqxIC>X>&AvPOyjN5l6*;ks|OTj zNN8|7g`UwgCE*8)ee~dCMZJsY`F^IquBH(DmqA|K*!t&C#rhPy(9488dkdM_Z@zjp zepEL(`!FWLSWG!EVDJhyoZRFaXSkX3UsB<8=N~hNiey>FA_NU(2HeVYYURRG{OmB1 ztA47pg2|7$eP_PDK;8O)LilYrjotGTuZ+_SQrn<=$KQ94D0<}*J=0=qib?7PE>I4& z&2t>SL-@jGuMKFtL4=jMid|ToVbFqsDIu0S?=A*g@x<_ z+N3W&R8bpyUbSg<_VecL&o6bWNI+J8Yc9U5FrY*QS~r4?cg5_zOH|#>HEaFy=kAIn zqf4`*ETpe(ydIOBf{MFmZ0$op#KVprB!J@AjqexA_1})fIrgoP)g8mb-Ps^L^cTQ6 zV5u=eSG6IL#&{iT9M{x6InfHE4&y@e*1Ps^(BC8od5K`(U{;FP{rP)#tnDp76&*BG z+lFy$zRi?+N4D6{bCcWaJDRk1Am3f5D66>9r@L@BVN`vb|AO?91k$mu#i|2d!!{e> z=UKSJ&}LZ}jJl-!{&u*Me=YQOf)}K~8LDCp165<%k`D?rI2!M@wyd60Gz>T`KT~p@ zX{CBxV!*Xo^Vg3Phot4~`qP@#IxHY>eadV7VD)YxX_9Qi7#jN2rv7TI%aJaF_X3$H zhhlMM8}<#q{Dv)M>oq+Lh(sAE>EdsE_Co^M6e z+jyA!mv+f!tHr8XoXhW=^?F}&f1&{2WMaQ&72n`hrXf%l#`& zQ+}G1mlY~8c{vI=Fbo-NeAJr+>mf}(;y(Q;Y`L+t26N3`)#FaO?A2#hmyWr}D1=rK(m1S78e@$MPw zw4@2r%d+gKsaQK9uTDcaQNMmZaA1<4?WAoYzo0gM0+r7>9_z-&J|)an>9Wa$l*zQ; z9TaP+D9CevNcGYy7$JZvP|!M#>p7bdvlcBGVsgW8n~ZZFC!!nVrYCkTj=i};iE=sW z&j*k%khO(^8ebZP-PUSty<5bbSs5M-w0-mXvn{|-`P0;yir^87)?lFBU4u!}tgr~8y5=aFF?QSZrdnmv{+!=8OnH-mGJ!7j z;~`eEe3)HGSjE9sMe?urYN#Cb-U@H)Gr<;JTPEo9Lh~&uyfQ2Mt&O&?vtWtb9oXFr zgJ~Fx7c-V$HV!5XgIE8hfywrD2+i;=` z?U6pQvZ9kMNyG#aaqT2tP4AgGV=-E+sh$MC@&s!sQl~t9kn4zA4PB!)%;++V!yX!rZW_ zYJ4Q?d!<%pX>!3uuW;bVaa4yKLyPSV|GVcL@oTjWvFgVIa!RD0QllEuKF<2d?7USN zy{|QmdYX}wdK2NH2ZA{#-F**3;ICB*as8?-$M8Ul&;vh+P#Q-ML%Oje=VjW!yL+D= z7PBeQC9SoG>q+;1{c|bpd9Avxb!y*}@M!(}x0gpdD^*ybABQuAmIg<>4#U(D_r+^} zssDo8DENx@zOAU!)#>OMGY@#K-{j}prbV_GO>sKqc4Qdu$BUt_L{wO#wagB_8VloiK^dDiV5`Pmdo_t{iz&ix1eY&D|syn}yZn>*O=I)eP3V(N-1 z>W+BAM8{rOG=#o3AR~U>RQwKo^P`3Mox(BDH`YF{chRVvWAwmtlaA|m+DoE+FFVY; zzX`VNR1$}-+a+eQ4Kr+wgWB+7iPX*%DhS|ODD)%xWt~Fyg>@=h7ku@_S5v)(Z+^1 zTah`p_lY^3i74U>&*SD{eSn@0VQT>|XoSxsyyi+i9e7pAao3Z{NRqg)VT@61*cM7| zkFKc#%E-YoWjMsmhkE+-5@P1H&2Qw*=vZlw@f*vc43OKIJRX3$3BwqiQ7|)%DV_v`=e5jYtktWWBj*wQ+RezP8A$FMO z>B1=@Z@=}c^ug25uB}zSyD?z1w%0hT<8v70U+7}Wc;5qepP8%SXCe~)4`o7?J=dbgIF{iDA`i8&iq-n!_gWqe)co(A= zD!gl%=jG1(dg}LXM?Y*ICP!+|79v}1t|`XL5PCIJKbmV>Lp}^_UQhIL^xdP1^g(Fw z?yVP}`F}QQJnL(jIHLD;YfN^q-_-4}kV3@*g^UaKt_}5-Tk6G{D{~3m@1Ud#)C<{< z>v^|D-?R0mRDL97Ynrtz+?n(WFnS^H_{(5Pe(U!)gG@rX6hkbadGh!%ACgvLE=%FZ(PCVA_;B4g0Y z7x%q}YmI&BKX0VstQ#e@(Nv0NGlnud1=+J`UtmqI{@X`MI<~Rogop4lPM{exhqg4HZocE+k;WfHPA`<~)+9l)TC(-RI`MI`9 z3ozQZ+QY5XG^SB|g#>i20*` z2c!A<-ylM@1ymx1rq6thKS(8gh`&Suzwc-P?bt1DONFYMi! zrj!!ehD^6nN|++NQ&voP-*ItFBVb)kv&gW{i(E?yGI9sjr0Jxh;zV_>%QF@Hk7&2l z^~q5Q!Z}54sq!)lP_8T{AscWmBw@kXKM@;%DWtAaq4?$U!c#Hq z+7_$5amEb~Byfc>trlp0^IcaVB;gM5&PLu)R(B8RrO(92Op0?sCiGc8@Y)Z4RBHwr z-+1Hh{L1oN>kkz+IH_m$qoK&md`Qb)>SmlQB zyjrIS@##Mzvjq{0tDc{PpEF?xPC-zjFMR7Nak~A+F5}9GfmuqWA!y3*8OOD7{G|b& z!c;s9{}D%;kSFn+Hal#8;v)>uCuFJi_&vw2=H8#cGG{lA0Dq7$R`bAo{r2(}sz?0` z8RKFi*GpI}UZPMm_-%m9&xV(Xrw8drBW>MEBs#}~a_9lbv39uJG9-GCRCry+TN85n z+iZ2F?IUo0=r6Tp;a#oQ^)FMHEq1aqV*f6*l>onwHWFJ62WwF|JFgk_5qUn%XwOB) z07|&MiW_t>vFimOLO~x!%`y#pqffZ&hERmz4fDvR9$rwjXZLVc>~)YN77T{viH}C2#P2LGX zwsN?xTp(TTk0EQRdL_1a#}2ZO)jRc1G~nSx-IscM`3osOw8LcUgy^gJBWixBZ+ zNn-nPBOn}amp2gp$@v+rgrXFG8$Tuc^B>W(e?;wOE#?HWn_}*Gyfw~#I&hh;>$C&Z zY>u04hIcC4E6m0a_#SV1dtO=0%VP6^H~StcN$8gVOe#h=l~tLyRd0wuD-j`{A6N1G z5$GA+UT)a94ed?3e(Wn!+vit->DT{{+|Mpf(Z*He(3nk$dUsxR0ad*oZD&m>BNLzC z`gwn)kMH==0V|0`o+q3jRIq+(U`>mh0?0|gm8TQV#6BvcDh+xL>#Z*3-Mjj9I?eLN059R_Y4Kjmq7ldjHd@l_hwbPJ>&q_@s~U9Qmq9-&B0m@B{wB_6Ul5`a`U}H}1ZI3jx5zedSi+a$~%mmj^>kw29($^59)M zcvCN7jkp(F>E}O$h%UQS3&Fl+ZkCAd#=%tn>B=i#;EHq>=mw(1ttu-rPSoz<-HCH_ zn!n$=Wc~}c+o1qeLt^v;jyLIb?GE0l zgrOCYA@~sEOB4&-tME!M0&0kE)KrCVo_L~=gBbZ<{xRuz7gDCdu~T4BMYTQDz2KV} za}_y2B26?Pa-~~;Ku1)AE8B2wcGwHgC!G)AIV~MJ!&8TVr5Yne8#5ATFM8s({t?B_ zPIu#M|6Gzm5!h!kuIM(h<(DYf!wMn4+pGxCou#WZlkwTg%So#T zwo&e;E^Fs~8vZ3`0JfDot{+MU1{FW~{o1ic4JzUqatEdP=zn&5s}_MA>=C!$L^&Rl|!};6|Z4#(NYH;jX*pp+sh zsenj#mqiN*NT&iKjL|V*FhS|T1O${6k&c1H=kTjqmUL{@t}**NOM+ywCI8 z_vcjCl>i2JqT`@*h#!WUBQ%jl2!IcNK{D@C>d0kBF9iC~HQRb=bYC=5c7& zsf_c9avfg|p}wo7JLcnvaT zd=Om)+#hn2JN$nnQ{Km~P+eW*n9`pp|G=Cfg=z>DP*FKQ*sR>RIK_%;I0Cl^j0iy1 zR6o{rr>0%)BgKJF(tio*?fBom3c*wp?s1mLt%+=NNU877)lg$W&G$7$F0S)gRrlRs zaT5K=x&GEcdLs}TZ+|D7runO)|il&c65>sj)9t~6&?AGwJr*u=4h!gJIn;|hU5e_C zin+t?9|i&2)yi_>r~Bj5sIhL?wl<4Z+8`M;65`W?Tfcmlpbw^HXg3!9N61n5IblQd z64>l2HYZB$x0e&jw4CEx1!M>(6e3Es`{q-1a9?Tj+avLiIaL|Fj+3IMR?OH}!LpAp z=SmxpBSsd}%eJP{b+WFEcomFNrpGgc9_0P=_j-?^`};8s8T@_FfWo~XL5zieX$3?W z@Zs=8$$pa2t5!0+|3yTE(*H6>AaAPVN%TQ~w9DIwb@_m49`?4MP+Jf1+9@%hTC#A6 zU6=IYH^>&U&lye`bTBmBaD6>=BMULoroZXdkCm3Xd{Tmn3tf<-?hwTnEjHUBA6Y^& zS1r4uY4sIS?E6&QW;f$;?u8&?L>f>57kW9lv72Q<&gR=j#yWhC3N%pIqYuugAA3&GeT8VmC; zYJKWE>en}ER8^z9C^e%xA-jhQrr&2G(WYwFPI%nIumVjTfjs-H)62tYoZ=oE^Rx}- zPW+o>l+=j|U*6MqvMlGTpv~nl*n`q0Hf-3^Imxg~@tcb<%OVku5VCUu1UX7QCxnKq z4QtYC)tK_j9VzdN5zPf++2fFK1z+^C8c^a!BR^!ct!lmO^&0a#_>jTqVPf7p07`enjhePN!o$*uaNc4yRJ9 zU+lNuxcE}L$dq{gb!>V#y3p+;NG#u>xmnhozp?dq{4llC%zfOVqyZCjGGu)GAJN<1 zGvz#|GsntQxRfkNE%~*u$utj!tUR7_(DM3suz zcb73$UY>F_ZEU;yn&}EETqkyDNr&o6?8Sb3pxhLcoi3qcNvg3dX8u6$iO9oCa^l`@ z7yOh%SNf~zw$aX96ZLq>zRXIwG{zTa-JZjnrkSJ)ltNW`%hugb)F*HHf3!YP-)tVv z(Qg~OaPg7UdOdRU#AO;vPE}C5(i|cgPao5>o+pD>=3AGfj;f1Ice2%*ug_MnXBUhA z_ONG8xvep&za7(^ONa>z>Y#sE`}*F5kRN*_k_CebHfU{#zp#S0>NG$X{f}`G+_SfwPGd)I!`4#}4svUE(|9aLlx6XBr~A+{g9o7m+c;>I6BKsJ#q4KZ?~)+fL~&1+>_sqNP)=Hln8pv`Bqb&^5S@UvmhLC4mjz&~?GeJ4}A zS>Cgf_0V44!?^uj3oi#g!`g)LLPfZdkPyB4S_2Pe`rzYiZ2Cn315v-LOlT@A^M~U> zALHe!^aUdiE-ACcO3ahzT;QngYBl%F*Sl69)^zXA>{j%$!i`0HzBJGBXqQxa{+w5q zVU^iVDOxQGpGqOys86`{BAd^y=QfS#j1SW1{jSWZ%HEJ}@lcw#+@VAFb#m-#7_P(U zbZ>#zUICC%(LKv7qI|ZF^Hd-6P8TI-9cEolDkr>(>y*NE>~g0sjB;}4{GpfsBE$A2 zh`q!g(N@AY-DY+BKcc9RDJIw|$CnxnB_os?2@%CgnpcnH_Q!J3Eb^+XbO9C3*il*Q zaQ15Y=0A|BwyD0p?v5h&r(GAunCNPbGRuC>FwyhWtT_xRyefw~>Za?%k(UZ;Y^ zMYr8%dYgN@?k%|0Dy#>?-KT00x3T`+t9cQdGnnFkfWmFYAo=o=?XoWXNTJG8y#KO-{y*YR3ft!Q7ZbJ{F;cdzKa8O^X02 zPCOevG#cEj7e*Icv-hW9*Vm}OoV$kKJj)oj)Of71(35N*O&+|*JU!Q#-DXV#eF=tM z2kxr`2g&Mr1}=eiF6b*$x{q^Luz0F2=XzUmW#E=lWs z4mQ3E9NIMoc&o75X1F5{AZLga`RsGa|17L}U+~*VpU%D?Q^e9sb`e=taZBG|<fj?glx_S==c6N4;%-UONQo?1~+?MZ~6snBWD%crqd&Py|@-=c`m8zA!ecIX? zQ~{K_dA?16*N43;#eQlPW{W;K_wbD5ycrw2`4ArvcQw7iet=tLsUT2& zeY4;X@py5r5JZRL2bw!|kRr_q<&|tUf$@Ww=5*@zfL%IU`Pp|){wCZrL}D5&?~vy; zFTEHlG@ZIP9S#U?3c#=KFa~RoEu-W19#-qJH=HuUeHpU>Gh_CP65fQ*!CIns|ktX6L^(wDQ%J1-WOC}FDD^Egb? z4R2m4@j)4j@&Imao~OK77sWNErqC z0@=IEAarwwSWUl)$)eZpW}@p3C>Ho!0`zbW8{Tyx|9&8TcHs$6ng$j1O|VWfF85Dz zeU#E6Hu{+oJvK4`=Ae@eUw=M%bqD9&VZAo_H;OLwI9!mcx~PM0;SX?=iSQrk#MTL-4aRYoZfG*XpW!sbJW zZV{S0j1PvtxGK%B)XTb8+3_#|g<6JokQ83qVD)$oVH6ih4qC(Jz03$M)z{sa#N9zJ zox9*!$?hnfe4)h`N=2P{8v45>$o#86)#M4jS)BUwA5oXSN^1b}zIBW8+<*=dau|@K zJ<`{L&EZz7wvYOS&#eDD`uK4<5rrOsC`YW&yI-=2tz4U^{7>8WPa8#Z6~4cF83?gKaZ zN6TPB2B4?xW3MQSr;dhw)-QZIQMuEj+?hJzy1}1!2T!`=X?!E6Z0~jW8mHFgqwju6 zG+fHRYCYe3Gl`L2D;5kP4KFcqAVe+$`$f8oB9Vw7s`ksVuTrM*z0ieIA*5>^5M|d!)5;sQEAhwmFlz#lLAiBQ@%sIq%Ry zgEr8u1J&LGMugNigHhMX7ssi?gzROG#F`OFGIf$%FxO4h$tC_#Eh=cL?vX7sScp9? zqrq79?oH=>`CxQKDFOxb54A0TDt!wZ8`@+F)h)Oc@Ti&uF*2PKw0P8v?hg_$Y(0H? zo87V`bJvO!cgv~$h5^~}EdC7z)WA#A_Uo1kn{FO@`d`q}STTD=EQ*Y)_;BUr=qRGUO+cOuRu8_x%1CO9CaYB6kcoXjs$bfO(|RG6t= zwKMAWe9>LE*3&iNg4Qo){$a0}Dsh!=|2b1bR!v_TwaDznrwb(1T-2nODludIr98`X zYp&E>PX|Fay~*rznUs}(y^X~jrEr12KE6;cu??D};6h)-(1suN3b`*}tVIPymBn{| z|3~D82$9&YIm@JH2>fl{FD~RCc7I-NI||2hb8mR9h*J2%a6)Oe8r1Ll(Bdaa2yFt3 znsPc+cMXx;ak;8X>9ui74n94jjMlYOtOE4O`l&sBD&f&WM$Rl7l2s_Z-Od2hc>T8C z3?ozFi^>LsMeP**1z|Q#TXOW2CHBQN+oaFZwB3ha$#%c(d|jQ8wyX7lqMP0e@8i2k ziPg#q_4c{sJF@v??;!Z2u3W$pN`-SNcd;hRB;;2?`lz4B8M=A2{ffe$VlVZ2nt6sS zO)%54_`N9QpWQy|HTVt>CreJPn&#R>4hwUaO*>%WKFt*W46U1qnt%VV)@|(Ir)u9U zhus6v5S}z!%&{)EXE90JYIBuwH;ME*<@nsxHiQt*Z1?FSov8sCsryYJKmY3FzuhL* z=9-g%BX9o1_XuT9Xbq9!_|!AQC#7MwyZrY4BMXS79yRhIr>fyFXr6iGMyzx7=mQyN zV-?ac7ciHyW3sdu<%~;F#_wyx)Wh4B=9{;QzatHnGWyovkG5K4Up`CNG|84Fo(vvV z*p@UqZs*i-wDwSHZzabXn_({X-a|C$j_MU$|01D)sBl{ArNHH7XlR6+ccDd$Y~ahd}UrWBJ!*1;kr z)02;+^`l^lFk*JQqg(C!q?2>#H~OEsd?mD9%FVs!z_AQVHEOHidiB)?d8^JacumRd z?=LvoQthOuKa0FkT{^W|@HYS=T0+ig2%08D)ebnaQPz|-igj;r*FTV3N96eUv(FZT zJN$ftsrerh*7db>&1zfJ=46|kNatuj(Kh8r!QgthT{sL#ExOXk{>OhnxEL!g`UtOBKX!z|wqO@ShYOSJC#cWr_-r%{l zKyx)$gUeq24kOVkl=Jf{*BKid0zI$&q75XkGT693H%#yM zC5yv_jQ}d027efLHIiW-vQKY3gBwco|Djzv%4@077QxnO{yGAFg2}|1PoAzYMJlk*Y=6-i#253@ZR+w@f27oQP-U z4`@sE%iVcQVf9I1re&nd@6RedN_t%|Va;nZtft8~!k@`BA^(}{dLVrP++d2XzI+Fy zI9B>qY2BmWnd92s)?mc7+A53gQLkD(Xp(h(*iAP$97lN~)XT*QB;Xx26Pz}{P`%&% z?wgf4pCZcUHy&(U!<2p&XFgcYMb&V@>`)72ucYaRU-_-?>V@?iKKGc{?bykAmXT*A zaMZ+&4Fy-?tk1N(skw)!x)tRJft1<>rbYELR(?sS>YCe#E889QVgHWNCQfYpe?;2j zm4U3K`tl}0!>)IJa46*Q2)1kxoXF09josJ=Do!L9&OejBU74C(K?6}*Hd{{8^HG`9 zi?5dl$w#py#F(lL`Zg=qG zP7qJnV7~-?lPSq1JZf7;c2CY&97Fvgs?T@gjPoX%gIe)77g%wI8sJ$cO3H$2yT*4u zK{DT@1OO2puDoGeagn{U95-}4ZPpL9SC1LzN*dcz$-_@vI2_u<(oWBZWcRVgdZb~( z4IuOZ;d@J2F)wb~J&nHI&iJQOo!t`6q>BU&=YXm~$=ayiWBtjl6V^x}2VYqkeERGB zBJ(EB7wqoR4a&c=&R;KeUuH4>ig%qnZqUUT=+&0f6q?2n!vy~IwGhE9rgQL2H#5y> zos??##XXw&wxYVlx5a(;IOjPYpEZ+X?`|!rmxM))aP^G&P5bQ8q6AuJLR58v5?8y5 zvh+2a90or@sN29#PmVjy%=*FHf5)v}i9TJqWn#-&t*wGFej7h+8qlMu4OTcNnv+qz z9o$`WW4L!CS&PmC_QtfkQzv`MHq+jBO=+G5qGziiG`&3=>uiagc>PI;h7)LAFzPIAZs8s_bl0?q=!C zY*~f&@kP>Gq2P*(Z%iE#)bG?z^9u$8WAp2~2E1c{N@*n0RpH5pwcV$0JDCSJ2b~F? z%>NPjma6!nA}b@jO}N3*nv<3M6Nx!E38LXgb{nhpEbC!wjRtRP<1e<q($-}M-dB~|p#Naq)jv@)(;eq8!)BSnG40zH(Ko5b*8eBkOA zlL-S5w6nfEy>AV|lCG-c)~=LDo~vl}a#ls@zcn}FcwomyP}{c_h3Yg{VNe}`T&=hI z6)M_4ZZdEA3W=!W-P5M0tZ5ZAfJ?5FleNR|BJ|dgeO`<1{r3l&&_=b}o))v0;v4XQ zM77yb?TXE1y50C4*yG$uXFcabs}orplQd_Jl?TSBrTZr3i(3l!n_fT@yHsB)T>nRO zT{O_dx6fe3K24dC<0OGAiE4Y%8o%Kqn)!0MQ5;|zF=c8`6vHng3DeH@>Sr#KJB5^^ z@FA$Pp{8TU(K$P@);hE9!cUr`X)zT}O-|=vJkQBm!7e*3vQn!m+M*%_qy8`{f^V?v z<(2Nz@WlNw%ss8w7b6ta6&ye0C^p#Y1B;UCFx6kzh&Sm|=uf4nm7X&AS}`-S@(X{= z%T}0CofFfA(*was)Se9j@WOC(iM5sTbz-dTz^Zb^9K~_N0*T(%8iBP z(Sh9Qu2m%9JB$D`*#^8 zoN5}SSLn22J#Te)V8oV_dcU6I#&CXjnF_N43fOhgKUo#LahRiZz_n0YWbpg8dc?f# zuW(FUyZ^%AiwCvx_*g?9cGt9xM@pXQU+|{YlZK>u>&IGsqfY*Yb*;2;DMFU95c7Be z@~Ou1drZJltV+UwvX33C9`7jII`h2kc0B9_9Ety_V}uvQ#pr*H^uhn)*F%`&xNSN>w)J6gsltlPzJn~ zi#r;xmq8t_|Ji8H`-d)=J=s-4fBm!UVr>8ldeKXF?Eo}0BKG{7qhKq!LE_EL_DSUI zp}s!PM^($aGBFZkzf_6XMTrv5kORP?fznS`>av5%cCHOKr=o2T{t0y(0yZx8w|!X^ z-K`6o(VugNv0!$3b>w40_UidYh6!#!xw3M#=JH~Ds-oS{YKkVdVZcL2Ol?#6kbI5t zH=%56N??oTkCPm@kG6He*5v(qMtuU^H4csIKtr0_F0KS*bCyn*1D)TDUBZzNHiI155&QIY?={(=u9w%<$)l@*Aka(=_@IXTXx{JUt-CXul+ zpw=0wuYTf+<>fR-P(hY>g>G3aedbHLm?u}*P?>nx(h1?g)2La4T%$9%-tE{Y4Yhrg zjx~JS2f?eV1T5oer6lVnk5AmU9D&TrT#7di=zXk_@PTw7dVSURAOL-VdyjTWS1Kr( zw@BAAh*iVA&pQ;Z@L_R#{t6L?BK=Y+tVH5Yaf_J-H+vy~JA#n?IG+uX^U_}4HA1{yy~T83%e25ENR!CsoyRZQ;mNu9~gkE^p-0$$F;M2(&qlE0qHc9ZBra5-d1= z$I6LNp*vB^(hg6LT@FyTQht05y7)W3Y=zYYJGeX*UvPTY%@}v_{G*Pv^oRXJ>-#`I zhShud07(myN=*o;PxESFblro=;e0R3layRh=>`NByOuLMwIfmLTm6fL>UlT=Yd>p7s5IBtOPGVc4mHWv zV36gG@$l+03q;3z9jDwuOzYKR9=Z;&m=JSnU2;583Y@^hrIU;yhH4k>8YlTU+RX{i zaGK-|$bUD}Z>7Y)_b$DO zn;}Q09>xc~ub(^4= z-!=mS1AUxAyhg}C3z!Rsu~xbn9~-!Nf_dYn%cbGY)hk948uwn52?!ar6!xi5E3qti z_x4J9B_P#ALB)-1=F+GoO!3uQc8=+No`c=upG~X+Zy_?+W4rrF2>SD;)eLwmuFQEb~1z~kPkY3 zcG#7zkc5z)mXzJAquA;+HBw3^gq3ntWA_2MrK|=DL*e{#PrAz=hXKMLVXZH6PMaOl z-{dW)zx|w-#ithf)TnU3ZU3IeJLc>*Vy6Tada+*o?9LSPU6U|Q-{OlEW+Mq9_*mS7j88K z88RH&22EXCWqb_Hd(d;x)1a)_sfOc%N@0xKj_;Yw?OQQn?liWUcQZxIYBUMmlMj4Em7p^|(d#Kbs~&y-k~^ZM6N)dWicks@BXs^k8%Miv0ZOb`z0EQ~ zw{0q^xHPo8wFRtIszKEYc*JjbDwXA6;9i+-e5I0h-TGCK;fY$urR~~$-6Qp_j0yLd3f>kI&0TVecK%6X*Axwns00Q?ugC zrUf2xJ?|#n)%$G42*NMOM3G#IwI4r%zki&RA1OJDd&Sk|dA~Em@a5(HSjq5OyWccxrR0eZ*sOb6n)c|ERJ0#k9-kVEYq+Q;DVufkx*55H5+a9M8pIFtg0@w7 zgI&PVdhwp2v4;;11=9!d1gGJP;x%d0zwec-d>-}Sc{>$ z7coX_>V`TR7Ch`3AoZPy#QBlANgCE;Pg+df!=z7p$IG;J-jNgF^{cXfBFhJ&re-l~ zj%H59)nnCuCYV$2i^9l!$<4p-UX8up>aly?zD49iaBM7AG?+*>8o$k*)`vVzoKcf0 zmi4OggL%CmI;q(()+)vZt-iD9|3^n8qbvGa=Egfx=qDoIugjU&qQIzUxKXi^Kk=Wo z(D!|?}eG?tUfZ%Hmn(O@#F;s*5r(VkC5N#s*^r6gETX}sLi}vqxua3 z!*mygF)WFSyw&zX=J5+A75QSPjYKodUvJzE*|lh1`97zTv0%Gn*4zLReol7dU`5Ht z38ugf7mmHM`lRJ({7)yEF`6`_;Hn*XGdRu(u$6x9`WN#o@*jR7 zcKCBlGY^tH)=7G;hoRKYr$f+7>(AM~~?8@B`muy*b`8sSR1kxG61l=`o(avB4(XSvB zu)b{B`FPEc>Gle3mp-3f32@A+8`X+jh}!Et{(VKWORMBo->Kv}!Ocn!GeU-WB&Q$^ z1EbNfQ}hbyw;MJ*U0LJA0bc8`MSqpl?!^Jm#Q81x0`Iw^efV@ftlb7;Y&^g_<$IoWw8klmjm5-xl=<0JdD1h0j4{{!X2 z(9B(ZF?kV=q)wdh_gFhQ@|CKXFkPIo7&*%7I2VUpFH(VFX$NL#q$M;FK&QSxR3Lokr1qLNxE8l!khr_1*kuUEkwIK;hAl<$T|L1xc_k&_*|53;Bi=jOL4S-U24BU zZ_l1ODe!#n8;>bhG>5LE2M4RmJw4eF!Vz|XPp8NaxO^F?!au;%&NpavFwmnLIVLIUtuhUejO187I7ScKAwJm0stG zx7O`y++S5nX<|WBCVq24RJ!@Mm5nY2ym7h3OLl2YFO zB61%_@NUODkAJ8@Rf2NsaN4;O)nEIR)Zq@yspD~0NH_Exv2gO1E{d;kW^q4iOL92o zK^gs5?bBAmY}6r1*kKrHy!i55!<^?2Yw6cfz7DaAd!Me?#qII=A(^T2M7v)P4-FRZ zFPo>3$?xMY4w04Tk>ETU&u=TdN+JwwW!)l$&lBz|&BXFKbl` zKO?PjFT+2GKQ#f}X$+(L?ixe;Xp2hh_t|>Nk-=AsF&3_*?jooRw&E|;u=>P8GOYX| zQ{>F8ZC2ylm)lX<7pZEl}9gYjzAOo*js&#}pztX1h|Gm3Y&rp)~FuLF~=3s>>W3NGf?A8@*bK*H~S2(vxs| z?NqMR_P1f+kE%`dM7$X6{*|=BlsDM=ZGbh+RTn5yY5G_IT~6 zYJK0^n#Mw6`r4zU;bF@kU>;xj;^wLP7OFup8^k7?B*>{#-CSLrNIma58O8en#mA-G zdbmK~ub>Q1BzxYwLmU6@!ugCnm}a+b2$(o(n+l(o2+0EI&!cCtqaokfxIV<=$b|<~ zWzR}oJC+%QvZlDu+YxFP?{&X5Xn=n_TYBjpOUA*P16lE(6b@dZ583a=IMI9R0y+#b zLSM=Lf?DDaDZ@WkZb5cDnVJ`Z!fkb~!Gf-pjXDdEY%Lmff$WKrDb-5U_pjyb$KX}< z?btvkr#iR9r43Em-Th|}rEIj6!}#Hm7g$BIC&l1`@f*Ykz7f+cW7Hx`w+vCcaK(-If>J% z7sGsO>gyVh`md#A@%(Z%e{UX@COXj|8is^BG;H2|8(iq2X{`GvX9Zbe)r;ULpB=gsHW$zW5- ze7*efXy(Td*Hu_lt}A&veR*kH$>ZH=eX>auMFpLLEF-&+-*A4m4xzlkYFuHQJ8fi6 zBm^>;r4r#~LG+Tb`<3pt;Ten!LasJ?;94cGweDWge`eWTI=^e? zg}SFOGQ>H{7eZ24(5nYO2@KpnQ@f$`m^i~Bu>6-DsevL-U%H}~^{L|EPkRX^e>bRR z*jl`i{V+DDP(oXb1Frr@jD1AEJDB;2H-+>EAQo57b1@P@6jK6geW%2wmDg1Q zS*9wrT5~y~%E_baIhg4>c6;8YAJVD%Zl}ID=7h~<-Ztv(L(Ob{gb#?VPIF;yMwHWoT(E^*pB1EYpAehIb#u-T4{vt{768RU zALh0j_LbvHHZP+UF(iu}HKuf-kG_3ox1Sp~J_d7rZu|~ImH*F^*6dAuwfqASxj+?N z934b{^(Gs~kF{;P5jLuxEUn~@P_lcW% z)8gg$>z$k8T(gr`=4?@$6(craWtno{igpB9iYNbTwv*JY|1Z`wkt4>IGKuuqc@ciS zUFB(C?h+HX9DyAk{Oo|$M((HFsT7YPu6z|~?@3Wl-5a7egGlvJYB)#s3CH>$j?IwVtznh{AvZj%N)KeC1kIB#eey2zi@R#8=DyuWEa$Z-xizAHZ@}f;ijO*@Ka=N z0wfw(SHSS0ZYbELMT2eSuLDc)e?(JAVTqW+?2&%v3#x6v|4OMKN*V!M$x*sjp0Q)v z>Fp}wm#)ytCMcw%nL#asoY;YKuN`9@WF;VYZ+q#Ojb=emVRh2?yjheD|CexO-j$Rq z&YOGkIKj`EmB)Cxl%wg6B=O}PB~~=$O%b08|C`p?q&e|gx0iQ6woikDvdw`Oo6Xj418?2d89qNn9)?xqf}cm~ZzsuIxa3(1DCzd*wdihe_W(JZZZ z0fg<;TqolKA{2v?$<@#TaoWZ<-IX{7Y&5>+8}&e(PW#iAq4S3Apdir87zD&cmxN>q zAe&~@wXJg$Z&14D4~te%;~dO!GPrj$U=xh^iKui_Ki=Q9PH1NO<)ZWqR2ucL^si}R zU^)p5v2FIjQ3kIjC6?`UY+ z;^eRn7nWGvm3={a^V<0ieZa~3^bI+i?KeKg?hldbN;SyA<8WVXLQcW68zG7MA2!*H z9YV05bk~6K@=n)?0&E5L2+xNejy(WZ9=`NiRNa#mEbTyBz8ZdfD=+pQ<#c9Wv#gip ziy1BMwyPkJc|S8hi-U*F`f`HV>hmVmU)@ph+qKtp!AZ?&&2CoYsJczwoo_W*f@DKWNs&+$l01eMC!-_wAcMwZ}0YFeg-m5L3oW z@t!F0@pw-w?dCbA{nKS?+dkMz>JmdeHW+7%HkUKrFr!5R*5uvB5;R-D)z;FQTmm)m?u3^AnN z&SG90M=cC-oYH-iIv@tlAPCS_BF5LSvxSbLIjFWlCvkeixwX&ZTQ>rpJwV8lJr6%i zja-zdk{___o1wBJ|2O#E#$fCTF=;-h!?3)UEx6XBwe>6G4eC};lyxJ@*FI)lOqR9h z@H!~*8j#)H`HkV}u%-%X^PN+Rfie|AToi3P{GFvP4erD*>Hia`e~nnX(&+xJlz64F zV(AX}+4sb*DBUG8xzw3ST>J(E|rnkWiLF`16eod{Ccy*vn!{WEi0KxvzcdfVCp?>GGx zK!ASyc>A0B3L@`6B8zEyx#Z(V2R=4?!XHP`(_PU80zpqr7VJm5X(~Y@@JF%AkGs*O zS?+7p2rH)gp9$`srLzxSLp!c^?kesFTm;*Cu2U-3E`j4kG!93C631@3tjS7)o&lq2 za=a+$abWX%3}wwX-L`sOF{(f%vkTt7k=nK1r5wa0lhS>>Rpq213UOJ)T_Oy%%{S@U+*LBugq@M!LBw&s^xq%T1Gi)t;IkBqiH zWwy;}p?>OsU0aQL@^|>#lvi?XJA)n)f*WAU5efz^je5Gh&agk3B&W7=Doy>f zK(5RO(VZFjMMNqeJVZRKY57c#Q-dUL=|7_PHLGg5pv^Sktyi69EcDIj=E1mfZUhXKmMr`4hj=_y9*?#-D~# zcUxLXazZ*2*=&jno-IFk5$(>o zQ)(yU!{CudmB(c5lp-juAAcebHytdmeP$uNqJB|ADL(uUal7!%3&L1NSk8~iUk^nP ziXNmAmOP){7WCJ?HbRHxl;j$}MWw9!-j%Ofj15$01PWRsmG1UI?+!2_{lW_+%dy-# zys>^>(qmVzF~^wue7NiA&p&6cPh}HA6tO4b>Dj_3Cw^IQ0Qmra%)u6feJr(Dw)9>&2jhnAmGrLygXY<=Lo zZC}os7)DB&UkFo9_!Mnxnuy{Cw?tW}ZMEb2xrWU3r;a6fpG=%v3SG}Fd*lm)pB9mf zP$x$P)fa+JEnE<98e*5MQ-q)OX%HL0?Sd`Gu=h`{(R%Ho%`Xg;7>cF>`y@Ntrw; zfa0|O>*f_q4)GR99iA3c;w2*~UJCG2V}Ak+d0GrDt=DAJ{I>QWv_kI9TdshK>1iUo z)>v{_bfSt`Mgl8n)6Vvplm2-6N|N93o;YvbBjemdUvADy~#OX@k{o3$0-d6YNPdgeF=2`~oMwb&o-$D8e$ zrOx+5LiJpzuWLP|R)<|0anl)te|Fnj#}^j~<-$pxAf|V>dP6D1M31kHxO2m|-!Mhv z_rKh`YTXk(?sSc8f7Ph$pxkg;@IGC7;MaL<7GOoCwm6D11tUbuV^y`U*hShhGOen* z@BpTmp+k`ODCa4wu5K{Kjc8GfqsLfLeSJ^f2-TVy^AIxJc2TEO*|uz5X=c-TJ%OhEQ0zd5N5U=TCQoiur! z{L!R8i~ZB#ZO>63d4AmC_zs(;9BvGNH!)XLG{Ploh(tL^PQF7rp~E;uUA9!r=^I<& zYcb3I`#ri#Mn;Cpv7iHL8f=i)>9hHte@VY9PsSb2Rj+YnS&3QdZFSKkl5&r1db@@- z&ujTOkJ=cJXRm+1XE1+lq}<}l^x3Gnf;xKKpazYX7Ojjgsp@FWOUzb&GUK;R5^VJq z-TV%isyqqDE8Zj6%&oZl-SC6P{5Gh2bcVZw)c) z5nemUPrd*r-43vszINwMZ%WOXGkf!RHQU0^bhwXLq5SY`)Hx!&`7ZwT%4b0Uqyek+ zl4o7=E>yb+e6=BUD>0_&MvjGrB5J?s5oEl*iJ>w2O#tHl?#Y!%KfqdayN$s3D3`A!`9oIXZ^q5CEuBLeUU9++n2CE+^AqAO|u`sr+2Bh zW}LPu9XR}>*%85xMVeu>e@!|cHzWd6WM38 z`Q4&I@%h*8&?ywJz?Z@=Ci$DgC4Sw9A9AHblw_&23XitIj`sN521m;L-{xV?p zYHiX+7$ECE&;&&YYEC{z{-|K4ZkAZOVvZ$UgJ4i~!^3NS9W3{a2&nOb^D zw*yk6hiiH!O<6&I$X&%ewnhIuxVrY)@xL}Mb~IkneAS=|{mt|qzCpchHH!qgyUq=P zLRlN;yLAgA8cy*Y^B>dxtp_b_c;EKfw|boqmB7H-m=DE=P$&#p*qY21NS6Gq-p_Rp zHY(2vuTo9fAN<~m6l#2-1K0*r$C7=cI)}OS>a-*a*LL?|i&Lzz!-{KIic5)ifJ1;Q zTamj2wL8m=5%-Hili#vKZq%&%uKH*$Yj%lQXMxJ+2cJ-xeGHy$53_%1hLCu(2LR-} zPE)M^G(ak70NDfk#aSTTi&ay`?A83oOxsX5^Rm`$#Ym*`!zdj}gGF}5i2P&w@;)cv z>yk*mO9S*ooYTA6`CM?>=-ZL+8t^b;om+A6?Oa);^wc>S@=PsLw-)61f&FtZdjc_2 z-=j24!tuBavI^>o#sGGkE}W`%iJxD-RJP0fSLK-~;)gw~)I)yfbSJK{XYAo@WEi8u ze^yS(#`OQH2Bt>U6>3^OwmjNT7+RT+UvYH@Na?$OZx^;nsssJK*Hh{-$j&lsk!f|+ zSM9Glbs<~<(Yg94(08(k;80vmHyym`?qul)xp?b68gWF@1LM$(b)t32-I2fX24-v- zkH`)8X{LkOj^B*|uI}4Omr{Q$a_U~6&a~bVQB( zb-5kv@ELWN(*Y`m1bE>`KsQ0QWY&Dw?PXzSpzGsBuGKdqWz3#iMDiOe*T%ZVkSK@p z@uyK^TSMELsr@vqV&!!kE1bmL@2FJlBYzy%rW+ciI<4EFw_<)f7342{p1L@ujTUc+ zdsHTLkJ}R-JiKa_p%{PJW?eJ+*wkj?wT9C*#Hczo)}y$;f@$dPQPmihy!)|aMxz6; zF_yBgIoS~DI?`~DDgBqfTAubb_J#BGuba{G7+B|!&1jXSay#*QEH{el$^a-h1vxBi zifoUzrvPz%+Me0lwnSb36UiW2M`Yek%HM~B6noQ8tt@@ZX0l0HgARTCnN#6!g#gZk=v8ChDkTB>M8t5#O2LviPU1=7L>+6x=J?5&R2nW)7h(73yX8 z9_GI5H;mG8>YkhGH?9ZugUrKzwcu_&dOA<_?N*Bmz9=53cN8V|AGGH0BJUi=KgR|B zB-=A)>`U8DnH{Z~H`F#TR8Kjg=6!S5D)r~KV62AT5Ot16U-UmwN3&7cXyk(#zbBYe;l1PFEd%l{A53Q3~lQ~R#~9ml;H8P?^gctx+Vqx!kI+H`O}sjY5eTH z^J+YDn$S0By=WM%3ny&)f_TcCZoKok$Mj&xLnfX`DLJ^%&p;VZHmq1u--N^rCQ2jh ztb47qHX%ZCS9)c*`g<>uSJ(UN_G}+QoG)J)wK_SVz=$3gD7117up!P!FsUu62SF~K zjYtMqIPFT4j0%u8d>2Ibqy;FaSWn#C$&J?SC@E{|EZBhELAPGvnd@CO)8+wWPgf(bf>)<)av@g5;NpN=5s!GG#bmlU~Cyn&I-zt{4Dc%M{x{>gVp!LH;GXte(pf6PXVo?e9kJ?+s^LW+jU!Ca=O#xBB z0?e!XjI!`i#8B@^G?etNstZC|5bGDO_8*?|C!rJCRf#>0w1~7vkN(QSkrNdPC!at1 zZyxriJpK=_B!Ya1;NI^F*FZ+jaY1T3O$>IdOb60Eq0-lp7MKjdx z3T`0lSi3ge!Fk|SUJJ!D8nsPkGW)dKr|5*I1pdApGHI=~^!YWq3mVNu#o0NreYwx* z%T`|1J|1(*kfaTx!f8AY-5CjEj;ztYh+FLA`w=mpE2Hxh!UO8#w*M^Pm#v~5NG@12 zZa%;K>3rGP6c^au-2xDlz>nI4&!or)!_NwCxiL4lN_SmyT)zV$25r;FYua6>f*=RP z)^!Lvf0N_q@MdvX(`!&+A@w>xy5{fQe7%y`pWK)Wyg}4h+q0j zIVAC!sT1Tc`lwauglFXd&p*OWeN3Onu<7CO)qN;rJq7?Nj%vM}-eGR&=zNGz`$AjH27XgFKliD(@9 z8TiX=)?=5iIpy(I0PDDBL%J1iX46mqj0}y(-2!^7Tf_eoHR`|Z@{R$7=L&eJ;?C4S zw8Y7a9dkw}RidAW?$jlz1K@YM;IFh^YeDE*(6+tQW~3t4i)h=Xt)4{|Fhmm13jYY2$I{v3mhME=Bp06PwkHlk@Q*jos^=dQ+86*RQ zRjED2Qqjy%Oik|6NbNp=WCZ#>yFAKo;0{*50AM5}-#FdX-3z*PC%NxX*x-d{2)~e;jb^Dh6=u9MyNc+6H=LEwkdBj$I zKMrVub(a2wc8piGl>l8^VF^_xvbVOsK%bI~VmT6{z%wid7g_IcHz4Mp1Oo8A6eDTb zG545Dpvoyp)^^L8l%0PdJhu5Bc4U+x?qQ!4nVvYc3qkpk4-Owg9Fsq>QiOECZ%X5t zOaqCJ1UyMZOYSvq*}&70YT-@Z)K7sQU}E_rqgUFW6yY2F3jAm#nOJK zKv&ZK-s=Se1cHzURNAYdpslF^FqzO`sn|ZD8cswqHHM$HJxKbLJ}z~7!jdGX9DpmU zcd9sP!kW~cSzo7kIC+F0P~y~tCYTdrkf}pk<`Vr%ny}bC&e9llD=`W3L?^4? zDzZ6-49guYKP)janZy!gqzrIg_F+h);ec zvfA-f-)3SLAJPse)XGR*@E8lOJ%r?iYT=%5g233&|6%Rr>`^sVrxs)g{fqU4FMY#n zzvHf?0Ux@eO&LXS5sVVG(GwKLX-JbKq&UAQNeH+rLIy&C4-L14^nd^!3;?%+XMwm6 zkN|8vE8^Tad!M_F$wQnk^^~CzLhxSWsZ2iDYDDeZF;wz!@{^oekU;UWCuR3(Wb}lO z6b@>^rwdTp-B-b;$+pi*-Zmyj4-b*a6#S_ECSI1%^y&d8?m?+84#Tm0g2QamdxfUv ze3#JS!$pyW`!g&L^85$VIjd`}aG%uR@A7D4r}Obyrpud`D(Bd}Z27y+{3Cw!Et;%p1V-Yz2v=j-D#}cWCO%`r{mZ-{W&z)G5Kwg9tnal$V8M*l2?IzwYUcdn0 zXkPT@LbutHWwhGaA0CE3D~bwhyK#T(XnmH4z}qTfQh3q!^-0^o_&9e=h2%YJSn!TM z$eX3O0?a3RN2YdQ=$@ih8j)!y0rP$Ohbm1iG`kp<;HW#g&GyWU#X>@)K}0p?ReU$_ zEPFTRJK+d}pLfsmWR5fFH&0v)eaa8Q-pw>HE?4w7T;`Lv6)5e&E8y4nHSQ%(1Q^D7 z&Y65Gyc^BFH`(&o>besdl5*$7s=x9Vla!;f2(V}uUFLsEyO2ISVTnDj1TY2W3q9DA zG+Y^+&hNAoWHel&KeC$6>EhQvTP(4ixujk#0VKj^)|a%@524ROph^O@9O^Xo_yPPY z5$tEjeB%xs@qLYZ@`ZQRMr%7`V5xrnPM^2-UHB)I*nI$FV3m%~Jz;C6h!-lBt>(Jr zF<^z@X>pJb}$n)E4BIK&rwFrC%r&M3Q34s!f3AIVn|Wa2b7JalIXj+Fn+G)ZRl%TXuq zqR4~n-}3vILbAer-dTzG0N3re>3aSrSnh3nEHY7|2jnO0WK}7eEb%ZfYhP9Br-V|D zJd+hF>l4;~-C?#}p5-#W{P*=Uv(|RhvS`w8?A1T;p!aI?9r2)z5X$!T(l_Z1UBkzlGm$guJxI?2E=!Cne;mx2dpG$B5k5m5y{PYGKv%!ezP0?LK8s@7z zpRWj(LKt4I?%BJfY;DNZe4Y@AN|Q-)kA>;V%~-(&8(*5X%5iTve_ZNE^fXL;u5ioN z6vuqs-7;d|^588cj3HpWfY5}`UolqYdF_>(sR>jJfV`baOUK!H!GeeRo4MZUHkOdh zS6B_%&eDxLQ9t&qyiYRA6m0!~zOE(NVYsV5 zpmemGBsal}FBoGX6)!VpgoEk49#f0?O@&x}nTxY8rTMs+oJkp$oVq8w1i|FfMW~^^ zL?E*GVx#G=kkIQpR@&CK+&!pYf(Vmw#M3{^UgUV$dzG)P`8G`Z>ICJ!dxRD&KOf2q zdu~NlaBR!gkIRLOfiSkKfPP{ieTeW{`Q%R^7|$`c*_SxbwC}STA}^MYxPT{VgRWXN zD-m73oGeu@$m38V&~KaoO21|r1U)=@Mw_`qWb&+=VqH4nT>5=MH}l+N0;I097Re(3 zsP*`8;aM-M#>shjI-9s?g}zq_#zihENC)bU4Z>vrAN_Ph;f8j&(y=*lji}{OGu{UC z(_R^D#L+4M8q5~sd0x@{;n)usSe7;Q1=*NC?bXPbLt5yWl@9yHhiess*&9;DeKt0J zdnRI*6r2_J7bB)BnavDY6#2eo>1sh|ne~jm#%mR5UC3QagTRm@W4?sb??FL!H#X%( zliXRHbXu_Wk*$WPloq-4?0})foZg2EqB=>X>4N-UIMIC=>F7KBiD@MtlrPXI!5q z5jh&d{jBl3&y%~q-xBI-_9op9VI4lVz?s*Jww+B)hOdkY4!3h(anjJUN0SJH7fd4q z1Ca9t>s{oo8tz4VS0=P)1yW)`q5{1MLGfRuZOIt`$yK{J{K&N5WVop4Ezqi1sUeAa^T2ra>@K?rg+QzW>`y-|Ju0e?*0WUw40} zy*aTe{Upa1hBh2%N(7VUZRc&}gDxh1dQA~~Z9NR&SvE_L_yOTPOJ=*DZ5^11^2Uk8 z?z1cr!1JfDL2pEJ;3%;?2=bD6YjX^Eisd17roJ$S0s zzd8*ofMSJPV}{DC4+3c83(Z&5%GT6Bu}L*@T7Fo$^i>eeeuCsBqnn)GO z>j9q)n1viEm;Pgw#2f|uwjA+x5WE}{{t~{o@aoMYsR#aXTcPsnIcp?pOr-uy!<*l< z*j=_MMbo1)-)UYj=kqnfP!CrE6N^IH9(!acgnuBuI>%9SWfuH+LWUeIpqnKX;EbYxD`CVUu=`9VK4t+Y`hU;E@mz6INha>N zT>#0VIn&q3XuQ&^LGs+ePnQzNl2yvU9_9`iTq8LbklbJy&)9B7?0G#AVsDKd%7)|7 zZ8wuJ5rCxk`O;@$!>@yS_f4)|ZXT|=w@R3J3@$!q6MNnVB8q(D$gnSqmYhU+1c~#M zyCuczH#T(D;}iX{k^8%VGqFK!LWn-Li~Tr&2LxmW(E4m7w$Fb`W&W(bt!8Wc<= zs?Qe3FC-^{$gBG4Dc^3;&guY4F(cVzQ8*wUu$-JAZej4*;l#tuZXaWG>|Jt*w?E?(MzdO0`&2D# zj_%2241LuOkm6sh_<4ZL^I0foSeh&wd$(cvv*T-Z)}hG2QcEVF&ZFhb%0d+U(y4Z? zJ)o@TmutG2n~;6yx{W$+yRR(*`VTLewOIkS=*C9+`Xojjq0%lnvsf9e)pm||9rBUc zc^e_KSw+7qXQG>qgKGq=G^k-zK}6_FIi1i$dzsJY$J3UG=+ObWh*uvR*3pB~RUCt? zTxiy|(&bf5+d!jajSi1MvAGRD%VGS9rPIzmUkxn;14n*dz>%FmO#lPQ3h z_}=>$VqDPq(&9b$Vzd339}IOFou03m(_xBF!^mr@r7+H$iGZFi^65o$0m02@id+-Q z4TsP#*jl(JPIKLE*w@8+e7;of*wx__k^XtCRDzi$D4tdQ}0MXEb`MU0_W*2v7+f+lubT7 zfHpILJq7f>*?U4)D{8?W7u%!-Sf9BwnApXI8EvZnxaIee>}$K$)~E0yD=psI#Vs=$ zyI=1sD#T0;zVq!=+ahb)AifRb0UqN1`BM8~!ocr2OiEWHa^z5tKyDSd0uiQhp2cF+ zU0##NzLjFQ`8^XAu3O-Z)D7b?z@<7mEBwg}C{(csC_bK7$T@9UdkytJt>flS*hCKm zCDo$c-(fx@bB;nxXBFnWx0R`MIP9Ose`b&eWQL80q_}y5a+j+}uDTFnL-xjh&jVB8 zqR0N->+7}<-YfD&KC86bOCJ=Ht07IZR`Uzq$h&%(k2u!McFT_&VMXo*Aw7!i;)QNE zL@|EWd_De@_gCf}#tlu)+|2xSiG@*cq6qf`A#&WcoS`5TxZ5A$*U=So(dI6HaH#{Z zy7i4cqso7JmIhJC=rPEykCSIK<_fp(?($$^y@oh|oZQQr=}ab!c$^G(J}xa}W3T4; z(F9{@@1k!1IC#(Acn@s@z3%7lsac=4w4w4#i}!ED`zM|Sti0g-q2JD~gqN$A$%ufOYvf~(PaDBV`D32)!UlY(vUHUihxo`qZItTVe-0{8vlU{A6gi*EynIen46=C| z{9Qk^yvQSbC>RHw=fd%tZU01A^@C5>#oJ2}okz3nYA$s+w)4t~s}uN@yn5rnM&yMhr5Ng(XXeQoSP`4`ZZ`e9pF%8dtD#Wxj)a-Al+J=;e^St;9L?sUI znX0+};@Z_-#+SO%I=QPR5Tza6+fh7}rFj|-`!}P>qr&QE?XZR01~~v3M>ga=dO7M3 zul|m?>~;nx&ptiras|=~rf%TuhJ#i*ayOsFt#8sGpiW^OLMuQ&o3 z3Eqb@dJf@ocGT?kiabCXr>=ZPfkR`IR*QEjpjt2;EaGUX~MzgFaG+6ZxI zVHgVD=~ogf5S6W|_bQoDpt&onQkc2b!a#c+axQ%E?ca;2>aq5lrX2iGC{X+j-0lNkc@k2ieBqT3UXpiBb0QS>Cr&dfAbTKxb#!mPs;2%7f5pcelq z)ojuH`p1}nc4kf4-=Fj=_P z?VG@!!%8{H_tXhrJ{%K7K{&vhxgk$afuCA2e=ov#Pw8yy)V8FCR!KxK4rkUxLt6E8 zaqOyoA}SQ8Q{)CgyhO9vyBXB`U?#(|Yi9K>JMdOG+`^ths;N_I2*MSkd0QT8UEcri ztPWrC?x|Eal?r5Z~>e*~Dq@ zq12Pxm~f{E2{X0wLiHedI+w}mx=~sLOSl^^iP#p1FdOWy^x;E8iQ$O@J)fwI8+aRuYuGDx%sI6I9DVx{Fwu|Uo zsev1sp>?7QrSDeO(!F!T{=@s~)cFEs@OxEy_t{%7jrFtC*A*xWXb>eac z8d?WlfecsHde4bz^B>WHdlb0#!ETH>RxRA)dBc;lIIDm$Fgm>|5AEIyj*4i_W!U{8 zlzgC%sd|TSZ0TN}usYk9zH{g9Yb{9KOr*F7^!yRZd)6Np=3dS{nkEcnnVd53Zf(w% zZ?7jwU*1X!hn)=_4gUo8ZbRaDX7GZkanSAF@0=(uRIL~jRz<(OOPbK1$j#9*^rF-E zt{Q9TxXC0y*)Wp1I~>FTY9VF#U>`|T?5$|yFT3C6g$gz@=`S~l4xIwtR*%$3`4C(6C|WSxz(R^ZB| zY{u3}h#ZaVG!9rUYJ6YkavLeo8o&(+REb5`%A9-=64^;%riWXA+-Hhu>hHV_%`jpH z=w}45c32{D^2KoAq>W;QMN_Yio^mqa!?dT#8>8RSM0?|*IdOW*2V4Sz2|B?O018*= zv*EaD(lH5z_b1u0zQo^sH(BP+tUw8zwh6w_=j(}{s&xjLf0tDQA2WL><3gZpsOK$~ zPiB_u$#ckORulcX{lH)9YaR+`Sr+pSxeX6QhWiyeIwkvj`^@CY?nUGV3t5L6gw#b( zyTq@bcz?DbH`GQorUrR3r%zr2#fw}5Mub(C3kf3hI*bI*UleN4I@HWm9Xn<|@#s%U zmOMm#z9g!)X;^qoJDUKf1isc?=Ov2uFTGuO3AMyzWpDj| z4Z@Gdt`NENwQ6`oDU#W$a4P-JCX%-;cT3ESJI?4F*Q=1XqN9i8-zq=XrtkBqveCHG znd~P!hq%5FG%nS~3-bvv*mhwud2Jys)GRZ^;e95=y=R_qIwDKAP{DME#m^qe8k`~+ z_RYG=dB1J`evtVtVLOn?y*zCPt^qPiEx(ORscmcsFhSeR7TLEd`@yCUYxFbT z3p8XJoBpP38`iqXUWnkxTYpJGq7pG{SSQW<8I2~esJt+4dXr$PjgU1eFVbS*7gz#Q zfZ^O&cknGkns{53Z{xI2RWm!A@}5ayx;?f(e-qKZpHb2vlgUWiD$Mm)sBYgoaMo5lW&H=kJn!XZ79(-WkzTl&K$cB_B6d_4|pue4>v3>(d)ONdC7&Noo zpAgcUB8@IOyZ+^}$@T7>9g+-~XzN-W)1PYR+Pt$g&gQgit8k1ZX1j17NlC6AeO&qe z$Db^xcc~@nBjE)Zbob1)=?-=DrKx28FKNhe)=!vUh}x@tACtZnADjf9QBFfLA=EJd z0&=Fi)bE!NAoy5h%oegmBsD27J6AwZdP&r`L`yXU2A405l@Z3hWL1h>SXS>7!Iys| zV?v(D{ShnLD^EMBb)F?6_rac`)Q8$f55EBF1nG766U^GUMkeMZR%X_-ehNn=1fxW* zjply+HrQ4pH8l`1+6r+_>U@N;?Dtn-gWJo5W-;Dpk^Vtj=q6t}zoALpB&ZXi=gfA- znJ!6PgMra;-#k8S+1a~nX8nm&Y);uWOL@Fm3iSd|$*BnStr22`_asCYfO?+%O=%(s zK}EYV>oD(Og^c|!!gkWig;0?OG*Rry`JH$*r>HV#r$xJK`cz& zoZd_Uu~kVC7jlEE|t$11J{b=9|2cMJtk5F9p z7Mb)7oMc<#dcT7lpHiyn$Aa90b+-6>7CxdcCk=NN`a6|3MIoO@nG;iMuD{n6ma$wT z+mF}!WV({Y`3&tr0vFNIg}NCLyc|oX=}BAb`SN@V_gwz~{c{qDx1z^o>#^8p#3D%0=69uPvQ&dyRy(rENbcZPqzwNWW=+1 zWIs5q$V)Cy^mDUA5^2)0;Yo+N>icDxs7)OI?|#nFKVK)IvCnIlfcE+hoY?MHt$|5J zNo;Ni#RzPoSU*2%;lJ)|ZL%CR`u_A-mSdQ|+qe54slSZ=SHu1B_ljdi-mKCX;Lm?H z#CjBgX`}Nvnr%uu?>*$BEy~U{7uty-xY)gM!@x~vV7D#;7abDhqFN|}9*^)jJ{Yzz#%^4_|du0HZfnI(lzFS``=jp31RBg z+!8W+=12hJX&1}#GLAz3Owl{4g5-<9*6Ts3GOl0OjYmQhWHR8*zf6O@a8%;a&-JeU+qgWf}% zH~k;Um|6_1m<#Bs(okvC)Afm9tlJ%(*k`x1jj}}?W)Qg}>CuVZ>oi*q7yG>T znqoW-8CQ=F`q|o)Ik?*O2U)JAR=Y2lP04t>?BnT-H5QpWYoUo^D)E{%HH|Y3SB4F5 ze}q-vUEXv2Be!*rD(Ew{SZ?Ae?zzu%r_32n)H`{{s%c*+sK&ssJKY&Mg|+{I;piiL zO-?xE$Msj0$T+XsOnUg*&VWjxCuF{0Aerg_h2hqkdY-I11(Ow!(eM)~S`w(ubPiC^ zqB(&1<0pZ$x1z-d4$24qUhl9)HyQSbXi#%jQqU4Osx#ORje?sY-R>>0HdYFZZOxKg z^`(gfx*;ZSEmLG5L?N%jdeyB_f-gi=g|LzoOgpue^4#!cAZ{d{EDj`3R^aSGj^j=< z2+5PeX~7M3hm=Sz!EP}~tEiWI-Pd9uD9)n39;4+)2#@7oGcenU-d1|$c=wkN%Xueu zZMu&uJOBJk*&YWOpP_x{8&mtpv3$3GCmHctKw3JyKgyQ=vR+pY=&&JK>rp$?vMw8` z)k1<#k}itJf0BsmO7p4)hp-ig!*E2YlkoR3- zNh;$m!F!`i*13IKRDCorRma@Kq1>7z&-K}@k)`E72+c8@rb>L@{c~WWnupPs^#I_i zs-*L_;Uss$ydIXSV4Xb14?yQuKq;qOtb$DB=({4Ni#c!m*0(Myh$(c7hZzpzw3A%#7vRHlz$x(akNNZZbrC{mElpV1mWr z+sKbypTpS|i3WAdv5<1*hdu96|KX{LNliz*<5(70m3mL#eL71Lz)&WKQ{EFk#8@Lc zT(U4gB-^J`gl@86t!b2ef1|0R%85zJe7uF$zten$wY!VqH{;edzpF}lC|ICP}r4|{p)8mGHO8oqG@IN*fWQbG? z2~2Z)~^zZ1kk&aX(n1ydPQkxMI8@7-z>L zv_gf$_ysoC5XwkLcxO)UpGD;LD);OSAlB`0Sk zmsNY%CEev_X)Pe9!uF*d@UX`@c#x@CkvFF+`t5GSSnh4y9bp3|CIs!=oh_7H$v%Bv zKn(0*sf-T-Ge54Au+Q*Px0CePLPy9Sta~CPE@w7Yj+-_x+ck5=EzKf78(Xx#fo39~ z*5y*=+B&yf7037(i;E4MBL1EIhljspbwGjGLxfdcAu{fe2!!REmb9R}?zj50X}U5= zxX!bSu1lA@ubmX(Blj$gXu3gbC7j}GjY@DmmZ>+NE!T2!FL3wpO7e$CY99?rjn~tB zolqg1e5oEI0Qk~wU544frEG&}*Y^-cPrRT$g66~{qTOUze)Fec8oT2E)?mP+oQhCM zbmCCoWX!xPAkjlT>fKyEmHc>4*Pq5lG-ZP@bEdp#7@>P?LMa#Zd&NQCQ^1=;oI4T9 zxDTfNi~?-j$Gbi_Jz_L%IYUn3#hNe1_S`Oq@fBb;P-D0fpL56Qc9r*)7|~s$4b@t14=UP_V1Qr754!19$(NQz9!=Vpr z4$Y=4Fo(*kA)h}YGqdRK>S@R#)%}y&4S^Sf@0E(KxAO1{*`TSee z+$3T{fB4pulQOep<_a}lvM-cR7d1s6n=8SelKFu&+$YZ(Dyfz?)L{#M!%j+}ZD^{~};5w%eG1YRq4+U2<|TTv33D=-Tzy!4npZuma=S%K%D z%=ll_5?SKf*d;Sba_o5}ssoWcFj&J*@~P z!qWKPZCU~t0{L`cjo%LGGAW1C`pF5tY_Ut){8rPv5&$5>lH>20%alto)jbx|lE>)+ zRNfzIMs}-1MEt1@{ivQ}f#2M)Ituj(Cu408sqXtUU zUr4lCdEu3{(|NSqk?$Ib3%KoC&h!W6jNw10X}g)1_*u4Zk+{Bk|KUN6xoM}2@6G&G z^n%4GN$oex`bBl-@9fkIVXF}{zsxp;SdVfq$4xxPQ}y#3lYUm&Z0FQp*(>iN@G@F& z$%BVG^Vi~%`dtm)BZ*{~>B#eVYbcE#J9g#0LGy!-Z96BE4i zc$=w-;j!F}|Kx@lZnu=^So@qa{mQ?YlGolYxfu9Y4k-Wou3@6d=?#f@{j-Xhrt1U! zSM|T1>2fWIV+fah!m{`5@wWJ)1lMd;EaH)b^-r?bj=Tq+AV`J?C!4O1jL8#78wNYx+?sd8k=0WG@Z8xkaYvi9 zzb3Y`pBZN{@Di0X+qLo(&+ZhY&aV8+XKqfDPy3UT^yJxV_kwUYrnMLur&3u}#|}jK zcgsmp0w8FmBCZw9D!1{Pj`D=V?R+U2YK^)qt+E<_&EpD4nTi$_;pRxrA2hPbJ4%~L*%=M@0F5DYvtRfb3K`un_GFIg8KQ4UzC6B8@}y% z*|y~NSjuIFvxSJ^kZ99PK@_!GzbSj>NI2{23}XLEG}=(r7pd1IUQS!Q73lV=a>X%c zB4zZqNKI%n=Z%+pR@+=@ZEL=&*pnPK!OfAQxP+H1Jl6TT#Qa8E+Z-9_$%uBf^%XnW z+d$jaj(iU0f8XuFjK22!eNCZGbyP@-&c?HWO63Q}*j`h6X9CJuHpOc=#2LCa3En;S z6XAKvW&icYlw}iw-&c9yY@APP;-#Ic>R(yCxIiX`0oxQg90zEU4P|2BRytZp&+N=S zX74C6jfpUgoz^yU+7R_&D-sIJWipX#Q)x7ja)^N2_(x?1>pwjqX9@WiwGOM{IgANO z8Y}sLN%48Os9JtrkrfJFzzpYemH2lo!tqvbRQ>XzD)acy7k%|a2bU-ca-|Sc>2;vU zMGN$2;&!H8`O~u7Lp?nE`6iEsrD4Bx+Zt`pRlQele`^tK@+IqA1^)K_yWeM&l8O4N z-++3=#L)q`(@X#_(L=-3RL!WDCYiItj%ps?+Z`^ioR-u(P6>G+MHOBB(axUPHPR*u z{rK2}ao@vpd36~-n(sPC?U&b0k|kzSV2_`MPuspmeOwNiUS70*Q^xoN4(CZwoF9qY;SN`y}+oCumW z?gOEPMc1hg<*F>WaSJX^-6*OOQ98jQmXtqN>2y@SgY-g|9Zzb`A1K5|$ z#y}-jW-BkrI3COO264V86TyCVlBRl3M>3~6P<^&*5vUW2fkaV zF@IbjW-*L+t;a#0K%o=V_4)m++F+jYJ%x@NTxS|9&g{MP#VMn!flZcva9V^vh9E_1 zw^$hs+yuskORWtzt&*H>PahYjaS-N49CAiT(jNc-s7c+IBLfj1Mn42--#y4=b96jojf#amoAmBf*>Rer<`4`BH#nE&b&xVQBNuU*er~ zOJ<<|p9C^(MY`{g`)GFQJ$3nNXF~1bOI?Bi(W^(#pKy91UWi(Co*Hju&BbL-1ipLo zusxtZp9PoR5gzgqHYW#w?xmiP#B-+hM{&|}ZX_AkMoqcTchX~NFtKRmUKyQAnUai+ z6~)&7SmW7q zV!jY-!TAY&rxRi)t*`qPI~VLJJ`o>N$GsFt(Eaxevw|ON6P-c=xHwuog}X-=9E}05 zww~^X6NFD?brLmZDd`cf24?k329TwVv7oQ}BvCY0N8Ci!ZX;Qj{%F%^ki#sDg~T=6 zN3-uw*1LqtH(N&|A9C%RM+Sm*g|!xSSJjM3v5`J!-WNxFJtV;tLZI!CQt z5q8w;4I+4R_US<=H~K-l7!vfv8BH^oRoW-ZtLM!3T|l(W=-AFnCgBz?2H6-3aD8yMml(WH_Xpv)0?lIvTOeYSS&Q#8YT1|VY!oaKK*^^MeD@76> z>d#w-mripdq&ZpVKCiDu7Y?>TklL3x_UwN9y6LZ4Mq=|mxVwin!l{F(m+f(={00s_ z9o*BxeUJ3a(2pvZg54

Vvo6Xwr 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 J*QbBi{sX{Gol*b* literal 0 HcmV?d00001 diff --git a/onnx/onnx-simple.sh b/onnx/onnx-simple.sh new file mode 100644 index 0000000..eed4d41 --- /dev/null +++ b/onnx/onnx-simple.sh @@ -0,0 +1 @@ +python3 -m onnxsim $1 $2 \ No newline at end of file diff --git a/ptocr/__init__.py b/ptocr/__init__.py new file mode 100644 index 0000000..eab50c3 --- /dev/null +++ b/ptocr/__init__.py @@ -0,0 +1,6 @@ +#-*- coding:utf-8 _*- +""" +@author:fxw +@file: __init__.py.py +@time: 2020/08/07 +""" \ No newline at end of file diff --git a/ptocr/dataloader/DetLoad/DBProcess.py b/ptocr/dataloader/DetLoad/DBProcess.py new file mode 100644 index 0000000..7fbe42b --- /dev/null +++ b/ptocr/dataloader/DetLoad/DBProcess.py @@ -0,0 +1,123 @@ +#-*- coding:utf-8 _*- +""" +@author:fxw +@file: DBProcess.py +@time: 2020/08/11 +""" +import cv2 +import torch +import numpy as np +from PIL import Image +from torch.utils import data +from .MakeBorderMap import MakeBorderMap +from .transform_img import Random_Augment +from .MakeSegMap import MakeSegMap +import torchvision.transforms as transforms +from ptocr.utils.util_function import resize_image + +class DBProcessTrain(data.Dataset): + def __init__(self,config): + super(DBProcessTrain,self).__init__() + self.crop_shape = config['base']['crop_shape'] + self.MBM = MakeBorderMap() + self.TSM = Random_Augment(self.crop_shape) + self.MSM = MakeSegMap(shrink_ratio = config['base']['shrink_ratio']) + img_list, label_list = self.get_base_information(config['trainload']['train_file']) + self.img_list = img_list + self.label_list = label_list + + def order_points(self, pts): + rect = np.zeros((4, 2), dtype="float32") + s = pts.sum(axis=1) + rect[0] = pts[np.argmin(s)] + rect[2] = pts[np.argmax(s)] + diff = np.diff(pts, axis=1) + rect[1] = pts[np.argmin(diff)] + rect[3] = pts[np.argmax(diff)] + return rect + + def get_bboxes(self,gt_path): + polys = [] + tags = [] + with open(gt_path, 'r', encoding='utf-8') as fid: + lines = fid.readlines() + for line in lines: + line = line.replace('\ufeff', '').replace('\xef\xbb\xbf', '') + gt = line.split(',') + if "#" in gt[-1]: + tags.append(True) + else: + tags.append(False) + # box = [int(gt[i]) for i in range(len(gt)//2*2)] + box = [int(gt[i]) for i in range(8)] + polys.append(box) + return np.array(polys), tags + + def get_base_information(self,train_txt_file): + label_list = [] + img_list = [] + with open(train_txt_file,'r',encoding='utf-8') as fid: + lines = fid.readlines() + for line in lines: + line = line.strip('\n').split('\t') + img_list.append(line[0]) + result = self.get_bboxes(line[1]) + label_list.append(result) + return img_list,label_list + + def __len__(self): + return len(self.img_list) + + def __getitem__(self, index): + + img = Image.open(self.img_list[index]).convert('RGB') + img = np.array(img)[:,:,::-1] + + polys, dontcare = self.label_list[index] + + img, polys = self.TSM.random_scale(img, polys, self.crop_shape[0]) + img, polys = self.TSM.random_rotate(img, polys) + img, polys = self.TSM.random_flip(img, polys) + img, polys, dontcare = self.TSM.random_crop_db(img, polys, dontcare) + img, gt, gt_mask = self.MSM.process(img, polys, dontcare) + img, thresh_map, thresh_mask = self.MBM.process(img, polys, dontcare) + + img = Image.fromarray(img).convert('RGB') + img = transforms.ColorJitter(brightness=32.0 / 255, saturation=0.5)(img) + img = self.TSM.normalize_img(img) + + + gt = torch.from_numpy(gt).float() + gt_mask = torch.from_numpy(gt_mask).float() + thresh_map = torch.from_numpy(thresh_map).float() + thresh_mask = torch.from_numpy(thresh_mask).float() + + return img,gt,gt_mask,thresh_map,thresh_mask + + + +class DBProcessTest(data.Dataset): + def __init__(self,config): + super(DBProcessTest,self).__init__() + self.img_list = self.get_img_files(config['testload']['test_file']) + self.TSM = Random_Augment(config['base']['crop_shape']) + self.test_size = config['testload']['test_size'] + self.config =config + + def get_img_files(self,test_txt_file): + img_list = [] + with open(test_txt_file, 'r', encoding='utf-8') as fid: + lines = fid.readlines() + for line in lines: + line = line.strip('\n') + img_list.append(line) + return img_list + + def __len__(self): + return len(self.img_list) + def __getitem__(self, index): + ori_img = cv2.imread(self.img_list[index]) + img = resize_image(ori_img,self.config['base']['algorithm'], self.test_size,stride = self.config['testload']['stride']) + img = Image.fromarray(img).convert('RGB') + img = self.TSM.normalize_img(img) + return img,ori_img \ No newline at end of file diff --git a/ptocr/dataloader/DetLoad/MakeBorderMap.py b/ptocr/dataloader/DetLoad/MakeBorderMap.py new file mode 100644 index 0000000..3275993 --- /dev/null +++ b/ptocr/dataloader/DetLoad/MakeBorderMap.py @@ -0,0 +1,114 @@ +#-*- coding:utf-8 _*- +""" +@author:fxw +@file: MakeBorderMap.py.py +@time: 2020/08/11 +""" + +import numpy as np +import cv2 +from shapely.geometry import Polygon +import pyclipper + +class MakeBorderMap(): + def __init__(self,shrink_ratio=0.4,thresh_min=0.3,thresh_max=0.7): + super(MakeBorderMap,self).__init__() + self.shrink_ratio = shrink_ratio + self.thresh_min = thresh_min + self.thresh_max = thresh_max + def process(self, img,polys,dontcare): + thresh_map = np.zeros(img.shape[:2], dtype=np.float32) + thresh_mask = np.zeros(img.shape[:2], dtype=np.float32) + + for i in range(len(polys)): + if dontcare[i]: + continue + self.draw_border_map(polys[i], thresh_map, mask=thresh_mask) + thresh_map = thresh_map * (self.thresh_max - self.thresh_min) + self.thresh_min + return img,thresh_map,thresh_mask + + def draw_border_map(self, polygon, canvas, mask): + polygon = np.array(polygon) + assert polygon.ndim == 2 + assert polygon.shape[1] == 2 + + polygon_shape = Polygon(polygon) + distance = polygon_shape.area * \ + (1 - np.power(self.shrink_ratio, 2)) / polygon_shape.length + subject = [tuple(l) for l in polygon] + padding = pyclipper.PyclipperOffset() + padding.AddPath(subject, pyclipper.JT_ROUND, + pyclipper.ET_CLOSEDPOLYGON) + padded_polygon = np.array(padding.Execute(distance)[0]) + cv2.fillPoly(mask, [padded_polygon.astype(np.int32)], 1.0) + + xmin = padded_polygon[:, 0].min() + xmax = padded_polygon[:, 0].max() + ymin = padded_polygon[:, 1].min() + ymax = padded_polygon[:, 1].max() + width = xmax - xmin + 1 + height = ymax - ymin + 1 + + polygon[:, 0] = polygon[:, 0] - xmin + polygon[:, 1] = polygon[:, 1] - ymin + + xs = np.broadcast_to( + np.linspace(0, width - 1, num=width).reshape(1, width), (height, width)) + ys = np.broadcast_to( + np.linspace(0, height - 1, num=height).reshape(height, 1), (height, width)) + + distance_map = np.zeros( + (polygon.shape[0], height, width), dtype=np.float32) + for i in range(polygon.shape[0]): + j = (i + 1) % polygon.shape[0] + absolute_distance = self.distance(xs, ys, polygon[i], polygon[j]) + distance_map[i] = np.clip(absolute_distance / distance, 0, 1) + distance_map = distance_map.min(axis=0) + + xmin_valid = min(max(0, xmin), canvas.shape[1] - 1) + xmax_valid = min(max(0, xmax), canvas.shape[1] - 1) + ymin_valid = min(max(0, ymin), canvas.shape[0] - 1) + ymax_valid = min(max(0, ymax), canvas.shape[0] - 1) + canvas[ymin_valid:ymax_valid + 1, xmin_valid:xmax_valid + 1] = np.fmax( + 1 - distance_map[ + ymin_valid-ymin:ymax_valid-ymax+height, + xmin_valid-xmin:xmax_valid-xmax+width], + canvas[ymin_valid:ymax_valid + 1, xmin_valid:xmax_valid + 1]) + + def distance(self, xs, ys, point_1, point_2): + ''' + compute the distance from point to a line + ys: coordinates in the first axis + xs: coordinates in the second axis + point_1, point_2: (x, y), the end of the line + ''' + height, width = xs.shape[:2] + square_distance_1 = np.square( + xs - point_1[0]) + np.square(ys - point_1[1]) + square_distance_2 = np.square( + xs - point_2[0]) + np.square(ys - point_2[1]) + square_distance = np.square( + point_1[0] - point_2[0]) + np.square(point_1[1] - point_2[1]) + + cosin = (square_distance - square_distance_1 - square_distance_2) / \ + (2 * np.sqrt(square_distance_1 * square_distance_2)) + square_sin = 1 - np.square(cosin) + square_sin = np.nan_to_num(square_sin) + result = np.sqrt(square_distance_1 * square_distance_2 * + square_sin / square_distance) + + result[cosin < 0] = np.sqrt(np.fmin( + square_distance_1, square_distance_2))[cosin < 0] + # self.extend_line(point_1, point_2, result) + return result + + def extend_line(self, point_1, point_2, result): + ex_point_1 = (int(round(point_1[0] + (point_1[0] - point_2[0]) * (1 + self.shrink_ratio))), + int(round(point_1[1] + (point_1[1] - point_2[1]) * (1 + self.shrink_ratio)))) + cv2.line(result, tuple(ex_point_1), tuple(point_1), + 4096.0, 1, lineType=cv2.LINE_AA, shift=0) + ex_point_2 = (int(round(point_2[0] + (point_2[0] - point_1[0]) * (1 + self.shrink_ratio))), + int(round(point_2[1] + (point_2[1] - point_1[1]) * (1 + self.shrink_ratio)))) + cv2.line(result, tuple(ex_point_2), tuple(point_2), + 4096.0, 1, lineType=cv2.LINE_AA, shift=0) + return ex_point_1, ex_point_2 \ No newline at end of file diff --git a/ptocr/dataloader/DetLoad/MakeSegMap.py b/ptocr/dataloader/DetLoad/MakeSegMap.py new file mode 100644 index 0000000..3a72c7d --- /dev/null +++ b/ptocr/dataloader/DetLoad/MakeSegMap.py @@ -0,0 +1,171 @@ +#-*- coding:utf-8 _*- +""" +@author:fxw +@file: MakeSegMap.py +@time: 2020/08/11 +""" +#-*- coding:utf-8 _*- +""" +@author:fxw +@file: MakeSegMap.py +@time: 2020/04/28 +""" + +import cv2 +import pyclipper +from shapely.geometry import Polygon +import numpy as np +import Polygon as plg + +class MakeSegMap(): + r''' + Making binary mask from detection data with ICDAR format. + Typically following the process of class `MakeICDARData`. + ''' + def __init__(self, algorithm='DB',min_text_size = 8,shrink_ratio = 0.4,is_training = True): + self.min_text_size =min_text_size + self.shrink_ratio = shrink_ratio + self.is_training = is_training + self.algorithm = algorithm + def process(self, img,polys,dontcare): + ''' + requied keys: + image, polygons, ignore_tags, filename + adding keys: + mask + ''' + h, w = img.shape[:2] + if self.is_training: + polys, dontcare = self.validate_polygons( + polys, dontcare, h, w) + gt = np.zeros((h, w), dtype=np.float32) + mask = np.ones((h, w), dtype=np.float32) + if self.algorithm =='PAN': + gt_text = np.zeros((h, w), dtype=np.float32) + gt_text_key = np.zeros((h, w), dtype=np.float32) + gt_kernel_key = np.zeros((h, w), dtype=np.float32) + + for i in range(len(polys)): + polygon = polys[i] + height = max(polygon[:, 1]) - min(polygon[:, 1]) + width = max(polygon[:, 0]) - min(polygon[:, 0]) + if dontcare[i] or min(height, width) < self.min_text_size: + cv2.fillPoly(mask, polygon.astype( + np.int32)[np.newaxis, :, :], 0) + dontcare[i] = True + else: + if self.algorithm == 'PAN': + cv2.fillPoly(gt_text, [polygon.astype(np.int32)], 1) + cv2.fillPoly(gt_text_key, [polygon.astype(np.int32)], i + 1) + polygon_shape = Polygon(polygon) + distance = polygon_shape.area * \ + (1 - np.power(self.shrink_ratio, 2)) / polygon_shape.length + subject = [tuple(l) for l in polys[i]] + padding = pyclipper.PyclipperOffset() + padding.AddPath(subject, pyclipper.JT_ROUND, + pyclipper.ET_CLOSEDPOLYGON) + shrinked = padding.Execute(-distance) + if shrinked == []: + cv2.fillPoly(mask, polygon.astype( + np.int32)[np.newaxis, :, :], 0) + dontcare[i] = True + continue + shrinked = np.array(shrinked[0]).reshape(-1, 2) + cv2.fillPoly(gt, [shrinked.astype(np.int32)], 1) + if self.algorithm == 'PAN': + cv2.fillPoly(gt_kernel_key, [shrinked.astype(np.int32)], i + 1) + if self.algorithm == 'PAN': + return img,gt_text,gt_text_key,gt,gt_kernel_key,mask + return img,gt,mask + + def validate_polygons(self, polygons, ignore_tags, h, w): + ''' + polygons (numpy.array, required): of shape (num_instances, num_points, 2) + ''' + if len(polygons) == 0: + return polygons, ignore_tags + assert len(polygons) == len(ignore_tags) + for polygon in polygons: + polygon[:, 0] = np.clip(polygon[:, 0], 0, w - 1) + polygon[:, 1] = np.clip(polygon[:, 1], 0, h - 1) + + for i in range(len(polygons)): + area = self.polygon_area(polygons[i]) + if abs(area) < 1: + ignore_tags[i] = True + if area > 0: + polygons[i] = polygons[i][::-1, :] + return polygons, ignore_tags + + def polygon_area(self, polygon): + edge = 0 + for i in range(polygon.shape[0]): + next_index = (i + 1) % polygon.shape[0] + edge += (polygon[next_index, 0] - polygon[i, 0]) * (polygon[next_index, 1] - polygon[i, 1]) + + return edge / 2. + + +class MakeSegPSE(): + + def __init__(self, kernel_num = 7,shrink_ratio = 0.4): + self.kernel_num = kernel_num + self.shrink_ratio = shrink_ratio + + def dist(self,a, b): + return np.sqrt(np.sum((a - b) ** 2)) + + def perimeter(self,bbox): + peri = 0.0 + for i in range(bbox.shape[0]): + peri += self.dist(bbox[i], bbox[(i + 1) % bbox.shape[0]]) + return peri + + def shrink(self,bboxes, rate, max_shr=20): + rate = rate * rate + shrinked_bboxes = [] + for bbox in bboxes: + area = plg.Polygon(bbox).area() + peri = self.perimeter(bbox) + + pco = pyclipper.PyclipperOffset() + pco.AddPath(bbox, pyclipper.JT_ROUND, pyclipper.ET_CLOSEDPOLYGON) + offset = min((int)(area * (1 - rate) / (peri + 0.001) + 0.5), max_shr) + + shrinked_bbox = pco.Execute(-offset) + if len(shrinked_bbox) == 0: + shrinked_bboxes.append(bbox) + continue + + shrinked_bbox = np.array(shrinked_bbox)[0] + shrinked_bbox = np.array(shrinked_bbox) + if shrinked_bbox.shape[0] <= 2: + shrinked_bboxes.append(bbox) + continue + + shrinked_bboxes.append(shrinked_bbox) + + return np.array(shrinked_bboxes) + + def process(self,img, bboxes, tags): + + bboxes = np.array(bboxes).astype(np.int) + gt_text = np.zeros(img.shape[0:2], dtype='uint8').copy() + training_mask = np.ones(img.shape[0:2], dtype='uint8').copy() + + if bboxes.shape[0] > 0: + for i in range(bboxes.shape[0]): + cv2.drawContours(gt_text, [bboxes[i]], -1, 1, -1) + if tags[i]: + cv2.drawContours(training_mask, [bboxes[i]], -1, 0, -1) + gt_kernels = [] + for i in range(1, self.kernel_num): + rate = 1.0 - (1.0 - self.shrink_ratio) / (self.kernel_num - 1) * i + gt_kernel = np.zeros(img.shape[0:2], dtype='uint8') + kernel_bboxes = self.shrink(bboxes, rate) + for j in range(bboxes.shape[0]): + cv2.drawContours(gt_kernel, [kernel_bboxes[j]], -1, 1, -1) + gt_kernels.append(gt_kernel) + return img, training_mask, gt_text, gt_kernels + + diff --git a/ptocr/dataloader/DetLoad/PANProcess.py b/ptocr/dataloader/DetLoad/PANProcess.py new file mode 100644 index 0000000..8da1e7c --- /dev/null +++ b/ptocr/dataloader/DetLoad/PANProcess.py @@ -0,0 +1,121 @@ +#-*- coding:utf-8 _*- +""" +@author:fxw +@file: PANProcess.py +@time: 2020/08/11 +""" +import cv2 +import torch +import numpy as np +from PIL import Image +from torch.utils import data +from .transform_img import Random_Augment +from .MakeSegMap import MakeSegMap +import torchvision.transforms as transforms +from ptocr.utils.util_function import resize_image + +class PANProcessTrain(data.Dataset): + def __init__(self,config): + super(PANProcessTrain,self).__init__() + self.crop_shape = config['base']['crop_shape'] + self.TSM = Random_Augment(self.crop_shape) + self.MSM = MakeSegMap(algorithm=config['base']['algorithm'],shrink_ratio = config['base']['shrink_ratio']) + img_list, label_list = self.get_base_information(config['trainload']['train_file']) + self.img_list = img_list + self.label_list = label_list + + def order_points(self, pts): + rect = np.zeros((4, 2), dtype="float32") + s = pts.sum(axis=1) + rect[0] = pts[np.argmin(s)] + rect[2] = pts[np.argmax(s)] + diff = np.diff(pts, axis=1) + rect[1] = pts[np.argmin(diff)] + rect[3] = pts[np.argmax(diff)] + return rect + + def get_bboxes(self,gt_path): + polys = [] + tags = [] + with open(gt_path, 'r', encoding='utf-8') as fid: + lines = fid.readlines() + for line in lines: + line = line.replace('\ufeff', '').replace('\xef\xbb\xbf', '') + gt = line.split(',') + if "###" in gt[-1]: + tags.append(True) + else: + tags.append(False) + # box = [int(gt[i]) for i in range(len(gt)//2*2)] + box = [int(gt[i]) for i in range(8)] + polys.append(box) + return np.array(polys), tags + + def get_base_information(self,train_txt_file): + label_list = [] + img_list = [] + with open(train_txt_file,'r',encoding='utf-8') as fid: + lines = fid.readlines() + for line in lines: + line = line.strip('\n').split('\t') + img_list.append(line[0]) + result = self.get_bboxes(line[1]) + label_list.append(result) + return img_list,label_list + + def __len__(self): + return len(self.img_list) + + def __getitem__(self, index): + img = cv2.imread(self.img_list[index]) + polys, dontcare = self.label_list[index] + + img, polys = self.TSM.random_scale_pan(img, polys) + img, polys = self.TSM.random_rotate(img, polys) + img, polys = self.TSM.random_flip(img, polys) + img,gt_text,gt_text_key,gt_kernel,gt_kernel_key,train_mask = self.MSM.process(img, polys, dontcare) + + imgs = [img, gt_text, gt_text_key, gt_kernel, gt_kernel_key, train_mask] + imgs = self.TSM.random_crop_pan(imgs) + img, gt_text, gt_text_key, gt_kernel, gt_kernel_key, train_mask = imgs[0], imgs[1], imgs[2], imgs[3], imgs[ + 4], imgs[5] + + img = Image.fromarray(img).convert('RGB') + img = transforms.ColorJitter(brightness=32.0 / 255, saturation=0.5)(img) + img = self.TSM.normalize_img(img) + + + gt_text = torch.from_numpy(gt_text).float() + gt_text_key = torch.from_numpy(gt_text_key).float() + gt_kernel = torch.from_numpy(gt_kernel).float() + gt_kernel_key = torch.from_numpy(gt_kernel_key).float() + train_mask = torch.from_numpy(train_mask).float() + + return img,gt_text, gt_text_key, gt_kernel, gt_kernel_key, train_mask + + +class PANProcessTest(): + def __init__(self, config): + super(PANProcessTest, self).__init__() + self.img_list = self.get_img_files(config['testload']['test_file']) + self.TSM = Random_Augment(config['base']['crop_shape']) + self.test_size = config['testload']['test_size'] + self.config = config + + def get_img_files(self, test_txt_file): + img_list = [] + with open(test_txt_file, 'r', encoding='utf-8') as fid: + lines = fid.readlines() + for line in lines: + line = line.strip('\n') + img_list.append(line) + return img_list + def __len__(self): + return len(self.img_list) + def __getitem__(self, index): + ori_img = cv2.imread(self.img_list[index]) + img = resize_image(ori_img,self.config['base']['algorithm'], self.test_size,stride = self.config['testload']['stride']) + img = Image.fromarray(img).convert('RGB') + img = self.TSM.normalize_img(img) + return img, ori_img + diff --git a/ptocr/dataloader/DetLoad/PSEProcess.py b/ptocr/dataloader/DetLoad/PSEProcess.py new file mode 100644 index 0000000..e637786 --- /dev/null +++ b/ptocr/dataloader/DetLoad/PSEProcess.py @@ -0,0 +1,120 @@ +#-*- coding:utf-8 _*- +""" +@author:fxw +@file: PSEProcess.py +@time: 2020/08/11 +""" +import cv2 +import torch +import numpy as np +from PIL import Image +from torch.utils import data +from .transform_img import Random_Augment +from .MakeSegMap import MakeSegPSE +import torchvision.transforms as transforms +from ptocr.utils.util_function import resize_image + +class PSEProcessTrain(data.Dataset): + def __init__(self,config): + super(PSEProcessTrain,self).__init__() + self.crop_shape = config['base']['crop_shape'] + self.TSM = Random_Augment(self.crop_shape) + self.MSM = MakeSegPSE(config['base']['classes'],config['base']['shrink_ratio']) + img_list, label_list = self.get_base_information(config['trainload']['train_file']) + self.img_list = img_list + self.label_list = label_list + + def order_points(self, pts): + rect = np.zeros((4, 2), dtype="float32") + s = pts.sum(axis=1) + rect[0] = pts[np.argmin(s)] + rect[2] = pts[np.argmax(s)] + diff = np.diff(pts, axis=1) + rect[1] = pts[np.argmin(diff)] + rect[3] = pts[np.argmax(diff)] + return rect + + def get_bboxes(self,gt_path): + polys = [] + tags = [] + with open(gt_path, 'r', encoding='utf-8') as fid: + lines = fid.readlines() + for line in lines: + line = line.replace('\ufeff', '').replace('\xef\xbb\xbf', '') + gt = line.split(',') + if "###" in gt[-1]: + tags.append(True) + else: + tags.append(False) + # box = [int(gt[i]) for i in range(len(gt)//2*2)] + box = [int(gt[i]) for i in range(8)] + polys.append(box) + return np.array(polys), tags + + def get_base_information(self,train_txt_file): + label_list = [] + img_list = [] + with open(train_txt_file,'r',encoding='utf-8') as fid: + lines = fid.readlines() + for line in lines: + line = line.strip('\n').split('\t') + img_list.append(line[0]) + result = self.get_bboxes(line[1]) + label_list.append(result) + return img_list,label_list + + def __len__(self): + return len(self.img_list) + + def __getitem__(self, index): + img = cv2.imread(self.img_list[index]) + polys, dontcare = self.label_list[index] + + img, polys = self.TSM.random_scale(img, polys, self.crop_shape[0]) + img, polys = self.TSM.random_flip(img, polys) + img, polys = self.TSM.random_rotate(img, polys) + img, train_mask, gt_text, gt_kernels = self.MSM.process(img, polys, dontcare) + + imgs = [img, gt_text, train_mask] + imgs.extend(gt_kernels) + imgs = self.TSM.random_crop_pse(imgs) + img, gt_text, train_mask, gt_kernels = imgs[0], imgs[1], imgs[2], imgs[3:] + + + img = Image.fromarray(img).convert('RGB') + img = transforms.ColorJitter(brightness=32.0 / 255, saturation=0.5)(img) + img = self.TSM.normalize_img(img) + + gt_text = torch.from_numpy(gt_text).float() + train_mask = torch.from_numpy(train_mask).float() + gt_kernels = torch.from_numpy(np.array(gt_kernels)).float() + + return img,gt_text,gt_kernels,train_mask + + + +class PSEProcessTest(): + def __init__(self, config): + super(PSEProcessTest, self).__init__() + self.img_list = self.get_img_files(config['testload']['test_file']) + self.TSM = Random_Augment(config['base']['crop_shape']) + self.test_size = config['testload']['test_size'] + self.config = config + + def get_img_files(self, test_txt_file): + img_list = [] + with open(test_txt_file, 'r', encoding='utf-8') as fid: + lines = fid.readlines() + for line in lines: + line = line.strip('\n') + img_list.append(line) + return img_list + def __len__(self): + return len(self.img_list) + def __getitem__(self, index): + ori_img = cv2.imread(self.img_list[index]) + img = resize_image(ori_img,self.config['base']['algorithm'], self.test_size,stride = self.config['testload']['stride']) + img = Image.fromarray(img).convert('RGB') + img = self.TSM.normalize_img(img) + return img, ori_img + diff --git a/ptocr/dataloader/DetLoad/SASTProcess.py b/ptocr/dataloader/DetLoad/SASTProcess.py new file mode 100644 index 0000000..c2f64f5 --- /dev/null +++ b/ptocr/dataloader/DetLoad/SASTProcess.py @@ -0,0 +1,504 @@ +""" +#!-*- coding=utf-8 -*- +@author: BADBADBADBADBOY +@contact: 2441124901@qq.com +@software: PyCharm Community Edition +@file: SASTProcess.py +@time: 2020/8/23 14:16 + +""" + +import math +import cv2 +import numpy as np +import torch +from PIL import Image +import torch.utils.data as data +import torchvision.transforms as transforms +from .transform_img import Random_Augment +from ptocr.utils.util_function import resize_image + + +def get_img(img_path): + img = Image.open(img_path).convert('RGB') + img = np.array(img) + return img + +class SASTProcessTrain(data.Dataset): + """ + SAST process function for training + """ + + def __init__(self, config): + self.TSM = Random_Augment(config['base']['crop_shape'],max_tries=20, + min_crop_side_ratio=config['trainload']['min_crop_side_ratio']) + self.img_set_dir = config['trainload']['train_file'] + image_shape = config['base']['crop_shape'] + self.input_size = image_shape[0] + self.min_text_size = config['trainload']['min_text_size'] + self.max_text_size = image_shape[0] + + self.img_files = [] + self.gt_files = [] + with open(self.img_set_dir, 'r') as fid: + lines = fid.readlines() + for line in lines: + img_file = line.strip('\n').split('\t')[0] + gt_file = line.strip('\n').split('\t')[1] + self.img_files.append(img_file) + self.gt_files.append(gt_file) + + def adjust_point(self, poly): + """ + adjust point order. + """ + point_num = poly.shape[0] + if point_num == 4: + len_1 = np.linalg.norm(poly[0] - poly[1]) + len_2 = np.linalg.norm(poly[1] - poly[2]) + len_3 = np.linalg.norm(poly[2] - poly[3]) + len_4 = np.linalg.norm(poly[3] - poly[0]) + + if (len_1 + len_3) * 1.5 < (len_2 + len_4): + poly = poly[[1, 2, 3, 0], :] + + elif point_num > 4: + vector_1 = poly[0] - poly[1] + vector_2 = poly[1] - poly[2] + cos_theta = np.dot(vector_1, vector_2) / (np.linalg.norm(vector_1) * np.linalg.norm(vector_2) + 1e-6) + theta = np.arccos(np.round(cos_theta, decimals=4)) + + if abs(theta) > (70 / 180 * math.pi): + index = list(range(1, point_num)) + [0] + poly = poly[np.array(index), :] + return poly + + def gen_min_area_quad_from_poly(self, poly): + """ + Generate min area quad from poly. + """ + point_num = poly.shape[0] + min_area_quad = np.zeros((4, 2), dtype=np.float32) + if point_num == 4: + min_area_quad = poly + center_point = np.sum(poly, axis=0) / 4 + else: + rect = cv2.minAreaRect(poly.astype(np.int32)) # (center (x,y), (width, height), angle of rotation) + center_point = rect[0] + box = np.array(cv2.boxPoints(rect)) + + first_point_idx = 0 + min_dist = 1e4 + for i in range(4): + dist = np.linalg.norm(box[(i + 0) % 4] - poly[0]) + \ + np.linalg.norm(box[(i + 1) % 4] - poly[point_num // 2 - 1]) + \ + np.linalg.norm(box[(i + 2) % 4] - poly[point_num // 2]) + \ + np.linalg.norm(box[(i + 3) % 4] - poly[-1]) + if dist < min_dist: + min_dist = dist + first_point_idx = i + + for i in range(4): + min_area_quad[i] = box[(first_point_idx + i) % 4] + + return min_area_quad, center_point + + def shrink_quad_along_width(self, quad, begin_width_ratio=0., end_width_ratio=1.): + """ + Generate shrink_quad_along_width. + """ + ratio_pair = np.array([[begin_width_ratio], [end_width_ratio]], dtype=np.float32) + p0_1 = quad[0] + (quad[1] - quad[0]) * ratio_pair + p3_2 = quad[3] + (quad[2] - quad[3]) * ratio_pair + return np.array([p0_1[0], p0_1[1], p3_2[1], p3_2[0]]) + + def shrink_poly_along_width(self, quads, shrink_ratio_of_width, expand_height_ratio=1.0): + """ + shrink poly with given length. + """ + upper_edge_list = [] + + def get_cut_info(edge_len_list, cut_len): + for idx, edge_len in enumerate(edge_len_list): + cut_len -= edge_len + if cut_len <= 0.000001: + ratio = (cut_len + edge_len_list[idx]) / edge_len_list[idx] + return idx, ratio + + for quad in quads: + upper_edge_len = np.linalg.norm(quad[0] - quad[1]) + upper_edge_list.append(upper_edge_len) + + # length of left edge and right edge. + left_length = np.linalg.norm(quads[0][0] - quads[0][3]) * expand_height_ratio + right_length = np.linalg.norm(quads[-1][1] - quads[-1][2]) * expand_height_ratio + + shrink_length = min(left_length, right_length, sum(upper_edge_list)) * shrink_ratio_of_width + # shrinking length + upper_len_left = shrink_length + upper_len_right = sum(upper_edge_list) - shrink_length + + left_idx, left_ratio = get_cut_info(upper_edge_list, upper_len_left) + left_quad = self.shrink_quad_along_width(quads[left_idx], begin_width_ratio=left_ratio, end_width_ratio=1) + right_idx, right_ratio = get_cut_info(upper_edge_list, upper_len_right) + right_quad = self.shrink_quad_along_width(quads[right_idx], begin_width_ratio=0, end_width_ratio=right_ratio) + + out_quad_list = [] + if left_idx == right_idx: + out_quad_list.append([left_quad[0], right_quad[1], right_quad[2], left_quad[3]]) + else: + out_quad_list.append(left_quad) + for idx in range(left_idx + 1, right_idx): + out_quad_list.append(quads[idx]) + out_quad_list.append(right_quad) + + return np.array(out_quad_list), list(range(left_idx, right_idx + 1)) + + def vector_angle(self, A, B): + """ + Calculate the angle between vector AB and x-axis positive direction. + """ + AB = np.array([B[1] - A[1], B[0] - A[0]]) + return np.arctan2(*AB) + + def theta_line_cross_point(self, theta, point): + """ + Calculate the line through given point and angle in ax + by + c =0 form. + """ + x, y = point + cos = np.cos(theta) + sin = np.sin(theta) + return [sin, -cos, cos * y - sin * x] + + def line_cross_two_point(self, A, B): + """ + Calculate the line through given point A and B in ax + by + c =0 form. + """ + angle = self.vector_angle(A, B) + return self.theta_line_cross_point(angle, A) + + def average_angle(self, poly): + """ + Calculate the average angle between left and right edge in given poly. + """ + p0, p1, p2, p3 = poly + angle30 = self.vector_angle(p3, p0) + angle21 = self.vector_angle(p2, p1) + return (angle30 + angle21) / 2 + + def line_cross_point(self, line1, line2): + """ + line1 and line2 in 0=ax+by+c form, compute the cross point of line1 and line2 + """ + a1, b1, c1 = line1 + a2, b2, c2 = line2 + d = a1 * b2 - a2 * b1 + + if d == 0: + # print("line1", line1) + # print("line2", line2) + print('Cross point does not exist') + return np.array([0, 0], dtype=np.float32) + else: + x = (b1 * c2 - b2 * c1) / d + y = (a2 * c1 - a1 * c2) / d + + return np.array([x, y], dtype=np.float32) + + def quad2tcl(self, poly, ratio): + """ + Generate center line by poly clock-wise point. (4, 2) + """ + ratio_pair = np.array([[0.5 - ratio / 2], [0.5 + ratio / 2]], dtype=np.float32) + p0_3 = poly[0] + (poly[3] - poly[0]) * ratio_pair + p1_2 = poly[1] + (poly[2] - poly[1]) * ratio_pair + return np.array([p0_3[0], p1_2[0], p1_2[1], p0_3[1]]) + + def poly2tcl(self, poly, ratio): + """ + Generate center line by poly clock-wise point. + """ + ratio_pair = np.array([[0.5 - ratio / 2], [0.5 + ratio / 2]], dtype=np.float32) + tcl_poly = np.zeros_like(poly) + point_num = poly.shape[0] + + for idx in range(point_num // 2): + point_pair = poly[idx] + (poly[point_num - 1 - idx] - poly[idx]) * ratio_pair + tcl_poly[idx] = point_pair[0] + tcl_poly[point_num - 1 - idx] = point_pair[1] + return tcl_poly + + def gen_quad_tbo(self, quad, tcl_mask, tbo_map): + """ + Generate tbo_map for give quad. + """ + # upper and lower line function: ax + by + c = 0; + up_line = self.line_cross_two_point(quad[0], quad[1]) + lower_line = self.line_cross_two_point(quad[3], quad[2]) + + quad_h = 0.5 * (np.linalg.norm(quad[0] - quad[3]) + np.linalg.norm(quad[1] - quad[2])) + quad_w = 0.5 * (np.linalg.norm(quad[0] - quad[1]) + np.linalg.norm(quad[2] - quad[3])) + + # average angle of left and right line. + angle = self.average_angle(quad) + + xy_in_poly = np.argwhere(tcl_mask == 1) + for y, x in xy_in_poly: + point = (x, y) + line = self.theta_line_cross_point(angle, point) + cross_point_upper = self.line_cross_point(up_line, line) + cross_point_lower = self.line_cross_point(lower_line, line) + ##FIX, offset reverse + upper_offset_x, upper_offset_y = cross_point_upper - point + lower_offset_x, lower_offset_y = cross_point_lower - point + tbo_map[y, x, 0] = upper_offset_y + tbo_map[y, x, 1] = upper_offset_x + tbo_map[y, x, 2] = lower_offset_y + tbo_map[y, x, 3] = lower_offset_x + tbo_map[y, x, 4] = 1.0 / max(min(quad_h, quad_w), 1.0) * 2 + return tbo_map + + def poly2quads(self, poly): + """ + Split poly into quads. + """ + quad_list = [] + point_num = poly.shape[0] + + # point pair + point_pair_list = [] + for idx in range(point_num // 2): + point_pair = [poly[idx], poly[point_num - 1 - idx]] + point_pair_list.append(point_pair) + + quad_num = point_num // 2 - 1 + for idx in range(quad_num): + # reshape and adjust to clock-wise + quad_list.append((np.array(point_pair_list)[[idx, idx + 1]]).reshape(4, 2)[[0, 2, 3, 1]]) + + return np.array(quad_list) + + def generate_tcl_label(self, hw, polys, tags, ds_ratio, + tcl_ratio=0.3, shrink_ratio_of_width=0.15): + """ + Generate polygon. + """ + h, w = hw + h, w = int(h * ds_ratio), int(w * ds_ratio) + polys = polys * ds_ratio + + score_map = np.zeros((h, w,), dtype=np.float32) + tbo_map = np.zeros((h, w, 5), dtype=np.float32) + training_mask = np.ones((h, w,), dtype=np.float32) + direction_map = np.ones((h, w, 3)) * np.array([0, 0, 1]).reshape([1, 1, 3]).astype(np.float32) + + for poly_idx, poly_tag in enumerate(zip(polys, tags)): + poly = poly_tag[0] + tag = poly_tag[1] + + # generate min_area_quad + min_area_quad, center_point = self.gen_min_area_quad_from_poly(poly) + min_area_quad_h = 0.5 * (np.linalg.norm(min_area_quad[0] - min_area_quad[3]) + + np.linalg.norm(min_area_quad[1] - min_area_quad[2])) + min_area_quad_w = 0.5 * (np.linalg.norm(min_area_quad[0] - min_area_quad[1]) + + np.linalg.norm(min_area_quad[2] - min_area_quad[3])) + + if min(min_area_quad_h, min_area_quad_w) < self.min_text_size * ds_ratio \ + or min(min_area_quad_h, min_area_quad_w) > self.max_text_size * ds_ratio: + continue + + if tag: + # continue + cv2.fillPoly(training_mask, poly.astype(np.int32)[np.newaxis, :, :], 0.15) + else: + tcl_poly = self.poly2tcl(poly, tcl_ratio) + tcl_quads = self.poly2quads(tcl_poly) + poly_quads = self.poly2quads(poly) + # stcl map + stcl_quads, quad_index = self.shrink_poly_along_width(tcl_quads, + shrink_ratio_of_width=shrink_ratio_of_width, + expand_height_ratio=1.0 / tcl_ratio) + # generate tcl map + cv2.fillPoly(score_map, np.round(stcl_quads).astype(np.int32), 1.0) + + # generate tbo map + for idx, quad in enumerate(stcl_quads): + quad_mask = np.zeros((h, w), dtype=np.float32) + quad_mask = cv2.fillPoly(quad_mask, np.round(quad[np.newaxis, :, :]).astype(np.int32), 1.0) + tbo_map = self.gen_quad_tbo(poly_quads[quad_index[idx]], quad_mask, tbo_map) + return score_map, tbo_map, training_mask + + def generate_tvo_and_tco(self, hw, polys, tags, tcl_ratio=0.3, ds_ratio=0.25): + """ + Generate tcl map, tvo map and tbo map. + """ + h, w = hw + h, w = int(h * ds_ratio), int(w * ds_ratio) + polys = polys * ds_ratio + poly_mask = np.zeros((h, w), dtype=np.float32) + + tvo_map = np.ones((9, h, w), dtype=np.float32) + tvo_map[0:-1:2] = np.tile(np.arange(0, w), (h, 1)) + tvo_map[1:-1:2] = np.tile(np.arange(0, w), (h, 1)).T + poly_tv_xy_map = np.zeros((8, h, w), dtype=np.float32) + + # tco map + tco_map = np.ones((3, h, w), dtype=np.float32) + tco_map[0] = np.tile(np.arange(0, w), (h, 1)) + tco_map[1] = np.tile(np.arange(0, w), (h, 1)).T + poly_tc_xy_map = np.zeros((2, h, w), dtype=np.float32) + + poly_short_edge_map = np.ones((h, w), dtype=np.float32) + + for poly, poly_tag in zip(polys, tags): + + if poly_tag == True: + continue + + # adjust point order for vertical poly + poly = self.adjust_point(poly) + + # generate min_area_quad + min_area_quad, center_point = self.gen_min_area_quad_from_poly(poly) + min_area_quad_h = 0.5 * (np.linalg.norm(min_area_quad[0] - min_area_quad[3]) + + np.linalg.norm(min_area_quad[1] - min_area_quad[2])) + min_area_quad_w = 0.5 * (np.linalg.norm(min_area_quad[0] - min_area_quad[1]) + + np.linalg.norm(min_area_quad[2] - min_area_quad[3])) + + # generate tcl map and text, 128 * 128 + tcl_poly = self.poly2tcl(poly, tcl_ratio) + + # generate poly_tv_xy_map + for idx in range(4): + cv2.fillPoly(poly_tv_xy_map[2 * idx], + np.round(tcl_poly[np.newaxis, :, :]).astype(np.int32), + float(min(max(min_area_quad[idx, 0], 0), w))) + cv2.fillPoly(poly_tv_xy_map[2 * idx + 1], + np.round(tcl_poly[np.newaxis, :, :]).astype(np.int32), + float(min(max(min_area_quad[idx, 1], 0), h))) + + # generate poly_tc_xy_map + for idx in range(2): + cv2.fillPoly(poly_tc_xy_map[idx], + np.round(tcl_poly[np.newaxis, :, :]).astype(np.int32), float(center_point[idx])) + + # generate poly_short_edge_map + cv2.fillPoly(poly_short_edge_map, + np.round(tcl_poly[np.newaxis, :, :]).astype(np.int32), + float(max(min(min_area_quad_h, min_area_quad_w), 1.0))) + + # generate poly_mask and training_mask + cv2.fillPoly(poly_mask, np.round(tcl_poly[np.newaxis, :, :]).astype(np.int32), 1) + + tvo_map *= poly_mask + tvo_map[:8] -= poly_tv_xy_map + tvo_map[-1] /= poly_short_edge_map + tvo_map = tvo_map.transpose((1, 2, 0)) + + tco_map *= poly_mask + tco_map[:2] -= poly_tc_xy_map + tco_map[-1] /= poly_short_edge_map + tco_map = tco_map.transpose((1, 2, 0)) + + return tvo_map, tco_map + + def get_bboxes(self, gt_path): + polys = [] + tags = [] + with open(gt_path, 'r', encoding='utf-8') as fid: + lines = fid.readlines() + for line in lines: + line = line.replace('\ufeff', '').replace('\xef\xbb\xbf', '') + gt = line.split(',') + if "###" in gt[-1]: + tags.append(True) + else: + tags.append(False) + # box = [int(gt[i]) for i in range(len(gt)//2*2)] + box = [int(gt[i]) for i in range(8)] + polys.append(box) + return np.array(polys), tags + + def __len__(self): + return len(self.img_files) + + def __getitem__(self, index): + + img = get_img(self.img_files[index]) + polys, dontcare = self.get_bboxes(self.gt_files[index]) + + img, polys = self.TSM.random_scale(img, polys, self.input_size) + img, polys = self.TSM.random_rotate(img, polys) + img, polys = self.TSM.random_flip(img, polys) + img, text_polys, text_tags = self.TSM.random_crop_db(img, polys, dontcare) + text_polys = np.array(text_polys) + + score_map, border_map, training_mask = self.generate_tcl_label((self.input_size, self.input_size), + text_polys, text_tags, 0.25) + + # SAST head + tvo_map, tco_map = self.generate_tvo_and_tco((self.input_size, self.input_size), text_polys, text_tags, + tcl_ratio=0.3, ds_ratio=0.25) + + # add gaussian blur + if np.random.rand() < 0.1 * 0.5: + ks = np.random.permutation(5)[0] + 1 + ks = int(ks / 2) * 2 + 1 + img = cv2.GaussianBlur(img, ksize=(ks, ks), sigmaX=0, sigmaY=0) + # add brighter + if np.random.rand() < 0.1 * 0.5: + img = img * (1.0 + np.random.rand() * 0.5) + img = np.clip(img, 0.0, 255.0) + # add darker + if np.random.rand() < 0.1 * 0.5: + img = img * (1.0 - np.random.rand() * 0.5) + img = np.clip(img, 0.0, 255.0) + + img = Image.fromarray(img.astype(np.uint8)).convert('RGB') + img.save('img.jpg') + + cv2.imwrite('score.jpg',np.array(score_map)*255) + + + img = self.TSM.normalize_img(img) + + score_map = torch.Tensor(score_map[np.newaxis, :, :]) + border_map = torch.Tensor(border_map.transpose((2, 0, 1))) + training_mask = torch.Tensor(training_mask[np.newaxis, :, :]) + tvo_map = torch.Tensor(tvo_map.transpose((2, 0, 1))) + tco_map = torch.Tensor(tco_map.transpose((2, 0, 1))) + + return img, score_map, border_map, training_mask, tvo_map, tco_map + + + +class SASTProcessTest(data.Dataset): + """ + SAST process function for test + """ + + def __init__(self, config): + super(SASTProcessTest, self).__init__() + self.img_list = self.get_img_files(config['testload']['test_file']) + self.TSM = Random_Augment(config['base']['crop_shape']) + self.test_size = config['testload']['test_size'] + self.config = config + + def get_img_files(self, test_txt_file): + img_list = [] + with open(test_txt_file, 'r', encoding='utf-8') as fid: + lines = fid.readlines() + for line in lines: + line = line.strip('\n') + img_list.append(line) + return img_list + + def __len__(self): + return len(self.img_list) + + def __getitem__(self, index): + ori_img = get_img(self.img_list[index]) + img = resize_image(ori_img, self.config['base']['algorithm'], self.test_size,self.config['testload']['stride']) + img = Image.fromarray(img).convert('RGB') + img = self.TSM.normalize_img(img) + return img, ori_img diff --git a/ptocr/dataloader/DetLoad/SASTProcess_ori.py b/ptocr/dataloader/DetLoad/SASTProcess_ori.py new file mode 100644 index 0000000..5760d1e --- /dev/null +++ b/ptocr/dataloader/DetLoad/SASTProcess_ori.py @@ -0,0 +1,800 @@ +#-*- coding:utf-8 _*- +""" +@author:fxw +@file: SASTProcess.py +@time: 2020/08/18 +""" + +import math +import cv2 +import numpy as np +import torch +from PIL import Image +import torch.utils.data as data +import torchvision.transforms as transforms +from .transform_img import Random_Augment +from ptocr.utils.util_function import resize_image + +class SASTProcessTrain(data.Dataset): + """ + SAST process function for training + """ + + def __init__(self,config): + self.TSM = Random_Augment(config['base']['crop_shape']) + self.img_set_dir = config['trainload']['train_file'] + self.min_crop_side_ratio = config['trainload']['min_crop_side_ratio'] + self.min_crop_size = config['trainload']['min_crop_size'] + image_shape = config['base']['crop_shape'] + self.input_size = image_shape[0] + self.min_text_size = config['trainload']['min_text_size'] + self.max_text_size = image_shape[0] + + self.img_files = [] + self.gt_files = [] + with open(self.img_set_dir, 'r') as fid: + lines = fid.readlines() + for line in lines: + img_file = line.strip('\n').split('\t')[0] + gt_file = line.strip('\n').split('\t')[1] + self.img_files.append(img_file) + self.gt_files.append(gt_file) + + def quad_area(self, poly): + """ + compute area of a polygon + :param poly: + :return: + """ + edge = [ + (poly[1][0] - poly[0][0]) * (poly[1][1] + poly[0][1]), + (poly[2][0] - poly[1][0]) * (poly[2][1] + poly[1][1]), + (poly[3][0] - poly[2][0]) * (poly[3][1] + poly[2][1]), + (poly[0][0] - poly[3][0]) * (poly[0][1] + poly[3][1]) + ] + return np.sum(edge) / 2. + + def gen_quad_from_poly(self, poly): + """ + Generate min area quad from poly. + """ + point_num = poly.shape[0] + min_area_quad = np.zeros((4, 2), dtype=np.float32) + if True: + rect = cv2.minAreaRect(poly.astype(np.int32)) # (center (x,y), (width, height), angle of rotation) + center_point = rect[0] + box = np.array(cv2.boxPoints(rect)) + + first_point_idx = 0 + min_dist = 1e4 + for i in range(4): + dist = np.linalg.norm(box[(i + 0) % 4] - poly[0]) + \ + np.linalg.norm(box[(i + 1) % 4] - poly[point_num // 2 - 1]) + \ + np.linalg.norm(box[(i + 2) % 4] - poly[point_num // 2]) + \ + np.linalg.norm(box[(i + 3) % 4] - poly[-1]) + if dist < min_dist: + min_dist = dist + first_point_idx = i + for i in range(4): + min_area_quad[i] = box[(first_point_idx + i) % 4] + + return min_area_quad + + def check_and_validate_polys(self, polys, tags, xxx_todo_changeme): + """ + check so that the text poly is in the same direction, + and also filter some invalid polygons + :param polys: + :param tags: + :return: + """ + (h, w) = xxx_todo_changeme + if polys.shape[0] == 0: + return polys, np.array([]), np.array([]) + polys[:, :, 0] = np.clip(polys[:, :, 0], 0, w - 1) + polys[:, :, 1] = np.clip(polys[:, :, 1], 0, h - 1) + + validated_polys = [] + validated_tags = [] + hv_tags = [] + for poly, tag in zip(polys, tags): + quad = self.gen_quad_from_poly(poly) + p_area = self.quad_area(quad) + if abs(p_area) < 1: + print('invalid poly') + continue + if p_area > 0: + if tag == False: + print('poly in wrong direction') + tag = True # reversed cases should be ignore + poly = poly[(0, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1), :] + quad = quad[(0, 3, 2, 1), :] + + len_w = np.linalg.norm(quad[0] - quad[1]) + np.linalg.norm(quad[3] - quad[2]) + len_h = np.linalg.norm(quad[0] - quad[3]) + np.linalg.norm(quad[1] - quad[2]) + hv_tag = 1 + + if len_w * 2.0 < len_h: + hv_tag = 0 + + validated_polys.append(poly) + validated_tags.append(tag) + hv_tags.append(hv_tag) + return np.array(validated_polys), np.array(validated_tags), np.array(hv_tags) + + def crop_area(self, im, polys, tags, hv_tags, txts, crop_background=False, max_tries=25): + """ + make random crop from the input image + :param im: + :param polys: + :param tags: + :param crop_background: + :param max_tries: 50 -> 25 + :return: + """ + h, w, _ = im.shape + pad_h = h // 10 + pad_w = w // 10 + h_array = np.zeros((h + pad_h * 2), dtype=np.int32) + w_array = np.zeros((w + pad_w * 2), dtype=np.int32) + for poly in polys: + poly = np.round(poly, decimals=0).astype(np.int32) + minx = np.min(poly[:, 0]) + maxx = np.max(poly[:, 0]) + w_array[minx + pad_w: maxx + pad_w] = 1 + miny = np.min(poly[:, 1]) + maxy = np.max(poly[:, 1]) + h_array[miny + pad_h: maxy + pad_h] = 1 + # ensure the cropped area not across a text + h_axis = np.where(h_array == 0)[0] + w_axis = np.where(w_array == 0)[0] + if len(h_axis) == 0 or len(w_axis) == 0: + return im, polys, tags, hv_tags, txts + for i in range(max_tries): + xx = np.random.choice(w_axis, size=2) + xmin = np.min(xx) - pad_w + xmax = np.max(xx) - pad_w + xmin = np.clip(xmin, 0, w - 1) + xmax = np.clip(xmax, 0, w - 1) + yy = np.random.choice(h_axis, size=2) + ymin = np.min(yy) - pad_h + ymax = np.max(yy) - pad_h + ymin = np.clip(ymin, 0, h - 1) + ymax = np.clip(ymax, 0, h - 1) + # if xmax - xmin < ARGS.min_crop_side_ratio * w or \ + # ymax - ymin < ARGS.min_crop_side_ratio * h: + if xmax - xmin < self.min_crop_size or \ + ymax - ymin < self.min_crop_size: + # area too small + continue + if polys.shape[0] != 0: + poly_axis_in_area = (polys[:, :, 0] >= xmin) & (polys[:, :, 0] <= xmax) \ + & (polys[:, :, 1] >= ymin) & (polys[:, :, 1] <= ymax) + selected_polys = np.where(np.sum(poly_axis_in_area, axis=1) == 4)[0] + else: + selected_polys = [] + if len(selected_polys) == 0: + # no text in this area + if crop_background: + txts_tmp = [] + for selected_poly in selected_polys: + txts_tmp.append(txts[selected_poly]) + txts = txts_tmp + return im[ymin: ymax + 1, xmin: xmax + 1, :], \ + polys[selected_polys], tags[selected_polys], hv_tags[selected_polys], txts + else: + continue + im = im[ymin: ymax + 1, xmin: xmax + 1, :] + polys = polys[selected_polys] + tags = tags[selected_polys] + hv_tags = hv_tags[selected_polys] + txts_tmp = [] + for selected_poly in selected_polys: + txts_tmp.append(txts[selected_poly]) + txts = txts_tmp + polys[:, :, 0] -= xmin + polys[:, :, 1] -= ymin + return im, polys, tags, hv_tags, txts + + return im, polys, tags, hv_tags, txts + + def generate_direction_map(self, poly_quads, direction_map): + """ + """ + width_list = [] + height_list = [] + for quad in poly_quads: + quad_w = (np.linalg.norm(quad[0] - quad[1]) + np.linalg.norm(quad[2] - quad[3])) / 2.0 + quad_h = (np.linalg.norm(quad[0] - quad[3]) + np.linalg.norm(quad[2] - quad[1])) / 2.0 + width_list.append(quad_w) + height_list.append(quad_h) + norm_width = max(sum(width_list) / (len(width_list) + 1e-6), 1.0) + average_height = max(sum(height_list) / (len(height_list) + 1e-6), 1.0) + + for quad in poly_quads: + direct_vector_full = ((quad[1] + quad[2]) - (quad[0] + quad[3])) / 2.0 + direct_vector = direct_vector_full / (np.linalg.norm(direct_vector_full) + 1e-6) * norm_width + direction_label = tuple(map(float, [direct_vector[0], direct_vector[1], 1.0 / (average_height + 1e-6)])) + cv2.fillPoly(direction_map, quad.round().astype(np.int32)[np.newaxis, :, :], direction_label) + return direction_map + + def calculate_average_height(self, poly_quads): + """ + """ + height_list = [] + for quad in poly_quads: + quad_h = (np.linalg.norm(quad[0] - quad[3]) + np.linalg.norm(quad[2] - quad[1])) / 2.0 + height_list.append(quad_h) + average_height = max(sum(height_list) / len(height_list), 1.0) + return average_height + + def generate_tcl_label(self, hw, polys, tags, ds_ratio, + tcl_ratio=0.3, shrink_ratio_of_width=0.15): + """ + Generate polygon. + """ + h, w = hw + h, w = int(h * ds_ratio), int(w * ds_ratio) + polys = polys * ds_ratio + + score_map = np.zeros((h, w,), dtype=np.float32) + tbo_map = np.zeros((h, w, 5), dtype=np.float32) + training_mask = np.ones((h, w,), dtype=np.float32) + direction_map = np.ones((h, w, 3)) * np.array([0, 0, 1]).reshape([1, 1, 3]).astype(np.float32) + + for poly_idx, poly_tag in enumerate(zip(polys, tags)): + poly = poly_tag[0] + tag = poly_tag[1] + + # generate min_area_quad + min_area_quad, center_point = self.gen_min_area_quad_from_poly(poly) + min_area_quad_h = 0.5 * (np.linalg.norm(min_area_quad[0] - min_area_quad[3]) + + np.linalg.norm(min_area_quad[1] - min_area_quad[2])) + min_area_quad_w = 0.5 * (np.linalg.norm(min_area_quad[0] - min_area_quad[1]) + + np.linalg.norm(min_area_quad[2] - min_area_quad[3])) + + if min(min_area_quad_h, min_area_quad_w) < self.min_text_size * ds_ratio \ + or min(min_area_quad_h, min_area_quad_w) > self.max_text_size * ds_ratio: + continue + + if tag: + # continue + cv2.fillPoly(training_mask, poly.astype(np.int32)[np.newaxis, :, :], 0.15) + else: + tcl_poly = self.poly2tcl(poly, tcl_ratio) + tcl_quads = self.poly2quads(tcl_poly) + poly_quads = self.poly2quads(poly) + # stcl map + stcl_quads, quad_index = self.shrink_poly_along_width(tcl_quads, + shrink_ratio_of_width=shrink_ratio_of_width, + expand_height_ratio=1.0 / tcl_ratio) + # generate tcl map + cv2.fillPoly(score_map, np.round(stcl_quads).astype(np.int32), 1.0) + + # generate tbo map + for idx, quad in enumerate(stcl_quads): + quad_mask = np.zeros((h, w), dtype=np.float32) + quad_mask = cv2.fillPoly(quad_mask, np.round(quad[np.newaxis, :, :]).astype(np.int32), 1.0) + tbo_map = self.gen_quad_tbo(poly_quads[quad_index[idx]], quad_mask, tbo_map) + return score_map, tbo_map, training_mask + + def generate_tvo_and_tco(self, hw, polys, tags, tcl_ratio=0.3, ds_ratio=0.25): + """ + Generate tcl map, tvo map and tbo map. + """ + h, w = hw + h, w = int(h * ds_ratio), int(w * ds_ratio) + polys = polys * ds_ratio + poly_mask = np.zeros((h, w), dtype=np.float32) + + tvo_map = np.ones((9, h, w), dtype=np.float32) + tvo_map[0:-1:2] = np.tile(np.arange(0, w), (h, 1)) + tvo_map[1:-1:2] = np.tile(np.arange(0, w), (h, 1)).T + poly_tv_xy_map = np.zeros((8, h, w), dtype=np.float32) + + # tco map + tco_map = np.ones((3, h, w), dtype=np.float32) + tco_map[0] = np.tile(np.arange(0, w), (h, 1)) + tco_map[1] = np.tile(np.arange(0, w), (h, 1)).T + poly_tc_xy_map = np.zeros((2, h, w), dtype=np.float32) + + poly_short_edge_map = np.ones((h, w), dtype=np.float32) + + for poly, poly_tag in zip(polys, tags): + + if poly_tag == True: + continue + + # adjust point order for vertical poly + poly = self.adjust_point(poly) + + # generate min_area_quad + min_area_quad, center_point = self.gen_min_area_quad_from_poly(poly) + min_area_quad_h = 0.5 * (np.linalg.norm(min_area_quad[0] - min_area_quad[3]) + + np.linalg.norm(min_area_quad[1] - min_area_quad[2])) + min_area_quad_w = 0.5 * (np.linalg.norm(min_area_quad[0] - min_area_quad[1]) + + np.linalg.norm(min_area_quad[2] - min_area_quad[3])) + + # generate tcl map and text, 128 * 128 + tcl_poly = self.poly2tcl(poly, tcl_ratio) + + # generate poly_tv_xy_map + for idx in range(4): + cv2.fillPoly(poly_tv_xy_map[2 * idx], + np.round(tcl_poly[np.newaxis, :, :]).astype(np.int32), + float(min(max(min_area_quad[idx, 0], 0), w))) + cv2.fillPoly(poly_tv_xy_map[2 * idx + 1], + np.round(tcl_poly[np.newaxis, :, :]).astype(np.int32), + float(min(max(min_area_quad[idx, 1], 0), h))) + + # generate poly_tc_xy_map + for idx in range(2): + cv2.fillPoly(poly_tc_xy_map[idx], + np.round(tcl_poly[np.newaxis, :, :]).astype(np.int32), float(center_point[idx])) + + # generate poly_short_edge_map + cv2.fillPoly(poly_short_edge_map, + np.round(tcl_poly[np.newaxis, :, :]).astype(np.int32), + float(max(min(min_area_quad_h, min_area_quad_w), 1.0))) + + # generate poly_mask and training_mask + cv2.fillPoly(poly_mask, np.round(tcl_poly[np.newaxis, :, :]).astype(np.int32), 1) + + tvo_map *= poly_mask + tvo_map[:8] -= poly_tv_xy_map + tvo_map[-1] /= poly_short_edge_map + tvo_map = tvo_map.transpose((1, 2, 0)) + + tco_map *= poly_mask + tco_map[:2] -= poly_tc_xy_map + tco_map[-1] /= poly_short_edge_map + tco_map = tco_map.transpose((1, 2, 0)) + + return tvo_map, tco_map + + def adjust_point(self, poly): + """ + adjust point order. + """ + point_num = poly.shape[0] + if point_num == 4: + len_1 = np.linalg.norm(poly[0] - poly[1]) + len_2 = np.linalg.norm(poly[1] - poly[2]) + len_3 = np.linalg.norm(poly[2] - poly[3]) + len_4 = np.linalg.norm(poly[3] - poly[0]) + + if (len_1 + len_3) * 1.5 < (len_2 + len_4): + poly = poly[[1, 2, 3, 0], :] + + elif point_num > 4: + vector_1 = poly[0] - poly[1] + vector_2 = poly[1] - poly[2] + cos_theta = np.dot(vector_1, vector_2) / (np.linalg.norm(vector_1) * np.linalg.norm(vector_2) + 1e-6) + theta = np.arccos(np.round(cos_theta, decimals=4)) + + if abs(theta) > (70 / 180 * math.pi): + index = list(range(1, point_num)) + [0] + poly = poly[np.array(index), :] + return poly + + def gen_min_area_quad_from_poly(self, poly): + """ + Generate min area quad from poly. + """ + point_num = poly.shape[0] + min_area_quad = np.zeros((4, 2), dtype=np.float32) + if point_num == 4: + min_area_quad = poly + center_point = np.sum(poly, axis=0) / 4 + else: + rect = cv2.minAreaRect(poly.astype(np.int32)) # (center (x,y), (width, height), angle of rotation) + center_point = rect[0] + box = np.array(cv2.boxPoints(rect)) + + first_point_idx = 0 + min_dist = 1e4 + for i in range(4): + dist = np.linalg.norm(box[(i + 0) % 4] - poly[0]) + \ + np.linalg.norm(box[(i + 1) % 4] - poly[point_num // 2 - 1]) + \ + np.linalg.norm(box[(i + 2) % 4] - poly[point_num // 2]) + \ + np.linalg.norm(box[(i + 3) % 4] - poly[-1]) + if dist < min_dist: + min_dist = dist + first_point_idx = i + + for i in range(4): + min_area_quad[i] = box[(first_point_idx + i) % 4] + + return min_area_quad, center_point + + def shrink_quad_along_width(self, quad, begin_width_ratio=0., end_width_ratio=1.): + """ + Generate shrink_quad_along_width. + """ + ratio_pair = np.array([[begin_width_ratio], [end_width_ratio]], dtype=np.float32) + p0_1 = quad[0] + (quad[1] - quad[0]) * ratio_pair + p3_2 = quad[3] + (quad[2] - quad[3]) * ratio_pair + return np.array([p0_1[0], p0_1[1], p3_2[1], p3_2[0]]) + + def shrink_poly_along_width(self, quads, shrink_ratio_of_width, expand_height_ratio=1.0): + """ + shrink poly with given length. + """ + upper_edge_list = [] + + def get_cut_info(edge_len_list, cut_len): + for idx, edge_len in enumerate(edge_len_list): + cut_len -= edge_len + if cut_len <= 0.000001: + ratio = (cut_len + edge_len_list[idx]) / edge_len_list[idx] + return idx, ratio + + for quad in quads: + upper_edge_len = np.linalg.norm(quad[0] - quad[1]) + upper_edge_list.append(upper_edge_len) + + # length of left edge and right edge. + left_length = np.linalg.norm(quads[0][0] - quads[0][3]) * expand_height_ratio + right_length = np.linalg.norm(quads[-1][1] - quads[-1][2]) * expand_height_ratio + + shrink_length = min(left_length, right_length, sum(upper_edge_list)) * shrink_ratio_of_width + # shrinking length + upper_len_left = shrink_length + upper_len_right = sum(upper_edge_list) - shrink_length + + left_idx, left_ratio = get_cut_info(upper_edge_list, upper_len_left) + left_quad = self.shrink_quad_along_width(quads[left_idx], begin_width_ratio=left_ratio, end_width_ratio=1) + right_idx, right_ratio = get_cut_info(upper_edge_list, upper_len_right) + right_quad = self.shrink_quad_along_width(quads[right_idx], begin_width_ratio=0, end_width_ratio=right_ratio) + + out_quad_list = [] + if left_idx == right_idx: + out_quad_list.append([left_quad[0], right_quad[1], right_quad[2], left_quad[3]]) + else: + out_quad_list.append(left_quad) + for idx in range(left_idx + 1, right_idx): + out_quad_list.append(quads[idx]) + out_quad_list.append(right_quad) + + return np.array(out_quad_list), list(range(left_idx, right_idx + 1)) + + def vector_angle(self, A, B): + """ + Calculate the angle between vector AB and x-axis positive direction. + """ + AB = np.array([B[1] - A[1], B[0] - A[0]]) + return np.arctan2(*AB) + + def theta_line_cross_point(self, theta, point): + """ + Calculate the line through given point and angle in ax + by + c =0 form. + """ + x, y = point + cos = np.cos(theta) + sin = np.sin(theta) + return [sin, -cos, cos * y - sin * x] + + def line_cross_two_point(self, A, B): + """ + Calculate the line through given point A and B in ax + by + c =0 form. + """ + angle = self.vector_angle(A, B) + return self.theta_line_cross_point(angle, A) + + def average_angle(self, poly): + """ + Calculate the average angle between left and right edge in given poly. + """ + p0, p1, p2, p3 = poly + angle30 = self.vector_angle(p3, p0) + angle21 = self.vector_angle(p2, p1) + return (angle30 + angle21) / 2 + + def line_cross_point(self, line1, line2): + """ + line1 and line2 in 0=ax+by+c form, compute the cross point of line1 and line2 + """ + a1, b1, c1 = line1 + a2, b2, c2 = line2 + d = a1 * b2 - a2 * b1 + + if d == 0: + # print("line1", line1) + # print("line2", line2) + print('Cross point does not exist') + return np.array([0, 0], dtype=np.float32) + else: + x = (b1 * c2 - b2 * c1) / d + y = (a2 * c1 - a1 * c2) / d + + return np.array([x, y], dtype=np.float32) + + def quad2tcl(self, poly, ratio): + """ + Generate center line by poly clock-wise point. (4, 2) + """ + ratio_pair = np.array([[0.5 - ratio / 2], [0.5 + ratio / 2]], dtype=np.float32) + p0_3 = poly[0] + (poly[3] - poly[0]) * ratio_pair + p1_2 = poly[1] + (poly[2] - poly[1]) * ratio_pair + return np.array([p0_3[0], p1_2[0], p1_2[1], p0_3[1]]) + + def poly2tcl(self, poly, ratio): + """ + Generate center line by poly clock-wise point. + """ + ratio_pair = np.array([[0.5 - ratio / 2], [0.5 + ratio / 2]], dtype=np.float32) + tcl_poly = np.zeros_like(poly) + point_num = poly.shape[0] + + for idx in range(point_num // 2): + point_pair = poly[idx] + (poly[point_num - 1 - idx] - poly[idx]) * ratio_pair + tcl_poly[idx] = point_pair[0] + tcl_poly[point_num - 1 - idx] = point_pair[1] + return tcl_poly + + def gen_quad_tbo(self, quad, tcl_mask, tbo_map): + """ + Generate tbo_map for give quad. + """ + # upper and lower line function: ax + by + c = 0; + up_line = self.line_cross_two_point(quad[0], quad[1]) + lower_line = self.line_cross_two_point(quad[3], quad[2]) + + quad_h = 0.5 * (np.linalg.norm(quad[0] - quad[3]) + np.linalg.norm(quad[1] - quad[2])) + quad_w = 0.5 * (np.linalg.norm(quad[0] - quad[1]) + np.linalg.norm(quad[2] - quad[3])) + + # average angle of left and right line. + angle = self.average_angle(quad) + + xy_in_poly = np.argwhere(tcl_mask == 1) + for y, x in xy_in_poly: + point = (x, y) + line = self.theta_line_cross_point(angle, point) + cross_point_upper = self.line_cross_point(up_line, line) + cross_point_lower = self.line_cross_point(lower_line, line) + ##FIX, offset reverse + upper_offset_x, upper_offset_y = cross_point_upper - point + lower_offset_x, lower_offset_y = cross_point_lower - point + tbo_map[y, x, 0] = upper_offset_y + tbo_map[y, x, 1] = upper_offset_x + tbo_map[y, x, 2] = lower_offset_y + tbo_map[y, x, 3] = lower_offset_x + tbo_map[y, x, 4] = 1.0 / max(min(quad_h, quad_w), 1.0) * 2 + return tbo_map + + def poly2quads(self, poly): + """ + Split poly into quads. + """ + quad_list = [] + point_num = poly.shape[0] + + # point pair + point_pair_list = [] + for idx in range(point_num // 2): + point_pair = [poly[idx], poly[point_num - 1 - idx]] + point_pair_list.append(point_pair) + + quad_num = point_num // 2 - 1 + for idx in range(quad_num): + # reshape and adjust to clock-wise + quad_list.append((np.array(point_pair_list)[[idx, idx + 1]]).reshape(4, 2)[[0, 2, 3, 1]]) + + return np.array(quad_list) + + def extract_polys(self, poly_txt_path): + """ + Read text_polys, txt_tags, txts from give txt file. + """ + text_polys, txt_tags, txts = [], [], [] + + with open(poly_txt_path, 'r', encoding='utf-8') as f: + for line in f.readlines(): + poly_str = line.strip().replace('\ufeff', '').split(',') + txt = poly_str[-1] + poly_str = poly_str[:8] + poly = list(map(float, poly_str)) + text_polys.append(np.array(poly, dtype=np.float32).reshape(-1, 2)) + txts.append(txt) + if txt == '###': + txt_tags.append(True) + else: + txt_tags.append(False) + + return np.array(text_polys), \ + np.array(txt_tags, dtype=np.bool), txts + + def __len__(self): + return len(self.img_files) + + def __getitem__(self, index): + + im_path = self.img_files[index] + text_polys, text_tags, text_strs = self.extract_polys(self.gt_files[index]) + im = cv2.imread(im_path) + + return im,text_polys, text_tags, text_strs + + def img_tranform(self,im,text_polys, text_tags, text_strs): + + if im is None: + return None + if text_polys.shape[0] == 0: + return None + + h, w, _ = im.shape + text_polys, text_tags, hv_tags = self.check_and_validate_polys(text_polys, text_tags, (h, w)) + + if text_polys.shape[0] == 0: + return None + + # set aspect ratio and keep area fix + asp_scales = np.arange(1.0, 1.55, 0.1) + asp_scale = np.random.choice(asp_scales) + + if np.random.rand() < 0.5: + asp_scale = 1.0 / asp_scale + asp_scale = math.sqrt(asp_scale) + + asp_wx = asp_scale + asp_hy = 1.0 / asp_scale + im = cv2.resize(im, dsize=None, fx=asp_wx, fy=asp_hy) + text_polys[:, :, 0] *= asp_wx + text_polys[:, :, 1] *= asp_hy + + h, w, _ = im.shape + if max(h, w) > 2048: + rd_scale = 2048.0 / max(h, w) + im = cv2.resize(im, dsize=None, fx=rd_scale, fy=rd_scale) + text_polys *= rd_scale + h, w, _ = im.shape + if min(h, w) < 16: + return None + + # no background + im, text_polys, text_tags, hv_tags, text_strs = self.crop_area(im, text_polys, text_tags, hv_tags, text_strs,crop_background=False) + if text_polys.shape[0] == 0: + return None + # continue for all ignore case + if np.sum((text_tags * 1.0)) >= text_tags.size: + return None + new_h, new_w, _ = im.shape + if (new_h is None) or (new_w is None): + return None + # resize image + std_ratio = float(self.input_size) / max(new_w, new_h) + rand_scales = np.array([0.25, 0.375, 0.5, 0.625, 0.75, 0.875, 1.0, 1.0, 1.0, 1.0, 1.0]) + rz_scale = std_ratio * np.random.choice(rand_scales) + + + im = cv2.resize(im, dsize=None, fx=rz_scale, fy=rz_scale) + # Padding the im to [input_size, input_size] + new_h, new_w, _ = im.shape + if min(new_w, new_h) < self.input_size * 0.5: + return None + + text_polys[:, :, 0] *= rz_scale + text_polys[:, :, 1] *= rz_scale + + # add gaussian blur + if np.random.rand() < 0.1 * 0.5: + ks = np.random.permutation(5)[0] + 1 + ks = int(ks / 2) * 2 + 1 + im = cv2.GaussianBlur(im, ksize=(ks, ks), sigmaX=0, sigmaY=0) + # add brighter + if np.random.rand() < 0.1 * 0.5: + im = im * (1.0 + np.random.rand() * 0.5) + im = np.clip(im, 0.0, 255.0) + # add darker + if np.random.rand() < 0.1 * 0.5: + im = im * (1.0 - np.random.rand() * 0.5) + im = np.clip(im, 0.0, 255.0) + + + im_padded = np.ones((self.input_size, self.input_size, 3), dtype=np.float32) + im_padded[:, :, 2] = 0.485 * 255 + im_padded[:, :, 1] = 0.456 * 255 + im_padded[:, :, 0] = 0.406 * 255 + + # Random the start position + del_h = self.input_size - new_h + del_w = self.input_size - new_w + sh, sw = 0, 0 + if del_h > 1: + sh = int(np.random.rand() * del_h) + if del_w > 1: + sw = int(np.random.rand() * del_w) + + # Padding + im_padded[sh: sh + new_h, sw: sw + new_w, :] = im.copy() + text_polys[:, :, 0] += sw + text_polys[:, :, 1] += sh + + score_map, border_map, training_mask = self.generate_tcl_label((self.input_size, self.input_size), + text_polys, text_tags, 0.25) + + # SAST head + tvo_map, tco_map = self.generate_tvo_and_tco((self.input_size, self.input_size), text_polys, text_tags, + tcl_ratio=0.3, ds_ratio=0.25) + # print("test--------tvo_map shape:", tvo_map.shape) + + cv2.imwrite('result.jpg',np.array(im_padded)) + cv2.imwrite('score.jpg',np.array(score_map)*255) + + im_padded = im_padded.astype(np.uint8) + im_padded = Image.fromarray(im_padded).convert('RGB') + im_padded = self.TSM.normalize_img(im_padded) + + score_map = torch.Tensor(score_map[np.newaxis, :, :]) + border_map = torch.Tensor(border_map.transpose((2, 0, 1))) + training_mask = torch.Tensor(training_mask[np.newaxis, :, :]) + tvo_map = torch.Tensor(tvo_map.transpose((2, 0, 1))) + tco_map = torch.Tensor(tco_map.transpose((2, 0, 1))) + + return im_padded, score_map, border_map, training_mask, tvo_map, tco_map + + +class alignCollate(object): + + def __init__(self, train_dataset): + self.train_dataset = train_dataset + + def __call__(self, batch): + new_batch = [] + for item in batch: + im, text_polys, text_tags, text_strs = item + max_tried = 30 + i = 0 + while True: + out = self.train_dataset.img_tranform(im, text_polys, text_tags, text_strs) + if(out is not None or i > max_tried): + break + i+=1 + if(out is not None): + new_batch.append(out) + data = zip(*new_batch) + images, score_map, border_map, training_mask, tvo_map, tco_map = data + images = torch.stack(images, 0) + score_map = torch.stack(score_map, 0) + border_map = torch.stack(border_map, 0) + training_mask = torch.stack(training_mask, 0) + tvo_map = torch.stack(tvo_map, 0) + tco_map = torch.stack(tco_map, 0) + return images, score_map, border_map, training_mask, tvo_map, tco_map + + +class SASTProcessTest(data.Dataset): + """ + SAST process function for test + """ + + def __init__(self, config): + super(SASTProcessTest, self).__init__() + self.img_list = self.get_img_files(config['testload']['test_file']) + self.TSM = Random_Augment(config['base']['crop_shape']) + self.test_size = config['testload']['test_size'] + self.config = config + + def get_img_files(self, test_txt_file): + img_list = [] + with open(test_txt_file, 'r', encoding='utf-8') as fid: + lines = fid.readlines() + for line in lines: + line = line.strip('\n') + img_list.append(line) + return img_list + def __len__(self): + return len(self.img_list) + + def __getitem__(self, index): + ori_img = cv2.imread(self.img_list[index]) + img = resize_image(ori_img,self.config['base']['algorithm'],self.test_size,self.config['testload']['stride']) + img = Image.fromarray(img).convert('RGB') + img = self.TSM.normalize_img(img) + return img, ori_img + + + + + + diff --git a/ptocr/dataloader/DetLoad/SASTProcess_ori1.py b/ptocr/dataloader/DetLoad/SASTProcess_ori1.py new file mode 100644 index 0000000..27154e3 --- /dev/null +++ b/ptocr/dataloader/DetLoad/SASTProcess_ori1.py @@ -0,0 +1,862 @@ +#-*- coding:utf-8 _*- +""" +@author:fxw +@file: SASTProcess.py +@time: 2020/08/18 +""" +import os +import math +import cv2 +import json +import numpy as np +import torch +from PIL import Image +import torch.utils.data as data +import torchvision.transforms as transforms +from .transform_img import Random_Augment +from ptocr.utils.util_function import resize_image + +class SASTProcessTrain(data.Dataset): + """ + SAST process function for training + """ + + def __init__(self,config): + self.TSM = Random_Augment(config['base']['crop_shape']) + self.min_crop_side_ratio = config['trainload']['min_crop_side_ratio'] + self.min_crop_size = config['trainload']['min_crop_size'] + image_shape = config['base']['crop_shape'] + self.input_size = image_shape[0] + self.min_text_size = config['trainload']['min_text_size'] + self.max_text_size = image_shape[0] + + data_num = len(config['trainload']['train_file_extre']) + train_file_extre = config['trainload']['train_file_extre'] + train_file_target = config['trainload']['train_file_target'] + extre_img_all = [] + extre_gt_all = {} + self.target_files = [] + self.target_gts = [] + + for i in range(data_num): + with open(os.path.join(config['trainload']['data_dir'],train_file_extre[i]), 'r',encoding='utf-8') as fid: + lines = fid.readlines() + for line in lines: + line = line.strip('\n').split('\t') + img_file = line[0] + gt = json.loads(line[1]) + extre_img_all.append(os.path.join(config['trainload']['data_dir'],img_file)) + extre_gt_all[os.path.join(config['trainload']['data_dir'],img_file)] = gt + + with open(os.path.join(config['trainload']['data_dir'],train_file_target), 'r',encoding='utf-8') as fid: + lines = fid.readlines() + for line in lines: + line = line.strip('\n').split('\t') + img_file = line[0] + gt = json.loads(line[1]) + self.target_files.append(os.path.join(config['trainload']['data_dir'],img_file)) + self.target_gts.append(gt) + + self.config = config + self.extre_img_all = extre_img_all + self.extre_gt_all = extre_gt_all + self.train_file_ratio = config['trainload']['train_file_ratio'] + self.data_num = data_num + self.gen_train_img() +# import pdb +# pdb.set_trace() + + def gen_train_img(self,): + self.img_files = [] + self.train_gts = [] + self.img_files.extend(self.target_files) + self.train_gts.extend(self.target_gts) + extre_num = int(self.config['trainload']['train_file_ratio']*len(self.target_files)) + for i in range(extre_num): + self.img_files.append(self.extre_img_all[i]) + self.train_gts.append(self.extre_gt_all[self.extre_img_all[i]]) + + np.random.shuffle(self.extre_img_all) + print(self.img_files[-1]) + + def order_points(self, pts): + rect = np.zeros((4, 2), dtype="float32") + s = pts.sum(axis=1) + rect[0] = pts[np.argmin(s)] + rect[2] = pts[np.argmax(s)] + diff = np.diff(pts, axis=1) + rect[1] = pts[np.argmin(diff)] + rect[3] = pts[np.argmax(diff)] + return rect + + def quad_area(self, poly): + """ + compute area of a polygon + :param poly: + :return: + """ + edge = [ + (poly[1][0] - poly[0][0]) * (poly[1][1] + poly[0][1]), + (poly[2][0] - poly[1][0]) * (poly[2][1] + poly[1][1]), + (poly[3][0] - poly[2][0]) * (poly[3][1] + poly[2][1]), + (poly[0][0] - poly[3][0]) * (poly[0][1] + poly[3][1]) + ] + return np.sum(edge) / 2. + + def gen_quad_from_poly(self, poly): + """ + Generate min area quad from poly. + """ + point_num = poly.shape[0] + min_area_quad = np.zeros((4, 2), dtype=np.float32) + if True: + rect = cv2.minAreaRect(poly.astype(np.int32)) # (center (x,y), (width, height), angle of rotation) + center_point = rect[0] + box = np.array(cv2.boxPoints(rect)) + + first_point_idx = 0 + min_dist = 1e4 + for i in range(4): + dist = np.linalg.norm(box[(i + 0) % 4] - poly[0]) + \ + np.linalg.norm(box[(i + 1) % 4] - poly[point_num // 2 - 1]) + \ + np.linalg.norm(box[(i + 2) % 4] - poly[point_num // 2]) + \ + np.linalg.norm(box[(i + 3) % 4] - poly[-1]) + if dist < min_dist: + min_dist = dist + first_point_idx = i + for i in range(4): + min_area_quad[i] = box[(first_point_idx + i) % 4] + + return min_area_quad + + def check_and_validate_polys(self, polys, tags, xxx_todo_changeme): + """ + check so that the text poly is in the same direction, + and also filter some invalid polygons + :param polys: + :param tags: + :return: + """ + (h, w) = xxx_todo_changeme + if polys.shape[0] == 0: + return polys, np.array([]), np.array([]) + polys[:, :, 0] = np.clip(polys[:, :, 0], 0, w - 1) + polys[:, :, 1] = np.clip(polys[:, :, 1], 0, h - 1) + + validated_polys = [] + validated_tags = [] + hv_tags = [] + for poly, tag in zip(polys, tags): + quad = self.gen_quad_from_poly(poly) + p_area = self.quad_area(quad) + if abs(p_area) < 1: + print('invalid poly') + continue + if p_area > 0: + if tag == False: + print('poly in wrong direction') + tag = True # reversed cases should be ignore + poly = poly[(0, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1), :] + quad = quad[(0, 3, 2, 1), :] + + len_w = np.linalg.norm(quad[0] - quad[1]) + np.linalg.norm(quad[3] - quad[2]) + len_h = np.linalg.norm(quad[0] - quad[3]) + np.linalg.norm(quad[1] - quad[2]) + hv_tag = 1 + + if len_w * 2.0 < len_h: + hv_tag = 0 + + validated_polys.append(poly) + validated_tags.append(tag) + hv_tags.append(hv_tag) + return np.array(validated_polys), np.array(validated_tags), np.array(hv_tags) + + def crop_area(self, im, polys, tags, hv_tags, txts, crop_background=False, max_tries=25): + """ + make random crop from the input image + :param im: + :param polys: + :param tags: + :param crop_background: + :param max_tries: 50 -> 25 + :return: + """ + h, w, _ = im.shape + pad_h = h // 10 + pad_w = w // 10 + h_array = np.zeros((h + pad_h * 2), dtype=np.int32) + w_array = np.zeros((w + pad_w * 2), dtype=np.int32) + for poly in polys: + poly = np.round(poly, decimals=0).astype(np.int32) + minx = np.min(poly[:, 0]) + maxx = np.max(poly[:, 0]) + w_array[minx + pad_w: maxx + pad_w] = 1 + miny = np.min(poly[:, 1]) + maxy = np.max(poly[:, 1]) + h_array[miny + pad_h: maxy + pad_h] = 1 + # ensure the cropped area not across a text + h_axis = np.where(h_array == 0)[0] + w_axis = np.where(w_array == 0)[0] + if len(h_axis) == 0 or len(w_axis) == 0: + return im, polys, tags, hv_tags, txts + for i in range(max_tries): + xx = np.random.choice(w_axis, size=2) + xmin = np.min(xx) - pad_w + xmax = np.max(xx) - pad_w + xmin = np.clip(xmin, 0, w - 1) + xmax = np.clip(xmax, 0, w - 1) + yy = np.random.choice(h_axis, size=2) + ymin = np.min(yy) - pad_h + ymax = np.max(yy) - pad_h + ymin = np.clip(ymin, 0, h - 1) + ymax = np.clip(ymax, 0, h - 1) + # if xmax - xmin < ARGS.min_crop_side_ratio * w or \ + # ymax - ymin < ARGS.min_crop_side_ratio * h: + if xmax - xmin < self.min_crop_size or \ + ymax - ymin < self.min_crop_size: + # area too small + continue + if polys.shape[0] != 0: + poly_axis_in_area = (polys[:, :, 0] >= xmin) & (polys[:, :, 0] <= xmax) \ + & (polys[:, :, 1] >= ymin) & (polys[:, :, 1] <= ymax) + selected_polys = np.where(np.sum(poly_axis_in_area, axis=1) == 4)[0] + else: + selected_polys = [] + if len(selected_polys) == 0: + # no text in this area + if crop_background: + txts_tmp = [] + for selected_poly in selected_polys: + txts_tmp.append(txts[selected_poly]) + txts = txts_tmp + return im[ymin: ymax + 1, xmin: xmax + 1, :], \ + polys[selected_polys], tags[selected_polys], hv_tags[selected_polys], txts + else: + continue + im = im[ymin: ymax + 1, xmin: xmax + 1, :] + polys = polys[selected_polys] + tags = tags[selected_polys] + hv_tags = hv_tags[selected_polys] + txts_tmp = [] + for selected_poly in selected_polys: + txts_tmp.append(txts[selected_poly]) + txts = txts_tmp + polys[:, :, 0] -= xmin + polys[:, :, 1] -= ymin + return im, polys, tags, hv_tags, txts + + return im, polys, tags, hv_tags, txts + + def generate_direction_map(self, poly_quads, direction_map): + """ + """ + width_list = [] + height_list = [] + for quad in poly_quads: + quad_w = (np.linalg.norm(quad[0] - quad[1]) + np.linalg.norm(quad[2] - quad[3])) / 2.0 + quad_h = (np.linalg.norm(quad[0] - quad[3]) + np.linalg.norm(quad[2] - quad[1])) / 2.0 + width_list.append(quad_w) + height_list.append(quad_h) + norm_width = max(sum(width_list) / (len(width_list) + 1e-6), 1.0) + average_height = max(sum(height_list) / (len(height_list) + 1e-6), 1.0) + + for quad in poly_quads: + direct_vector_full = ((quad[1] + quad[2]) - (quad[0] + quad[3])) / 2.0 + direct_vector = direct_vector_full / (np.linalg.norm(direct_vector_full) + 1e-6) * norm_width + direction_label = tuple(map(float, [direct_vector[0], direct_vector[1], 1.0 / (average_height + 1e-6)])) + cv2.fillPoly(direction_map, quad.round().astype(np.int32)[np.newaxis, :, :], direction_label) + return direction_map + + def calculate_average_height(self, poly_quads): + """ + """ + height_list = [] + for quad in poly_quads: + quad_h = (np.linalg.norm(quad[0] - quad[3]) + np.linalg.norm(quad[2] - quad[1])) / 2.0 + height_list.append(quad_h) + average_height = max(sum(height_list) / len(height_list), 1.0) + return average_height + + def generate_tcl_label(self, hw, polys, tags, ds_ratio, + tcl_ratio=0.3, shrink_ratio_of_width=0.15): + """ + Generate polygon. + """ + h, w = hw + h, w = int(h * ds_ratio), int(w * ds_ratio) + polys = polys * ds_ratio + + score_map = np.zeros((h, w,), dtype=np.float32) + tbo_map = np.zeros((h, w, 5), dtype=np.float32) + training_mask = np.ones((h, w,), dtype=np.float32) + direction_map = np.ones((h, w, 3)) * np.array([0, 0, 1]).reshape([1, 1, 3]).astype(np.float32) + + for poly_idx, poly_tag in enumerate(zip(polys, tags)): + poly = poly_tag[0] + tag = poly_tag[1] + + # generate min_area_quad + min_area_quad, center_point = self.gen_min_area_quad_from_poly(poly) + min_area_quad_h = 0.5 * (np.linalg.norm(min_area_quad[0] - min_area_quad[3]) + + np.linalg.norm(min_area_quad[1] - min_area_quad[2])) + min_area_quad_w = 0.5 * (np.linalg.norm(min_area_quad[0] - min_area_quad[1]) + + np.linalg.norm(min_area_quad[2] - min_area_quad[3])) + + if min(min_area_quad_h, min_area_quad_w) < self.min_text_size * ds_ratio \ + or min(min_area_quad_h, min_area_quad_w) > self.max_text_size * ds_ratio: + continue + + if tag: + # continue + cv2.fillPoly(training_mask, poly.astype(np.int32)[np.newaxis, :, :], 0.15) + else: + tcl_poly = self.poly2tcl(poly, tcl_ratio) + tcl_quads = self.poly2quads(tcl_poly) + poly_quads = self.poly2quads(poly) + # stcl map + stcl_quads, quad_index = self.shrink_poly_along_width(tcl_quads, + shrink_ratio_of_width=shrink_ratio_of_width, + expand_height_ratio=1.0 / tcl_ratio) + # generate tcl map + cv2.fillPoly(score_map, np.round(stcl_quads).astype(np.int32), 1.0) + + # generate tbo map + for idx, quad in enumerate(stcl_quads): + quad_mask = np.zeros((h, w), dtype=np.float32) + quad_mask = cv2.fillPoly(quad_mask, np.round(quad[np.newaxis, :, :]).astype(np.int32), 1.0) + tbo_map = self.gen_quad_tbo(poly_quads[quad_index[idx]], quad_mask, tbo_map) + return score_map, tbo_map, training_mask + + def generate_tvo_and_tco(self, hw, polys, tags, tcl_ratio=0.3, ds_ratio=0.25): + """ + Generate tcl map, tvo map and tbo map. + """ + h, w = hw + h, w = int(h * ds_ratio), int(w * ds_ratio) + polys = polys * ds_ratio + poly_mask = np.zeros((h, w), dtype=np.float32) + + tvo_map = np.ones((9, h, w), dtype=np.float32) + tvo_map[0:-1:2] = np.tile(np.arange(0, w), (h, 1)) + tvo_map[1:-1:2] = np.tile(np.arange(0, w), (h, 1)).T + poly_tv_xy_map = np.zeros((8, h, w), dtype=np.float32) + + # tco map + tco_map = np.ones((3, h, w), dtype=np.float32) + tco_map[0] = np.tile(np.arange(0, w), (h, 1)) + tco_map[1] = np.tile(np.arange(0, w), (h, 1)).T + poly_tc_xy_map = np.zeros((2, h, w), dtype=np.float32) + + poly_short_edge_map = np.ones((h, w), dtype=np.float32) + + for poly, poly_tag in zip(polys, tags): + + if poly_tag == True: + continue + + # adjust point order for vertical poly + poly = self.adjust_point(poly) + + # generate min_area_quad + min_area_quad, center_point = self.gen_min_area_quad_from_poly(poly) + min_area_quad_h = 0.5 * (np.linalg.norm(min_area_quad[0] - min_area_quad[3]) + + np.linalg.norm(min_area_quad[1] - min_area_quad[2])) + min_area_quad_w = 0.5 * (np.linalg.norm(min_area_quad[0] - min_area_quad[1]) + + np.linalg.norm(min_area_quad[2] - min_area_quad[3])) + + # generate tcl map and text, 128 * 128 + tcl_poly = self.poly2tcl(poly, tcl_ratio) + + # generate poly_tv_xy_map + for idx in range(4): + cv2.fillPoly(poly_tv_xy_map[2 * idx], + np.round(tcl_poly[np.newaxis, :, :]).astype(np.int32), + float(min(max(min_area_quad[idx, 0], 0), w))) + cv2.fillPoly(poly_tv_xy_map[2 * idx + 1], + np.round(tcl_poly[np.newaxis, :, :]).astype(np.int32), + float(min(max(min_area_quad[idx, 1], 0), h))) + + # generate poly_tc_xy_map + for idx in range(2): + cv2.fillPoly(poly_tc_xy_map[idx], + np.round(tcl_poly[np.newaxis, :, :]).astype(np.int32), float(center_point[idx])) + + # generate poly_short_edge_map + cv2.fillPoly(poly_short_edge_map, + np.round(tcl_poly[np.newaxis, :, :]).astype(np.int32), + float(max(min(min_area_quad_h, min_area_quad_w), 1.0))) + + # generate poly_mask and training_mask + cv2.fillPoly(poly_mask, np.round(tcl_poly[np.newaxis, :, :]).astype(np.int32), 1) + + tvo_map *= poly_mask + tvo_map[:8] -= poly_tv_xy_map + tvo_map[-1] /= poly_short_edge_map + tvo_map = tvo_map.transpose((1, 2, 0)) + + tco_map *= poly_mask + tco_map[:2] -= poly_tc_xy_map + tco_map[-1] /= poly_short_edge_map + tco_map = tco_map.transpose((1, 2, 0)) + + return tvo_map, tco_map + + def adjust_point(self, poly): + """ + adjust point order. + """ + point_num = poly.shape[0] + if point_num == 4: + len_1 = np.linalg.norm(poly[0] - poly[1]) + len_2 = np.linalg.norm(poly[1] - poly[2]) + len_3 = np.linalg.norm(poly[2] - poly[3]) + len_4 = np.linalg.norm(poly[3] - poly[0]) + + if (len_1 + len_3) * 1.5 < (len_2 + len_4): + poly = poly[[1, 2, 3, 0], :] + + elif point_num > 4: + vector_1 = poly[0] - poly[1] + vector_2 = poly[1] - poly[2] + cos_theta = np.dot(vector_1, vector_2) / (np.linalg.norm(vector_1) * np.linalg.norm(vector_2) + 1e-6) + theta = np.arccos(np.round(cos_theta, decimals=4)) + + if abs(theta) > (70 / 180 * math.pi): + index = list(range(1, point_num)) + [0] + poly = poly[np.array(index), :] + return poly + + def gen_min_area_quad_from_poly(self, poly): + """ + Generate min area quad from poly. + """ + point_num = poly.shape[0] + min_area_quad = np.zeros((4, 2), dtype=np.float32) + if point_num == 4: + min_area_quad = poly + center_point = np.sum(poly, axis=0) / 4 + else: + rect = cv2.minAreaRect(poly.astype(np.int32)) # (center (x,y), (width, height), angle of rotation) + center_point = rect[0] + box = np.array(cv2.boxPoints(rect)) + + first_point_idx = 0 + min_dist = 1e4 + for i in range(4): + dist = np.linalg.norm(box[(i + 0) % 4] - poly[0]) + \ + np.linalg.norm(box[(i + 1) % 4] - poly[point_num // 2 - 1]) + \ + np.linalg.norm(box[(i + 2) % 4] - poly[point_num // 2]) + \ + np.linalg.norm(box[(i + 3) % 4] - poly[-1]) + if dist < min_dist: + min_dist = dist + first_point_idx = i + + for i in range(4): + min_area_quad[i] = box[(first_point_idx + i) % 4] + + return min_area_quad, center_point + + def shrink_quad_along_width(self, quad, begin_width_ratio=0., end_width_ratio=1.): + """ + Generate shrink_quad_along_width. + """ + ratio_pair = np.array([[begin_width_ratio], [end_width_ratio]], dtype=np.float32) + p0_1 = quad[0] + (quad[1] - quad[0]) * ratio_pair + p3_2 = quad[3] + (quad[2] - quad[3]) * ratio_pair + return np.array([p0_1[0], p0_1[1], p3_2[1], p3_2[0]]) + + def shrink_poly_along_width(self, quads, shrink_ratio_of_width, expand_height_ratio=1.0): + """ + shrink poly with given length. + """ + upper_edge_list = [] + + def get_cut_info(edge_len_list, cut_len): + for idx, edge_len in enumerate(edge_len_list): + cut_len -= edge_len + if cut_len <= 0.000001: + ratio = (cut_len + edge_len_list[idx]) / edge_len_list[idx] + return idx, ratio + + for quad in quads: + upper_edge_len = np.linalg.norm(quad[0] - quad[1]) + upper_edge_list.append(upper_edge_len) + + # length of left edge and right edge. + left_length = np.linalg.norm(quads[0][0] - quads[0][3]) * expand_height_ratio + right_length = np.linalg.norm(quads[-1][1] - quads[-1][2]) * expand_height_ratio + + shrink_length = min(left_length, right_length, sum(upper_edge_list)) * shrink_ratio_of_width + # shrinking length + upper_len_left = shrink_length + upper_len_right = sum(upper_edge_list) - shrink_length + + left_idx, left_ratio = get_cut_info(upper_edge_list, upper_len_left) + left_quad = self.shrink_quad_along_width(quads[left_idx], begin_width_ratio=left_ratio, end_width_ratio=1) + right_idx, right_ratio = get_cut_info(upper_edge_list, upper_len_right) + right_quad = self.shrink_quad_along_width(quads[right_idx], begin_width_ratio=0, end_width_ratio=right_ratio) + + out_quad_list = [] + if left_idx == right_idx: + out_quad_list.append([left_quad[0], right_quad[1], right_quad[2], left_quad[3]]) + else: + out_quad_list.append(left_quad) + for idx in range(left_idx + 1, right_idx): + out_quad_list.append(quads[idx]) + out_quad_list.append(right_quad) + + return np.array(out_quad_list), list(range(left_idx, right_idx + 1)) + + def vector_angle(self, A, B): + """ + Calculate the angle between vector AB and x-axis positive direction. + """ + AB = np.array([B[1] - A[1], B[0] - A[0]]) + return np.arctan2(*AB) + + def theta_line_cross_point(self, theta, point): + """ + Calculate the line through given point and angle in ax + by + c =0 form. + """ + x, y = point + cos = np.cos(theta) + sin = np.sin(theta) + return [sin, -cos, cos * y - sin * x] + + def line_cross_two_point(self, A, B): + """ + Calculate the line through given point A and B in ax + by + c =0 form. + """ + angle = self.vector_angle(A, B) + return self.theta_line_cross_point(angle, A) + + def average_angle(self, poly): + """ + Calculate the average angle between left and right edge in given poly. + """ + p0, p1, p2, p3 = poly + angle30 = self.vector_angle(p3, p0) + angle21 = self.vector_angle(p2, p1) + return (angle30 + angle21) / 2 + + def line_cross_point(self, line1, line2): + """ + line1 and line2 in 0=ax+by+c form, compute the cross point of line1 and line2 + """ + a1, b1, c1 = line1 + a2, b2, c2 = line2 + d = a1 * b2 - a2 * b1 + + if d == 0: + # print("line1", line1) + # print("line2", line2) + print('Cross point does not exist') + return np.array([0, 0], dtype=np.float32) + else: + x = (b1 * c2 - b2 * c1) / d + y = (a2 * c1 - a1 * c2) / d + + return np.array([x, y], dtype=np.float32) + + def quad2tcl(self, poly, ratio): + """ + Generate center line by poly clock-wise point. (4, 2) + """ + ratio_pair = np.array([[0.5 - ratio / 2], [0.5 + ratio / 2]], dtype=np.float32) + p0_3 = poly[0] + (poly[3] - poly[0]) * ratio_pair + p1_2 = poly[1] + (poly[2] - poly[1]) * ratio_pair + return np.array([p0_3[0], p1_2[0], p1_2[1], p0_3[1]]) + + def poly2tcl(self, poly, ratio): + """ + Generate center line by poly clock-wise point. + """ + ratio_pair = np.array([[0.5 - ratio / 2], [0.5 + ratio / 2]], dtype=np.float32) + tcl_poly = np.zeros_like(poly) + point_num = poly.shape[0] + + for idx in range(point_num // 2): + point_pair = poly[idx] + (poly[point_num - 1 - idx] - poly[idx]) * ratio_pair + tcl_poly[idx] = point_pair[0] + tcl_poly[point_num - 1 - idx] = point_pair[1] + return tcl_poly + + def gen_quad_tbo(self, quad, tcl_mask, tbo_map): + """ + Generate tbo_map for give quad. + """ + # upper and lower line function: ax + by + c = 0; + up_line = self.line_cross_two_point(quad[0], quad[1]) + lower_line = self.line_cross_two_point(quad[3], quad[2]) + + quad_h = 0.5 * (np.linalg.norm(quad[0] - quad[3]) + np.linalg.norm(quad[1] - quad[2])) + quad_w = 0.5 * (np.linalg.norm(quad[0] - quad[1]) + np.linalg.norm(quad[2] - quad[3])) + + # average angle of left and right line. + angle = self.average_angle(quad) + + xy_in_poly = np.argwhere(tcl_mask == 1) + for y, x in xy_in_poly: + point = (x, y) + line = self.theta_line_cross_point(angle, point) + cross_point_upper = self.line_cross_point(up_line, line) + cross_point_lower = self.line_cross_point(lower_line, line) + ##FIX, offset reverse + upper_offset_x, upper_offset_y = cross_point_upper - point + lower_offset_x, lower_offset_y = cross_point_lower - point + tbo_map[y, x, 0] = upper_offset_y + tbo_map[y, x, 1] = upper_offset_x + tbo_map[y, x, 2] = lower_offset_y + tbo_map[y, x, 3] = lower_offset_x + tbo_map[y, x, 4] = 1.0 / max(min(quad_h, quad_w), 1.0) * 2 + return tbo_map + + def poly2quads(self, poly): + """ + Split poly into quads. + """ + quad_list = [] + point_num = poly.shape[0] + + # point pair + point_pair_list = [] + for idx in range(point_num // 2): + point_pair = [poly[idx], poly[point_num - 1 - idx]] + point_pair_list.append(point_pair) + + quad_num = point_num // 2 - 1 + for idx in range(quad_num): + # reshape and adjust to clock-wise + quad_list.append((np.array(point_pair_list)[[idx, idx + 1]]).reshape(4, 2)[[0, 2, 3, 1]]) + + return np.array(quad_list) + +# def extract_polys(self, poly_txt_path): +# """ +# Read text_polys, txt_tags, txts from give txt file. +# """ +# text_polys, txt_tags, txts = [], [], [] + +# with open(poly_txt_path, 'r', encoding='utf-8') as f: +# for line in f.readlines(): +# poly_str = line.strip().replace('\ufeff', '').split(',') +# txt = poly_str[-1] +# poly_str = poly_str[:8] +# poly = list(map(float, poly_str)) +# text_polys.append(np.array(poly, dtype=np.float32).reshape(-1, 2)) +# txts.append(txt) +# if txt == '###': +# txt_tags.append(True) +# else: +# txt_tags.append(False) + +# return np.array(text_polys), \ +# np.array(txt_tags, dtype=np.bool), txts + + def extract_polys(self, label_data): + text_polys, txt_tags, txts = [], [], [] + for rect in label_data: + points = np.array(rect['points']).astype(np.float32) + transcription = rect['transcription'] + points = self.order_points(points) + if cv2.contourArea(points) > 0: + text_polys.append(points) + txts.append(transcription) + txt_tags.append(transcription in ['*','###']) + return np.array(text_polys),np.array(txt_tags, dtype=np.bool), txts + + + def __len__(self): + return len(self.img_files) + + def __getitem__(self, index): + + im_path = self.img_files[index] + text_polys, text_tags, text_strs = self.extract_polys(self.train_gts[index]) + im = cv2.imread(im_path) + + return im,text_polys, text_tags, text_strs + + def img_tranform(self,im,text_polys, text_tags, text_strs): + + if im is None: + return None + if text_polys.shape[0] == 0: + return None + + h, w, _ = im.shape + text_polys, text_tags, hv_tags = self.check_and_validate_polys(text_polys, text_tags, (h, w)) + + if text_polys.shape[0] == 0: + return None + + # set aspect ratio and keep area fix + asp_scales = np.arange(1.0, 1.55, 0.1) + asp_scale = np.random.choice(asp_scales) + + if np.random.rand() < 0.5: + asp_scale = 1.0 / asp_scale + asp_scale = math.sqrt(asp_scale) + + asp_wx = asp_scale + asp_hy = 1.0 / asp_scale + im = cv2.resize(im, dsize=None, fx=asp_wx, fy=asp_hy) + text_polys[:, :, 0] *= asp_wx + text_polys[:, :, 1] *= asp_hy + + h, w, _ = im.shape + if max(h, w) > 2048: + rd_scale = 2048.0 / max(h, w) + im = cv2.resize(im, dsize=None, fx=rd_scale, fy=rd_scale) + text_polys *= rd_scale + h, w, _ = im.shape + if min(h, w) < 16: + return None + + # no background + im, text_polys, text_tags, hv_tags, text_strs = self.crop_area(im, text_polys, text_tags, hv_tags, text_strs,crop_background=False) + if text_polys.shape[0] == 0: + return None + # continue for all ignore case + if np.sum((text_tags * 1.0)) >= text_tags.size: + return None + new_h, new_w, _ = im.shape + if (new_h is None) or (new_w is None): + return None + # resize image + std_ratio = float(self.input_size) / max(new_w, new_h) + rand_scales = np.array([0.25, 0.375, 0.5, 0.625, 0.75, 0.875, 1.0, 1.0, 1.0, 1.0, 1.0]) + rz_scale = std_ratio * np.random.choice(rand_scales) + + + im = cv2.resize(im, dsize=None, fx=rz_scale, fy=rz_scale) + # Padding the im to [input_size, input_size] + new_h, new_w, _ = im.shape + if min(new_w, new_h) < self.input_size * 0.5: + return None + + text_polys[:, :, 0] *= rz_scale + text_polys[:, :, 1] *= rz_scale + + # add gaussian blur + if np.random.rand() < 0.1 * 0.5: + ks = np.random.permutation(5)[0] + 1 + ks = int(ks / 2) * 2 + 1 + im = cv2.GaussianBlur(im, ksize=(ks, ks), sigmaX=0, sigmaY=0) + # add brighter + if np.random.rand() < 0.1 * 0.5: + im = im * (1.0 + np.random.rand() * 0.5) + im = np.clip(im, 0.0, 255.0) + # add darker + if np.random.rand() < 0.1 * 0.5: + im = im * (1.0 - np.random.rand() * 0.5) + im = np.clip(im, 0.0, 255.0) + + + im_padded = np.ones((self.input_size, self.input_size, 3), dtype=np.float32) + im_padded[:, :, 2] = 0.485 * 255 + im_padded[:, :, 1] = 0.456 * 255 + im_padded[:, :, 0] = 0.406 * 255 + + # Random the start position + del_h = self.input_size - new_h + del_w = self.input_size - new_w + sh, sw = 0, 0 + if del_h > 1: + sh = int(np.random.rand() * del_h) + if del_w > 1: + sw = int(np.random.rand() * del_w) + + # Padding + im_padded[sh: sh + new_h, sw: sw + new_w, :] = im.copy() + text_polys[:, :, 0] += sw + text_polys[:, :, 1] += sh + + score_map, border_map, training_mask = self.generate_tcl_label((self.input_size, self.input_size), + text_polys, text_tags, 0.25) + + # SAST head + tvo_map, tco_map = self.generate_tvo_and_tco((self.input_size, self.input_size), text_polys, text_tags, + tcl_ratio=0.3, ds_ratio=0.25) + # print("test--------tvo_map shape:", tvo_map.shape) + + cv2.imwrite('result.jpg',np.array(im_padded)) + cv2.imwrite('score.jpg',np.array(score_map)*255) + + im_padded = im_padded.astype(np.uint8) + im_padded = Image.fromarray(im_padded).convert('RGB') + im_padded = self.TSM.normalize_img(im_padded) + + score_map = torch.Tensor(score_map[np.newaxis, :, :]) + border_map = torch.Tensor(border_map.transpose((2, 0, 1))) + training_mask = torch.Tensor(training_mask[np.newaxis, :, :]) + tvo_map = torch.Tensor(tvo_map.transpose((2, 0, 1))) + tco_map = torch.Tensor(tco_map.transpose((2, 0, 1))) + + return im_padded, score_map, border_map, training_mask, tvo_map, tco_map + + +class alignCollate(object): + + def __init__(self, train_dataset): + self.train_dataset = train_dataset + + def __call__(self, batch): + new_batch = [] + for item in batch: + im, text_polys, text_tags, text_strs = item + max_tried = 30 + i = 0 + while True: + out = self.train_dataset.img_tranform(im, text_polys, text_tags, text_strs) + if(out is not None or i > max_tried): + break + i+=1 + if(out is not None): + new_batch.append(out) + data = zip(*new_batch) + images, score_map, border_map, training_mask, tvo_map, tco_map = data + images = torch.stack(images, 0) + score_map = torch.stack(score_map, 0) + border_map = torch.stack(border_map, 0) + training_mask = torch.stack(training_mask, 0) + tvo_map = torch.stack(tvo_map, 0) + tco_map = torch.stack(tco_map, 0) + return images, score_map, border_map, training_mask, tvo_map, tco_map + + +class SASTProcessTest(data.Dataset): + """ + SAST process function for test + """ + + def __init__(self, config): + super(SASTProcessTest, self).__init__() + self.img_list = self.get_img_files(config['testload']['test_file']) + self.TSM = Random_Augment(config['base']['crop_shape']) + self.test_size = config['testload']['test_size'] + self.config = config + + def get_img_files(self, test_txt_file): + img_list = [] + with open(test_txt_file, 'r', encoding='utf-8') as fid: + lines = fid.readlines() + for line in lines: + line = line.strip('\n') + img_list.append(line) + return img_list + def __len__(self): + return len(self.img_list) + + def __getitem__(self, index): + ori_img = cv2.imread(self.img_list[index]) + img = resize_image(ori_img,self.config['base']['algorithm'],self.test_size,self.config['testload']['stride']) + img = Image.fromarray(img).convert('RGB') + img = self.TSM.normalize_img(img) + return img, ori_img + + + + + + diff --git a/ptocr/dataloader/DetLoad/__init__.py b/ptocr/dataloader/DetLoad/__init__.py new file mode 100644 index 0000000..8529416 --- /dev/null +++ b/ptocr/dataloader/DetLoad/__init__.py @@ -0,0 +1,6 @@ +#-*- coding:utf-8 _*- +""" +@author:fxw +@file: __init__.py.py +@time: 2020/08/11 +""" diff --git a/ptocr/dataloader/DetLoad/__pycache__/DBProcess.cpython-36.pyc b/ptocr/dataloader/DetLoad/__pycache__/DBProcess.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..69d33da8e94a23810b830d78902054a1f1a9b4a5 GIT binary patch literal 4664 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 literal 0 HcmV?d00001 diff --git a/ptocr/dataloader/DetLoad/__pycache__/SASTProcess_ori1.cpython-36.pyc b/ptocr/dataloader/DetLoad/__pycache__/SASTProcess_ori1.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e3eaeabd413bb032c6496e17dad42c6a2f7c2ddb GIT binary patch literal 23827 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∨R0CEf##=Wpas}Jd;JJdTe2wID1 zE!@@W0FDE^M9#Z_B}c|tv?v%?Ix=qDTE@71REo+Rw|x;it^+WEA2z`agk6ABY$+US zsfI-W7ChZFf{#$HCgt~aw<9

E9TRAl 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 bLG#PBasfZfk~27-$MNSF_MBa=e)+!vy_ye6 literal 0 HcmV?d00001 diff --git a/ptocr/dataloader/DetLoad/transform_img.py b/ptocr/dataloader/DetLoad/transform_img.py new file mode 100644 index 0000000..ce02ead --- /dev/null +++ b/ptocr/dataloader/DetLoad/transform_img.py @@ -0,0 +1,329 @@ +#-*- coding:utf-8 _*- +""" +@author:fxw +@file: transform_img.py +@time: 2020/08/11 +""" +import cv2 +import numpy as np +import imgaug.augmenters as aug_img +import imgaug +import random +import math +import torchvision.transforms as transforms + +def solve_polys(polys): + len_max = 0 + for poly in polys: + if (len(poly) // 2 > len_max): + len_max = len(poly) // 2 + new_polys = [] + for poly in polys: + new_poly = [] + if (len(poly) // 2 < len_max): + new_poly.extend(poly) + for i in range(len(poly) // 2, len_max): + new_poly.extend([poly[0], poly[1]]) + else: + new_poly = poly + new_polys.append(new_poly) + return np.array(new_polys), len_max + + +def scale_aligned(img, h_scale, w_scale): + h, w = img.shape[0:2] + h = int(h * h_scale + 0.5) + w = int(w * w_scale + 0.5) + if h % 32 != 0: + h = h + (32 - h % 32) + if w % 32 != 0: + w = w + (32 - w % 32) + img = cv2.resize(img, dsize=(w, h)) + return img + + +class RandomCropData(): + def __init__(self, max_tries=10, min_crop_side_ratio=0.1, crop_size=(640, 640)): + self.size = crop_size + self.min_crop_side_ratio = min_crop_side_ratio + self.max_tries = max_tries + + def process(self, img, polys, dont_care): + all_care_polys = [] + for i in range(len(dont_care)): + if (dont_care[i] is False): + all_care_polys.append(polys[i]) + crop_x, crop_y, crop_w, crop_h = self.crop_area(img, all_care_polys) + scale_w = self.size[0] / crop_w + scale_h = self.size[1] / crop_h + scale = min(scale_w, scale_h) + h = int(crop_h * scale) + w = int(crop_w * scale) + padimg = np.zeros( + (self.size[1], self.size[0], img.shape[2]), img.dtype) + padimg[:h, :w] = cv2.resize( + img[crop_y:crop_y + crop_h, crop_x:crop_x + crop_w], (w, h)) + img = padimg + + new_polys = [] + new_dotcare = [] + for i in range(len(polys)): + poly = polys[i] + poly = ((np.array(poly) - + (crop_x, crop_y)) * scale) + if not self.is_poly_outside_rect(poly, 0, 0, w, h): + new_polys.append(poly) + new_dotcare.append(dont_care[i]) + + return img, new_polys, new_dotcare + + def is_poly_in_rect(self, poly, x, y, w, h): + poly = np.array(poly) + if poly[:, 0].min() < x or poly[:, 0].max() > x + w: + return False + if poly[:, 1].min() < y or poly[:, 1].max() > y + h: + return False + return True + + def is_poly_outside_rect(self, poly, x, y, w, h): + poly = np.array(poly) + if poly[:, 0].max() < x or poly[:, 0].min() > x + w: + return True + if poly[:, 1].max() < y or poly[:, 1].min() > y + h: + return True + return False + + def split_regions(self, axis): + regions = [] + min_axis = 0 + for i in range(1, axis.shape[0]): + if axis[i] != axis[i - 1] + 1: + region = axis[min_axis:i] + min_axis = i + regions.append(region) + return regions + + def random_select(self, axis, max_size): + xx = np.random.choice(axis, size=2) + xmin = np.min(xx) + xmax = np.max(xx) + xmin = np.clip(xmin, 0, max_size - 1) + xmax = np.clip(xmax, 0, max_size - 1) + return xmin, xmax + + def region_wise_random_select(self, regions, max_size): + selected_index = list(np.random.choice(len(regions), 2)) + selected_values = [] + for index in selected_index: + axis = regions[index] + xx = int(np.random.choice(axis, size=1)) + selected_values.append(xx) + xmin = min(selected_values) + xmax = max(selected_values) + return xmin, xmax + + def crop_area(self, img, polys): + h, w, _ = img.shape + h_array = np.zeros(h, dtype=np.int32) + w_array = np.zeros(w, dtype=np.int32) + for points in polys: + points = np.round(points, decimals=0).astype(np.int32) + minx = np.min(points[:, 0]) + maxx = np.max(points[:, 0]) + w_array[minx:maxx] = 1 + miny = np.min(points[:, 1]) + maxy = np.max(points[:, 1]) + h_array[miny:maxy] = 1 + # ensure the cropped area not across a text + h_axis = np.where(h_array == 0)[0] + w_axis = np.where(w_array == 0)[0] + + if len(h_axis) == 0 or len(w_axis) == 0: + return 0, 0, w, h + + h_regions = self.split_regions(h_axis) + w_regions = self.split_regions(w_axis) + + for i in range(self.max_tries): + if len(w_regions) > 1: + xmin, xmax = self.region_wise_random_select(w_regions, w) + else: + xmin, xmax = self.random_select(w_axis, w) + if len(h_regions) > 1: + ymin, ymax = self.region_wise_random_select(h_regions, h) + else: + ymin, ymax = self.random_select(h_axis, h) + + if xmax - xmin < self.min_crop_side_ratio * w or ymax - ymin < self.min_crop_side_ratio * h: + # area too small + continue + num_poly_in_rect = 0 + for poly in polys: + if not self.is_poly_outside_rect(poly, xmin, ymin, xmax - xmin, ymax - ymin): + num_poly_in_rect += 1 + break + + if num_poly_in_rect > 0: + return xmin, ymin, xmax - xmin, ymax - ymin + + return 0, 0, w, h + + +class Random_Augment(): + def __init__(self,crop_size, max_tries=10, min_crop_side_ratio=0.1): + super(Random_Augment, self).__init__() + self.random_crop_data = RandomCropData(crop_size = crop_size, max_tries=max_tries, min_crop_side_ratio=min_crop_side_ratio) + self.crop_size = crop_size + + def normalize_img(self,img): + img = transforms.ToTensor()(img) + img = transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])(img) + return img + + def augment_poly(self, aug, img_shape, poly): + keypoints = [imgaug.Keypoint(p[0], p[1]) for p in poly] + keypoints = aug.augment_keypoints([imgaug.KeypointsOnImage(keypoints, shape=img_shape[:2])])[0].keypoints + poly = [(p.x, p.y) for p in keypoints] + return np.array(poly) + + def random_rotate(self, img, polys, random_range=[-10,10]): + angle = np.random.randint(random_range[0], random_range[1]) + aug_bin = aug_img.Sequential([aug_img.Affine(rotate=angle)]) + img = aug_bin.augment_image(img) + new_polys = [] + for poly in polys: + poly = self.augment_poly(aug_bin, img.shape, poly) + poly = np.maximum(poly, 0) + new_polys.append(poly) + return img, new_polys + + def random_scale(self, img, polys, min_size): + polys, len_max = solve_polys(polys) + h, w = img.shape[0:2] + new_polys = [] + for poly in polys: + poly = np.asarray(poly) + poly = poly / ([w * 1.0, h * 1.0] * len_max) + new_polys.append(poly) + new_polys = np.array(new_polys) + if max(h, w) > 1280: + scale = 1280.0 / max(h, w) + img = cv2.resize(img, dsize=None, fx=scale, fy=scale) + h, w = img.shape[0:2] + random_scale = np.array([0.5,1.0,2.0,3.0]) + scale = np.random.choice(random_scale) + if min(h, w) * scale <= min_size: + scale = (min_size + 10) * 1.0 / min(h, w) + img = cv2.resize(img, dsize=None, fx=scale, fy=scale) + new_polys = np.reshape(new_polys * ([img.shape[1], img.shape[0]] * len_max), + (new_polys.shape[0], polys.shape[1] // 2, 2)) + return img, new_polys + + def random_scale_pan(self, img, polys, short_size=736): + + polys, len_max = solve_polys(polys) + h, w = img.shape[0:2] + new_polys = [] + for poly in polys: + poly = np.asarray(poly) + poly = poly / ([w * 1.0, h * 1.0] * len_max) + new_polys.append(poly) + new_polys = np.array(new_polys) + + scale = np.random.choice(np.array([0.7, 0.8, 0.9, 1.0, 1.1, 1.2, 1.3])) + scale = (scale * short_size) / min(h, w) + + aspect = np.random.choice(np.array([0.9, 0.95, 1.0, 1.05, 1.1])) + h_scale = scale * math.sqrt(aspect) + w_scale = scale / math.sqrt(aspect) + + img = scale_aligned(img, h_scale, w_scale) + + new_polys = np.reshape(new_polys * ([img.shape[1], img.shape[0]] * len_max), + (new_polys.shape[0], polys.shape[1] // 2, 2)) + return img, new_polys + + def random_flip(self, img, polys): + if (np.random.rand(1)[0] > 0.5): + aug_bin = aug_img.Sequential([aug_img.Fliplr((1))]) + img = aug_bin.augment_image(img) + new_polys = [] + for poly in polys: + poly = self.augment_poly(aug_bin, img.shape, poly) + poly = np.maximum(poly, 0) + new_polys.append(poly) + else: + new_polys = polys + return img, new_polys + + def random_crop_db(self, img, polys, dont_care): + img, new_polys, new_dotcare = self.random_crop_data.process(img, polys, dont_care) + return img, new_polys, new_dotcare + + def random_crop_pse(self, imgs, ): + h, w = imgs[0].shape[0:2] + th, tw = self.crop_size + if w == tw and h == th: + return imgs + + if random.random() > 3.0 / 8.0 and np.max(imgs[1]) > 0: + tl = np.min(np.where(imgs[1] > 0), axis=1) - self.crop_size + tl[tl < 0] = 0 + br = np.max(np.where(imgs[1] > 0), axis=1) - self.crop_size + br[br < 0] = 0 + br[0] = min(br[0], h - th) + br[1] = min(br[1], w - tw) + + i = random.randint(tl[0], br[0]) + j = random.randint(tl[1], br[1]) + else: + i = random.randint(0, h - th) + j = random.randint(0, w - tw) + + # return i, j, th, tw + for idx in range(len(imgs)): + if len(imgs[idx].shape) == 3: + imgs[idx] = imgs[idx][i:i + th, j:j + tw, :] + else: + imgs[idx] = imgs[idx][i:i + th, j:j + tw] + return imgs + + def random_crop_pan(self,imgs,): + h, w = imgs[0].shape[0:2] + t_w, t_h = self.crop_size + p_w, p_h = self.crop_size + if w == t_w and h == t_h: + return imgs + + t_h = t_h if t_h < h else h + t_w = t_w if t_w < w else w + + if random.random() > 3.0 / 8.0 and np.max(imgs[1]) > 0: + # make sure to crop the text region + tl = np.min(np.where(imgs[1] > 0), axis=1) - (t_h, t_w) + tl[tl < 0] = 0 + br = np.max(np.where(imgs[1] > 0), axis=1) - (t_h, t_w) + br[br < 0] = 0 + br[0] = min(br[0], h - t_h) + br[1] = min(br[1], w - t_w) + + i = random.randint(tl[0], br[0]) if tl[0] < br[0] else 0 + j = random.randint(tl[1], br[1]) if tl[1] < br[1] else 0 + else: + i = random.randint(0, h - t_h) if h - t_h > 0 else 0 + j = random.randint(0, w - t_w) if w - t_w > 0 else 0 + + n_imgs = [] + for idx in range(len(imgs)): + if len(imgs[idx].shape) == 3: + s3_length = int(imgs[idx].shape[-1]) + img = imgs[idx][i:i + t_h, j:j + t_w, :] + img_p = cv2.copyMakeBorder(img, 0, p_h - t_h, 0, p_w - t_w, borderType=cv2.BORDER_CONSTANT, + value=tuple(0 for i in range(s3_length))) + else: + img = imgs[idx][i:i + t_h, j:j + t_w] + img_p = cv2.copyMakeBorder(img, 0, p_h - t_h, 0, p_w - t_w, borderType=cv2.BORDER_CONSTANT, value=(0,)) + n_imgs.append(img_p) + return n_imgs + + diff --git a/ptocr/dataloader/RecLoad/CRNNProcess.py b/ptocr/dataloader/RecLoad/CRNNProcess.py new file mode 100644 index 0000000..fb32fe2 --- /dev/null +++ b/ptocr/dataloader/RecLoad/CRNNProcess.py @@ -0,0 +1,100 @@ +import lmdb +import torch +import six,re,glob,os +import numpy as np +from PIL import Image,ImageFile +ImageFile.LOAD_TRUNCATED_IMAGES = True +from torch.utils.data import Dataset +import torchvision.transforms as transforms +from ptocr.dataloader.RecLoad.DataAgument import transform_img_shape,DataAugment +from ptocr.utils.util_function import create_module,PILImageToCV,CVImageToPIL + + +def get_img(path,is_gray = False): + img = Image.open(path).convert('RGB') + if(is_gray): + img = img.convert('L') + return img + +class CRNNProcessLmdbLoad(Dataset): + def __init__(self, config,lmdb_type): + self.config = config + self.lmdb_type = lmdb_type + + if lmdb_type=='train': + lmdb_file = config['trainload']['train_file'] + workers = config['trainload']['num_workers'] + elif lmdb_type == 'val': + lmdb_file = config['valload']['val_file'] + workers = config['valload']['num_workers'] + else: + assert 1 == 1 + raise('lmdb_type error !!!') + + self.env = lmdb.open(lmdb_file,max_readers=workers,readonly=True,lock=False,readahead=False,meminit=False) + + if not self.env: + print('cannot creat lmdb from %s' % (lmdb_file)) + sys.exit(0) + + with self.env.begin(write=False) as txn: + nSamples = int(txn.get('num-samples'.encode('utf-8'))) + self.nSamples = nSamples + + self.transform_label = create_module(config['label_transform']['label_function']) + + self.bg_img = [] + for path in glob.glob(os.path.join(config['trainload']['bg_path'], '*')): + self.bg_img.append(path) + + def __len__(self): + return self.nSamples + + def __getitem__(self, index): + assert index <= len(self), 'index range error' + index += 1 + with self.env.begin(write=False) as txn: + img_key = 'image-%09d' % index + imgbuf = txn.get(img_key.encode('utf-8')) + + buf = six.BytesIO() + buf.write(imgbuf) + buf.seek(0) + try: + img = Image.open(buf).convert('RGB') + except IOError: + print('Corrupted image for %d' % index) + return self[index + 1] + + label_key = 'label-%09d' % index + label = txn.get(label_key.encode('utf-8')).decode() + label = self.transform_label(label, char_type=self.config['label_transform']['char_type'],t_type=self.config['label_transform']['t_type']) + if self.config['base']['is_gray']: + img = img.convert('L') + img = PILImageToCV(img,self.config['base']['is_gray']) + if self.lmdb_type == 'train': + try: + bg_index = np.random.randint(0, len(self.bg_img)) + bg_img = PILImageToCV(get_img(self.bg_img[bg_index]),self.config['base']['is_gray']) + img = transform_img_shape(img, self.config['base']['img_shape']) + img = DataAugment(img, bg_img, self.config['base']['img_shape']) + img = transform_img_shape(img, self.config['base']['img_shape']) + except IOError: + print('Corrupted image for %d' % index) + return self[index + 1] + elif self.lmdb_type == 'val': + img = transform_img_shape(img, self.config['base']['img_shape']) + img = CVImageToPIL(img,self.config['base']['is_gray']) + img = transforms.ToTensor()(img) + img.sub_(0.5).div_(0.5) + return (img, label) + +class alignCollate(object): + def __init__(self,): + pass + def __call__(self, batch): + images, labels = zip(*batch) + images = torch.stack(images,0) + return images,labels + + \ No newline at end of file diff --git a/ptocr/dataloader/RecLoad/CRNNProcess1.py b/ptocr/dataloader/RecLoad/CRNNProcess1.py new file mode 100644 index 0000000..de9c6ec --- /dev/null +++ b/ptocr/dataloader/RecLoad/CRNNProcess1.py @@ -0,0 +1,244 @@ +#-*- coding:utf-8 _*- +""" +@author:fxw +@file: CRNNProcess.py +@time: 2021/03/23 +""" + +import lmdb +import torch +import six,re,glob,os +import numpy as np +from PIL import Image +from PIL import ImageFile +ImageFile.LOAD_TRUNCATED_IMAGES = True +from torch.utils.data import Dataset +import torchvision.transforms as transforms +from ptocr.dataloader.RecLoad.DataAgument import transform_img_shape,transform_image_one +from ptocr.utils.util_function import create_module + + +def get_img(path,is_gray = False): + try: + img = Image.open(path).convert('RGB') + except: + print(path) + img = np.zeros(3,320,32) + img = Image.fromarray(img).convert('RGB') + if(is_gray): + img = img.convert('L') + return img + +class alignCollate(object): + def __init__(self, ): + pass + def __call__(self, batch): + images, labels = zip(*batch) + images = torch.stack(images, 0) + return images, labels + +class CRNNProcessTrainLmdb(Dataset): + def __init__(self, config,lmdb_path): + self.env = lmdb.open( + lmdb_path, + max_readers=config['trainload']['num_workers'], + readonly=True, + lock=False, + readahead=False, + meminit=False) + + with self.env.begin(write=False) as txn: + nSamples = int(txn.get('num-samples'.encode('utf-8'))) + self.nSamples = nSamples + + self.config = config + self.transform_label = create_module(config['transform']['function']) + self.bg_img = [] + for path in glob.glob(os.path.join(config['trainload']['bg_path'],'*')): + self.bg_img.append(path) + + + def __len__(self): + return self.nSamples + + def __getitem__(self, index): + assert index <= len(self), 'index range error' + index += 1 + with self.env.begin(write=False) as txn: + + img_key = 'image-%09d' % index + imgbuf = txn.get(img_key.encode('utf-8')) + buf = six.BytesIO() + buf.write(imgbuf) + buf.seek(0) + + try: + img = Image.open(buf).convert('RGB') + except IOError: + print('IO image for %d' % index) + return self[index + 1] + + label_key = 'label-%09d' % index + label = txn.get(label_key.encode('utf-8')) + + if self.config['base']['is_gray']: + img = img.convert('L') + + img = np.array(img) + if isinstance(label,bytes): + label = label.decode() + + label = self.transform_label(label,char_type=self.config['transform']['char_type'],t_type=self.config['transform']['t_type']) + + + try: + bg_index = np.random.randint(0,len(self.bg_img)) + bg_img = np.array(get_img(self.bg_img[bg_index])) + img = transform_img_shape(img,self.config['base']['img_shape']) + img = transform_image_one(img,bg_img,self.config['base']['img_shape']) + img = transform_img_shape(img,self.config['base']['img_shape']) + except: + print('Corrupted image for %d' % index) + img = np.zeros((32,100)).astype(np.uint8) + + img = Image.fromarray(img) + img = transforms.ToTensor()(img) + img.sub_(0.5).div_(0.5) + + return (img, label) + +def GetDataLoad(config,data_type='train'): + if data_type == 'train': + lmdb_path_list = config['trainload']['train_file'] + ratio = config['trainload']['batch_ratio'] + elif data_type == 'val': + lmdb_path_list = config['valload']['val_file'] + + num = len(lmdb_path_list) + train_data_loaders = [] + sum_num = 0 + for i in range(num): + if data_type=='train': + if i==num-1: + batch_size = config['trainload']['batch_size'] - sum_num + else: + batch_size = int(config['trainload']['batch_size']*ratio[i])//2*2 + sum_num+=batch_size + dataset = CRNNProcessTrainLmdb(config, lmdb_path_list[i]) + num_workers = config['trainload']['num_workers'] + shuffle = True + elif data_type == 'val': + batch_size = 1 + num_workers = config['valload']['num_workers'] + dataset = CRNNProcessTrainLmdb(config, lmdb_path_list[i]) + shuffle = False + + train_data_loader = torch.utils.data.DataLoader( + dataset, + batch_size=batch_size, + shuffle=shuffle, + num_workers=num_workers, + collate_fn=alignCollate(), + drop_last=True, + pin_memory=True) + train_data_loaders.append(train_data_loader) + return train_data_loaders + +def GetValDataLoad(config): + + val_dir = config['valload']['dir'] + root= config['valload']['root'] + + num = len(val_dir) + data_loaders = [] + sum_num = 0 + for i in range(num): + + batch_size = 1 + num_workers = config['valload']['num_workers'] + config['valload']['test_file'] = os.path.join(root,val_dir[i],'val_train.txt') + dataset = CRNNProcessTest(config) + shuffle = False + + data_loader = torch.utils.data.DataLoader( + dataset, + batch_size=batch_size, + shuffle=shuffle, + num_workers=num_workers, + collate_fn=alignCollate(), + drop_last=True, + pin_memory=True) + data_loaders.append(data_loader) + return data_loaders + +class CRNNProcessTest(Dataset): + def __init__(self, config): + super(CRNNProcessTest,self).__init__() + with open(config['valload']['test_file'],'r',encoding='utf-8') as fid: + self.label_list = [] + self.image_list = [] + + for line in fid.readlines(): + line = line.strip('\n').replace('\ufeff','').split('\t') + self.label_list.append(line[1]) + self.image_list.append(line[0]) + + self.config = config + + def __len__(self): + return len(self.label_list) + + def __getitem__(self, index): + img = get_img(self.image_list[index],is_gray=self.config['base']['is_gray']) + img = transform_img_shape(img,self.config['base']['img_shape']) + img = Image.fromarray(img) + img = transforms.ToTensor()(img) + img.sub_(0.5).div_(0.5) + label = self.label_list[index] + return (img,label) + + +# if __name__ == "__main__": + +# config = {} +# config['base'] = {} +# config['base']['img_shape'] = [32,100] +# config['trainload'] = {} +# config['valload'] = {} +# config['trainload']['bg_path']='./bg_img/' +# config['base']['is_gray'] = True +# config['trainload']['train_file'] =['/src/notebooks/MyworkData/EnglishCrnnData/train_lmdb/SynthText/','/src/notebooks/MyworkData/EnglishCrnnData/train_lmdb/MJSynth'] + +# config['valload']['val_file'] = ['D:\BaiduNetdiskDownload\data\evaluation\CUTE80', +# 'D:\BaiduNetdiskDownload\data\evaluation\IC03_860', +# 'D:\BaiduNetdiskDownload\data\evaluation\IC03_867'] +# config['trainload']['num_workers'] = 0 +# config['valload']['num_workers'] = 0 +# config['trainload']['batch_size'] = 128 +# config['trainload']['batch_ratio'] = [0.33, 0.33, 0.33] +# config['transform']={} + +# config['transform']['char_type'] = 'En' +# config['transform']['t_type']='lower' +# config['transform']['function'] = 'ptocr.dataloader.RecLoad.DataAgument,transform_label' +# import time +# train_data_loaders = GetDataLoad(config,data_type='train') +# # import pdb +# # pdb.set_trace() + +# # t = time.time() +# # for idx,data1 in enumerate(train_data_loaders[0]): +# # pass +# # print(time.time()-t) + +# data1 = enumerate(train_data_loaders[0]) +# for i in range(100): +# try: +# t = time.time() +# index,(data,label) = next(data1) +# print(time.time()-t) +# print(label) +# except: +# print('end') + + \ No newline at end of file diff --git a/ptocr/dataloader/RecLoad/DataAgument.py b/ptocr/dataloader/RecLoad/DataAgument.py new file mode 100644 index 0000000..c9af073 --- /dev/null +++ b/ptocr/dataloader/RecLoad/DataAgument.py @@ -0,0 +1,390 @@ +# -*- coding:utf-8 _*- +""" +@author:fxw +@file: DataAgument.py +@time: 2019/06/06 +""" +import cv2,re +import numpy as np +from skimage.util import random_noise +import os +from math import floor +from PIL import Image +import numpy as np +import random +import cv2 + + +def Add_Padding(image, top, bottom, left, right, color): + padded_image = cv2.copyMakeBorder(image, top, bottom, + left, right, cv2.BORDER_CONSTANT, value=color) + return padded_image + + +def cvtColor(img): + """ + cvtColor + """ + hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) + delta = 0.001 * random.random() * (1 if random.random() > 0.5000001 else -1) + hsv[:, :, 2] = hsv[:, :, 2] * (1 + delta) + new_img = cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR) + return new_img + + +class Operation(object): + def __init__(self, probability): + self.probability = probability + + def __str__(self): + return self.__class__.__name__ + + def perform_operation(self, images): + raise RuntimeError("Illegal call to base class.") + + +class Distort(Operation): + def __init__(self, probability, grid_width, grid_height, magnitude): + Operation.__init__(self, probability) + self.grid_width = grid_width + self.grid_height = grid_height + self.magnitude = abs(magnitude) + # TODO: Implement non-random magnitude. + self.randomise_magnitude = True + + def perform_operation(self, images): + w, h = images[0].size + + horizontal_tiles = self.grid_width + vertical_tiles = self.grid_height + + width_of_square = int(floor(w / float(horizontal_tiles))) + height_of_square = int(floor(h / float(vertical_tiles))) + + width_of_last_square = w - (width_of_square * (horizontal_tiles - 1)) + height_of_last_square = h - (height_of_square * (vertical_tiles - 1)) + + dimensions = [] + + for vertical_tile in range(vertical_tiles): + for horizontal_tile in range(horizontal_tiles): + if vertical_tile == (vertical_tiles - 1) and horizontal_tile == (horizontal_tiles - 1): + dimensions.append([horizontal_tile * width_of_square, + vertical_tile * height_of_square, + width_of_last_square + (horizontal_tile * width_of_square), + height_of_last_square + (height_of_square * vertical_tile)]) + elif vertical_tile == (vertical_tiles - 1): + dimensions.append([horizontal_tile * width_of_square, + vertical_tile * height_of_square, + width_of_square + (horizontal_tile * width_of_square), + height_of_last_square + (height_of_square * vertical_tile)]) + elif horizontal_tile == (horizontal_tiles - 1): + dimensions.append([horizontal_tile * width_of_square, + vertical_tile * height_of_square, + width_of_last_square + (horizontal_tile * width_of_square), + height_of_square + (height_of_square * vertical_tile)]) + else: + dimensions.append([horizontal_tile * width_of_square, + vertical_tile * height_of_square, + width_of_square + (horizontal_tile * width_of_square), + height_of_square + (height_of_square * vertical_tile)]) + + # For loop that generates polygons could be rewritten, but maybe harder to read? + # polygons = [x1,y1, x1,y2, x2,y2, x2,y1 for x1,y1, x2,y2 in dimensions] + + # last_column = [(horizontal_tiles - 1) + horizontal_tiles * i for i in range(vertical_tiles)] + last_column = [] + for i in range(vertical_tiles): + last_column.append((horizontal_tiles - 1) + horizontal_tiles * i) + + last_row = range((horizontal_tiles * vertical_tiles) - horizontal_tiles, horizontal_tiles * vertical_tiles) + + polygons = [] + for x1, y1, x2, y2 in dimensions: + polygons.append([x1, y1, x1, y2, x2, y2, x2, y1]) + + polygon_indices = [] + for i in range((vertical_tiles * horizontal_tiles) - 1): + if i not in last_row and i not in last_column: + polygon_indices.append([i, i + 1, i + horizontal_tiles, i + 1 + horizontal_tiles]) + + for a, b, c, d in polygon_indices: + dx = random.randint(-self.magnitude, self.magnitude) + dy = random.randint(-self.magnitude, self.magnitude) + + x1, y1, x2, y2, x3, y3, x4, y4 = polygons[a] + polygons[a] = [x1, y1,x2, y2,x3 + dx, y3 + dy,x4, y4] + + x1, y1, x2, y2, x3, y3, x4, y4 = polygons[b] + polygons[b] = [x1, y1, x2 + dx, y2 + dy,x3, y3,x4, y4] + + x1, y1, x2, y2, x3, y3, x4, y4 = polygons[c] + polygons[c] = [x1, y1,x2, y2,x3, y3,x4 + dx, y4 + dy] + + x1, y1, x2, y2, x3, y3, x4, y4 = polygons[d] + polygons[d] = [x1 + dx, y1 + dy,x2, y2,x3, y3,x4, y4] + + generated_mesh = [] + for i in range(len(dimensions)): + generated_mesh.append([dimensions[i], polygons[i]]) + + def do(image): + + return image.transform(image.size, Image.MESH, generated_mesh, resample=Image.BICUBIC) + + augmented_images = [] + + for image in images: + augmented_images.append(do(image)) + + return augmented_images + + +def GetRandomDistortImage(images): + # images need use Image.open() + width = np.random.randint(2, 4) + height = np.random.randint(2, 4) + mag = np.random.randint(2, 5) + d = Distort(probability=1, grid_width=width, grid_height=height, magnitude=mag) + tansformed_images = d.perform_operation(images) + tansformed_images = [np.array(item) for item in tansformed_images] + return tansformed_images + + +def random_dilute(image, sele_value=50, set_value=20, num_ratio=[0.1, 0.2]): + index = np.where(image < (image.min() + sele_value)) + tag = [] + for i in range(len(index[0])): + tag.append([index[0][i], index[1][i]]) + np.random.shuffle(tag) + tag = np.array(tag) + total_num = len(tag[:, 0]) + num = int(total_num * np.random.choice(num_ratio, 1)[0]) + tag1 = tag[:num, 0] + tag2 = tag[:num, 1] + index = (tag1, tag2) + start = image.min() + sele_value + set_value + if (start >= 230): + start = start - 50 + ra_value = min(np.random.randint(min(225,start), 230), 230) + image[index] = ra_value + return image + + +def RandomAddLine(image): + a = np.random.randint(0, 15) + line_color = (a, a, a) + thickness = np.random.randint(1, 3) + if (np.random.randint(0, 10) > 7): + tag1 = np.random.randint(1, 10) + tag2 = np.random.randint(1, 5) + image = cv2.line(image, + (tag1, image.shape[0] - tag2), + (image.shape[1] - tag1, image.shape[0] - tag2), + color=line_color, + thickness=thickness, + lineType=cv2.LINE_AA) + if (np.random.randint(0, 10) > 7): + tag1 = np.random.randint(1, 10) + tag2 = np.random.randint(1, 5) + image = cv2.line(image, + (tag1, tag2), + (image.shape[1] - tag1, tag2), + color=line_color, + thickness=thickness, + lineType=cv2.LINE_AA) + if (np.random.randint(0, 10) > 7): + tag1 = np.random.randint(1, 5) + tag2 = np.random.randint(0, 4) + image = cv2.line(image, + (tag1, tag2), + (tag1, image.shape[0] - tag2), + color=line_color, + thickness=thickness, + lineType=cv2.LINE_AA) + if (np.random.randint(0, 10) > 7): + tag1 = np.random.randint(1, 5) + tag2 = np.random.randint(0, 4) + image = cv2.line(image, + (image.shape[1] - tag1, tag2), + (image.shape[1] - tag1, image.shape[0] - tag2), + color=line_color, + thickness=thickness, + lineType=cv2.LINE_AA) + return image + + +class DataAugmentatonMore(): + def __init__(self, image): + self.image = image + + def motion_blur(self, degree=5, angle=180): + # degree建议:2 - 5 + # angle建议:0 - 360 + # 都为整数 + image = np.array(self.image) + # 这里生成任意角度的运动模糊kernel的矩阵, degree越大,模糊程度越高 + M = cv2.getRotationMatrix2D((degree / 2, degree / 2), angle, 1) + motion_blur_kernel = np.diag(np.ones(degree)) + motion_blur_kernel = cv2.warpAffine(motion_blur_kernel, M, (degree, degree)) + + motion_blur_kernel = motion_blur_kernel / degree + blurred = cv2.filter2D(image, -1, motion_blur_kernel) + + # convert to uint8 + cv2.normalize(blurred, blurred, 0, 255, cv2.NORM_MINMAX) + blurred_image = np.array(blurred, dtype=np.uint8) + return blurred_image + + def gaussian_blur(self, k_size=7, sigmaX=0, sigmaY=0): + # k_size越大越模糊,且为奇数,建议[1,3,5,7,9] + blurred_image = cv2.GaussianBlur(self.image, ksize=(k_size, k_size), + sigmaX=sigmaX, sigmaY=sigmaY) + return blurred_image + + def Contrast_and_Brightness(self, alpha, beta=0): + # alpha:调节亮度,越小越暗,越大越亮,等于1为原始亮度 + # 建议使用0.6-1.3 + blank = np.zeros(self.image.shape, self.image.dtype) + # dst = alpha * img + beta * blank + brighted_image = cv2.addWeighted(self.image, alpha, blank, 1 - alpha, beta) + return brighted_image + + def Add_Padding(self, top, bottom, left, right, color): + padded_image = cv2.copyMakeBorder(self.image, top, bottom, + left, right, cv2.BORDER_CONSTANT, value=color) + return padded_image + + def Add_gaussian_noise(self, mode='gaussian'): + ##mode : 'gaussian' ,'salt' , 'pepper ' + noise_image = random_noise(self.image, mode=mode) + return noise_image + + def Perspective(self, ratio=30, type='top'): + h, w = self.image.shape[:2] + pts1 = np.float32([[0, 0], [0, h - 1], [w - 1, h - 1], [w - 1, 0]]) + if type == 'top': + pts2 = np.float32([[ratio, ratio], [0, h], [w, h], [w - ratio, ratio]]) + M = cv2.getPerspectiveTransform(pts1, pts2) + dst = cv2.warpPerspective(self.image, M, (w, h)) + dst = dst[ratio:, :] + elif (type == 'left'): + pts2 = np.float32([[ratio, ratio // 2], [ratio, h - ratio // 2], [w, h], [w, 0]]) + M = cv2.getPerspectiveTransform(pts1, pts2) + dst = cv2.warpPerspective(self.image, M, (w, h)) + dst = dst[:, ratio:] + elif (type == 'right'): + pts2 = np.float32([[0, 0], [0, h], [w - ratio, h - ratio // 2], [w - ratio, ratio // 2]]) + M = cv2.getPerspectiveTransform(pts1, pts2) + dst = cv2.warpPerspective(self.image, M, (w - ratio, h)) + else: + pts2 = np.float32([[0, 0], [0 + ratio, h - ratio], [w - ratio, h - ratio], [w, 0]]) + M = cv2.getPerspectiveTransform(pts1, pts2) + dst = cv2.warpPerspective(self.image, M, (w, h - ratio)) + return dst + + def resize_blur(self, ratio): + image = cv2.resize(self.image, dsize=None, fy=ratio, fx=ratio) + new_width = np.round((self.image.shape[0] / image.shape[0]) * image.shape[1]).astype(np.int) + image = cv2.resize(image, dsize=(new_width, self.image.shape[0])) + return image + + +def transform_img_shape(img, img_shape): + img = np.array(img) + H, W = img_shape + h, w = img.shape[:2] + new_w = int((float(H)/h) * w) + if (new_w > W): + img = cv2.resize(img, (W, H)) + else: + img = cv2.resize(img, (new_w, H)) + img = Add_Padding(img, 0, 0, 0, W - new_w, color=(0, 0, 0)) + return img + +def random_crop(image): + h, w= image.shape[:2] + top_min = 1 + top_max = h//5 + top_crop = int(random.randint(top_min, top_max)) + top_crop = min(top_crop, h - 1) + crop_img = image.copy() + ratio = random.randint(0, 1) + if ratio: + crop_img = crop_img[top_crop:h, :] + else: + crop_img = crop_img[0:h - top_crop, :] + return crop_img + +def get_background_Amg(img,bg_img,img_shape=[32,200]): + H,W = img_shape + x = np.random.randint(0, img.shape[0] - H + 1) + y = np.random.randint(0, img.shape[1] - W + 1) + img_crop = bg_img[x:x + H, y:y + W] + ratio = np.random.randint(1,4)/10.0+np.random.randint(0,10)/100.0 +# print(img.shape,img_crop.shape) + img = np.array(Image.fromarray(img).convert('RGB')) + dst = cv2.addWeighted(img, 1-ratio, img_crop, ratio, 2) + dst = np.array(Image.fromarray(dst).convert('L')) + return dst + +def DataAugment(image,bg_img,img_shape): + image = np.array(image) + if (np.random.choice([True, False], 1)[0]): + dataAu = DataAugmentatonMore(image) + index = np.random.randint(0,11) + if (index == 0): + degree = np.random.randint(2, 6) + angle = np.random.randint(0, 360) + image = dataAu.motion_blur(degree, angle) + elif (index == 1): + id = np.random.randint(0, 4) + k_size = [1, 3, 5, 7,9] + image = dataAu.gaussian_blur(k_size[id]) + elif (index == 2): + alpha = np.random.uniform(0.6, 1.3) + image = dataAu.Contrast_and_Brightness(alpha) + elif (index == 3): + types = ['top', 'botttom'] + id = np.random.randint(0, 2) + ratio = np.random.randint(0, image.shape[0]//3) + image = dataAu.Perspective(ratio, types[id]) + elif (index == 4): + ratio = np.random.uniform(0.35, 0.5) + image = dataAu.resize_blur(ratio) + elif(index==5): + image = random_dilute(image) + elif(index==6): + image = Image.fromarray(image) + image = GetRandomDistortImage([image])[0] + elif(index==7): + image = Image.fromarray(image).convert('RGB') + image = np.array(image) + image = cvtColor(image) + image = Image.fromarray(image).convert('L') + image = np.array(image) + elif(index==8): + image = RandomAddLine(image) + elif(index==9): + image = random_crop(image) + elif(index==10): + image = get_background_Amg(image,bg_img,img_shape) + del dataAu,bg_img + + return image + + +def transform_label(label,char_type='En',t_type = 'lower'): + if char_type == 'En': + if t_type == 'lower': + label = ''.join(re.findall('[0-9a-zA-Z]+', label)).lower() + elif t_type == 'upper': + label = ''.join(re.findall('[0-9a-zA-Z]+', label)).upper() + else: + label = ''.join(re.findall('[0-9a-zA-Z]+', label)) + elif(char_type=='Ch'): + return label + return label + + + diff --git a/ptocr/dataloader/RecLoad/__init__.py b/ptocr/dataloader/RecLoad/__init__.py new file mode 100644 index 0000000..8529416 --- /dev/null +++ b/ptocr/dataloader/RecLoad/__init__.py @@ -0,0 +1,6 @@ +#-*- coding:utf-8 _*- +""" +@author:fxw +@file: __init__.py.py +@time: 2020/08/11 +""" diff --git a/ptocr/dataloader/RecLoad/__pycache__/CRNNProcess.cpython-36.pyc b/ptocr/dataloader/RecLoad/__pycache__/CRNNProcess.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2328be61316a9e1c83e08627608e804519128601 GIT binary patch literal 3613 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 literal 0 HcmV?d00001 diff --git a/ptocr/dataloader/RecLoad/__pycache__/CRNNProcess1.cpython-36.pyc b/ptocr/dataloader/RecLoad/__pycache__/CRNNProcess1.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9fb131a06a79d3219c28e8df2949586f85682f2b GIT binary patch literal 5858 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 literal 0 HcmV?d00001 diff --git a/ptocr/dataloader/RecLoad/__pycache__/DataAgument.cpython-36.pyc b/ptocr/dataloader/RecLoad/__pycache__/DataAgument.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..df27415d2d682b9597804729da84c12d34093533 GIT binary patch literal 11013 zcma)CTZ|-Ed9Hg`chB_B&i3wHc5RQ1ZMMOz*JI-V*?6zUyRbW6)(#65JMEf2H9bAk z(>;5tW_NZ{4TQyD9;_G?B>_cBLL?+m6e5HoA)!3v0fCgfcpCD64U?@{r zZ8I?CX$6)%?ZCm)Zo6w<;F)=!LQp`P)ArYjK~dV=c4@5~l>6^<7^^)NjL8@r(^G}d z7{R#mRT0n13x+DG@@EWHjtWTs(_hRGk}@7X9Y*qtU8X?W9o!@0M9A)pn3>UrkYa^18Q15qTYe_8Ff-U zuHK23S!KOs)M{JrEu9NDlI5=cz|!WO(z&HpJNm%fr7#K4H#gRzPI7AfZs}aoT0`sn z>8IZR&j~($VAQh1=BE=6P zONS19P2X}9W?Tx5rXLjTEd{$|6*Y92nSECDEtsq)nvpH$d=yBlJTCvQZJt0+CW8zEWwyi)b+7^Br`PI5=G zO|xW9n40y5iHtu|nTsUbUPmF{Hn)4v>Vei0d&RzIRE_+ptW`&!RCdQwUe)MXDd<7O$Ue zzW3k0@m|CD!P#xDcwyT@zWsCjYOdyWeq(7hbqAfBB`4W?p{qgJv|% z<5kVc?ABT{u$SZ8nX9695(b4%bO)Gj3YPQiFoxF0kWC}SC1i$EG3U(FX2q2D2Ac*y zF6tao{3x=>06Q5udxD^CWh)0yN4d(w(^Um*+RutFu17jdTHQ{A;QaB)Bxk`nL3~CX zybl1A0fR1}sp8559tJ6yAbuK8D@y)Ht;f$vSuZLtn zorr8P4-S*sMq5}*X!@ZyTQzdM`xC| z2k&_dFbQPXJ@eB*AttvLwvJzEx1(m*o@<2d_FU4PTMXl9PF8aYC>gu4(IN9cuQj;Z zk|kxHuuZ&oX@`OFrLJD9cL%6{*T^D|?<{g(zlEBoA4cD-RIjgf)kYip&&KNYTN`0J z$A*3guk|C$9%Dv^9m#~APN3}_NWzT1>D#`o*>*53@y-2d3CQuX!lhQ6bam1o2lL0L zjU>nT1PVDfr{+D&G$6=~I)#~0w-YCI?vc76(vW+7Y(qrJiexugsi|A4ey639<*Y2_ zF+2c8NL_=9?}Y4l2OnVU}H5$clh>Q8#*%^#zNaZ&qGZ!9h6 z{Z}phy>u)eOUk8{0_u>L=utrK=d~##En<#x)tEAx7{xSheQIZnw@huNdm+8IfgH{V z$TRyOcjheYoAds&?CX2exTS9zz{U9H$ss0A^eX8%_9)m(owULoaX)XFx6+#iag@5) zw=<2z5@i1&j4`o!IxPWrCtq=ThkKK~BfV-mfpHF}lStL{h@{D;-yiEcGV{^R>zp}t zss>L-uzvrM5hdozi@QUnc^$`pnrta)}{%kgv~JvL*!;t$r}JJEZf_h9cK*#p;qBAvyo52VMD zKbSt$^a*jsKtA<0aD=G}%n85_;HCs_`fcDiANv#jA;Qai(*iehA2{}%l)3Wt?#wrH z#(2xR^+96&^Ty^AiI|5(Mjn5gd?5Ozs-W*Q) zVc?fg)!rlN!+@GgAK^Hl5c}3wZyM>t5NGa!rUueSknPe-RP$_%sf%$GOlD%O*l!rf zVgA19h^l}#@O2c2YwPW(=4QnNQYogqhoWq4MVh8g=3IOJrOR32!j+4^jszV>n-7qc zX4IiRhRwAW#ml*fqSVeT)xC#juFI^g#|A=apOQFrrrm{-ik~~By0tM)iKd5w4^~Gf z(UezWfrd2r!VXM9FI-=bI!beMS%HF-9VsR=yB&3E@5sd{6?taf$;{~or?$-~A%&vEr>M=BwjsPNpd!(g0>;k=>4Ei12={M4xl@v5=Cx)#O z2jjcu3J&dAOi&g;Sg{*xoy=?nzSMR1PT;S1+jpB7BeOQ2%B;IcoAY?i2Z#D^V6>=K zBZ@OK%*@5iY-FYa{x^~C2G-`&fpr&Y^BFv!(N6;-!9=*xq!TbOm18+iIX#^1Ji2I6 zz0}FSf)w9IW|$QVD#ogsRcpeWupHzSa~jq$)Q&lc^1hOlw@iBu-kz|ISq}QmAwOnT z&6CzK)F!QIwCtqErOyd#%y9r+wYFyNOF0WrmNYN)thFEs2xpatbLXO~aD# zHCu*ULylp(h!lSm8O?wdaTsaT@2XkDnoW~D0n_F_k8e8 zQWreuFX$>p-{vZ|X?qCs)|}iX9n!u~^Kfz0;RJXIDW-|t1Vg`C$(IF7#r%SG3k;o~ zp_N&k^~?>m4r%dPr%q>1D~Za+z|h-kNYch zzjhdP2MQi4lkjEXG>+fTU9%`G7>=aIn1Q2Kdv3JxcIL9;`JJLaFrkfR5# z<97fYSCAQ21!Pr$b5=sGKZ2HR((X2~+x+sE`1|(Re&PFP8$=&}<48oioU;Sz**Ev- zyul=zGFEw34g|2f`K%Z{kd`xO6A}IhJXv=Iij-}shXe(lmvT{l>j@}tW5t8OhRI{H z?6U-;3Tj*#%7Ei%E&&E@g#^Zo^e&~gg7e5xq3TVTQs|rdgTB4uux=5ySArQ-TC#G3 zEnhbs$X+=uD|^+||BNS0hh8}$t$HhXDmN`-22d;$Os_F`>JHQ#od$12k!=46=+QTj zZT|~0h-IE~=H6M3G~DpDR!2X}9`9#H0nwMk@$$yfQajQgX45msGOw}Rg=`YqsEv!5 z2}*FlqPoNYf?^ycgJNf6tuDSD{QZuKHsSAsR(=h`1jVFFxs4u~&CJ1@rzD@x+&Bq! z68JjI2iimA**LWWLK&*n-bkV^W4M?OhXEl(LG%bLX9yZ|7IZXW`jULqd{e)MUPA_? zWT1;DRP&d8s@aha;C}oq*}Z{efJ6|(*-g0t zbI(|Tivg-~r;Q~zAgCckAg18%H*ib<<0=15ueT$w26O%UaP+sE%iS{r>u)`>S%_(&fJN}&@dayeX&vclCX3(wcjpNAx* zxuh>33rg%!mvEJi*!n87-(v0QqzDh_54q8Wwp+g$da2ERk&XSsjw0ST)BC)sJ4hfTP=UP`s2gf&fG<#w?x&4{PEp}{| z9D`!H&^gYy2dXr~+&XcW+ecU3~#Jjz-wu5&3Ylqa-#qzSDKG0NcCwJ_1G z&G}22qgr7zbGjh$taK;T>*tr2NWwk>#z~~nqu7Bj5Vqk8WaWhyZ(OTiyRvZY{KqnP z0~YuDYJNXD!Gp#V5*W?l;>*SegpzS!Q-_0>psqIOVVMMo+d?-%lL?OyUP zkAVIYhKnbW8P1qRK|N{tX2q_ewDr(ICp!YG6~F{lLj!=ye|WeI7!3|LKPC!4x<@qQ zfO3Sa?Z927Gn9F8tGO0_tpEHt#Dt)B?1gY6j$2{p0=LG^_v1L3w^|o3>bJOvl-XTo zds)5NhZjWs2YTd@XBapobL-)QcDM^-NVdCnb{gXhcDBOk4%|M7XGCy7pr~QD*k@`Y zzF^&Rhr7+K(OkY|E`E11oRu-ls0YC)4z%pr-2=q%Q^Te-E)>SK%LQz55_6+z0mH>xnsxIKT(?j#HKk z3LA#i?h6V{G{PR|Hnr#k;UdZe$bE^=Q6}j!Ge%!EO-j)iTGYz!6Qas`8l^UEKEKy!zQ=JwX(2c5!xpzR@&hNMXp5|^_241a^C#U%>Dg?~m!vTqJasMMC6fy4czNTW057n8jVSVE4=p=27M>*9I0M{$~;V+?jK@ zx?f_xe5QQXe7??iFq z4f_r-IHIL+%DIiDg6AygbmYC>%>H z3kqt4&|&ZQpRhAnf(Jm)aD~=FLF^FPBot#qh$V2tP!_mQT(AqMe*$X>p`fMBome5_ zJYT>fpua_!dk6tC+9$^%C*tbv1_Eq6B1GF_!7N=m$VFl9H&K@Eun=$&C|>?P)aIj# zR(p`Br3fWzPr+zC7!;FW$t(H3TXIWNns0|kOz}npiirCJ z3i%P^sBpx?I~EF;fyi?PH!ZkTa&XLy^5dojrl1X1@kXwxalml-A+n6l$$pDBXh{kw z;`#hTFK^nwjj~NwP+T3vh8YSO;E5g$&?rnf%s>D!9mN#FO#OFo ze1J(ewWokHIyAgJdOZ(VG0a7{JOBl1AB@95i1Lo(_N(Wk&B+F7cK~sWaiac zX|GQ&{2;+Bl3-R`(%m%)s%C{ox5JQrZX^CS-U3?K!&_|1iTys>^dB($5@F5F%)A@; zWB{I*mIGxdb1%0{Lig1d!^Ub;1aSTQTJ!7bN=bCVu;~!Qo+)86#?{9 zKoP|fhh~XH4_1JM#0a1>gNr-;KVT4w99!PIU)$X?LCZ{w;-et zcfwKAmcmG#&(cl<%vXjZF&0#SM~hFQ_!(5w<@GT9VLvTM?2}N}0R>w}e^;P1p1W39-3fOp=tF>!#Q$GF4upIG?ln7RhlKCmN5#Vp}1`0d=#Jmijj&k z(lh<}@DWzOCoZBnU+1sTr!0LgC*wKpI$uXyLE73$W#v#GqZ6so1j%tmz;XX$(&t-) zJ}ZY+m1APA!6{7kVf&+?$I;zAI?rL8V=~V7k|X{3--WhuY5T{d+HX6Cwkc`*adH$f zLs$ZcBp9EtKY_Wx0@DjGe{dvBAifH#XQic<%W03g2Eem*n^NXZw8ggR}aNILaS$019kjvf8l`%`(l_k29mj%&!WD z3OOrmbXvS}%r&!c-HuXzjR537{U^*m!t5)|DDEVBCDKhe0ih2OuYbx{O)PQYNXe@$dUSZG)PkdLl zVkDHTWMybQetFh2ZY}3di@v^Dxo z=l$V(w$8sNc=g?g0pS)_2iC=9+#EYA-Bu^Fa6wQ&2ub0)BLULi1SAd7cDNX|vtna8 z)OF%J^AdUP6@Z6!%mo3tORVRn0?aSL`!p^geW*75ZH{g2tgv=G!fLBZA%DZU|@I*#Bjg}WH|tFF$a)HVTfW#VGL%_WU4ada!4#K$;dCVN~}zQ`1q9! zMNB|5!Ne~$J^g}`{Ny72l*E$6oczR;)FS<$)MOtZOCQP9TO2mI`6;D2sdj82HvusN E064QRdjJ3c literal 0 HcmV?d00001 diff --git a/ptocr/dataloader/__init__.py b/ptocr/dataloader/__init__.py new file mode 100644 index 0000000..8529416 --- /dev/null +++ b/ptocr/dataloader/__init__.py @@ -0,0 +1,6 @@ +#-*- coding:utf-8 _*- +""" +@author:fxw +@file: __init__.py.py +@time: 2020/08/11 +""" diff --git a/ptocr/dataloader/__pycache__/__init__.cpython-36.pyc b/ptocr/dataloader/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..92fbd3727ee3d1f60046397eb8f023eecec974c5 GIT binary patch literal 178 zcmXr!<>fLBZA%DZU|@I*#Bjg}WH|tFF$a)HVTfW#VGL%_WU4ada!4#K$;dCVN~}zQ`1q9! xMNB|5!Ne~)J^g}`{Ny72l*E$6oczR;)FOQ(({6FtOi(!h-k5FTe9! z9wC2{xnY972{Y@05Tru{6)dKG);Bt))~!D4u&;ualPcURZ$ct_wRX@@8$!>W|*CMXf*hERdcs!3S&qS8ur*8^b^b zgXLbr!`&cBqBz?)xt5OdvlpL45^myIZ$CP9W0uRFIBgyVLiCcyr?&6(g1MLbOgYs~ zW#ZDdPnFY4hH(%^ZNILpY&49d#Mmj9^Ip=+Iae%6)FQ@rTLwusOtWZRC^H0us^1Ip zaJQYx0hriMqRfZUH{UB$LLO9Yub1y~RDD;OSrm6weJ&`fF&_n0pZjcOm)|Luz!Pbs za6o{!HO|90$TH4Xf7X7&Myg7is2$MgWw-ltQI$s2HORM!+>K-<147vK)Lh(f#n8fZR~=iyJ$ zr}A}N(BdjuoNg-j0x9sKbd!4UvtjOEIOb-x(YGW5SR%?>68X~dZOm2@3?j&u$2o*! z6r!sLnLg$2!W)~+1o+<$a0PQxdy5OaY8|1sIf?CeJn|I(TaL9Y^MQAW*05wyqz(q0yl1$1)uaGmSr8QY1D`C}k&5 zqXARF@?F?5gQY~oC`at^5c`5ROnV8mIM5|xRJo;LCy@x$$-3?h_SsN98=!aLDOI^_ z(rdb|@1Lu}`p@XZTD66D-2ic-pp!nE6!LLd=BLH{J=iU=2gvtP+yqe-^D~k81?FowuL&gSt0EF(eg~gkm1IZiEMOA|4G=i+#c0sq`Y|X4?UOP2!8|bFXC7EN zoCga6XB-AFvQTyzo(&HT0EW?4P7(z&%5pd@n0N+JCbn0A%7^eUMO)a!^GFWUILITQ z77|p^XK;83gE&%@EBdvfk7lIO?L@f%uwXEZqc35<{s2PHF?k!Vri~Z^LZ-6-IXL+=~idnATA5GH)lX!lsnm~>~D3a s){V8S^E#gx6N&Gcb8A|kmHS!Q-#^DpXUHxSPBk7|y1aC0sp(mN1BZ@$t^fc4 literal 0 HcmV?d00001 diff --git a/ptocr/model/__pycache__/__init__.cpython-36.pyc b/ptocr/model/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..52112162b6b114ad8a8ccf8cfd75c83a7462b424 GIT binary patch literal 235 zcmXr!<>fLBZA)lmU|@I*#Bjg_WH|tFu?UbzVTfW#VN7AlVa#QUVq#=SVGd@{WT`Uc za!4#K$;dCVN~1{Wt!2l2S~ zxO+Svcim2hE}wna6G9${UpY7h2t$!#LJr6d*}9F}mFdndZ9rgGvW6DWFalU z;9OzTz{(uekvH;&-gM|q=*o5K7C@{xz@6v@rhe(QUHEKggf=XRisQAs2+6{Vr;*M> utxkelgS%Clr|ZRrovq@UiieK#(_cM?(q# literal 0 HcmV?d00001 diff --git a/ptocr/model/architectures/__pycache__/__init__.cpython-36.pyc b/ptocr/model/architectures/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6b35176eb5e68e6df6c9802e5370925614cf48dc GIT binary patch literal 187 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 literal 0 HcmV?d00001 diff --git a/ptocr/model/architectures/__pycache__/stn_head.cpython-36.pyc b/ptocr/model/architectures/__pycache__/stn_head.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..62560ad3f977b5212a09d19b26515a8271c9f998 GIT binary patch literal 2972 zcmZ`*OK&7e5zfqdc6GbE$9Q38cUdeDEX~1Y#t0-{Gh%?@uw#vOWF>i>IZ*bv&kl>4~w#V#1mNGIjGV)OwUqs~I z#zyq3+j~Fn_Zj;uyHQ;54^Zm`h-8u%Y|h7=6E6xe7PfW9u5G=sXIp>lqjd^@9*hI4 z>lWd>Gmb3pf$xsH;JY%A;RPS}9x>UG(FK!H7R)wQ&bTkdQ#R^dVplfeW{@1zRk74r zoX;1P()*B!vILDL%QDq@RVD>`ZeHg4l4DZp&{Px;H9&ocT7L~hvl(w#!zGsjpYwrt z?m#9QqE5qC9IY!|>3zUg&biyTIEjDu=HAKPc3PD$=p5K@`#9H!+lxfXyqsRrZpsH4 zIi`D*sWL0#IzP*dUu%`i%mi!AXE9WsI3+R-33=>NT%ASY#a0+2SHO&6M#DiYh(&h^bFu@IYbs@-`dvtl<<>0z$3R4-Ll@7B7E53@w>EKbb^ErH*R?eq=It$QF$YzdbO9`e3W zuc4ok@Tw3(sMKEv$=H}n22@1vvkBd=bI}<)be9(l81ZF*){~*^p!MHpE*l59-L46q zKHL8hj$z!gDzo%zQECB6M%PsT14J_oP61zg*7aRI6f9<)sFw61dWfgy3zFsR#qsT|T^HV7Q06US+h)HTjaNLE`!-U4|Ywf-sy115b=p9_5UUa5T`H-dD?H$@NQXTvqn zcCKzBr8}iUSR)U9h+6*<QOi6|e~V3Rs^Vb=2>OgHDeS{5)E(yMc8-T+c@EvncpqdIRW1 zFLxnKw`;##@h2^2ynRK1&_<;x7%C5@|N7|fe|~)Mp!5L9ryS^Rm`SnP#oo z$?Gl1Opn4$dYF_5XtnWM+Zk6TIx#_s=#&((ap#FXgbkl7ZQO%Asf~A*sVY|Fy2hL2 zMG>nnfFHSPL?XZ4+3>mHWoz1(_I(=~-$1QzgTTJ9+Uxit%k?cC^0TcgJKn_9wJjse zBzsz0{t1laups#uf)ZH-As@DcFQVRNjYEMR5=4;|>JP z10Ox4LEuC8XM?7*;{#t#WE5Eds4tP0KTp&& zFUPOd$$WwRr!j>i#42P?WXy;HQ@qf1d}8|7{AsMKg}HU3e^6;%%}wuGL<)oJ-g>y( z3T&MFB;vk|)BFIneiMWtlJxnet(#~Gc>;CozoN%g?7BuAJu$8kH$w+DMt*|j$!dJX ze(Ii!bD-?pLtg-S$UGQZKRM2QaIO@MkK90W*vyj-5b`eNmg{wZDvX5idIvgw!zc7i z3iewk|IM$d{NcX(Cib_eP~Res$f%<}j|N_fSBZK=;x7^*OSFdMKWyhw03HRcqw*6I z9neFhea!IK@I%9o$6-F7#*-}3pSVh3qf*C-lJ8-WdJ}}<>V~*Fa^J}EXM-!(UF#xW z5kf~HrKd8PXK`$zIBsJE_+A`8UnWI6V#B|p7@^2)>c=2vW1T0`43TD?D=45~<*$&E z%^Q>e$x~{|o_NcAHtdH(n--yFpKb%%cCw_05muqRpzeYg54c>Ms_zi*)Tgx~u(VIN z&?0@OEEO4FJt0Cb&NgZKk8Omto7}i5I+{I`3BTR`<##Dk+xzN?5a}uerF7MEqTYW1 Dqyo*v literal 0 HcmV?d00001 diff --git a/ptocr/model/architectures/__pycache__/tps_spatial_transformer.cpython-36.pyc b/ptocr/model/architectures/__pycache__/tps_spatial_transformer.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e69719612b0692f57adb4d97ab5ca359f87233d2 GIT binary patch literal 3463 zcmZuzTaV;M6|VBV-90^*?cJTdB!Unw4bf~Oqy>qPEQ)}`E6GaxwWZqV}cCthz+OG4b+L6VJ zT)Wevs^YP)-J)(6N;|Y#dudWXNu>6gg@QH;&15`J&!s!wMmxcS-jT$m5F`4 z_doYf?zO5(#l3lx<<-4Jq>l@gr)nW`c~7-6mTjVnq>7bDYB_DhJQpYJbKzohh$e^V z7$5L}JJt<6N8HS%7U^;I&}gwSn2~Vu~E&tvGi39qu!G8FYT>bOGLPan|{8fIAW7 z06~sFr;nbY;ri#tV^;(awDY8Z4_wv2HrlS5nI4dJVal~bdeLBQ^R_8!C8J^5)Jil} zydG;l(fqRr<}bw4LgsN+OsCQ4jbSW*zeq&>#v-YrzQ|hv?(6^1! zW)83CahnL^CfacWGx7)><00<5cEI1`M^*suh+EL^d^5mc7vz7&pyR)_mK5Od&~4b> zgfaZmS;wq%R`$|`#)8B<8&>tUBk~u8_R@z2;of;O_%d@|S<7If@pqaYH1I*!gT^&$ zLgkiT7a}J5T^~O2CgKoZv+=~gKQ_(*T%F`uA&t9$0;_IyP!%--GzD6kI+xm&DoM|D zA09{(mDgwjq{FoLFt24J#shs+FXr(T5Bc*%%y3c^tcCd5PI?|)Hyf0IKb4HzT*R`- z@+eHVs%P-{R(>9h;Kok(&YA{D4aaXg*GRYG=83PO9m^0qMkeONE-bIZ%0R;_3;63V2VI|bWI){=222d-yv=BUeK(-# z13hL3PU?4|vBRF3qyA>sqc7{inY~#x#2Lq#*9C_8NcOQV%s6-!EAfw{3%cP>?$Mc) zi{b0x1=l>(=JxJqQ!1e;*6YpZAgE%hgLdCH@++d`bC$k*Le36T9 z(ucl7%l0B_b$l(s=a;w@6_i+qNSAdg3j=`dvz4&7BeHs#fM?>V=r-{fmHjVCFwbdoU7vH5F)~8=Yhr3l9H&(xWgK558F7NSAEU_+(J?TIN4GTqlQ_-@{cU~&_ig@;71{%S7aHOl zVjQgC2RwjwgzWR;?XM+_ldU&*)fbuHL;D0x{vBOsm28EibY{r|#s7$6p+xv24I$k? z_wG(-uMp7C+}~*c;H9mc(j}rPQ9PowprE%2KtwN?BhZ>~2hprYARD5X&MW%>1Y0a9#aH{FG%8K~S5#^Qoj0X&vo{gn#y?yE&nCOE2fH*k z=7KWvIfDN6_&|p>u!Xc$G_@Z7OiDx?#Geb%2veA>!-mqE_z892L#Ms`S)0@-3sE)9 z7nO+&<6J=nfq0*~4``taKhIlJtBrf&r=+KPF1jw8g-G)&=|qQ*5|ut)1MDMOH=HMJ zdx7!jaJO$8d7@A2E^5$LUQJ;BCdLyil*i~8f@MTuW8I|yvPNhFNH5;{`oP)jAKR13 z1SK?Rp-$#GXd#Sa6ts(qetlq>sWh}Vi0Um-1EJHW< zAxO$;fi)Z(&6rQfL2*1?P^FCBR5@xFr2=Z>JGAjf)S22NFqPv;U87{FqifsI$#hYt z n x 1 x 2 -> n x F x 2 + P_tile = P.unsqueeze(1).repeat(1, F, 1) + C_tile = torch.unsqueeze(C, 0) # 1 x F x 2 + P_diff = P_tile - C_tile # n x F x 2 + # rbf_norm: n x F + rbf_norm = torch.norm(P_diff, p=2, dim=2, keepdim=False) + + # rbf: n x F + rbf = torch.mul( + torch.square(rbf_norm), torch.log(rbf_norm + eps)) + P_hat = torch.cat( + [torch.ones( + (n, 1)), P, rbf],1) + return P_hat # n x F+3 + + def get_expand_tensor(self, batch_C_prime): + B, H, C = batch_C_prime.shape + batch_C_prime = batch_C_prime.reshape([B, H * C]) + batch_C_ex_part_tensor = self.fc(batch_C_prime) + batch_C_ex_part_tensor = batch_C_ex_part_tensor.reshape([-1, 3, 2]) + return batch_C_ex_part_tensor + + +class TPS(nn.Module): + def __init__(self, in_channels, num_fiducial, model_name): + super(TPS, self).__init__() + self.loc_net = LocalizationNetwork(in_channels, num_fiducial, + model_name) + self.grid_generator = GridGenerator(self.loc_net.out_channels, + num_fiducial) + self.out_channels = in_channels + + def forward(self, image): + batch_C_prime = self.loc_net(image) + batch_P_prime = self.grid_generator(batch_C_prime, image.shape[2:]) + batch_P_prime = batch_P_prime.reshape( + [-1, image.shape[2], image.shape[3], 2]) + batch_I_r = F.grid_sample(image, grid=batch_P_prime,align_corners=True) + return batch_I_r + diff --git a/ptocr/model/architectures/rec_model.py b/ptocr/model/architectures/rec_model.py new file mode 100644 index 0000000..9f5c2d4 --- /dev/null +++ b/ptocr/model/architectures/rec_model.py @@ -0,0 +1,125 @@ +# -*- coding:utf-8 _*- +""" +@author:fxw +@file: det_model.py +@time: 2020/08/07 +""" +import torch +import torch.nn as nn +import torch.nn.functional as F +from .. import create_module +import cv2 + + +class RecModel(nn.Module): + def __init__(self, config): + super(RecModel, self).__init__() + self.algorithm = config['base']['algorithm'] + + self.backbone = create_module(config['backbone']['function'])(config['base']['pretrained'],config['base']['is_gray']) + if self.algorithm=='CRNN': + self.head = create_module(config['head']['function'])( + use_attention=config['base']['use_attention'], + use_lstm=config['base']['use_lstm'], + time_step=config['base']['img_shape'][1]//4, + lstm_num=config['base']['lstm_num'], + inchannel=config['base']['inchannel'], + hiddenchannel=config['base']['hiddenchannel'], + classes=config['base']['classes']) + elif self.algorithm=='FC': + self.head = create_module(config['head']['function'])(in_channels=config['base']['in_channels'], + out_channels=config['base']['out_channels'], + max_length = config['base']['max_length'], + num_class = config['base']['num_class']) + + def forward(self, x): + x1 = self.backbone(x) + x1,feau = self.head(x1) + return x1,feau + +# class RecModel(nn.Module): +# def __init__(self, config): +# super(RecModel, self).__init__() +# self.algorithm = config['base']['algorithm'] +# if config['base']['is_gray']: +# in_planes = 1 +# else: +# in_planes = 3 + +# self.backbone = create_module(config['backbone']['function'])(config['base']['pretrained'],config['base']['is_gray']) +# self.head = create_module(config['head']['function'])( +# use_conv=config['base']['use_conv'], +# use_attention=config['base']['use_attention'], +# use_lstm=config['base']['use_lstm'], +# lstm_num=config['base']['lstm_num'], +# inchannel=config['base']['inchannel'], +# hiddenchannel=config['base']['hiddenchannel'], +# classes=config['base']['classes']) + +# self.stn_head = create_module(config['stn']['function'])(in_planes,config) + +# def forward(self, x): +# cv2.imwrite('stn_ori1.jpg',(x[0,0].cpu().detach().numpy()*0.5+0.5)*255) + +# x1= self.stn_head(x) +# cv2.imwrite('stn1.jpg',(x1[0,0].cpu().detach().numpy()*0.5+0.5)*255) + +# x1 = self.backbone(x1) +# x1 = self.head(x1) +# return x1 + +# class RecModel(nn.Module): +# def __init__(self, config): +# super(RecModel, self).__init__() +# self.algorithm = config['base']['algorithm'] +# self.tps_inputsize = config['stn']['tps_inputsize'] +# if config['base']['is_gray']: +# in_planes = 1 +# else: +# in_planes = 3 + +# self.backbone = create_module(config['backbone']['function'])(config['base']['pretrained'],config['base']['is_gray']) +# self.head = create_module(config['head']['function'])( +# use_conv=config['base']['use_conv'], +# use_attention=config['base']['use_attention'], +# use_lstm=config['base']['use_lstm'], +# lstm_num=config['base']['lstm_num'], +# inchannel=config['base']['inchannel'], +# hiddenchannel=config['base']['hiddenchannel'], +# classes=config['base']['classes']) + +# self.tps = create_module(config['stn']['t_function'])( output_image_size=tuple(config['stn']['tps_outputsize']), +# num_control_points=config['stn']['num_control_points'], +# margins=tuple(config['stn']['tps_margins'])) +# self.stn_head = create_module(config['stn']['function'])(in_planes=in_planes, +# num_ctrlpoints=config['stn']['num_control_points'], +# activation=config['stn']['stn_activation']) + +# def forward(self, x): +# cv2.imwrite('stn_ori.jpg',(x[0,0].cpu().detach().numpy()*0.5+0.5)*255) +# stn_input = F.interpolate(x, self.tps_inputsize, mode='bilinear', align_corners=True) +# stn_img_feat, ctrl_points = self.stn_head(stn_input) + +# x1, _ = self.tps(x, ctrl_points) + +# cv2.imwrite('stn.jpg',(x1[0,0].cpu().detach().numpy()*0.5+0.5)*255) + +# x1 = self.backbone(x1) +# x1 = self.head(x1) +# return x1 + + +class RecLoss(nn.Module): + def __init__(self, config): + super(RecLoss, self).__init__() + self.algorithm = config['base']['algorithm'] + if (config['base']['algorithm']) == 'CRNN': + self.loss = create_module(config['loss']['function'])(config) + elif self.algorithm=='FC': + self.loss = create_module(config['loss']['function'])(ignore_index = config['base']['ignore_index']) + else: + assert True == False, ('not support this algorithm !!!') + + def forward(self, pre_batch, gt_batch): + return self.loss(pre_batch, gt_batch) + diff --git a/ptocr/model/backbone/__init__.py b/ptocr/model/backbone/__init__.py new file mode 100644 index 0000000..35de2ad --- /dev/null +++ b/ptocr/model/backbone/__init__.py @@ -0,0 +1,6 @@ +#-*- coding:utf-8 _*- +""" +@author:fxw +@file: __init__.py.py +@time: 2020/08/07 +""" diff --git a/ptocr/model/backbone/__pycache__/__init__.cpython-35.pyc b/ptocr/model/backbone/__pycache__/__init__.cpython-35.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8bfa25d9e351fe9f178fa6ca79c55d3c14ff8195 GIT binary patch literal 228 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 literal 0 HcmV?d00001 diff --git a/ptocr/model/backbone/__pycache__/det_mobilev3_dcd.cpython-36.pyc b/ptocr/model/backbone/__pycache__/det_mobilev3_dcd.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..24f3a9d8dc254c76495bd172a1206d64da3e51e0 GIT binary patch literal 11209 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 T@}tQgH|mWMJoU!8M*V*PkcM-~ literal 0 HcmV?d00001 diff --git a/ptocr/model/backbone/__pycache__/det_resnet.cpython-36.pyc b/ptocr/model/backbone/__pycache__/det_resnet.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..58ed83d48f9dc631ded50352c54e190a24bbaefe GIT binary patch literal 9459 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 literal 0 HcmV?d00001 diff --git a/ptocr/model/backbone/__pycache__/det_resnet_3_3.cpython-36.pyc b/ptocr/model/backbone/__pycache__/det_resnet_3_3.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6998a2abcdd9d1741859aa34921236a711e9bd09 GIT binary patch literal 6798 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 literal 0 HcmV?d00001 diff --git a/ptocr/model/backbone/__pycache__/det_resnet_sast.cpython-35.pyc b/ptocr/model/backbone/__pycache__/det_resnet_sast.cpython-35.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f76d0a09844b9343c694b7d3d003e202d37a8e91 GIT binary patch literal 9939 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 KG44z{)&B-vObWRG literal 0 HcmV?d00001 diff --git a/ptocr/model/backbone/__pycache__/det_resnet_sast_3_3.cpython-36.pyc b/ptocr/model/backbone/__pycache__/det_resnet_sast_3_3.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6786036566143a2e1817634c14e16bdba38b582c GIT binary patch literal 6900 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 Rz%c;&U&59 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 X3tU4PRjX>bPW58-YV}k#b<6(&O|w%I literal 0 HcmV?d00001 diff --git a/ptocr/model/backbone/__pycache__/rec_vgg.cpython-36.pyc b/ptocr/model/backbone/__pycache__/rec_vgg.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b16abd46e4382148156c8df4ba01b8d7c0dcb353 GIT binary patch literal 7165 zcmds6TW{OQ73Po>B~kJvj-6Z6tTs)PXuGmxD^VghXq{~CP24o`Ca8ddpm->mvP{`S zDsfaA2;u+>6wN-heQ1MXpB61pEcy=$^j8!E^r7fW{n8)Mr=BykY%57~c}d(*m@|iS zX6DSy`Q{@zDh~~rfB*Ggte+lJlz%Eat^=sgVhV0EMFFLz00XtnYE0&8ORH&8(_4v_ zQ8PdT9TH$b5>k)`6Ee+oEeTl|f?*heQP=}xuouRkrD~}w3QU0ZL;=m!TIqGIX2MC> z4+oyGS_TfnA@~-i!}$9)9KqkFY!0ChnYz{ik4Z5X}>N5MdC1U`lDKoYf4D8MmDp|%Gm;k%GVZ48QV95Sfw zg%X?q6SZ+r?We2Z|9YQQm8nUQ}yyx z`R$rg6me~D1o>@kNapp{eDFf*9WdRg3ill{Rtv;cHbPU$mD0SyIbhjU$LYb;{% z{FoeKUge``7HpM|V;+kMDoHjv{DIBkINk(uHJQnJ8FII2agc=NMV=_8(%Uzvtp0)g ztt%ImpFLY+xZW&8a$Z;Yb)(r`ot(qj+P>B4hC=6VyPH>qPHZExe#dWn zzH4(Ye8_3O8zh=PAOGgcKYCAon9qtNR_wMhzNQy=eh}Ke;|kUHh4ER(zdsE^r?`ue zMcZ5Q{HEo1_>$eWgb^b_=ylHyMB3@dwxK057wphkn8Rw=*tj9Ri{xgrt#~ax7cV}7 zmS|Un-wy5&6krpZ~hR=6=!ar4;>`7a_ZGSH$$iFJm#PVWj&SyQ7P{qap}zf`5tD6yum?7N3l zf>74JB*MtdZO~-+VHASh+@LLiD9S9l+;`hnfD|E&Amko`3du#~Q&cgzYy%)D=JQM> zJ%71vJ1*Kv{hsm$|A^|PZ`!N3I-T}3@EqA_PYV6Ed*gFq-f>sDu1^8Q=xI))i9H2Y zliLr4Stle(#0jHqKXiE@GM;Za3$~A=3Iw|^3t7oB=KorqGVAbGR`S0SL5>j-8}_+ z&JaA1t27LDs`nT-Qf_LhAQvbnUrcf*aU~mGv&-$!>-f2&=a1%1Gl$oC-VA1>Mt95H z4Y`fG8geJ=7}NYr?ph-^&%5rYIW%>-ALQ)KF0pQ*Um3f+wq!TmIX9GjUFKuBQv4zc z+-NhT69RjC`p>7y(qv3A5|sI@+=4Plyowb?S`ORlbvhzN?^;%uw}lxOqKbruWEQ#> zcn;FyqWdrq3HdUH-00fANO%1Jq30s9M`Zi$>WD;hd!BM|!dS$oNfY~!9>nOjooL2w ze(iw5EgF)sp|--Br?upgw*35kV;8NrM4h-eE?F|Uw~A)_q#WPRPKQnF689K&c)9J zuO|Wbj<7b5{3OBleOy-gI`_ti<5#K4H4@iJP;~eW5;sZAk@zu*TPX5L{u9!ruiK>E zAwekMpOd&tV(^_s4yT3XJ8*L7bvXHdB%bmsiRZ#QA)ZUP&VAycHQYropy52iv;zho zC|$f32=9|F2qA-XL7cpca6-fR%^*%bNV=%Jk}fX36S}yJ>)fXcdf(ecFreZ5X%Ge< zC|$f32=9|F2qA-XK?fbX2q!e0zYgN$gQSbs|%dz8ZS~vIY G{QVdGNtqJ> literal 0 HcmV?d00001 diff --git a/ptocr/model/backbone/__pycache__/reg_resnet_bd.cpython-36.pyc b/ptocr/model/backbone/__pycache__/reg_resnet_bd.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3fc663a050fbb8cbcbb91bab44b71d8be3f69245 GIT binary patch literal 5731 zcmcgw&2QY+5hvf0TrPLDl2?i?J5IcDQzz@%vg8jS2!cZXh=QQDP}p)@EZVTEebj2D z6?gr}b!C+cq>+*IVk79KK@s%cq9}SSdhVfrK@Ys+8FH!BhXd4YLW0BL zysvrl=J#eEuS`rh*Y1Dv%K*QKC-?|NYEtiL8~TQ*=a@G(^t%3@CWXvB z(PYlkH_Y8!-GaoBCM0Gm$wOkvJS6#4Vnbrf0wjf0Qjo`FQI?+Qbw^g@ge-$A%4s<% zr@)n@eow2-455X#+$1`>NJF}Ub0D5p*QHh$GXJTzrpuf(o)n;~DXk}3-S|{9w7Q9Y z?6`Qd>p!}&e7o`3Q$rmsmzp;9f{Wht^$dEdj;i&<69x^-9BN63L5WkqVO)Qq^e=_%jqxIufzi>)A3ZRy4NUPH>Z---)OuibICy3$jm@lb%&thm_r z-R643N5dd4b^BrVrO0B+Bw~TYR!epJy`WZ(&7j}&l%gJD+jZN1J9ORHX$S5j$s@#h z@XG7-W8wR8;rgT2_q*NBWf>RU9y4y^Q7g8{wh$UkyyN1HM%Y|m?y4<_#Ht?~cfH&9 zV}rWELKuP9SyKhl#v!t&T#-H}iIhQVdR%bbW~UJZuKOG9UvJD^>V@5=xPU02o+v;U1U~-xy`L(Y4Nkhq3@DY?iG~I&kQ%rZJ$F)yu)(rLtCQD2339PB# z*z#yiU0Tn6hxn4~KI}I-NeP=vy-EX4q~>mVosR47sZ-MxnNpEf>?^cn0WF?w03W!^ z6_z(4%B~lK>Ew-W7HcMJ}1MTdSsJEGu1#X=Os5RGSxH`*hX2A8J zFVoTvNy(adu!u3p0MBW~kSHkf1NAK;=`?Q|BUmknC(t>^kG`hOfmEU>RZ5&2@K^AVaQ%q zuVcKG=eRrpTb^u#IHAIKHhDZT<5>N}`3TA%%}_yI}+ z@-Dz4b@>z;8AK8|BvQa(de8nxBY_lTBrJkCfP}=i5p+~F!vF`L!5#(-1fP8vu*RmF zxxc!A-VXslT_h>nBy-F?2=q~yLBQdVnnIv5HmU#J`)S1<&{&!G&nlArAH3hX>jleR z$Sya$Ons6o(M66%;fxZH;Ez~@0|>y@4}l%ADS}nJb13b}f^?Am7Pxegq7BIRC9^=D+%6=cveEyH)fACoW13P!LJPELc*hgPJ^<7jk9=%4B8Z`;2QC&F@M(uRFY`_Zol1^M#8+5UMr z5t&ipq{h%*ua(v>WZP;pgcsof%5U!9}IW zi3(BCK=h!Vx__b`eM2A}k*_>82NQC3;6(PuR5Y=x>)L1f!yi5vwbk5X_+EOT`M=x# z?LLivm1_KCpT=RPu}~LMjlsz(Yy28CuA~|Zbt~1_O7%QSThU!VplvzRw#?d!hvfs> z{_}wb>rY4aGdeQN@Jf(>olaNcCC*CDE)OZu9CCc2kUjK>d`{#qAUhY4@%NAzc(U4| zqfk(7D6jg{L3OnsR@;6Ll;zcS0I9Okh5@6#ORa!cI?7ZSS12baaJrz0W zp=hhI9h<7*x4bxqWR~N1Y&Cj4&zH%mhxWACx#vCXdw$q%bYgRr(%smmgB$fyn;gs6 z7FpR%oCaD!eJWc*H&x%1UN2maOMZVVJM`6y^tIORgjnRd!@awmHV%`w(HV!mSZ}kn z>r<;)((Q7^dU2I9h2-Pt==U*t37&xRIrqPj; z$bEbT>7%1p&`(95;WL{Mr_;##Dx_0-MU0gSA!hfLPU$$+O(whi(eaf$;wCA+4D!7A znnqAM+Hox1K2F&twzCo>4|vj1??AMr&ebg4QkLTyb$l81jCUm)}TrFffSHDr?QKEFD@zh)@Y#ncLY?sw>sy{=sSI|>im z>jOx?PJ3OO|3>Y_NIc5f{#$!poB#Kb_M+^#Ch&V_^i0&J!>($s&-wn`THkL{7;AKr z*yM}}E}&s-kE+Jj9sVE0F)sO|Foz0^4EA9~3!`YXu?5XeK(YM}z=8is-=%*RyjNj= R5|y)+h06KLs#A8#{{f_wf|39L literal 0 HcmV?d00001 diff --git a/ptocr/model/backbone/det_mobilev3.py b/ptocr/model/backbone/det_mobilev3.py new file mode 100644 index 0000000..806b07e --- /dev/null +++ b/ptocr/model/backbone/det_mobilev3.py @@ -0,0 +1,244 @@ +#-*- coding:utf-8 _*- +""" +@author:fxw +@file: det_mobilev3.py +@time: 2020/08/07 +""" + +from torch import nn +import torch +import torch.nn.functional as F +from torch.nn import init +import numpy as np + +__all__ = ['mobilenet_v3_small','mobilenet_v3_large'] + +class hswish(nn.Module): + def forward(self, x): + out = x * F.relu6(x + 3, inplace=True) / 6 + return out + +class hswish1(nn.Module): + def forward(self, x): + out = x * (F.relu(x + 3, inplace=True)-F.relu(x - 3, inplace=True)) / 6 + return out + +class hsigmoid(nn.Module): + def forward(self, x): + out = F.relu6(x + 3, inplace=True) / 6 + return out + +class hsigmoid1(nn.Module): + def forward(self, x): + out =(F.relu(x + 3, inplace=True)-F.relu(x - 3, inplace=True))/ 6 + return out + + +class SeModule(nn.Module): + def __init__(self, in_size, reduction=4): + super(SeModule, self).__init__() + self.avg_pool = nn.AdaptiveAvgPool2d(1) + + self.se = nn.Sequential( + nn.Conv2d(in_size, in_size // reduction, kernel_size=1, stride=1, padding=0, bias=False), + nn.BatchNorm2d(in_size // reduction), + nn.ReLU(inplace=True), + nn.Conv2d(in_size // reduction, in_size, kernel_size=1, stride=1, padding=0, bias=False), + nn.BatchNorm2d(in_size), + hsigmoid() + ) + + def forward(self, x): + return x * self.se(x) + + +class Block(nn.Module): + '''expand + depthwise + pointwise''' + def __init__(self, kernel_size, in_size, expand_size, out_size, nolinear, semodule, stride): + super(Block, self).__init__() + self.stride = stride + + + self.conv1 = nn.Conv2d(in_size, expand_size, kernel_size=1, stride=1, padding=0, bias=False) + self.bn1 = nn.BatchNorm2d(expand_size) + self.nolinear1 = nolinear + self.conv2 = nn.Conv2d(expand_size, expand_size, kernel_size=kernel_size, stride=stride, padding=kernel_size//2, groups=expand_size, bias=False) + self.bn2 = nn.BatchNorm2d(expand_size) + self.nolinear2 = nolinear + self.conv3 = nn.Conv2d(expand_size, out_size, kernel_size=1, stride=1, padding=0, bias=False) + self.bn3 = nn.BatchNorm2d(out_size) + + self.shortcut = nn.Sequential() + + self.se = semodule + + if stride == 1 and in_size != out_size: + self.shortcut = nn.Sequential( + nn.Conv2d(in_size, out_size, kernel_size=1, stride=1, padding=0, bias=False), + nn.BatchNorm2d(out_size), + ) + + def forward(self, x): + out = self.nolinear1(self.bn1(self.conv1(x))) + out = self.nolinear2(self.bn2(self.conv2(out))) + out = self.bn3(self.conv3(out)) + if self.se != None: + out = self.se(out) + out = out + self.shortcut(x) if self.stride==1 else out + return out + + +class MobileNetV3_Large(nn.Module): + def __init__(self, ): + super(MobileNetV3_Large, self).__init__() + self.conv1 = nn.Conv2d(3, 16, kernel_size=3, stride=2, padding=1, bias=False) + self.bn1 = nn.BatchNorm2d(16) + self.hs1 = hswish() + + self.bneck = nn.Sequential( + Block(3, 16, 16, 16, nn.ReLU(inplace=True), None, 1), + Block(3, 16, 64, 24, nn.ReLU(inplace=True), None, 2), + Block(3, 24, 72, 24, nn.ReLU(inplace=True), None, 1), + Block(5, 24, 72, 40, nn.ReLU(inplace=True), SeModule(40), 2), + Block(5, 40, 120, 40, nn.ReLU(inplace=True), SeModule(40), 1), + Block(5, 40, 120, 40, nn.ReLU(inplace=True), SeModule(40), 1), + Block(3, 40, 240, 80, hswish(), None, 2), + Block(3, 80, 200, 80, hswish(), None, 1), + Block(3, 80, 184, 80, hswish(), None, 1), + Block(3, 80, 184, 80, hswish(), None, 1), + Block(3, 80, 480, 112, hswish(), SeModule(112), 2), + Block(3, 112, 672, 112, hswish(), SeModule(112), 1), + Block(5, 112, 672, 160, hswish(), SeModule(160), 1), + Block(5, 160, 672, 160, hswish(), SeModule(160), 1), + Block(5, 160, 960, 160, hswish(), SeModule(160), 1), + ) + self.init_params() + + def init_params(self): + for m in self.modules(): + if isinstance(m, nn.Conv2d): + init.kaiming_normal_(m.weight, mode='fan_out') + if m.bias is not None: + init.constant_(m.bias, 0) + elif isinstance(m, nn.BatchNorm2d): + init.constant_(m.weight, 1) + init.constant_(m.bias, 0) + elif isinstance(m, nn.Linear): + init.normal_(m.weight, std=0.001) + if m.bias is not None: + init.constant_(m.bias, 0) + + def forward(self, x): + out = self.hs1(self.bn1(self.conv1(x))) + i = 0 + model_list = [] + for solution in self.bneck.children(): + out = solution(out) + if(i==2 or i==5 or i==9 or i==14): + model_list.append(out) + i+=1 + p1 = model_list[0] + p2 = model_list[1] + p3 = model_list[2] + p4 = model_list[3] + return p1,p2,p3,p4 + + + +class MobileNetV3_Small(nn.Module): + def __init__(self, ): + super(MobileNetV3_Small, self).__init__() + self.conv1 = nn.Conv2d(3, 16, kernel_size=3, stride=2, padding=1, bias=False) + self.bn1 = nn.BatchNorm2d(16) + self.hs1 = hswish() + + self.bneck = nn.Sequential( + Block(3, 16, 16, 16, nn.ReLU(inplace=True), SeModule(16), 1), + Block(3, 16, 72, 24, nn.ReLU(inplace=True), None, 2), + Block(3, 24, 88, 24, nn.ReLU(inplace=True), None, 1), + Block(5, 24, 96, 40, hswish(), SeModule(40), 2), + Block(5, 40, 240, 40, hswish(), SeModule(40), 1), + Block(5, 40, 240, 40, hswish(), SeModule(40), 1), + Block(5, 40, 120, 48, hswish(), SeModule(48), 2), + Block(5, 48, 144, 48, hswish(), SeModule(48), 1), + Block(5, 48, 288, 96, hswish(), SeModule(96), 2), + Block(5, 96, 576, 96, hswish(), SeModule(96), 1), + Block(5, 96, 576, 96, hswish(), SeModule(96), 1), + ) +# self.bneck = nn.Sequential( +# Block(3, 16, 16, 16, nn.ReLU(inplace=True), None, 1), +# Block(3, 16, 72, 24, nn.ReLU(inplace=True), SeModule(24), 2), +# Block(3, 24, 88, 24, nn.ReLU(inplace=True), None, 1), +# Block(5, 24, 96, 40, hswish(), SeModule(40), 2), +# Block(5, 40, 240, 40, hswish(), SeModule(40), 1), +# Block(5, 40, 240, 40, hswish(), None, 1), +# Block(5, 40, 120, 48, hswish(), SeModule(48), 2), +# Block(5, 48, 144, 48, hswish(), None, 1), +# Block(5, 48, 288, 96, hswish(), SeModule(96), 2), +# Block(5, 96, 576, 96, hswish(), SeModule(96), 1), +# Block(5, 96, 576, 96, hswish(), None, 1), +# ) + self.init_params() + + def init_params(self): + for m in self.modules(): + if isinstance(m, nn.Conv2d): + init.kaiming_normal_(m.weight, mode='fan_out') + if m.bias is not None: + init.constant_(m.bias, 0) + elif isinstance(m, nn.BatchNorm2d): + init.constant_(m.weight, 1) + init.constant_(m.bias, 0) + elif isinstance(m, nn.Linear): + init.normal_(m.weight, std=0.001) + if m.bias is not None: + init.constant_(m.bias, 0) + + def forward(self, x): + out = self.hs1(self.bn1(self.conv1(x))) + i = 0 + model_list = [] + for solution in self.bneck.children(): + out = solution(out) + if (i == 2 or i == 5 or i == 7 or i == 10): + model_list.append(out) + i += 1 + p1 = model_list[0] + p2 = model_list[1] + p3 = model_list[2] + p4 = model_list[3] + return p1, p2, p3, p4 + +def mobilenet_v3_small(pretrained,**kwargs): + model = MobileNetV3_Small() + if pretrained: + if torch.cuda.is_available(): + pretrained_dict = torch.load('./pre_model/mbv3_small.old.pth.tar')['state_dict'] + else: + pretrained_dict = torch.load('./pre_model/mbv3_small.old.pth.tar',map_location='cpu')['state_dict'] + try: + model.load_state_dict(pretrained_dict) + except: + state = model.state_dict() + for key in state.keys(): + if 'module.' + key in pretrained_dict.keys(): + state[key] = pretrained_dict['module.' + key] + model.load_state_dict(state) + return model + +def mobilenet_v3_large(pretrained,**kwargs): + model = MobileNetV3_Large() + if pretrained: + if torch.cuda.is_available(): + pretrained_dict = torch.load('./pre_model/mbv3_small.old.pth.tar')['state_dict'] + else: + pretrained_dict = torch.load('./pre_model/mbv3_small.old.pth.tar', map_location='cpu')['state_dict'] + try: + model.load_state_dict(pretrained_dict) + except: + state = model.state_dict() + for key in state.keys(): + if 'module.'+key in pretrained_dict.keys(): + state[key] = pretrained_dict['module.'+key] + model.load_state_dict(state) + return model \ No newline at end of file diff --git a/ptocr/model/backbone/det_mobilev3_pytorch_qua.py b/ptocr/model/backbone/det_mobilev3_pytorch_qua.py new file mode 100644 index 0000000..c35c44c --- /dev/null +++ b/ptocr/model/backbone/det_mobilev3_pytorch_qua.py @@ -0,0 +1,137 @@ +#-*- coding:utf-8 _*- +""" +@author:fxw +@file: mobilev3_new.py +@time: 2020/11/02 +""" +import torch +from torch.quantization import QuantStub, DeQuantStub, fuse_modules +import torch.nn as nn +from torch.nn import init +from ..CommonFunction_Q import ConvBnRelu,ConvBn + + +class SeModule(nn.Module): + def __init__(self, in_size, reduction=4): + super(SeModule, self).__init__() + self.se = nn.Sequential( + ConvBnRelu(in_size, in_size // reduction, kernel_size=1, stride=1, padding=0,groups=1,bias=False), + ConvBn(in_size // reduction, in_size, kernel_size=1, stride=1, padding=0, groups=1,bias=False), + nn.Sigmoid() + ) + self.skip = nn.quantized.FloatFunctional() + + def forward(self, x): + return self.skip.mul(x,self.se(x)) + # return x*self.se(x) + + + +class Block(nn.Module): + + def __init__(self, kernel_size, in_size, expand_size, out_size, semodule, stride): + super(Block, self).__init__() + self.stride = stride + + self.conv_bn_relu1 = ConvBnRelu(in_size, expand_size, kernel_size=1, stride=1, padding=0,groups=1,bias=False) + + self.conv_bn_relu2 = ConvBnRelu(expand_size, expand_size, kernel_size=kernel_size, stride=stride, + padding=kernel_size // 2, groups=expand_size, bias=False) + self.conv_bn1 = ConvBn(expand_size, out_size, kernel_size=1, stride=1, padding=0,groups=1,bias=False) + + self.skip = nn.quantized.FloatFunctional() + + self.shortcut = nn.Sequential() + + self.se = semodule + + if stride == 1 and in_size != out_size: + self.shortcut = nn.Sequential( + ConvBn(in_size, out_size, kernel_size=1, stride=1, padding=0, groups=1,bias=False), + ) + + def forward(self, x): + + out =self.conv_bn_relu1(x) + out =self.conv_bn_relu2(out) + out = self.conv_bn1(out) + + out = self.se(out) + out = self.skip.add(out , self.shortcut(x)) if self.stride == 1 else out + return out + + + +class MobileNetV3_Small(nn.Module): + def __init__(self, ): + super(MobileNetV3_Small, self).__init__() + self.conv_bn_relu = ConvBnRelu(3, 16, kernel_size=3, stride=2, padding=1,groups=1, bias=False) + self.layer1 = nn.Sequential( + Block(3, 16, 16, 16,SeModule(16), 1), + Block(3, 16, 72, 24,SeModule(24), 2), + Block(3, 24, 88, 24, SeModule(24), 1)) + self.layer2 = nn.Sequential( + Block(5, 24, 96, 40, SeModule(40), 2), + Block(5, 40, 240, 40, SeModule(40), 1), + Block(5, 40, 240, 40, SeModule(40), 1)) + self.layer3 = nn.Sequential( + Block(5, 40, 120, 48, SeModule(48), 2), + Block(5, 48, 144, 48, SeModule(48), 1)) + self.layer4 = nn.Sequential( + Block(5, 48, 288, 96, SeModule(96), 2), + Block(5, 96, 576, 96, SeModule(96), 1), + Block(5, 96, 576, 96, SeModule(96), 1)) + + self.init_params() + self.fuse_model() + + def init_params(self): + for m in self.modules(): + if isinstance(m, nn.Conv2d): + init.kaiming_normal_(m.weight, mode='fan_out') + if m.bias is not None: + init.constant_(m.bias, 0) + elif isinstance(m, nn.BatchNorm2d): + init.constant_(m.weight, 1) + init.constant_(m.bias, 0) + elif isinstance(m, nn.Linear): + init.normal_(m.weight, std=0.001) + if m.bias is not None: + init.constant_(m.bias, 0) + + def forward(self, x): + x = self.conv_bn_relu(x) + p1 = self.layer1(x) + p2 = self.layer2(p1) + p3 = self.layer3(p2) + p4 = self.layer4(p3) + return [p1,p2,p3,p4] + + def fuse_model(self): + for m in self.modules(): + if type(m) == ConvBnRelu: + fuse_modules(m, ['conv', 'bn', 'relu'], inplace=True) + if type(m) == ConvBn: + fuse_modules(m, ['conv', 'bn'], inplace=False) + + +def mobilenet_v3_small(pretrained,**kwargs): + model = MobileNetV3_Small() + + if pretrained: + if torch.cuda.is_available(): + pretrained_dict = torch.load('./pre_model/mbv3_small.old.pth.tar')['state_dict'] + else: + pretrained_dict = torch.load('./pre_model/mbv3_small.old.pth.tar',map_location='cpu')['state_dict'] + try: + model.load_state_dict(pretrained_dict) + except: + state = model.state_dict() + for key in state.keys(): + if 'module.' + key in pretrained_dict.keys(): + state[key] = pretrained_dict['module.' + key] + model.load_state_dict(state) + return model + + + diff --git a/ptocr/model/backbone/det_resnet.py b/ptocr/model/backbone/det_resnet.py new file mode 100644 index 0000000..f8a74f8 --- /dev/null +++ b/ptocr/model/backbone/det_resnet.py @@ -0,0 +1,368 @@ +#-*- coding:utf-8 _*- +""" +@author:fxw +@file: det_resnet.py.py +@time: 2020/08/07 +""" +import torch +import torch.nn as nn +import math +import torch.utils.model_zoo as model_zoo + +BatchNorm2d = nn.BatchNorm2d + +__all__ = ['ResNet', 'resnet18', 'resnet34', 'resnet50', 'resnet101', + 'resnet152'] + +model_urls = { + 'resnet18': 'https://download.pytorch.org/models/resnet18-5c106cde.pth', + 'resnet34': 'https://download.pytorch.org/models/resnet34-333f7ec4.pth', + 'resnet50': 'https://download.pytorch.org/models/resnet50-19c8e357.pth', + 'resnet101': 'https://download.pytorch.org/models/resnet101-5d3b4d8f.pth', + 'resnet152': 'https://download.pytorch.org/models/resnet152-b121ed2d.pth', +} + + +def load_pre_model(model,pre_model_path): + pre_dict = torch.load(pre_model_path) + model_pre_dict = {} + for key in model.state_dict().keys(): + if('model.module.backbone.'+key in pre_dict.keys()): + model_pre_dict[key] = pre_dict['model.module.backbone.'+key] + else: + model_pre_dict[key] = model.state_dict()[key] + model.load_state_dict(model_pre_dict) + return model + + +def constant_init(module, constant, bias=0): + nn.init.constant_(module.weight, constant) + if hasattr(module, 'bias'): + nn.init.constant_(module.bias, bias) + + +def conv3x3(in_planes, out_planes, stride=1): + """3x3 convolution with padding""" + return nn.Conv2d(in_planes, out_planes, kernel_size=3, stride=stride, + padding=1, bias=False) + + +class BasicBlock(nn.Module): + expansion = 1 + + def __init__(self, inplanes, planes, stride=1, downsample=None, dcn=None): + super(BasicBlock, self).__init__() + self.with_dcn = dcn is not None + self.conv1 = conv3x3(inplanes, planes, stride) + self.bn1 = BatchNorm2d(planes) + self.relu = nn.ReLU(inplace=True) + self.with_modulated_dcn = False + if self.with_dcn: + fallback_on_stride = dcn.get('fallback_on_stride', False) + self.with_modulated_dcn = dcn.get('modulated', False) + # self.conv2 = conv3x3(planes, planes) + if not self.with_dcn or fallback_on_stride: + self.conv2 = nn.Conv2d(planes, planes, kernel_size=3, + padding=1, bias=False) + else: + deformable_groups = dcn.get('deformable_groups', 1) + if not self.with_modulated_dcn: + from models.dcn import DeformConv + conv_op = DeformConv + offset_channels = 18 + else: + from models.dcn import ModulatedDeformConv + conv_op = ModulatedDeformConv + offset_channels = 27 + self.conv2_offset = nn.Conv2d( + planes, + deformable_groups * offset_channels, + kernel_size=3, + padding=1) + self.conv2 = conv_op( + planes, + planes, + kernel_size=3, + padding=1, + deformable_groups=deformable_groups, + bias=False) + self.bn2 = BatchNorm2d(planes) + self.downsample = downsample + self.stride = stride + + def forward(self, x): + residual = x + + out = self.conv1(x) + out = self.bn1(out) + out = self.relu(out) + + # out = self.conv2(out) + if not self.with_dcn: + out = self.conv2(out) + elif self.with_modulated_dcn: + offset_mask = self.conv2_offset(out) + offset = offset_mask[:, :18, :, :] + mask = offset_mask[:, -9:, :, :].sigmoid() + out = self.conv2(out, offset, mask) + else: + offset = self.conv2_offset(out) + out = self.conv2(out, offset) + out = self.bn2(out) + + if self.downsample is not None: + residual = self.downsample(x) + + out += residual + out = self.relu(out) + + return out + + +class Bottleneck(nn.Module): + expansion = 4 + + def __init__(self, inplanes, planes, stride=1, downsample=None, dcn=None): + super(Bottleneck, self).__init__() + self.with_dcn = dcn is not None + self.conv1 = nn.Conv2d(inplanes, planes, kernel_size=1, bias=False) + self.bn1 = BatchNorm2d(planes) + fallback_on_stride = False + self.with_modulated_dcn = False + if self.with_dcn: + fallback_on_stride = dcn.get('fallback_on_stride', False) + self.with_modulated_dcn = dcn.get('modulated', False) + if not self.with_dcn or fallback_on_stride: + self.conv2 = nn.Conv2d(planes, planes, kernel_size=3, + stride=stride, padding=1, bias=False) + else: + deformable_groups = dcn.get('deformable_groups', 1) + if not self.with_modulated_dcn: + from models.dcn import DeformConv + conv_op = DeformConv + offset_channels = 18 + else: + from models.dcn import ModulatedDeformConv + conv_op = ModulatedDeformConv + offset_channels = 27 + self.conv2_offset = nn.Conv2d( + planes, deformable_groups * offset_channels, + kernel_size=3, + padding=1) + self.conv2 = conv_op( + planes, planes, kernel_size=3, padding=1, stride=stride, + deformable_groups=deformable_groups, bias=False) + self.bn2 = BatchNorm2d(planes) + self.conv3 = nn.Conv2d(planes, planes * 4, kernel_size=1, bias=False) + self.bn3 = BatchNorm2d(planes * 4) + self.relu = nn.ReLU(inplace=True) + self.downsample = downsample + self.stride = stride + self.dcn = dcn + self.with_dcn = dcn is not None + + def forward(self, x): + residual = x + + out = self.conv1(x) + out = self.bn1(out) + out = self.relu(out) + + # out = self.conv2(out) + if not self.with_dcn: + out = self.conv2(out) + elif self.with_modulated_dcn: + offset_mask = self.conv2_offset(out) + offset = offset_mask[:, :18, :, :] + mask = offset_mask[:, -9:, :, :].sigmoid() + out = self.conv2(out, offset, mask) + else: + offset = self.conv2_offset(out) + out = self.conv2(out, offset) + out = self.bn2(out) + out = self.relu(out) + + out = self.conv3(out) + out = self.bn3(out) + + if self.downsample is not None: + residual = self.downsample(x) + + out += residual + out = self.relu(out) + + return out + + +class ResNet(nn.Module): + def __init__(self, block, layers, num_classes=1000, + dcn=None, stage_with_dcn=(False, False, False, False)): + self.dcn = dcn + self.stage_with_dcn = stage_with_dcn + self.inplanes = 64 + super(ResNet, self).__init__() + self.conv1 = nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3, + bias=False) + self.bn1 = BatchNorm2d(64) + self.relu = nn.ReLU(inplace=True) + self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1) + self.layer1 = self._make_layer(block, 64, layers[0]) + self.layer2 = self._make_layer( + block, 128, layers[1], stride=2, dcn=dcn) + self.layer3 = self._make_layer( + block, 256, layers[2], stride=2, dcn=dcn) + self.layer4 = self._make_layer( + block, 512, layers[3], stride=2, dcn=dcn) + # self.avgpool = nn.AvgPool2d(7, stride=1) + # self.fc = nn.Linear(512 * block.expansion, num_classes) + + # self.smooth = nn.Conv2d(2048, 256, kernel_size=1, stride=1, padding=1) + + for m in self.modules(): + if isinstance(m, nn.Conv2d): + n = m.kernel_size[0] * m.kernel_size[1] * m.out_channels + m.weight.data.normal_(0, math.sqrt(2. / n)) + elif isinstance(m, BatchNorm2d): + m.weight.data.fill_(1) + m.bias.data.zero_() + if self.dcn is not None: + for m in self.modules(): + if isinstance(m, Bottleneck) or isinstance(m, BasicBlock): + if hasattr(m, 'conv2_offset'): + constant_init(m.conv2_offset, 0) + + def _make_layer(self, block, planes, blocks, stride=1, dcn=None): + downsample = None + if stride != 1 or self.inplanes != planes * block.expansion: + downsample = nn.Sequential( + nn.Conv2d(self.inplanes, planes * block.expansion, + kernel_size=1, stride=stride, bias=False), + BatchNorm2d(planes * block.expansion), + ) + + layers = [] + layers.append(block(self.inplanes, planes, + stride, downsample, dcn=dcn)) + self.inplanes = planes * block.expansion + for i in range(1, blocks): + layers.append(block(self.inplanes, planes, dcn=dcn)) + + return nn.Sequential(*layers) + + def forward(self, x): + x = self.conv1(x) + x = self.bn1(x) + x = self.relu(x) + x = self.maxpool(x) + + x2 = self.layer1(x) + x3 = self.layer2(x2) + x4 = self.layer3(x3) + x5 = self.layer4(x4) + + return x2, x3, x4, x5 + + +def resnet18(pretrained=True, load_url=True,**kwargs): + """Constructs a ResNet-18 model. + Args: + pretrained (bool): If True, returns a model pre-trained on ImageNet + """ + model = ResNet(BasicBlock, [2, 2, 2, 2], **kwargs) + if pretrained: + if load_url: + model.load_state_dict(model_zoo.load_url( + model_urls['resnet18']), strict=False) + else: + model = load_pre_model(model,'./pre_model/pre-trained-model-synthtext-resnet18.pth') + return model + + +def deformable_resnet18(pretrained=True,load_url=True, **kwargs): + """Constructs a ResNet-18 model. + Args: + pretrained (bool): If True, returns a model pre-trained on ImageNet + """ + model = ResNet(BasicBlock, [2, 2, 2, 2], + dcn=dict(modulated=True, + deformable_groups=1, + fallback_on_stride=False), + stage_with_dcn=[False, True, True, True], **kwargs) + if pretrained: + if load_url: + model.load_state_dict(model_zoo.load_url( + model_urls['resnet18']), strict=False) + else: + model = load_pre_model(model,'./pre_model/pre-trained-model-synthtext-resnet18.pth') + return model + + +def resnet34(pretrained=True, **kwargs): + """Constructs a ResNet-34 model. + Args: + pretrained (bool): If True, returns a model pre-trained on ImageNet + """ + model = ResNet(BasicBlock, [3, 4, 6, 3], **kwargs) + if pretrained: + model.load_state_dict(model_zoo.load_url( + model_urls['resnet34']), strict=False) + return model + + +def resnet50(pretrained=True,load_url=True,**kwargs): + """Constructs a ResNet-50 model. + Args: + pretrained (bool): If True, returns a model pre-trained on ImageNet + """ + model = ResNet(Bottleneck, [3, 4, 6, 3], **kwargs) + if pretrained: + if load_url: + model.load_state_dict(model_zoo.load_url( + model_urls['resnet50']), strict=False) + else: + model = load_pre_model(model,'./pre_model/pre-trained-model-synthtext-resnet50.pth') + return model + + +def deformable_resnet50(pretrained=True,load_url=True, **kwargs): + """Constructs a ResNet-50 model with deformable conv. + Args: + pretrained (bool): If True, returns a model pre-trained on ImageNet + """ + model = ResNet(Bottleneck, [3, 4, 6, 3], + dcn=dict(modulated=True, + deformable_groups=1, + fallback_on_stride=False), + stage_with_dcn=[False, True, True, True], + **kwargs) + if pretrained: + if load_url: + model.load_state_dict(model_zoo.load_url( + model_urls['resnet50']), strict=False) + else: + model = load_pre_model(model,'./pre_model/pre-trained-model-synthtext-resnet50.pth') + return model + + +def resnet101(pretrained=True, **kwargs): + """Constructs a ResNet-101 model. + Args: + pretrained (bool): If True, returns a model pre-trained on ImageNet + """ + model = ResNet(Bottleneck, [3, 4, 23, 3], **kwargs) + if pretrained: + model.load_state_dict(model_zoo.load_url( + model_urls['resnet101']), strict=False) + return model + + +def resnet152(pretrained=True, **kwargs): + """Constructs a ResNet-152 model. + Args: + pretrained (bool): If True, returns a model pre-trained on ImageNet + """ + model = ResNet(Bottleneck, [3, 8, 36, 3], **kwargs) + if pretrained: + model.load_state_dict(model_zoo.load_url( + model_urls['resnet152']), strict=False) + return model diff --git a/ptocr/model/backbone/det_resnet_3_3.py b/ptocr/model/backbone/det_resnet_3_3.py new file mode 100644 index 0000000..5e3e713 --- /dev/null +++ b/ptocr/model/backbone/det_resnet_3_3.py @@ -0,0 +1,233 @@ + +import os +import sys +import torch +import torch.nn as nn +import math + +try: + from urllib import urlretrieve +except ImportError: + from urllib.request import urlretrieve + + +__all__ = ['resnet18', 'resnet50', 'resnet101'] + +model_urls = { + 'resnet18': 'http://sceneparsing.csail.mit.edu/model/pretrained_resnet/resnet18-imagenet.pth', + 'resnet50': 'http://sceneparsing.csail.mit.edu/model/pretrained_resnet/resnet50-imagenet.pth', + 'resnet101': 'http://sceneparsing.csail.mit.edu/model/pretrained_resnet/resnet101-imagenet.pth' +} + + +def conv3x3(in_planes, out_planes, stride=1): + "3x3 convolution with padding" + return nn.Conv2d(in_planes, out_planes, kernel_size=3, stride=stride, + padding=1, bias=False) + + +class BasicBlock(nn.Module): + expansion = 1 + + def __init__(self, inplanes, planes, stride=1, downsample=None): + super(BasicBlock, self).__init__() + self.conv1 = conv3x3(inplanes, planes, stride) + self.bn1 = nn.BatchNorm2d(planes) + self.relu = nn.ReLU(inplace=True) + self.conv2 = conv3x3(planes, planes) + self.bn2 = nn.BatchNorm2d(planes) + self.downsample = downsample + self.stride = stride + + def forward(self, x): + residual = x + + out = self.conv1(x) + out = self.bn1(out) + out = self.relu(out) + + out = self.conv2(out) + out = self.bn2(out) + + if self.downsample is not None: + residual = self.downsample(x) + + out += residual + out = self.relu(out) + + return out + + +class Bottleneck(nn.Module): + expansion = 4 + + def __init__(self, inplanes, planes, stride=1, downsample=None): + super(Bottleneck, self).__init__() + self.conv1 = nn.Conv2d(inplanes, planes, kernel_size=1, bias=False) + self.bn1 = nn.BatchNorm2d(planes) + self.conv2 = nn.Conv2d(planes, planes, kernel_size=3, stride=stride, + padding=1, bias=False) + self.bn2 = nn.BatchNorm2d(planes) + self.conv3 = nn.Conv2d(planes, planes * 4, kernel_size=1, bias=False) + self.bn3 = nn.BatchNorm2d(planes * 4) + self.relu = nn.ReLU(inplace=True) + self.downsample = downsample + self.stride = stride + + def forward(self, x): + residual = x + + out = self.conv1(x) + out = self.bn1(out) + out = self.relu(out) + + out = self.conv2(out) + out = self.bn2(out) + out = self.relu(out) + + out = self.conv3(out) + out = self.bn3(out) + + if self.downsample is not None: + residual = self.downsample(x) + + out += residual + out = self.relu(out) + + return out + + +class Convkxk(nn.Module): + def __init__(self, in_planes, out_planes, kernel_size=1, stride=1, padding=0): + super(Convkxk, self).__init__() + self.conv = nn.Conv2d(in_planes, out_planes, kernel_size=kernel_size, stride=stride, padding=padding, + bias=False) + self.bn = nn.BatchNorm2d(out_planes) + self.relu = nn.ReLU(inplace=True) + + for m in self.modules(): + if isinstance(m, nn.Conv2d): + n = m.kernel_size[0] * m.kernel_size[1] * m.out_channels + m.weight.data.normal_(0, math.sqrt(2. / n)) + elif isinstance(m, nn.BatchNorm2d): + m.weight.data.fill_(1) + m.bias.data.zero_() + + def forward(self, x): + return self.relu(self.bn(self.conv(x))) + + +class ResNet(nn.Module): + + def __init__(self, block, layers, num_classes=1000): + super(ResNet, self).__init__() + self.inplanes = 128 + self.conv1 = conv3x3(3, 64, stride=2) + self.bn1 = nn.BatchNorm2d(64) + self.relu1 = nn.ReLU(inplace=True) + self.conv2 = conv3x3(64, 64) + self.bn2 = nn.BatchNorm2d(64) + self.relu2 = nn.ReLU(inplace=True) + self.conv3 = conv3x3(64, 128) + self.bn3 = nn.BatchNorm2d(128) + self.relu3 = nn.ReLU(inplace=True) + self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1) + + self.layer1 = self._make_layer(block, 64, layers[0]) + self.layer2 = self._make_layer(block, 128, layers[1], stride=2) + self.layer3 = self._make_layer(block, 256, layers[2], stride=2) + self.layer4 = self._make_layer(block, 512, layers[3], stride=2) + # self.avgpool = nn.AvgPool2d(7, stride=1) + # self.fc = nn.Linear(512 * block.expansion, num_classes) + + for m in self.modules(): + if isinstance(m, nn.Conv2d): + n = m.kernel_size[0] * m.kernel_size[1] * m.out_channels + m.weight.data.normal_(0, math.sqrt(2. / n)) + elif isinstance(m, nn.BatchNorm2d): + m.weight.data.fill_(1) + m.bias.data.zero_() + + def _make_layer(self, block, planes, blocks, stride=1): + downsample = None + if stride != 1 or self.inplanes != planes * block.expansion: + downsample = nn.Sequential( + nn.Conv2d(self.inplanes, planes * block.expansion, + kernel_size=1, stride=stride, bias=False), + nn.BatchNorm2d(planes * block.expansion), + ) + + layers = [] + layers.append(block(self.inplanes, planes, stride, downsample)) + self.inplanes = planes * block.expansion + for i in range(1, blocks): + layers.append(block(self.inplanes, planes)) + + return nn.Sequential(*layers) + + def forward(self, x): + x = self.relu1(self.bn1(self.conv1(x))) + x = self.relu2(self.bn2(self.conv2(x))) + x = self.relu3(self.bn3(self.conv3(x))) + x = self.maxpool(x) + + f = [] + x = self.layer1(x) + f.append(x) + x = self.layer2(x) + f.append(x) + x = self.layer3(x) + f.append(x) + x = self.layer4(x) + f.append(x) + + return tuple(f) + + # x = self.avgpool(x) + # x = x.view(x.size(0), -1) + # x = self.fc(x) + + # return x + + +def resnet18(pretrained=False, **kwargs): + """Constructs a ResNet-18 model. + Args: + pretrained (bool): If True, returns a model pre-trained on Places + """ + model = ResNet(BasicBlock, [2, 2, 2, 2], **kwargs) + if pretrained: + model.load_state_dict(load_url(model_urls['resnet18']), strict=False) + return model + + +def resnet50(pretrained=False, **kwargs): + """Constructs a ResNet-50 model. + Args: + pretrained (bool): If True, returns a model pre-trained on Places + """ + model = ResNet(Bottleneck, [3, 4, 6, 3], **kwargs) + if pretrained: + model.load_state_dict(load_url(model_urls['resnet50']), strict=False) + return model + + +def resnet101(pretrained=False, **kwargs): + """Constructs a ResNet-101 model. + Args: + pretrained (bool): If True, returns a model pre-trained on Places + """ + model = ResNet(Bottleneck, [3, 4, 23, 3], **kwargs) + if pretrained: + model.load_state_dict(load_url(model_urls['resnet101']), strict=False) + return model + +def load_url(url, model_dir='./pre_model', map_location=None): + if not os.path.exists(model_dir): + os.makedirs(model_dir) + filename = url.split('/')[-1] + cached_file = os.path.join(model_dir, filename) + if not os.path.exists(cached_file): + sys.stderr.write('Downloading: "{}" to {}\n'.format(url, cached_file)) + urlretrieve(url, cached_file) + return torch.load(cached_file, map_location=map_location) \ No newline at end of file diff --git a/ptocr/model/backbone/det_resnet_sast.py b/ptocr/model/backbone/det_resnet_sast.py new file mode 100644 index 0000000..b6da208 --- /dev/null +++ b/ptocr/model/backbone/det_resnet_sast.py @@ -0,0 +1,362 @@ +#-*- coding:utf-8 _*- +""" +@author:fxw +@file: det_resnet.py.py +@time: 2020/08/07 +""" +import torch +import torch.nn as nn +import math +import torch.utils.model_zoo as model_zoo + +BatchNorm2d = nn.BatchNorm2d + +__all__ = ['ResNet', 'resnet18', 'resnet34', 'resnet50', 'resnet101', + 'resnet152'] + +model_urls = { + 'resnet18': 'https://download.pytorch.org/models/resnet18-5c106cde.pth', + 'resnet34': 'https://download.pytorch.org/models/resnet34-333f7ec4.pth', + 'resnet50': 'https://download.pytorch.org/models/resnet50-19c8e357.pth', + 'resnet101': 'https://download.pytorch.org/models/resnet101-5d3b4d8f.pth', + 'resnet152': 'https://download.pytorch.org/models/resnet152-b121ed2d.pth', +} + +def load_pre_model(model,pre_model_path): + pre_dict = torch.load(pre_model_path) + model_pre_dict = {} + for key in model.state_dict().keys(): + if('model.module.backbone.'+key in pre_dict.keys()): + model_pre_dict[key] = pre_dict['model.module.backbone.'+key] + else: + model_pre_dict[key] = model.state_dict()[key] + model.load_state_dict(model_pre_dict) + return model + +def constant_init(module, constant, bias=0): + nn.init.constant_(module.weight, constant) + if hasattr(module, 'bias'): + nn.init.constant_(module.bias, bias) + + +def conv3x3(in_planes, out_planes, stride=1): + """3x3 convolution with padding""" + return nn.Conv2d(in_planes, out_planes, kernel_size=3, stride=stride, + padding=1, bias=False) + + +class BasicBlock(nn.Module): + expansion = 1 + + def __init__(self, inplanes, planes, stride=1, downsample=None, dcn=None): + super(BasicBlock, self).__init__() + self.with_dcn = dcn is not None + self.conv1 = conv3x3(inplanes, planes, stride) + self.bn1 = BatchNorm2d(planes) + self.relu = nn.ReLU(inplace=True) + self.with_modulated_dcn = False + if self.with_dcn: + fallback_on_stride = dcn.get('fallback_on_stride', False) + self.with_modulated_dcn = dcn.get('modulated', False) + # self.conv2 = conv3x3(planes, planes) + if not self.with_dcn or fallback_on_stride: + self.conv2 = nn.Conv2d(planes, planes, kernel_size=3, + padding=1, bias=False) + else: + deformable_groups = dcn.get('deformable_groups', 1) + if not self.with_modulated_dcn: + from models.dcn import DeformConv + conv_op = DeformConv + offset_channels = 18 + else: + from models.dcn import ModulatedDeformConv + conv_op = ModulatedDeformConv + offset_channels = 27 + self.conv2_offset = nn.Conv2d( + planes, + deformable_groups * offset_channels, + kernel_size=3, + padding=1) + self.conv2 = conv_op( + planes, + planes, + kernel_size=3, + padding=1, + deformable_groups=deformable_groups, + bias=False) + self.bn2 = BatchNorm2d(planes) + self.downsample = downsample + self.stride = stride + + def forward(self, x): + residual = x + + out = self.conv1(x) + out = self.bn1(out) + out = self.relu(out) + + # out = self.conv2(out) + if not self.with_dcn: + out = self.conv2(out) + elif self.with_modulated_dcn: + offset_mask = self.conv2_offset(out) + offset = offset_mask[:, :18, :, :] + mask = offset_mask[:, -9:, :, :].sigmoid() + out = self.conv2(out, offset, mask) + else: + offset = self.conv2_offset(out) + out = self.conv2(out, offset) + out = self.bn2(out) + + if self.downsample is not None: + residual = self.downsample(x) + + out += residual + out = self.relu(out) + + return out + + +class Bottleneck(nn.Module): + expansion = 4 + + def __init__(self, inplanes, planes, stride=1, downsample=None, dcn=None): + super(Bottleneck, self).__init__() + self.with_dcn = dcn is not None + self.conv1 = nn.Conv2d(inplanes, planes, kernel_size=1, bias=False) + self.bn1 = BatchNorm2d(planes) + fallback_on_stride = False + self.with_modulated_dcn = False + if self.with_dcn: + fallback_on_stride = dcn.get('fallback_on_stride', False) + self.with_modulated_dcn = dcn.get('modulated', False) + if not self.with_dcn or fallback_on_stride: + self.conv2 = nn.Conv2d(planes, planes, kernel_size=3, + stride=stride, padding=1, bias=False) + else: + deformable_groups = dcn.get('deformable_groups', 1) + if not self.with_modulated_dcn: + from models.dcn import DeformConv + conv_op = DeformConv + offset_channels = 18 + else: + from models.dcn import ModulatedDeformConv + conv_op = ModulatedDeformConv + offset_channels = 27 + self.conv2_offset = nn.Conv2d( + planes, deformable_groups * offset_channels, + kernel_size=3, + padding=1) + self.conv2 = conv_op( + planes, planes, kernel_size=3, padding=1, stride=stride, + deformable_groups=deformable_groups, bias=False) + self.bn2 = BatchNorm2d(planes) + self.conv3 = nn.Conv2d(planes, planes * 4, kernel_size=1, bias=False) + self.bn3 = BatchNorm2d(planes * 4) + self.relu = nn.ReLU(inplace=True) + self.downsample = downsample + self.stride = stride + self.dcn = dcn + self.with_dcn = dcn is not None + + def forward(self, x): + residual = x + + out = self.conv1(x) + out = self.bn1(out) + out = self.relu(out) + + # out = self.conv2(out) + if not self.with_dcn: + out = self.conv2(out) + elif self.with_modulated_dcn: + offset_mask = self.conv2_offset(out) + offset = offset_mask[:, :18, :, :] + mask = offset_mask[:, -9:, :, :].sigmoid() + out = self.conv2(out, offset, mask) + else: + offset = self.conv2_offset(out) + out = self.conv2(out, offset) + out = self.bn2(out) + out = self.relu(out) + + out = self.conv3(out) + out = self.bn3(out) + + if self.downsample is not None: + residual = self.downsample(x) + + out += residual + out = self.relu(out) + + return out + + +class ResNet(nn.Module): + def __init__(self, block, layers, num_classes=1000, + dcn=None, stage_with_dcn=(False, False, False, False)): + self.dcn = dcn + self.stage_with_dcn = stage_with_dcn + self.inplanes = 64 + super(ResNet, self).__init__() + self.conv1 = nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3, + bias=False) + self.bn1 = BatchNorm2d(64) + self.relu = nn.ReLU(inplace=True) + self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1) + self.layer1 = self._make_layer(block, 64, layers[0]) + self.layer2 = self._make_layer( + block, 128, layers[1], stride=2, dcn=dcn) + self.layer3 = self._make_layer( + block, 256, layers[2], stride=2, dcn=dcn) + self.layer4 = self._make_layer( + block, 512, layers[3], stride=2, dcn=dcn) + self.layer5 = self._make_layer( + block, 512, layers[4], stride=2, dcn=dcn) + # self.avgpool = nn.AvgPool2d(7, stride=1) + # self.fc = nn.Linear(512 * block.expansion, num_classes) + + # self.smooth = nn.Conv2d(2048, 256, kernel_size=1, stride=1, padding=1) + + for m in self.modules(): + if isinstance(m, nn.Conv2d): + n = m.kernel_size[0] * m.kernel_size[1] * m.out_channels + m.weight.data.normal_(0, math.sqrt(2. / n)) + elif isinstance(m, BatchNorm2d): + m.weight.data.fill_(1) + m.bias.data.zero_() + if self.dcn is not None: + for m in self.modules(): + if isinstance(m, Bottleneck) or isinstance(m, BasicBlock): + if hasattr(m, 'conv2_offset'): + constant_init(m.conv2_offset, 0) + + def _make_layer(self, block, planes, blocks, stride=1, dcn=None): + downsample = None + if stride != 1 or self.inplanes != planes * block.expansion: + downsample = nn.Sequential( + nn.Conv2d(self.inplanes, planes * block.expansion, + kernel_size=1, stride=stride, bias=False), + BatchNorm2d(planes * block.expansion), + ) + + layers = [] + layers.append(block(self.inplanes, planes, + stride, downsample, dcn=dcn)) + self.inplanes = planes * block.expansion + for i in range(1, blocks): + layers.append(block(self.inplanes, planes, dcn=dcn)) + + return nn.Sequential(*layers) + + def forward(self, x): + x0 = x.clone() + x = self.conv1(x) + x = self.bn1(x) + x = self.relu(x) + x1 = x.clone() + x = self.maxpool(x) + + x2 = self.layer1(x) + x3 = self.layer2(x2) + x4 = self.layer3(x3) + x5 = self.layer4(x4) + x6 = self.layer5(x5) + + return x6,x5, x4, x3, x2,x1,x0 + + +def resnet18(pretrained=True, **kwargs): + """Constructs a ResNet-18 model. + Args: + pretrained (bool): If True, returns a model pre-trained on ImageNet + """ + model = ResNet(BasicBlock, [2, 2, 2, 2], **kwargs) + if pretrained: + model.load_state_dict(model_zoo.load_url( + model_urls['resnet18']), strict=False) + return model + + +def deformable_resnet18(pretrained=True, **kwargs): + """Constructs a ResNet-18 model. + Args: + pretrained (bool): If True, returns a model pre-trained on ImageNet + """ + model = ResNet(BasicBlock, [2, 2, 2, 2], + dcn=dict(modulated=True, + deformable_groups=1, + fallback_on_stride=False), + stage_with_dcn=[False, True, True, True], **kwargs) + if pretrained: + model.load_state_dict(model_zoo.load_url( + model_urls['resnet18']), strict=False) + return model + + +def resnet34(pretrained=True, **kwargs): + """Constructs a ResNet-34 model. + Args: + pretrained (bool): If True, returns a model pre-trained on ImageNet + """ + model = ResNet(BasicBlock, [3, 4, 6, 3], **kwargs) + if pretrained: + model.load_state_dict(model_zoo.load_url( + model_urls['resnet34']), strict=False) + return model + + +def resnet50(pretrained=True,load_url=False,**kwargs): + """Constructs a ResNet-50 model. + Args: + pretrained (bool): If True, returns a model pre-trained on ImageNet + """ + model = ResNet(Bottleneck, [3, 4, 6, 3,3], **kwargs) + if pretrained: + if load_url: + model.load_state_dict(model_zoo.load_url( + model_urls['resnet50']), strict=False) + else: + model = load_pre_model(model,'./pre_model/pre-trained-model-synthtext-resnet50.pth') + return model + + +def deformable_resnet50(pretrained=True, **kwargs): + """Constructs a ResNet-50 model with deformable conv. + Args: + pretrained (bool): If True, returns a model pre-trained on ImageNet + """ + model = ResNet(Bottleneck, [3, 4, 6, 3,3], + dcn=dict(modulated=True, + deformable_groups=1, + fallback_on_stride=False), + stage_with_dcn=[False, True, True, True], + **kwargs) + if pretrained: + model.load_state_dict(model_zoo.load_url( + model_urls['resnet50']), strict=False) + return model + + +def resnet101(pretrained=True, **kwargs): + """Constructs a ResNet-101 model. + Args: + pretrained (bool): If True, returns a model pre-trained on ImageNet + """ + model = ResNet(Bottleneck, [3, 4, 23, 3], **kwargs) + if pretrained: + model.load_state_dict(model_zoo.load_url( + model_urls['resnet101']), strict=False) + return model + + +def resnet152(pretrained=True, **kwargs): + """Constructs a ResNet-152 model. + Args: + pretrained (bool): If True, returns a model pre-trained on ImageNet + """ + model = ResNet(Bottleneck, [3, 8, 36, 3], **kwargs) + if pretrained: + model.load_state_dict(model_zoo.load_url( + model_urls['resnet152']), strict=False) + return model diff --git a/ptocr/model/backbone/det_resnet_sast_3_3.py b/ptocr/model/backbone/det_resnet_sast_3_3.py new file mode 100644 index 0000000..2089232 --- /dev/null +++ b/ptocr/model/backbone/det_resnet_sast_3_3.py @@ -0,0 +1,238 @@ + +import os +import sys +import torch +import torch.nn as nn +import math + +try: + from urllib import urlretrieve +except ImportError: + from urllib.request import urlretrieve + + +__all__ = ['resnet18', 'resnet50', 'resnet101'] + +model_urls = { + 'resnet18': 'http://sceneparsing.csail.mit.edu/model/pretrained_resnet/resnet18-imagenet.pth', + 'resnet50': 'http://sceneparsing.csail.mit.edu/model/pretrained_resnet/resnet50-imagenet.pth', + 'resnet101': 'http://sceneparsing.csail.mit.edu/model/pretrained_resnet/resnet101-imagenet.pth' +} + + +def conv3x3(in_planes, out_planes, stride=1): + "3x3 convolution with padding" + return nn.Conv2d(in_planes, out_planes, kernel_size=3, stride=stride, + padding=1, bias=False) + + +class BasicBlock(nn.Module): + expansion = 1 + + def __init__(self, inplanes, planes, stride=1, downsample=None): + super(BasicBlock, self).__init__() + self.conv1 = conv3x3(inplanes, planes, stride) + self.bn1 = nn.BatchNorm2d(planes) + self.relu = nn.ReLU(inplace=True) + self.conv2 = conv3x3(planes, planes) + self.bn2 = nn.BatchNorm2d(planes) + self.downsample = downsample + self.stride = stride + + def forward(self, x): + residual = x + + out = self.conv1(x) + out = self.bn1(out) + out = self.relu(out) + + out = self.conv2(out) + out = self.bn2(out) + + if self.downsample is not None: + residual = self.downsample(x) + + out += residual + out = self.relu(out) + + return out + + +class Bottleneck(nn.Module): + expansion = 4 + + def __init__(self, inplanes, planes, stride=1, downsample=None): + super(Bottleneck, self).__init__() + self.conv1 = nn.Conv2d(inplanes, planes, kernel_size=1, bias=False) + self.bn1 = nn.BatchNorm2d(planes) + self.conv2 = nn.Conv2d(planes, planes, kernel_size=3, stride=stride, + padding=1, bias=False) + self.bn2 = nn.BatchNorm2d(planes) + self.conv3 = nn.Conv2d(planes, planes * 4, kernel_size=1, bias=False) + self.bn3 = nn.BatchNorm2d(planes * 4) + self.relu = nn.ReLU(inplace=True) + self.downsample = downsample + self.stride = stride + + def forward(self, x): + residual = x + + out = self.conv1(x) + out = self.bn1(out) + out = self.relu(out) + + out = self.conv2(out) + out = self.bn2(out) + out = self.relu(out) + + out = self.conv3(out) + out = self.bn3(out) + + if self.downsample is not None: + residual = self.downsample(x) + + out += residual + out = self.relu(out) + + return out + + +class Convkxk(nn.Module): + def __init__(self, in_planes, out_planes, kernel_size=1, stride=1, padding=0): + super(Convkxk, self).__init__() + self.conv = nn.Conv2d(in_planes, out_planes, kernel_size=kernel_size, stride=stride, padding=padding, + bias=False) + self.bn = nn.BatchNorm2d(out_planes) + self.relu = nn.ReLU(inplace=True) + + for m in self.modules(): + if isinstance(m, nn.Conv2d): + n = m.kernel_size[0] * m.kernel_size[1] * m.out_channels + m.weight.data.normal_(0, math.sqrt(2. / n)) + elif isinstance(m, nn.BatchNorm2d): + m.weight.data.fill_(1) + m.bias.data.zero_() + + def forward(self, x): + return self.relu(self.bn(self.conv(x))) + + +class ResNet(nn.Module): + + def __init__(self, block, layers, num_classes=1000): + super(ResNet, self).__init__() + self.inplanes = 128 + self.conv1 = conv3x3(3, 64, stride=2) + self.bn1 = nn.BatchNorm2d(64) + self.relu1 = nn.ReLU(inplace=True) + self.conv2 = conv3x3(64, 64) + self.bn2 = nn.BatchNorm2d(64) + self.relu2 = nn.ReLU(inplace=True) + self.conv3 = conv3x3(64, 128) + self.bn3 = nn.BatchNorm2d(128) + self.relu3 = nn.ReLU(inplace=True) + self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1) + + self.layer1 = self._make_layer(block, 64, layers[0]) + self.layer2 = self._make_layer(block, 128, layers[1], stride=2) + self.layer3 = self._make_layer(block, 256, layers[2], stride=2) + self.layer4 = self._make_layer(block, 512, layers[3], stride=2) + self.layer5 = self._make_layer(block, 512, layers[3], stride=2) + # self.avgpool = nn.AvgPool2d(7, stride=1) + # self.fc = nn.Linear(512 * block.expansion, num_classes) + + for m in self.modules(): + if isinstance(m, nn.Conv2d): + n = m.kernel_size[0] * m.kernel_size[1] * m.out_channels + m.weight.data.normal_(0, math.sqrt(2. / n)) + elif isinstance(m, nn.BatchNorm2d): + m.weight.data.fill_(1) + m.bias.data.zero_() + + def _make_layer(self, block, planes, blocks, stride=1): + downsample = None + if stride != 1 or self.inplanes != planes * block.expansion: + downsample = nn.Sequential( + nn.Conv2d(self.inplanes, planes * block.expansion, + kernel_size=1, stride=stride, bias=False), + nn.BatchNorm2d(planes * block.expansion), + ) + + layers = [] + layers.append(block(self.inplanes, planes, stride, downsample)) + self.inplanes = planes * block.expansion + for i in range(1, blocks): + layers.append(block(self.inplanes, planes)) + + return nn.Sequential(*layers) + + def forward(self, x): + x0 = x.clone() + x = self.relu1(self.bn1(self.conv1(x))) + x = self.relu2(self.bn2(self.conv2(x))) + x = self.relu3(self.bn3(self.conv3(x))) + x1 = x.clone() + x = self.maxpool(x) + + + x = self.layer1(x) + x2 = x.clone() + x = self.layer2(x) + x3 = x.clone() + x = self.layer3(x) + x4 = x.clone() + x = self.layer4(x) + x5 = x.clone() + x = self.layer5(x) + x6 = x.clone() + + return x6,x5, x4, x3, x2,x1,x0 + + # x = self.avgpool(x) + # x = x.view(x.size(0), -1) + # x = self.fc(x) + + # return x + + +def resnet18(pretrained=False, **kwargs): + """Constructs a ResNet-18 model. + Args: + pretrained (bool): If True, returns a model pre-trained on Places + """ + model = ResNet(BasicBlock, [2, 2, 2, 2], **kwargs) + if pretrained: + model.load_state_dict(load_url(model_urls['resnet18']), strict=False) + return model + + +def resnet50(pretrained=False, **kwargs): + """Constructs a ResNet-50 model. + Args: + pretrained (bool): If True, returns a model pre-trained on Places + """ + model = ResNet(Bottleneck, [3, 4, 6, 3], **kwargs) + if pretrained: + model.load_state_dict(load_url(model_urls['resnet50']), strict=False) + return model + + +def resnet101(pretrained=False, **kwargs): + """Constructs a ResNet-101 model. + Args: + pretrained (bool): If True, returns a model pre-trained on Places + """ + model = ResNet(Bottleneck, [3, 4, 23, 3], **kwargs) + if pretrained: + model.load_state_dict(load_url(model_urls['resnet101']), strict=False) + return model + +def load_url(url, model_dir='./pre_model', map_location=None): + if not os.path.exists(model_dir): + os.makedirs(model_dir) + filename = url.split('/')[-1] + cached_file = os.path.join(model_dir, filename) + if not os.path.exists(cached_file): + sys.stderr.write('Downloading: "{}" to {}\n'.format(url, cached_file)) + urlretrieve(url, cached_file) + return torch.load(cached_file, map_location=map_location) \ No newline at end of file diff --git a/ptocr/model/backbone/rec_mobilev3_bd.py b/ptocr/model/backbone/rec_mobilev3_bd.py new file mode 100644 index 0000000..417abba --- /dev/null +++ b/ptocr/model/backbone/rec_mobilev3_bd.py @@ -0,0 +1,279 @@ +import torch.nn.functional as F +import torch.nn as nn + + +class hswish(nn.Module): + def forward(self, x): + out = x * F.relu6(x + 3, inplace=True) / 6 + return out + +class hsigmoid(nn.Module): + def forward(self, x): + out = F.relu6(x + 3, inplace=True) / 6 + return out + +def make_divisible(v, divisor=8, min_value=None): + if min_value is None: + min_value = divisor + new_v = max(min_value, int(v + divisor / 2) // divisor * divisor) + if new_v < 0.9 * v: + new_v += divisor + return new_v + +class ConvBNLayer(nn.Module): + def __init__(self, + in_channels, + out_channels, + kernel_size, + stride, + padding, + groups=1, + if_act=True, + act=None): + super(ConvBNLayer, self).__init__() + self.if_act = if_act + self.act = act + if self.act == "hardswish": + self.hardswish = hswish() + + self.conv = nn.Conv2d( + in_channels=in_channels, + out_channels=out_channels, + kernel_size=kernel_size, + stride=stride, + padding=padding, + groups=groups) + + self.bn = nn.BatchNorm2d(out_channels) + + def forward(self, x): + x = self.conv(x) + x = self.bn(x) + if self.if_act: + if self.act == "relu": + x = F.relu(x) + elif self.act == "hardswish": + x = self.hardswish(x) + else: + print("The activation function({}) is selected incorrectly.". + format(self.act)) + exit() + return x + +class ResidualUnit(nn.Module): + def __init__(self, + in_channels, + mid_channels, + out_channels, + kernel_size, + stride, + use_se, + act=None): + super(ResidualUnit, self).__init__() + self.if_shortcut = stride == 1 and in_channels == out_channels + self.if_se = use_se + + self.expand_conv = ConvBNLayer( + in_channels=in_channels, + out_channels=mid_channels, + kernel_size=1, + stride=1, + padding=0, + if_act=True, + act=act) + self.bottleneck_conv = ConvBNLayer( + in_channels=mid_channels, + out_channels=mid_channels, + kernel_size=kernel_size, + stride=stride, + padding=int((kernel_size - 1) // 2), + groups=mid_channels, + if_act=True, + act=act) + if self.if_se: + self.mid_se = SEModule(mid_channels) + self.linear_conv = ConvBNLayer( + in_channels=mid_channels, + out_channels=out_channels, + kernel_size=1, + stride=1, + padding=0, + if_act=False, + act=None) + + def forward(self, inputs): + x = self.expand_conv(inputs) + x = self.bottleneck_conv(x) + if self.if_se: + x = self.mid_se(x) + x = self.linear_conv(x) + if self.if_shortcut: + x = inputs+x + return x + + +class SEModule(nn.Module): + def __init__(self, in_channels, reduction=4): + super(SEModule, self).__init__() + self.avg_pool = nn.AdaptiveAvgPool2d(1) + self.conv1 = nn.Conv2d( + in_channels=in_channels, + out_channels=in_channels // reduction, + kernel_size=1, + stride=1, + padding=0) + self.conv2 = nn.Conv2d( + in_channels=in_channels // reduction, + out_channels=in_channels, + kernel_size=1, + stride=1, + padding=0) + self.hardsigmoid = hsigmoid() + + def forward(self, inputs): + outputs = self.avg_pool(inputs) + outputs = self.conv1(outputs) + outputs = F.relu(outputs) + outputs = self.conv2(outputs) + outputs = self.hardsigmoid(outputs) + return inputs * outputs + +class MobileNetV3(nn.Module): + def __init__(self, + in_channels=3, + model_name='small', + scale=0.5, + large_stride=None, + small_stride=None, + **kwargs): + super(MobileNetV3, self).__init__() + if small_stride is None: + small_stride = [1, 2, 2, 2] + if large_stride is None: + large_stride = [1, 2, 2, 2] + + assert isinstance(large_stride, list), "large_stride type must " \ + "be list but got {}".format(type(large_stride)) + assert isinstance(small_stride, list), "small_stride type must " \ + "be list but got {}".format(type(small_stride)) + assert len(large_stride) == 4, "large_stride length must be " \ + "4 but got {}".format(len(large_stride)) + assert len(small_stride) == 4, "small_stride length must be " \ + "4 but got {}".format(len(small_stride)) + + if model_name == "large": + cfg = [ + # k, exp, c, se, nl, s, + [3, 16, 16, False, 'relu', large_stride[0]], + [3, 64, 24, False, 'relu', (large_stride[1], 1)], + [3, 72, 24, False, 'relu', 1], + [5, 72, 40, True, 'relu', (large_stride[2], 1)], + [5, 120, 40, True, 'relu', 1], + [5, 120, 40, True, 'relu', 1], + [3, 240, 80, False, 'hardswish', 1], + [3, 200, 80, False, 'hardswish', 1], + [3, 184, 80, False, 'hardswish', 1], + [3, 184, 80, False, 'hardswish', 1], + [3, 480, 112, True, 'hardswish', 1], + [3, 672, 112, True, 'hardswish', 1], + [5, 672, 160, True, 'hardswish', (large_stride[3], 1)], + [5, 960, 160, True, 'hardswish', 1], + [5, 960, 160, True, 'hardswish', 1], + ] + cls_ch_squeeze = 960 + elif model_name == "small": + cfg = [ + # k, exp, c, se, nl, s, + [3, 16, 16, True, 'relu', (small_stride[0], 1)], + [3, 72, 24, False, 'relu', (small_stride[1], 1)], + [3, 88, 24, False, 'relu', 1], + [5, 96, 40, True, 'hardswish', (small_stride[2], 1)], + [5, 240, 40, True, 'hardswish', 1], + [5, 240, 40, True, 'hardswish', 1], + [5, 120, 48, True, 'hardswish', 1], + [5, 144, 48, True, 'hardswish', 1], + [5, 288, 96, True, 'hardswish', (small_stride[3], 1)], + [5, 576, 96, True, 'hardswish', 1], + [5, 576, 96, True, 'hardswish', 1], + ] + cls_ch_squeeze = 576 + else: + raise NotImplementedError("mode[" + model_name + + "_model] is not implemented!") + + supported_scale = [0.35, 0.5, 0.75, 1.0, 1.25] + assert scale in supported_scale, \ + "supported scales are {} but input scale is {}".format(supported_scale, scale) + + inplanes = 16 + # conv1 + self.conv1 = ConvBNLayer( + in_channels=in_channels, + out_channels=make_divisible(inplanes * scale), + kernel_size=3, + stride=2, + padding=1, + groups=1, + if_act=True, + act='hardswish') + i = 0 + block_list = [] + inplanes = make_divisible(inplanes * scale) + for (k, exp, c, se, nl, s) in cfg: + block_list.append( + ResidualUnit( + in_channels=inplanes, + mid_channels=make_divisible(scale * exp), + out_channels=make_divisible(scale * c), + kernel_size=k, + stride=s, + use_se=se, + act=nl)) + inplanes = make_divisible(scale * c) + i += 1 + self.blocks = nn.Sequential(*block_list) + + self.conv2 = ConvBNLayer( + in_channels=inplanes, + out_channels=make_divisible(scale * cls_ch_squeeze), + kernel_size=1, + stride=1, + padding=0, + groups=1, + if_act=True, + act='hardswish') + + self.pool = nn.MaxPool2d(kernel_size=2, stride=2, padding=0) + self.out_channels = make_divisible(scale * cls_ch_squeeze) + + def forward(self, x): + x = self.conv1(x) + x = self.blocks(x) + x = self.conv2(x) + x = self.pool(x) + return x + + +def mobilenet_v3_small(pretrained, is_gray=False,**kwargs): + if is_gray: + in_channels = 1 + else: + in_channels = 3 + model = MobileNetV3( in_channels=in_channels, + model_name='small', + scale = 1) + if pretrained: + pass + return model + +def mobilenet_v3_large(pretrained,is_gray=False,**kwargs): + if is_gray: + in_channels = 1 + else: + in_channels = 3 + model = MobileNetV3( in_channels=in_channels, + model_name='large', + scale=1) + if pretrained: + pass + return model \ No newline at end of file diff --git a/ptocr/model/backbone/reg_mobilev3.py b/ptocr/model/backbone/reg_mobilev3.py new file mode 100644 index 0000000..5c799cd --- /dev/null +++ b/ptocr/model/backbone/reg_mobilev3.py @@ -0,0 +1,218 @@ +""" +#!-*- coding=utf-8 -*- +@author: BADBADBADBADBOY +@contact: 2441124901@qq.com +@software: PyCharm Community Edition +@file: mobilenet.py +@time: 2020/4/5 19:12 +""" +from torch import nn +import torch +import torch.nn.functional as F +from torch.nn import init + +__all__ = ['mobilenet_v3_small','mobilenet_v3_large'] + +class hswish(nn.Module): + def forward(self, x): + out = x * F.relu6(x + 3, inplace=True) / 6 + return out + + +class hsigmoid(nn.Module): + def forward(self, x): + out = F.relu6(x + 3, inplace=True) / 6 + return out + + +class SeModule(nn.Module): + def __init__(self, in_size, reduction=4): + super(SeModule, self).__init__() + self.avg_pool = nn.AdaptiveAvgPool2d(1) + + self.se = nn.Sequential( + nn.Conv2d(in_size, in_size // reduction, kernel_size=1, stride=1, padding=0, bias=False), + nn.BatchNorm2d(in_size // reduction), + nn.ReLU(inplace=True), + nn.Conv2d(in_size // reduction, in_size, kernel_size=1, stride=1, padding=0, bias=False), + nn.BatchNorm2d(in_size), + hsigmoid() + ) + + def forward(self, x): + return x * self.se(x) + + +class Block(nn.Module): + '''expand + depthwise + pointwise''' + def __init__(self, kernel_size, in_size, expand_size, out_size, nolinear, semodule, stride): + super(Block, self).__init__() + self.stride = stride + self.se = semodule + + self.conv1 = nn.Conv2d(in_size, expand_size, kernel_size=1, stride=1, padding=0, bias=False) + self.bn1 = nn.BatchNorm2d(expand_size) + self.nolinear1 = nolinear + self.conv2 = nn.Conv2d(expand_size, expand_size, kernel_size=kernel_size, stride=stride, padding=kernel_size//2, groups=expand_size, bias=False) + self.bn2 = nn.BatchNorm2d(expand_size) + self.nolinear2 = nolinear + self.conv3 = nn.Conv2d(expand_size, out_size, kernel_size=1, stride=1, padding=0, bias=False) + self.bn3 = nn.BatchNorm2d(out_size) + + self.shortcut = nn.Sequential() + if stride == 1 and in_size != out_size: + self.shortcut = nn.Sequential( + nn.Conv2d(in_size, out_size, kernel_size=1, stride=1, padding=0, bias=False), + nn.BatchNorm2d(out_size), + ) + + def forward(self, x): + out = self.nolinear1(self.bn1(self.conv1(x))) + out = self.nolinear2(self.bn2(self.conv2(out))) + out = self.bn3(self.conv3(out)) + if self.se != None: + out = self.se(out) + out = out + self.shortcut(x) if self.stride==1 else out + return out + + +class MobileNetV3_Large(nn.Module): + def __init__(self, is_gray): + super(MobileNetV3_Large, self).__init__() + if(is_gray): + self.conv1 = nn.Conv2d(1, 16, kernel_size=3, stride=2, padding=1, bias=False) + else: + self.conv1 = nn.Conv2d(3, 16, kernel_size=3, stride=2, padding=1, bias=False) + self.bn1 = nn.BatchNorm2d(16) + self.hs1 = hswish() + + self.bneck = nn.Sequential( + Block(3, 16, 16, 16, nn.ReLU(inplace=True), None, 1), + Block(3, 16, 64, 24, nn.ReLU(inplace=True), None, (2,1)), + Block(3, 24, 72, 24, nn.ReLU(inplace=True), None, 1), + Block(5, 24, 72, 40, nn.ReLU(inplace=True), SeModule(40), (2,1)), + Block(5, 40, 120, 40, nn.ReLU(inplace=True), SeModule(40), 1), + Block(5, 40, 120, 40, nn.ReLU(inplace=True), SeModule(40), 1), + Block(3, 40, 240, 80, hswish(), None, 2), + Block(3, 80, 200, 80, hswish(), None, 1), + Block(3, 80, 184, 80, hswish(), None, 1), + Block(3, 80, 184, 80, hswish(), None, 1), + Block(3, 80, 480, 112, hswish(), SeModule(112), (2,1)), + Block(3, 112, 672, 112, hswish(), SeModule(112), 1), + Block(5, 112, 672, 160, hswish(), SeModule(160), 1), + Block(5, 160, 672, 160, hswish(), SeModule(160), 1), + Block(5, 160, 960, 160, hswish(), SeModule(160), 1), + ) + self.init_params() + + def init_params(self): + for m in self.modules(): + if isinstance(m, nn.Conv2d): + init.kaiming_normal_(m.weight, mode='fan_out') + if m.bias is not None: + init.constant_(m.bias, 0) + elif isinstance(m, nn.BatchNorm2d): + init.constant_(m.weight, 1) + init.constant_(m.bias, 0) + elif isinstance(m, nn.Linear): + init.normal_(m.weight, std=0.001) + if m.bias is not None: + init.constant_(m.bias, 0) + + def forward(self, x): + out = self.hs1(self.bn1(self.conv1(x))) + out = self.bneck(out) + return out + + + +class MobileNetV3_Small(nn.Module): + def __init__(self, is_gray): + super(MobileNetV3_Small, self).__init__() + if(is_gray): + self.conv1 = nn.Conv2d(1, 16, kernel_size=3, stride=2, padding=1, bias=False) + else: + self.conv1 = nn.Conv2d(3, 16, kernel_size=3, stride=2, padding=1, bias=False) + self.bn1 = nn.BatchNorm2d(16) + self.hs1 = hswish() + + self.bneck = nn.Sequential( + Block(3, 16, 16, 16, nn.ReLU(inplace=True), SeModule(16), (2,1)), + Block(3, 16, 72, 24, nn.ReLU(inplace=True), None, 1), + Block(3, 24, 88, 24, nn.ReLU(inplace=True), None, (2,1)), + Block(5, 24, 96, 40, hswish(), SeModule(40), 1), + Block(5, 40, 240, 40, hswish(), SeModule(40), 1), + Block(5, 40, 240, 40, hswish(), SeModule(40), 2), + Block(5, 40, 120, 48, hswish(), SeModule(48), 1), + Block(5, 48, 144, 48, hswish(), SeModule(48), (2,1)), + Block(5, 48, 288, 96, hswish(), SeModule(96), 1), + Block(5, 96, 576, 96, hswish(), SeModule(96), 1), + Block(5, 96, 576, 96, hswish(), SeModule(96), 1), + ) + +# self.bneck = nn.Sequential( +# Block(3, 16, 16, 16, nn.ReLU(inplace=True), SeModule(16),1), +# Block(3, 16, 72, 24, nn.ReLU(inplace=True), None, (2,1)), +# Block(3, 24, 88, 24, nn.ReLU(inplace=True), None, 1), +# Block(5, 24, 96, 40, hswish(), SeModule(40), (2,1)), +# Block(5, 40, 240, 40, hswish(), SeModule(40), 1), +# Block(5, 40, 240, 40, hswish(), SeModule(40), 1), +# Block(5, 40, 120, 48, hswish(), SeModule(48), 2), +# Block(5, 48, 144, 48, hswish(), SeModule(48), 1), +# Block(5, 48, 288, 96, hswish(), SeModule(96),(2,1)), +# Block(5, 96, 576, 96, hswish(), SeModule(96), 1), +# Block(5, 96, 576, 96, hswish(), SeModule(96), 1), +# ) + + self.init_params() + + def init_params(self): + for m in self.modules(): + if isinstance(m, nn.Conv2d): + init.kaiming_normal_(m.weight, mode='fan_out') + if m.bias is not None: + init.constant_(m.bias, 0) + elif isinstance(m, nn.BatchNorm2d): + init.constant_(m.weight, 1) + init.constant_(m.bias, 0) + elif isinstance(m, nn.Linear): + init.normal_(m.weight, std=0.001) + if m.bias is not None: + init.constant_(m.bias, 0) + + def forward(self, x): + out = self.hs1(self.bn1(self.conv1(x))) + out = self.bneck(out) + return out + +def mobilenet_v3_small(pretrained, is_gray=False,**kwargs): + model = MobileNetV3_Small(is_gray=is_gray) + if pretrained: + pretrained_dict = torch.load('./pre_model/mbv3_small.old.pth.tar')['state_dict'] + + state = model.state_dict() + for key in state.keys(): + if 'module.' + key in pretrained_dict.keys(): + if(key=='conv1.weight'and is_gray): + state[key] = torch.mean(pretrained_dict['module.' + key],1).unsqueeze(1) + else: + state[key] = pretrained_dict['module.' + key] + model.load_state_dict(state) + return model + +def mobilenet_v3_large(pretrained,is_gray=False,**kwargs): + model = MobileNetV3_Large(is_gray=is_gray) + if pretrained: + pretrained_dict = torch.load('./pre_model/mbv3_large.old.pth.tar')['state_dict'] + + state = model.state_dict() + for key in state.keys(): + if 'module.'+key in pretrained_dict.keys(): + if(key=='conv1.weight'and is_gray): + state[key] = torch.mean(pretrained_dict['module.' + key],1).unsqueeze(1) + else: + state[key] = pretrained_dict['module.' + key] + model.load_state_dict(state) + return model + + diff --git a/ptocr/model/backbone/reg_resnet_bd.py b/ptocr/model/backbone/reg_resnet_bd.py new file mode 100644 index 0000000..0840aa2 --- /dev/null +++ b/ptocr/model/backbone/reg_resnet_bd.py @@ -0,0 +1,267 @@ +import torch.nn.functional as F +import torch.nn as nn + +class ConvBNLayer(nn.Module): + def __init__(self,in_channels, + out_channels, + kernel_size, + stride=1, + groups=1, + is_relu=False, + is_vd_mode=False): + super(ConvBNLayer,self).__init__() + + self.is_vd_mode = is_vd_mode + self.is_relu = is_relu + + if is_vd_mode: + self._pool2d_avg = nn.AvgPool2d(kernel_size=stride, stride=stride, padding=0, ceil_mode=True) + + self.conv = nn.Conv2d(in_channels=in_channels, + out_channels=out_channels, + kernel_size=kernel_size, + stride=1 if is_vd_mode else stride, + padding=(kernel_size - 1) // 2, + groups=groups) + self.bn = nn.BatchNorm2d(out_channels) + self.relu = nn.ReLU() + + def forward(self,x): + if self.is_vd_mode: + x = self._pool2d_avg(x) + x = self.bn(self.conv(x)) + if self.is_relu: + x = self.relu(x) + return x + + +class BottleneckBlock(nn.Module): + def __init__(self, + in_channels, + out_channels, + stride, + shortcut=True, + if_first=False): + super(BottleneckBlock, self).__init__() + + self.conv0 = ConvBNLayer( + in_channels=in_channels, + out_channels=out_channels, + kernel_size=1, + is_relu=True) + + self.conv1 = ConvBNLayer( + in_channels=out_channels, + out_channels=out_channels, + kernel_size=3, + stride=stride, + is_relu=True) + + self.conv2 = ConvBNLayer( + in_channels=out_channels, + out_channels=out_channels * 4, + kernel_size=1) + + if not shortcut: + self.short = ConvBNLayer( + in_channels=in_channels, + out_channels=out_channels * 4, + kernel_size=1, + stride=stride, + is_vd_mode=not if_first and stride[0] != 1) + + self.shortcut = shortcut + + def forward(self,x): + y = self.conv0(x) + y = self.conv2(self.conv1(y)) + if self.shortcut: + short = x + else: + short = self.short(x) + y = y+short + y = F.relu(y) + return y + + +class BasicBlock(nn.Module): + def __init__(self, + in_channels, + out_channels, + stride, + shortcut=True, + if_first=False): + super(BasicBlock, self).__init__() + self.stride = stride + self.conv0 = ConvBNLayer( + in_channels=in_channels, + out_channels=out_channels, + kernel_size=3, + stride=stride, + is_relu=True) + self.conv1 = ConvBNLayer( + in_channels=out_channels, + out_channels=out_channels, + kernel_size=3) + + if not shortcut: + self.short = ConvBNLayer( + in_channels=in_channels, + out_channels=out_channels, + kernel_size=1, + stride=stride, + is_vd_mode=not if_first and stride[0] != 1) + + self.shortcut = shortcut + + def forward(self, x): + y = self.conv0(x) + y = self.conv1(y) + + if self.shortcut: + short = x + else: + short = self.short(x) + y = y+short + y = F.relu(y) + return y + +class ResNet(nn.Module): + def __init__(self, in_channels=3, layers=50, **kwargs): + super(ResNet, self).__init__() + + self.layers = layers + supported_layers = [18, 34, 50, 101, 152, 200] + assert layers in supported_layers, \ + "supported layers are {} but input layer is {}".format( + supported_layers, layers) + + if layers == 18: + depth = [2, 2, 2, 2] + elif layers == 34 or layers == 50: + depth = [3, 4, 6, 3] + elif layers == 101: + depth = [3, 4, 23, 3] + elif layers == 152: + depth = [3, 8, 36, 3] + elif layers == 200: + depth = [3, 12, 48, 3] + num_channels = [64, 256, 512, + 1024] if layers >= 50 else [64, 64, 128, 256] + num_filters = [64, 128, 256, 512] + + self.conv1_1 = ConvBNLayer( + in_channels=in_channels, + out_channels=32, + kernel_size=3, + stride=1, + is_relu=True) + self.conv1_2 = ConvBNLayer( + in_channels=32, + out_channels=32, + kernel_size=3, + stride=1, + is_relu=True) + self.conv1_3 = ConvBNLayer( + in_channels=32, + out_channels=64, + kernel_size=3, + stride=1, + is_relu=True) + self.pool2d_max = nn.MaxPool2d(kernel_size=3, stride=2, padding=1) + + block_list = [] + if layers >= 50: + for block in range(len(depth)): + shortcut = False + for i in range(depth[block]): + if i == 0 and block != 0: + stride = (2, 1) + else: + stride = (1, 1) + bottleneck_block = BottleneckBlock( + in_channels=num_channels[block] + if i == 0 else num_filters[block] * 4, + out_channels=num_filters[block], + stride=stride, + shortcut=shortcut, + if_first=block == i == 0) + shortcut = True + block_list.append(bottleneck_block) + self.out_channels = num_filters[block] + else: + for block in range(len(depth)): + shortcut = False + for i in range(depth[block]): + if i == 0 and block != 0: + stride = (2, 1) + else: + stride = (1, 1) + + basic_block = BasicBlock( + in_channels=num_channels[block] + if i == 0 else num_filters[block], + out_channels=num_filters[block], + stride=stride, + shortcut=shortcut, + if_first=block == i == 0) + shortcut = True + block_list.append(basic_block) + self.out_channels = num_filters[block] + self.block = nn.Sequential(*block_list) + self.out_pool = nn.MaxPool2d(kernel_size=2, stride=2, padding=0) + + def forward(self, x): + y = self.conv1_1(x) + y = self.conv1_2(y) + y = self.conv1_3(y) + y = self.pool2d_max(y) + for block in self.block: + y = block(y) + y = self.out_pool(y) + return y + + + + +def resnet18(pretrained=False, is_gray=False,**kwargs): + """Constructs a ResNet-18 model. + Args: + pretrained (bool): If True, returns a model pre-trained on ImageNet + """ + if is_gray: + in_channels = 1 + else: + in_channels = 3 + model = ResNet(in_channels=in_channels, layers=18, **kwargs) + if pretrained: + pass + return model + +def resnet34(pretrained=False, is_gray=False, **kwargs): + """Constructs a ResNet-34 model. + Args: + pretrained (bool): If True, returns a model pre-trained on ImageNet + """ + if is_gray: + in_channels = 1 + else: + in_channels = 3 + model = ResNet(in_channels=in_channels, layers=34, **kwargs) + if pretrained: + pass + return model + +def resnet50(pretrained=False, is_gray=False, **kwargs): + """Constructs a ResNet-50 model. + Args: + pretrained (bool): If True, returns a model pre-trained on ImageNet + """ + if is_gray: + in_channels = 1 + else: + in_channels = 3 + model = ResNet(in_channels=in_channels, layers=50, **kwargs) + if pretrained: + pass + return model \ No newline at end of file diff --git a/ptocr/model/head/__init__.py b/ptocr/model/head/__init__.py new file mode 100644 index 0000000..35de2ad --- /dev/null +++ b/ptocr/model/head/__init__.py @@ -0,0 +1,6 @@ +#-*- coding:utf-8 _*- +""" +@author:fxw +@file: __init__.py.py +@time: 2020/08/07 +""" diff --git a/ptocr/model/head/__pycache__/__init__.cpython-35.pyc b/ptocr/model/head/__pycache__/__init__.cpython-35.pyc new file mode 100644 index 0000000000000000000000000000000000000000..307d7364d5bc8d2ead367985f592d2a2439d3ead GIT binary patch literal 224 zcmWgR<>mVJMJGOjfq~&M5W@i@kmUfx#auulg@GXoNHQ`6Ycf@taycZHmSp4?S*2B! zb2+4C=A>FF#K&jmWtPOp>lIW25tlfLBZA%DZU|@I*#Bjg}WH|tFF$a)HVTfW#VGL%_WU4ada!4#K$;dCVN~}zQ`1q9! wMNB|5!Ne~)J^g}`{Ny72-29Z(9Q};c#1wrb({6Ft03}LuQtjA4ZUJHj0IwV@Z~y=R literal 0 HcmV?d00001 diff --git a/ptocr/model/head/__pycache__/det_DBHead.cpython-35.pyc b/ptocr/model/head/__pycache__/det_DBHead.cpython-35.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d8eb8b0e6eb7008be9fac9ff6fb82aef14065eca GIT binary patch literal 2057 zcma)7-ESL35T8At@0_y}r%BT`RaNzYN;QZyI0>K#(I}}34-l%h2w@qmj=OVm=04oL zGf9K<6h8hIUidHgC-xDZEM9ov3Gwxtb=;%^qV8pW-kF`9otd54T$!EqzLIVEgG=-? zof|ghTcDx?2#N3$3JNVs1a&Z(%q{9ys8gX3e>NpHrB&)wDRrphQ0h|0CGlQZrO=_T zVZfoNLj4+@VABj6d)#u5*1au%SnlU)vv+vlZS~?L+PoD;rM$oWe&mNMgJ<4W8K)4o zSKF(1SKq$7`j$6_SF}5}fUS!5cJl1Y?d(yM40UZdDExGgM7lAZNk0q&-Y^{;9FO7+ zK!o5B4bbASz9Z_H;1JWdh}A@f5a55u=GDh7N4xj8B|G;Zn3}4>(hR7=W`7h!CH@4B zh)$3si%y6{S)mb3*J*iVj+!J9=Zq?JQWemZHk~RpE>NmYm1Zwca;8di7bv+#$s+d0 zq3e88aL_e4am%*Yt=dcK&AgYBX~Yf z{YxzGFHnDxP6UakPsEw(Wuy8;JlE(iQHV-}9*eH^hekx~w{bxq1Ri%TgLThx^}+hAB{SYv2w zJ0F%C2G66O&-M<7II;Wi8SX=+4^`feg0cW#<|^3#Xy;LPQ09T^BAh7c z?xSD3FT5-265MHkikkp(7OmUDeAXRdK5J1dh-J{CIGR6g`ITv21HLXE3m|U)9Z>Nb z08QSt>DZ#<3fJp#)s!&gRW7b$$FO)CY{R;SMGxW;z-rB~Xg<6dV9`-Fjj+Y#0zO9L zfc0pE(gj;LY@J3ZTd)l(Cuc$5;q4>z4dk;^InMIV$>*kW99lmoZyFzF%y#%NPo+nF zoPx%iqY+l;sb}*e!N$>bm(VHJuEPHiYBdz+LoG5~W>{k2{3xz!b%o(7!!?FwfUcSg z)0Mz4TMOzH7KuY`1?`R%tbx`+8~)|GnrY*GR2N|=63Ql&9eu? rEGXkVGp#*ID<@bxADYHBrq0ei7kcfTiM#Le6H#zCI--F%Z&?2T6WGM0 literal 0 HcmV?d00001 diff --git a/ptocr/model/head/__pycache__/det_DBHead.cpython-36.pyc b/ptocr/model/head/__pycache__/det_DBHead.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ba53d8e1324ce455ff42976b93dbc7199f144b1f GIT binary patch literal 1770 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 L+quQs;(PxEPe3aP literal 0 HcmV?d00001 diff --git a/ptocr/model/head/__pycache__/det_FPEM_FFM_Head.cpython-36.pyc b/ptocr/model/head/__pycache__/det_FPEM_FFM_Head.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3bb25e6504e7a9950700badc0cd496de283eace8 GIT binary patch literal 3585 zcmbVOTW=gm6|U<0^jv(2li1!R1PIxkh4p0|*d@E#m^c^&*dWlZr532wnd-5pGt)Dv z?jgqXJWZs1LgIy&{RjL9BwqH3pHLDn@D%Zac;t!iRL{kZ^8!7pQ*};VrmD_&zEgYk zy8pL7{v~|6#MnRBrO!fpA0<135G-JV3!}#au4^MP0~0N?XZ7vC?mL0gcLTTY1s-QV zWWo~mOD62tOezyI@L_R;3yV9oRAKQ%1(wRxQWM56S<}Dx$ls4f`Po1|?3};w_dDHQ z{P3=b^YHNJKR5~x50Anh#gW(={?^~myM6Svc3L~zJ0EWEeBi&rR@rgWQtp$J$Gt)O zneq2DsVzleJyRT~Yns6Xt*VI+j|W?Gvj%|3J5$nK#LGn|$M#^O}q92bcx{J3cEru-%KYEDZm|{`1b(_Ano`<#vA{;@ zVQ*Wr)!W(#yNg2pJwVBFhyv%^V861)#@HNNWBdHGoCyOP=Mz3Lp0Hm(89Rkh@WR;u z4mQ{aY-|+fR5!G47S>cZwQd#mRJXKlKfimzum+8@I~LvCv83C2r3G#66P_~TH z(^#3kIBmMJ0;A&Rf!B|Qt2J-!&07x?@5(Bsl!ER;xt)Q05lI1vv#bHZ_%dHZ zq2C(6sL~}($21-CMjG|wFjRgR;=)HjOsHyM_53~2fB*vPAek=K&?#E@U0ZX`>Ekt`=G6VBQ5^J(uY zde2LHazj|u2W>4`fo7K@Ps)&SjwN|d)=7{HPf_sw>(S}^2XFuL@88{g)VwJP*K&gd z#epPDsmf>=J|!;LT{K&Z=ANpG!HaY@gV*>fN`qh22_Dy@pIB)g{eOT` z@I~b33~_5s48%K25bqN{c42c16E+Vvqwrv>z*Z>?*nHT`!iTM@ZB^K6uvtY7wz{5M zfvo|XT{K`@g3T^m*p^{)ie=bVifU0WmWmb43?N8X(iSD1P`*XtZ4zH0afifLA)2fD z&Lka38RspOJ(LG1-wCSlJJ=!wYwQLt6y3OFZOyE$%VYOu*1e#vfiYjBqE)@hm7Z2G z?`d6~v$yEkTM$FZ2*e%lp=5L}27me7H{Mi0;pD|RJ;|FGFK>~!O+q7`R+H-_=F~TX zjYbTig}P__v!bKq3jfe`)0f?cm}NfU>fA>;uY%lX3+GDi^OT0zJS*$SJZuvQ(MRD( z+tY+}A2ffRkSLiHhM*r3gRp)N*p<{I>^Jmik`hQb*XC%t5=cGQdWg6T;jCTn@ib*s zrj%vpeqra;qy|#@GwOQMDC}e@0X#ujUrc*fh|o%VvMO9nS$1**)V3-R7&P*Poxhi_ zAz(PD@XpxDZx(pwB)1Aj7`NGk=eH3!d}3nkzfV}cK4CcX1`;C+H}(6#zf5+N9g&o( z-E=_;wZSM~Xw_%21XqMv_aatKmdmb)l{*A*!9$Wjrv37YJbaxtFWk;B?uWk{H{UAx zbC)J)RFU75oAn2S{A_pDXw4d+#IoywGO-_dm*#zq#MeoDgTy@&-y}h(lJAgsmqe3< z-r6!+SSik-{x|w}{ouUzh@y4rnhsF1FCk9pQAAX# zpHP}W8KD3q$3dx78wJuDYLkeyV~ySO2RV{)0xPg`f9&PJzu<{mcqLXQ28a?AjI`)c z3z*r*Gm0fdzC^=*QY|Wk`@DvlB13BvBww`f^tz%`OilYcmYC6ZUDLN;_;{2#$8$iG z$(Hb^@uL%Pvmrks!$T6^Bk>^#a-#Nua!*T7%1>ZYPBa|GsgRFIvGaj!pDD8q{}M&W zk0F}X1&0UDdD!a$!0%J<7Kv>VI}m~QG|FNkaqZl|0pyONVPJJeS-fDq1-X8TNz(*r zfnM(+Nv^}?pje$Q*HK^Gyz1f^#~LM%HJ$&9Ybk#A>CY+NJl e|K*hs`77_^^6Byk#ZyKm3;6c=y0KbcefPiWvldPO literal 0 HcmV?d00001 diff --git a/ptocr/model/head/__pycache__/det_FPNHead.cpython-35.pyc b/ptocr/model/head/__pycache__/det_FPNHead.cpython-35.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f3652548def3cb8bed56c513cc2a07653d9b830c GIT binary patch literal 2166 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 D^qW8K literal 0 HcmV?d00001 diff --git a/ptocr/model/head/__pycache__/rec_FCHead.cpython-36.pyc b/ptocr/model/head/__pycache__/rec_FCHead.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b66d2ebbb17a07756f5db4f5867c0acf7cfdbbd9 GIT binary patch literal 2841 zcma)8TW=dh6rP#AdgG)?h-gzvF>NUrp+tmo5vow5l!8!+sx}m4F3Wajli1m;-Pv`U zh7AumQlH90CHM(QkyJjp0d`kkU|S~e`UbYO8Rivx=*Jy^WT;!1vt zHT|bpk*&uhN5!p_{B0-+YjMe1Lh_a+>kru`m!9+wYb{&WWN^q@&O>IimW#EfwSR5x z%dYIF$^OZa7Bo;+)mHwo(KfWLlC;0B#b&InC@x~Z+KpPer-hpT2~n_umuw(9V!${n zy^@7iX+zqj1L>5uWIHuzZs}H(A1&SVRwN1*31dQJ=&Sz+_kANZcdj*b~QCmC+;tt&dVwu>m+ z-aq@(L8F~F;=WQzR-_LaS+{89{a&xDibO6ox%S#wFOA{^D(^LgnXJg|&3Wx$WTKRy z_53hwXYC>kwbNBHQ96Lf+V`TO-OZFoBj)b6i|w$o8oRC=lT_Vaw_j+HW!k;o&XPze za<2|FvhTE`Tmit$98jGkZ3+T+t%YHnMtL5FPuM?=rR84Hjn(pQS0?H5b`r^D zmBit-wa=k0^&Y4Ivj`RWISA&@aF>1};DMO6sC7AXK1))#e1C3)xuucYV)7ytYUzd6 zQ1c%k@RSG_JnuzlyaX5y2n<4f0cj1tvkPXFjZtJ3>dK@K+GLikTKIOVRVb{Sb>nR1 zU@N>YIew&*$rzfh@yoDtnyd>XW=N3NFz*x^&G*!+WHaI{Sv)-3{Yc5T@R7d^!73Nj z(Hf_~9f8=N9Xq6&@Z{)GoDGA!jdk@pR%m*Jwk+CBfVLnE@hXU8==CtX+mF(!#|uN* zjliM>cRKFXVJR-?D9lwE4{}n!Xt3aUmB`IZi-< zAs`|$A|PMZo*|$qT^rpv-#;~0pQj*m?e(V|JB-ZKXAABL2d6!;Yd+>196pozj6=^c}i`T zZW&BwmbH$H*@9SWG<(1~yL;nnR(Ku1to=&tQ!`9stt&dWqNi^LijHiaLR)E)>6z6$ zPZY6(iSHqaI4a7v675F4UXn?50#kHwE4kYTIklryJ9pA1{>(PjhYh>DP$&$ zkuOyd(wszvaj~a}eAIao7f8H8f|$vGUtJ{a#gKm!T_2$yhx|*5`eM{^p=(mfX_HaT zHDKpb9Qw$;mb3wO+hkjZC`NnN)Co#*jMzEvMWycl-h zp(iK!GBLyJe1@L}H4-HnpB)Dpfr6a3hMIo^F$Nllp2)s~|3HgK8UtZcMU^Bl3XzZW z0XSlm7>=TJAO)O>{~y#2tOE;96q^KD`$!Bw{THR_sY}>U+xObZef$B~j_?oQeOS~7 zBqkTNk3d}Py{pCE$^?89kW0)OUL7ExM-6y`@1Gc5eFSy)d2IhLSko(iOB;~Gd|vkz zS&%-QqO0O 0, Exception('lstm_num need to more than 0 if use_lstm = True') + for i in range(lstm_num): + if (i == 0): + if (lstm_num == 1): + setattr(self, 'lstm_{}'.format(i + 1), BLSTM(inchannel, hiddenchannel, classes)) + else: + setattr(self, 'lstm_{}'.format(i + 1), BLSTM(inchannel, hiddenchannel, hiddenchannel)) + elif (i == lstm_num - 1): + setattr(self, 'lstm_{}'.format(i + 1), BLSTM(hiddenchannel, hiddenchannel, classes)) + else: + setattr(self, 'lstm_{}'.format(i + 1), BLSTM(hiddenchannel, hiddenchannel, hiddenchannel)) + else: + self.out = nn.Linear(inchannel, classes) + + def forward(self, x): + b, c, h, w = x.size() + assert h == 1, "the height of conv must be 1" + + x = x.squeeze(2) + x = x.permute(0, 2, 1) # [b, w, c] + + ############ + if self.use_attention: + x = self.channel_attention(x) + x = self.time_attention(x) + + ############ + + feau = [] + if self.use_lstm: + for i in range(self.lstm_num): + x = getattr(self, 'lstm_{}'.format(i + 1))(x) + feau.append(x) + else: + feau.append(x) + x = self.out(x) + + return x, feau + + + def backward_hook(self, module, grad_input, grad_output): + for g in grad_input: + g[g != g] = 0 \ No newline at end of file diff --git a/ptocr/model/head/rec_FCHead.py b/ptocr/model/head/rec_FCHead.py new file mode 100644 index 0000000..6f6d95a --- /dev/null +++ b/ptocr/model/head/rec_FCHead.py @@ -0,0 +1,106 @@ +import torch +import torch.nn as nn + +class FCModule(nn.Module): + """FCModule + Args: + """ + def __init__(self, + in_channels, + out_channels, + bias=True, + activation='relu', + inplace=True, + dropout=None, + order=('fc', 'act')): + super(FCModule, self).__init__() + self.order = order + self.activation = activation + self.inplace = inplace + + self.with_activatation = activation is not None + self.with_dropout = dropout is not None + + self.fc = nn.Linear(in_channels, out_channels, bias) + + # build activation layer + if self.with_activatation: + # TODO: introduce `act_cfg` and supports more activation layers + if self.activation not in ['relu', 'tanh']: + raise ValueError('{} is currently not supported.'.format( + self.activation)) + if self.activation == 'relu': + self.activate = nn.ReLU(inplace=inplace) + elif self.activation == 'tanh': + self.activate = nn.Tanh() + + if self.with_dropout: + self.dropout = nn.Dropout(p=dropout) + + def forward(self, x): + if self.order == ('fc', 'act'): + x = self.fc(x) + + if self.with_activatation: + x = self.activate(x) + elif self.order == ('act', 'fc'): + if self.with_activatation: + x = self.activate(x) + x = self.fc(x) + + if self.with_dropout: + x = self.dropout(x) + + return x + +class FCModules(nn.Module): + """FCModules + Args: + """ + def __init__(self, + in_channels, + out_channels, + bias=True, + activation='relu', + inplace=True, + dropouts=None, + num_fcs=1): + super().__init__() + + if dropouts is not None: + assert num_fcs == len(dropouts) + dropout = dropouts[0] + else: + dropout = None + + layers = [FCModule(in_channels, out_channels, bias, activation, inplace, dropout)] + for ii in range(1, num_fcs): + if dropouts is not None: + dropout = dropouts[ii] + else: + dropout = None + layers.append(FCModule(out_channels, out_channels, bias, activation, inplace, dropout)) + + self.block = nn.Sequential(*layers) + + def forward(self, x): + feat = self.block(x) + return feat + + +class FC_Head(nn.Module): + def __init__(self,in_channels, + out_channels,max_length,num_class): + super(FC_Head,self).__init__() + self.adpooling = nn.AdaptiveAvgPool2d(1) + self.fc_end = FCModules(in_channels=in_channels,out_channels=out_channels) + self.fc_out = nn.Linear(out_channels,(num_class+1)*(max_length+1)) + self.num_class = num_class + self.max_length = max_length + def forward(self,x): + x = self.adpooling(x) + x = x.view(x.shape[0],-1) + x = self.fc_end(x) + x1 = self.fc_out(x) + x2 = x1.view(x1.shape[0],self.max_length+1,self.num_class+1) + return x2,x1 \ No newline at end of file diff --git a/ptocr/model/loss/__init__.py b/ptocr/model/loss/__init__.py new file mode 100644 index 0000000..35de2ad --- /dev/null +++ b/ptocr/model/loss/__init__.py @@ -0,0 +1,6 @@ +#-*- coding:utf-8 _*- +""" +@author:fxw +@file: __init__.py.py +@time: 2020/08/07 +""" diff --git a/ptocr/model/loss/__pycache__/__init__.cpython-35.pyc b/ptocr/model/loss/__pycache__/__init__.cpython-35.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3c4e8228e660116606270808414c5485fbeddeff GIT binary patch literal 224 zcmWgR<>k`(suLfI$#{!BK0YNsIX-?R zLlG0uR50<&%h@U>v^ce>I3_JIFTJ9)JT)^WpfWilu_!m7C_gJTxuh7#FUc=T&hU2* oiYX|`PcDkd%}+_qiOIfLBZA%DZU|@I*#Bjg}WH|tFF$a)HVTfW#VGL%_WU4ada!4#K$;dCVN~}zQ`1q9! wMNB|5!Ne~)J^g}`{Ny72-29Z(9Q~a9;$nRy({6Ft03}LuQtjA4ZUJHj0JSbGp8x;= literal 0 HcmV?d00001 diff --git a/ptocr/model/loss/__pycache__/basical_loss.cpython-35.pyc b/ptocr/model/loss/__pycache__/basical_loss.cpython-35.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d2b078662220924de93d7a98280623200fd76cd4 GIT binary patch literal 10424 zcmds7TZ|mpSw7WOeVv}3@p$a%UT3qtVV7(N8|PN znZEf{wLLSM;iB1WUO<9}JVd(D zYnJdr&D7~r=YIMA|NGBhb$))nbmjV6ZsiL~{hgY+9K_df2mgkktyB}CrP>+Qvs4oi z6VIqETQ%+E(NWE;`f3}`j`|Q49Oc_;E2j=oD36S+YUd;)H{>RyXsa&bj@1y>RH=nqSXFvG?Bwo)6iqFB3+u)wl{SS}9BVeb7#E-KvV zw*0&3vhzFt@F(B<*B}1z%~nQQREocLdBF}b+6$> z*|_D4Y@6FGt3+t69$HLR%Ux~-lU1g`rB>Obz?UVZpM;OpSw zhBxT!jGy`4hwtyUHa*>cUk|tZRv6$p9O~BQd$)h};V2xo^uzwJ?e`vX#U376KFyUW z;|>^6)>&)sOfuC=$*5YAhwLJ5PW3kK;GYp-UR#)_Qd^iH;xb#TX|3&W_6JDC8l)wX z^^g)-DUnh8fN5F+@ktv1FWRu#G72r5)+CluAxb)ulTpjS3rjjvsaBGTeb~yXm4j@O zi5}A`NoG!A^YJCie@fdcTpsr!7qVV+pu@AO9CF z%|(=;k&jBP;UMgG#=~(CIZwO(Gi+#g&yTXQFOeOL`?9%_)AzkWqo{MJ+$@f??*^@* z_M3%HXkt+*)LwVs_Pt;`vb@MzkE~W?wWDGifD|Fn1@f*4&xvy+Y+ zmd-PQ{R^V!5D`R`t#h`dw2+cs37&!vkn}$iN9ZsptPinT&}Z0`qQsF{Qcz1w&l`~~ zM}@aL9a*4`>G#LCTK_wd)r7u&nE@vd6+LwD)a#A?Mkd}tcKHoSiB}}u7O7H4wJAYM z1~?J4=>byuMbIuHGA(EjH;6(gGCY4JAg6Y|&xt_fP~y&`KT?o8$|Qslsd-g{O!8-$4HS|r@U-Qi{h?qzns77FJFFp?Y#zT9%wFO{GB4#_NDyCq72&DlO#y|BDQs&fFnfg(16(F+Jc3$bb>dAHP zDDb%rmIAT_tBeB>5a5!k9n49?$iQ&$d>%pM z42HU|Kg+1y_jV$CG<*g?4?BWY{WC1sVDPgD8jFHY><|)up21rPpwbL1qhhN0nt9MM z#UUyR=wKoFk<$(Rely=4wEZ1-JBftNv&Z1>o|w2D*~4+zwEd^9zRuo-2qtXs1*H7~ z?tl>m+%5=4=YdljcNt{;x>W~`YqJ<$Ifh{)!>JA?m{Smc1`osrLLdu@Obn?*Csi@9 zL@oVAlxpP6a?w5(0oKOBgwg@EJq0fZ5u>2JVpXiY3(1~dI$mpLhtmc8^v0Pc ztaY8is|-GeAhyel1RhZtlo{&ISlVcb&5Rbyksj3MQI48`fC_#srZOClpbNxQp{|Aj zg2!npg8_ma69Z)DM~2G&oD7IP0UuzKYUC_KfrhDIh9Ma!;I`r+Whx;*R)Se-NROR; z0=<=FMCcDiO)qE$|0Pw$fU7jL45fkeFabk9pfva+Y@swH0e9dfBoTSw-3KA4P0C}S zB(v9M$ZTs~CR|P^ts+AJS4kE2VmLQlrUw?7H(n;RTt11?sH(&)0|^mkr&5|;W$qmY zUt&N;&^H*QWEN#NdP6TX6eq+sKOwfL1Qv@yt#6`U)97lRFK_XYMwJ^hi!qP6-R8OD z1h$Rjw8YlQCALJwPNK4Cf!5UBHk6^EBIpYh{4|1TT3ZC2@#fj{tWa7tG0UncijOpu zqM7TNqWM4Z0JLpEZ%v;o1X{_tBCB=|~p2 z*aB?C8>AE|z?dLY|GOYs&Ir`9&XIG$#PCnes#DGd70o>nOG=9_r9LsL^&Q53iGjdw z0$H8`#G0DeV~x@TeiK6hCOrW_LqWgEN`x zui!2MAM!tC0|DCKHl9v7HGGtzr_+bF%+7?yPYfT+?}{6_nKR~2AzJy0*YgG~|F*^^ z|KcFj!_n?tL}T0))+U}&TLv-lit;(2WeyHF)iX+YQp$B`wTX)~Yy2(mqMosdDg(dc zh25vVtMM~5?or#o@g7TY+Y?umkY_Qe`}>FlvO3T{MQg8q%4AY&sbNPB+;QzF(+T0F zgZC{37r8a1|9`{-M-KNS0CouO!~_Im)u}X4Ez+>oLa)$5C`ehy;S%DGtb}>fEG8Nv z%qluRKzt-#oYZjMgz}Ltu%#fr!;D4O2>z1}mZ_2OoV2=(b7oi+_d|)whKEKpASc9! zatbAJqJRtxbZa6j96?^KP*W!f3(YCIz5y5Uu?e8(+eFne61`EkJ zqe3D9%}SC6`X3L%=0rB+IdZ6qoAYTY^O#gOz8A}Fme-9?8_j*h;H2XG9`1lAI8|D- zmaQuO&O?BfK<10qtC_Qi?Y(lEFePd*f1(;B%|yAc8*dZ)nrPTWh(&&>0KSVm;PA?s zYK}I$W@-y}dZoILRO27heGrsb{Xsyp>Lc^&EcGVGFM6QnHik4quxGOy7OT0xx+R9) zMAwmKc=4Bdj(5Pb^9$)ZG7^*?{5!GI%NeEj7!4Sum-Tw1O)tuJynf#^C~=I+^H^q` zfneuEhx4ByTh@(lc8a3;PMKe-`C6O-FV2W9-^Lw`5f~Dkqk4li4qJ-}&}ewo0u{JC zc_V{lym4VV%L?^YQDvq^tR&-Fahog~*R1lr56^;dX4C$IgF+@OCbG<0%#<6*npu^J z?C_K~fs}bSL74po?wAYSv~B{vr>#f=-|UI-eGXzDqlZ6bFI>X1bs?*n(!!lyDO&#z znEKp1gz%^3Y5K}u2)iLnSpkI$DT?8nH}K_DjiV>!Wm$r#GFfYCB%JNJ5TZAqgW?~d z%q)sOd*T#R45Qr%4yZ#@@+P3UD+21*5Hav1pMt_;CMVg7c?iT}im*oh-!mQ@57KP-r^pdsr^2s}s$kORlpk4O=9H}Cxe!#wB9>@z1FqL!#^8rp>o*sb-i7IK_bLtE7ThUv@z;*;UX$AKn9H*2l_HF5%Z6=?09opk&H@MfDSyt{Y+s#Cj^_Mre8+LoS>tF##L_6=qv zB8SS_z-Jc|Ee_Wot;E!_MVSlc)fDSMJ5&Cpj!e=qKD#L2yK2mfJSE)iT?L%ZU?h4C zLFB;p3XSn&d+R~scAkY=vp_@5r5EEg(ESY>6PK{E%XBGZUbXg?rk3g_fH&?{#BKy{ z){-rFV`@r~v`@I?w^?iqJQxIk6roVK%83{0csf)K3s`Cmi;Vx6@92QQ*8C<96|oKi zB(d1oI~XqXt(q8L_hN5hIY=MzarS6G(llB(&ORI;u$@3RKXZVRGZ~1-z5dIm>=imR zo-e-q;GqUw4{u0L&9Mi>@Q+SxRjtjLx41Z+Y|HCbvq)cwSHn?jpJLLDT=Guo$G6No zB^nI)4uX!eH?N6{q*2fxFflV28GAjieL16;S4%lB2zT+?^cyVW@I`r}h?k%I7DIm- zY4SFtnTI*;;$?bs9-rsnU8mnR?=$nTt0le2i1~tJF^#(Md-r0v$v%g@_7STjnj3EV z{n&f<_vkI4D_B`KGv!Rqx@b8WyavjYVf8Xj!+Rl9M_fNasT7M@JaFEE#eYJ?Fq@J1 z*kLLA1br}*2pNZW^GIU)KHxX_9Sob2b1gqHxbtXBpeg7hq|hod;g3`5TR?RZ*#Jbz ztEc9P+DVJ25!M5=(UbM$oABS7121q3FheNpJ!MFavtmly#1O*yoq5P58LZW%LeG8;eoib=$+1>zY9oFJ(*RkJ%Y)Nr5|MT+DAIiei6aEL%)A(1mD4k!l%m#8F^2n5z1l0%R{$^rQOzE@S< zGdtro@r`cPt5;vWdiDPNzVG|J@6~e)3&CH#6TdsSXc+%$%>5kXuj5F*k0dfWMr20T z$n2PMwuW}cmYmaZUpFE?YIkaoxRD2mS4ezFYLO3#Ur6eZ)T02BppXQR zG@>RX%|g5iF;?FW!zgX?kzxaHd=vXrI55Bc`P~y$+<$Z7@dz+?;D+^ zXf1ji=QEM@rm_BsgU<)o!`<}uL|xn5yBA#F9E{>??OS0o=!K*1Xp$tCcJ_no>0le; zXRkbaP8Jitg88$G7w&n+^^)5ydIM#tz_QT-dnrWrYrd%uoR@}e5v?R>*< zjE;-<2l>(sg_nG3oT|yr{!L^z9z=LPcedkj-1CY$d9LBGaU{lRA=hGp~2My-}DX-R^gdpIx|gc_*Fp)aC6-6pt=*j4vM>aMi$bEgVS`iD9)& z&$Q%!uy%aBE|tydad7w}T-lh{aU@reWX90Q%%M3ohE`-DXGiuL1~0RQ4zAp3dC!@e zQ^LaCcwpkW2mgbgI*Xhn>z-ObJ`Z}6aXRSlPIi;rxjTsOVVG`*J8^EMliZ8qyMtc5 z?kKiDcXlwGxtE5jAE&v4Q=*nwp(JNmq_#Ox_d*q&$3?=CHq3+7hk98Qdhi`I3GLfZ zpG3h8#lB(&FgN3HJI34u-7dy;cf@?N+r6_Jj`STFQ}qdy&lig6=*6Q^w>xXkx~Ud% zU$I#-=N$cnQ|eoW+44np1oM17e@8Lz3FfWH7R(d-^O&#Y{;L=vV*P{ne*8Co`d@$d z{);{7H!9+p^C&=HMdm2b0dk{j3$V=%jB$Vx>H-rYCb#3AWZjaHSD%5P>`*n>QjBAe zNXyNZd2ps|%nV3nuZUhYW6chiaL-OlY~cUwkM1FbGY z+o=Q8-J}QG(ee9fkqc544#wT>Fxkq@FgI`IW-m9RPCde8izS@_O(Tr+@F^V0IV6U; z%D?5CzO`hY!+*&P%!8$Bb_xZ+l>P!rLl>XKN%48rW;$_ zzi+l|0Z$K->Q_#F@*+f)8E>8h>B5}5kss9oaW4v@2F^7x?>+{8A@^VI_oee6=okFx zrH=*m&!H9#^?Hbk?}nq@xBz^o&R2b1B!wOyh5Z#gI|uu+VKdkh8T|1~`zf$*BU8eD z3dp7aE1(M3zeO8rPEBz#0O{kh`a_P*-gX#^LS~|)Fv=cHMO|iD_t$;oPI`H`tL(4!yl^ zrB(*{tH$sA{$ULYWkdC9YX;mU(iQaM&=;wFOj=K!%%8%6ZyCu2J$hBn4R@t2k zR8x!$YxT1_TrqRV)n3|?>=fYbCo`cuH7+UIY)7sFFaCPX8myoaHKxvLF zRK7NB44YF!{R^INVEo;Tw4qe!r_kjGHWE?oJ~HxyVHADmZS{F3)O?_^24}v#3%5;; z%Uu#8omF_eXK=g*!X(I8wjJ5?&gQF`;4z;PBG0x0O4+-6#Z) zNWdw=!6>{niq{+3@tsKq+mL`H?04_RJ(v^4S&KpO861^fAe*bKZ(%tohvlP@PB z$lY6E3j1RYV2Q$ha#f$6%^SR1wC^~27Ru(!voGLnNgIg)JL2GIiB)+9G~!9*n`RSs zCNO>T;9P~TBNbHSi)vKq!%uMmY8Y*Bi%m@NWv?nZ^KIcy2676l*bix?P1o2g!6LXxUs=73~wtIN2~?d@QzKLkf>w*bT$T)Yv zNw+&SVYJ6LtsS+@vw0C za9>ZtjV}RT!f)#!S;X5fS#(DfAwk$5da&UARBeOZ74n>Xmwh z$#o>TFT;U>Io^2s+XzQtS(oN5>&&}eLW%YIiQbi#LSC;p4v%!UbXJ`9ge(ot*6|(x ztiPrxKiTDSt}B<@`oFl`s2MH5_4?v^>j3N`S!SYn2TkC|a=UM$g2L^N9&)=kKiusO z@$8)2EgSW)+vW6E5Ky>?%tH&N#@>rED}@uL`mP#Vh8pd?hSeXIGUq{$DWB5ZTsd^W zHwhRR52VhT(YL7ng%sENo|fLZ^cLJxWPQOnwC)7R+ZZ)_jw!C&S}kcvMN8LRZQ5;E z>UJW#)dn>s%{2Df~fB?@2Pn{4k)l6V`X6RH$d$AOZp!X`fsTYT_DH7*Z7b!6(Q z5o)8Bk4{X~6%#Ua@)OMrWyDYX05yo87%rgIP4N{i z$QD@wPtrSSU-7f7DLrdu&69iu-Gq7y39LK0#HqeQeT8?w#^l$S(A27%Oe!ytyPKm) zm}=i49%EtVG4cTJp}?Bjz;lH$Z5=q8KA!g%P522O=a9C$Veuk%KSiQr|1HL9xciy1>%4`#TTxlnLW&KzB+qCTrvk>n0J zsQPVQpTG(tF=rbd zNebvn2Ye}GOI(5b0cgQFe?{@m$+Un?sAvIPJOZi=G9#cGJ43fTBKJEw!0L1mfO4=7 z2gK_6`B%abBB}B5h}s9J6hHs^EC^+5Q5AsVb;6FH6oi6KlrdJ!fU84M@1U?I3axS6 z57WWjxT|oQsL$XIJgTs^kflU$;zIq;aa*Eph`Ax~dC)$k2Ni`ty=!K5oMx_p&2q=PMD6d^`3WsK#iy7##mpYJ$%hD2D(T2EOE+a}+OT8hM zuEb)5Sl@fLEO(>^#J1|N8uBdiL0ZS+Bp5ajErWxbT5n?k1jkcny^te4Z=miPQe)VZ zI9B0G4t^N4i{HzLonEW1C}eE)T3ceT?Q7d%di&bD&um<5fB9njt@U>=w$t0O6dCjw z&2CRN+v7OBH&I(hD(R<>s6xkXk37RMe#BEO(uSMCK0+ENZLpsK(a=_LAB{<>>SSr2 zV~sS9!R;Aw)FHWr+_|-%#<@Mf*1%g7b47&})L^UxKh&Q**xVgU@4`{Oa%(US)qc0f z4TWy3cNFy6-AN}qTN2*Wuq#|9L8ERlq2A?w*~r{4!P{w-GPu> zDp2ntS@%!0So8?F_ca~K;CnwTSV(35E{=pDCf{m#slZlb2-ns*ML~8g0f6 z9I`?rfy#^qwFx#G`ev_!GQoBuvYR37L<>&z!%$fpI!NQ_8MEWP9Zi2Y?bwO&dudM( zryGuTZil(s54X2NZZJ%4b%M9t;};YpT1*arUbp-3@<7 zL;Bk4p0VP*f}CE2rW3l z%)D2efNzUBY%L?2`Ha-UA~HaEMWm6=g}GXF5YPs?!wb z-#COEOCwk@4?cPFjFd`aDXs)nhU7!MlbW#5qg)fw_0Kp;*HjbNMCr-e!w^TwpoKqU z!FS(3#-DBQXIl7q{_N$!8(=awuigY!dnT&kk7v6$5-v-Qsz<2`wpNATLjh3*5FGN~ zaz{=a)tU0hrh7JpuZrb z06qzE<{;vZ{S_{mL1(3$^u*CYF?`2R8(q~(%Pd*e=qeCH53PO^N$!B|rG*0zZzW}v z_9&Vq+7A2S*ZeqOuQR~cL+t?~@1ES@%%o9FV%W9nFRbvhA znwx*vmcjNNd-V$AqBvhK_}Qacz3!EpBR_gcZ;ntU@LdN3RnI*ukqv14CWY2`NAQx@ zR4fY@y+`7ONxF~CxjxG{+&9I_)s-!F^#)`T-tW{vv%A<^?JVGf8|ud77(!JqV*iUvReYw#&o5*-iaWATR&gpCwe==+ zIK+zY3NCcG_yy+9F_D-O`>gIG$s70#h>N>14z2USrkZSXB6jv=$;L0QWHu7{PJ-K> zNvCyW&lA^2<}#6h)A0!kWP&6#w8r@%zDQQ_#|*8QHxX27S?UiVg*V&5NiQar^IEqX zO?vpqM^-+*eARJjJXTyi>i$Vzyf@^VA^E^Ze^2u{7I>2h7d)~|5dNZs*oaA&q2N2` snp@fQ-2bBf4(UZ&58)Y(-YK(}7nfI-uPt9+{&e6kH8ew=KIi++Ieu?# zt$Fb8&!WF}82dY0X%61+;!#f^QkL=&>(#$JvwGIYEVWYmB};8#W%k_eIhf;&+_Bg5 zID^(rJ!rj!*2jpS1{ev1huJ~Of62OyGwjVCCM1`Q&ZVZl10h(Cr>tkCyl1D4PgtLu zeH%EF;%JP~`Q`zw)$q738F zPIxc8KU9evj)!?%4xfwgK*Z%#itvGmWj@RY;l0U37IAVE_6r%_7h)0~#EA$WiKH0h z!*W>UJK;zB`%l7mZ|}apa^^ibQ1x+ijeI(el2NRbP~pvCUUv6r=upV8=!XkMy_jbA zafv7$j+axZPnW`w$Q2!FzT>P3Tb)@DkCUbS=-0(#<549<$x2?axs_RS##x2;$gV6% zr?Mg4ie;|6UwWBe23Z3=jAjnzH|K0lvs?Mro7!4~@piU``K^=_E?(IjSb&+E!)S?Pym`CqhcPwhp3b2qQ+3Zmn2I2TKEJf2u?@m`t^m=eqHWSjJ-k zXKS}CWOAfk8Ru!Pon)HE+D{A+ly+!O?dOFY$D^q0Xh(@rUy`+Ci$t5Q#dRa9eG)}~ zX8#IzZcWM}k+;T0Dn_>mLAUA+4VWe`WCN=}7pAU4Fy7%F{aGHrVQpEbU%G(d&ho1g z@&YwlN?zH=qy7d_@eJIW^O;?OR~4)Wi`%K4I;s20%8YL^cWxOi%e*)A@LFYKjX!7C z+03b&EWqjp`8R_v;X8-J^Twm@oRc?at}Gy_2aB0f3$&+FH(yX$}vE-0^H zinb@kF#tUsYfqd^0Kytw8>Hi<_Tn@hk0bm7paY?uQIQV}?BqI4X-sqVY%0X5(3W_n z-TtVEOYI^Mj3?cOe9LTeqP_b2bo0VKJwF|)5+(%P>v#fo#pZxyG=sat9CSy>ZY9N1rQ7>7~F&V=JPJP|F_{!bOk(2hL zuT^^xmm_CsXHMy69vB9$n)tVfm{HTj)0T;+Z4>|a!|s|4u!sBs38KGTBSG=QAV^XW zkp?fF`a0w}`bh1@lga3X?j$Ch(QqHbCUWUEK`@G+3mFeY*D*+032Yx=)1^g?iH%w5 z`0wLU6agu4ZSnx2>vVI)4sXUR^1*q`s?fQJS$N}>o%(ZY#?7d)M`k^><((2y4Do|e zR6~q|hOG5_n&(mv7G#+(A60g4Q6I63WdT_gzm3Y7JBU+m)+E2B-YbiG@LEu36hq(B zU)LA^wxW!<7ykD441uJWj?(Z@go1z=mPI(sC)08nbujn;wR)f3*%t)@xs8vPUxqkC z9C+Pr$s2gr&hw!-uD#NH5|5_hBPk21J@AI;*~rClB*v5S1?W8l=p{Kv{)hy*U3MY5 zUd?rf5}tMq-Q_z_^wuvZAC*Y@g_O5x)j4r$#t^se;8CP%$^ZH9Ag4|VFIu6Dr<|fvxDl*$sDRIz^uUZ!Nx@&>_XK5 zJymwIW)+~W0FP;xsxh}QzquIkjWGil*~~RF{5h(-idHq?LBv30{$|BntnxQmpJ79$?hiS_j0IA{VOWmLU&vZJ`kHlDI_47a^ra zlH$Dl8VN!@sQ58{bZa2vG%BVg2rqv^+RGl2Kc!j7ko~^)64VbRibOiC*ETB`{`;Kg zbg)FnBP>^&5X|bpeSvXe$J*duCud&&oHLiJ9=^dcd`yXdGZNIWM@4E9qolo84!(%m z#EPHcS&*$=^90p_DL?{K0Pp0v$t&9Xxyi&PTlTKkx_DX^^m#j!J}7lcN&s0S{&Y!BQJAfzI3feH!4As3Uz-ffm_vb7VHR@u{5 zs)XP{cqLys^%XcV<7}W=&3HT>e>0x>_St9@z4X3+2^k^3$-NKuZxg~QdsBmsVxakUZLlSmN z7)d-p@l0^?>EvCdb>{3YBSl=ElqHlsx|Bsg|snMSdw97Rmzx} zfs&%h>bxrBz<9b@OVtI0oabd;b8h^sD(Cqkne1@*`l_x}c5Vil%4@0Mt_iJYR+h1A zn3ly{MUW*Kf9OJY-N1aDXGN+t=fB9GC$r;qU1jQcRS8)fW8KGCHX5__l^OsBHcgK~ z5jv(m-KRquIYWBAzr&pMb!PpXLWuQ1PC)bzP+eO=Lt`DXaW|}m*-xRRf?1EZ@msPa z!h^>9Ai-Bxy=mPg72y`y0e9fsZQa&uS?h1vCioXWm=dJ`DFVD;$+qN>z-h4b;1;Yl zZVxbrQ}P%GG&(ij8k(+6WEZaUYiaxLIE_RJPs;T2}3YWrBI7fHne=G{zHpV1Hwn zBuYrHC;cRMv9WJ}d}X?lK)#4`AV*W6J>>kNNsI2pl0zK6hTt4kwq7FAQ*;VISB&TiVz{qfxzZ;cN9tAgV{Yo zmOxg@qw;@9n=VcMLc08cZBr>g>Hx0t%^npgfyvw1+1c4|cfOf{u4H*4y*5Xs?-;usqm# zuycRsll#M+=n@a3gG&K9a82|g(J!A5pTphUdzutUmFD{jt52&&)$=pE5j>lwxz*nN ztWJ~S{|+fbUj4kaq8~xY!2m*rbk?yWU2cYOoTo%PU%P#S!KmP8jI*r5v|(R66pp$lu3E;U`Y!b5u0 z_*Z&B%hn;JrDJ!&FF!*Z40@)k7xP>x9~YQF%4s!iq*PsAh8c*lTq}Nv@rz-*ViHVn zyxuec>>9-7CS;pcLB6Z=Vyte#S8eivPCnNIVL~a>BGFpPpXtpP`@5sBb*}VioKz>T z7N_}iba<9d5>+0mdX}e+#=5ChI{9+{tI@ovQ#C5SG&Si4{`r@!^*qsXr|i=!SzwZ^iOktiWQ899ng0h4Xj=ry>hpyBzj+6@GEH0T`|?|51z>P&I-80VxhZi)6m zdcm|rW{uZORIVo`YG+v{bJP1*hwH|A(E^#CJ=xj@zS>}17e&=5&XMA5-KY<5G0&1F zAB5^QHsWxu@=P8l>BIyl4U{IGv=DxoH)@({ixP*ZzSdv?jmwv+JB)3`U1-K1*XlG; z8Cx`4NDOU(Ji;7_55#T96X&k(8*rMZtPs%5N7*(eBv7Qy&BJ z`qF_=@34VyvaQ9%skx*3;{Cu{(0aaq#HRR2`~!JyK+vY=)etHUg;qbwru6^KBva}ccb7xBi=65)Lf zX?S*!9Of%MYvujvcQ2B|>AWiMe<~)tYL(hso+gKSUZdph{@wlY{=Mw;$zI-W26yiQJcsmhJM?T|`IoqIA@)CvA~e|5qri%gfJJX+L!b zK?ztLWS)p#!juRqg7X-wWFkC=jJ^a4S%7uKh8;b!0WK0Wk{*J;Uj6<0r+wCIT=VIZg*AP^)DrWr&VU z#QD6LYtCh0*;rt`u7%PDG+}knb2bGNO}3XNLb<|zlqR+^M`BN@Dyylyfx7YoYv7kl zl@b$kUR1eKoc|2}ezG@S>b8*Mx)o(LuCVs;)w9U~+S#La5C9pGghco=a(1I{)ZS&r zh0V}@F&>-!V?63N1RX4(!`a%!SrhQRC%mb*3!Nu?2k9piE^J={3+Rz#&7TX!wo^kBEVR|M@H8V+%d7UrK;2o__ z%y7%}uFSw$_OdjTBkP&slWRFdQrebZdp)*Vid>gzEI-7HEMCe|@JU|GO!QnMp^I7X zBCJa-=Y?`!vKVKh7dVH8+Mx2L_3gkdq)a$%<%?X3kC0JT4CIb`>dz5JYL$q5{t5`aOU47 zl@e()kv2EC7!1uaA{7kYgBCix1|1r{4);Mi(CiZ=g9B#5;((PQVeZk~)=l%aYHhu}_<*V+_Z?9? zqUJdY11ZyhO`;pf8MV3|ra|8%6*nLt4Y5J&2Jhxn$#P6mB#7t@j@&#$rG=Jtu;**O z2L^4>G0kq#B83Jl!xuQ+xL8%SHL|F*`Idw(Y<62?Esf=3iRPk}(o$LXSP>9udL**M zkAyqXm@>B^24JRGp&c$bL~Btbx#+L5CMPLyT7eVhwa~%dSKhbRy@7P%NC-TB99bwK!cw zBgkAO+{ReR30%Nm2HfaZX_kHW>=Cvmh$T^D|?UMjXlM9H#u%p znX<$~{F?yljnHpt5PvP&kzZxZPC{=d);m*`JB_~_^G XY5#lNgBri;Cw)ynwZ|GG(lP%F0J+CP literal 0 HcmV?d00001 diff --git a/ptocr/model/loss/__pycache__/pan_loss.cpython-35.pyc b/ptocr/model/loss/__pycache__/pan_loss.cpython-35.pyc new file mode 100644 index 0000000000000000000000000000000000000000..045598a474d3beb6ef9ca47b40107e15bb506b00 GIT binary patch literal 2435 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 YimYTNzp*gvCgzmF%FP@;2Xx*07k&-bU;qFB literal 0 HcmV?d00001 diff --git a/ptocr/model/loss/__pycache__/sast_loss.cpython-35.pyc b/ptocr/model/loss/__pycache__/sast_loss.cpython-35.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d0b6d64c634defb7f599b75a4838c62acb74cf62 GIT binary patch literal 3250 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 R=OG_1u2H(kT}^4J{{a#OAOrva literal 0 HcmV?d00001 diff --git a/ptocr/model/loss/basical_loss.py b/ptocr/model/loss/basical_loss.py new file mode 100644 index 0000000..d1655f8 --- /dev/null +++ b/ptocr/model/loss/basical_loss.py @@ -0,0 +1,293 @@ +#-*- coding:utf-8 _*- +""" +@author:fxw +@file: basical_loss.py +@time: 2020/08/10 +""" +import torch +import torch.nn as nn +import numpy as np + + +class CrossEntropyLoss(nn.Module): + + def __init__(self, weight=None, size_average=None, ignore_index=-100, + reduce=None, reduction='mean'): + super(CrossEntropyLoss, self).__init__() + self.criteron = nn.CrossEntropyLoss(weight=weight, + size_average=size_average, + ignore_index=ignore_index, + reduce=reduce, + reduction=reduction) + + def forward(self, pred, target, *args): + return self.criteron(pred.contiguous().view(-1, pred.shape[-1]), target.to(pred.device).contiguous().view(-1)) + + +class DiceLoss(nn.Module): + def __init__(self,eps=1e-6): + super(DiceLoss,self).__init__() + self.eps = eps + def forward(self,pre_score,gt_score,train_mask): + pre_score = pre_score.contiguous().view(pre_score.size()[0], -1) + gt_score = gt_score.contiguous().view(gt_score.size()[0], -1) + train_mask = train_mask.contiguous().view(train_mask.size()[0], -1) + + pre_score = pre_score * train_mask + gt_score = gt_score * train_mask + + a = torch.sum(pre_score * gt_score, 1) + b = torch.sum(pre_score * pre_score, 1) + self.eps + c = torch.sum(gt_score * gt_score, 1) + self.eps + d = (2 * a) / (b + c) + dice_loss = torch.mean(d) + return 1 - dice_loss + + +class Agg_loss(nn.Module): + def __init__(self, Agg_Value=0.5): + super(Agg_loss, self).__init__() + self.agg_value = Agg_Value + + def get_tag(self, gt_kernel_key): + gt_kernel_key = gt_kernel_key.cpu().numpy() + return sorted(set(gt_kernel_key[gt_kernel_key != 0])) + + def cal_agg_batch(self, similarity_vector, gt_kernel_key, gt_text_key, training_mask): + similarity_vector = similarity_vector.permute((0, 2, 3, 1)) + Lagg_loss = [] + batch = similarity_vector.shape[0] + for i in range(batch): + tags1 = self.get_tag(gt_kernel_key[i] * training_mask[i]) + tags2 = self.get_tag(gt_text_key[i] * training_mask[i]) + if (len(tags1) < 1 or len(tags2) < 1 or len(tags1) != len(tags2)): + continue + loss_single = self.cal_agg_single(similarity_vector[i], tags1, tags2, gt_text_key[i], gt_kernel_key[i]) + Lagg_loss.append(loss_single) + if (len(Lagg_loss) == 0): + Lagg_loss = torch.tensor(0.0) + else: + Lagg_loss = torch.mean(torch.stack(Lagg_loss)) + if torch.cuda.is_available(): + Lagg_loss = Lagg_loss.cuda() + return Lagg_loss + + def cal_agg_single(self, similarity_vector, tags1, tags2, gt_text, gt_kernel): + sum_agg = [] + loss_base = torch.tensor(0.0) + if torch.cuda.is_available(): + loss_base = loss_base.cuda() + for item in tags1: + if (item not in tags2): + continue + index_k = (gt_kernel == item) + index_t = (gt_text == item) + + similarity_vector_t = similarity_vector[index_t] + similarity_vector_k = torch.sum(similarity_vector[index_k], 0) / similarity_vector[index_k].shape[0] + out = torch.norm((similarity_vector_t - similarity_vector_k), 2, 1) - self.agg_value + out = torch.max(out, loss_base).pow(2) + ev_ = torch.log(out + 1).mean() + sum_agg.append(ev_) + if (len(sum_agg) == 0): + loss_single = torch.tensor(0.0) + else: + loss_single = torch.mean(torch.stack(sum_agg)) + if torch.cuda.is_available(): + loss_single = loss_single.cuda() + return loss_single + + def forward(self, gt_text_key, gt_kernel_key, training_mask, similarity_vector): + loss_agg = self.cal_agg_batch(similarity_vector, gt_kernel_key, gt_text_key, training_mask) + return loss_agg + + +class Dis_loss(nn.Module): + def __init__(self,Lgg_Value=3): + super(Dis_loss, self).__init__() + self.lgg_value = Lgg_Value + def get_kernel_compose(self, tag): + get_i = 0 + out = [] + while (get_i < (len(tag) - 1)): + for get_j in range(get_i + 1, len(tag)): + out.append([tag[get_i], tag[get_j]]) + out.append([tag[get_j], tag[get_i]]) + get_i += 1 + return out + + def get_tag(self, gt_kernel_key): + gt_kernel_key = gt_kernel_key.cpu().numpy() + return sorted(set(gt_kernel_key[gt_kernel_key != 0])) + + def cal_Ldis_single(self, similarity_vector, gt_compose, gt_kernel): + loss_sum = [] + loss_base = torch.tensor(0.0) + if torch.cuda.is_available(): + loss_base = loss_base.cuda() + for tag_s in gt_compose: + index_k_i = (gt_kernel == tag_s[0]) + similarity_vector_k_i = torch.sum(similarity_vector[index_k_i], 0) / similarity_vector[index_k_i].shape[0] + index_k_j = (gt_kernel == tag_s[1]) + similarity_vector_k_j = torch.sum(similarity_vector[index_k_j], 0) / similarity_vector[index_k_j].shape[0] + out = torch.max(self.lgg_value - torch.norm(similarity_vector_k_i - similarity_vector_k_j), + loss_base).pow(2) + out = torch.log(out + 1) + loss_sum.append(out) + if (len(loss_sum) == 0): + loss_single = torch.tensor(0.0).float() + else: + loss_single = torch.mean(torch.stack(loss_sum)) + if torch.cuda.is_available(): + loss_single = loss_single.cuda() + return loss_single + + def cal_Ldis_batch(self, similarity_vector, gt_kernel_key, training_mask): + similarity_vector = similarity_vector.permute((0, 2, 3, 1)) + Ldis_loss = [] + batch = similarity_vector.shape[0] + for i in range(batch): + tags = self.get_tag(gt_kernel_key[i] * training_mask[i]) + if (len(tags) < 2): + continue + gt_compose = self.get_kernel_compose(tags) + loss_single = self.cal_Ldis_single(similarity_vector[i], gt_compose, gt_kernel_key[i]) + Ldis_loss.append(loss_single) + + if (len(Ldis_loss) == 0): + Ldis_loss = torch.tensor(0.0) + else: + Ldis_loss = torch.mean(torch.stack(Ldis_loss)) + if torch.cuda.is_available(): + Ldis_loss = Ldis_loss.cuda() + return Ldis_loss + + def forward(self, gt_kernel_key, training_mask, similarity_vector): + loss_dis = self.cal_Ldis_batch(similarity_vector, gt_kernel_key, training_mask) + return loss_dis + +class BalanceCrossEntropyLoss(nn.Module): + + def __init__(self, negative_ratio=3.0, eps=1e-6): + super(BalanceCrossEntropyLoss, self).__init__() + self.negative_ratio = negative_ratio + self.eps = eps + + def forward(self, + pred: torch.Tensor, + gt: torch.Tensor, + mask: torch.Tensor): + ''' + Args: + pred: shape :math:`(N, H, W)`, the prediction of network + gt: shape :math:`(N, H, W)`, the target + mask: shape :math:`(N, H, W)`, the mask indicates positive regions + ''' + positive = (gt * mask).byte() + negative = ((1 - gt) * mask).byte() + positive_count = int(positive.float().sum()) + negative_count = min(int(negative.float().sum()), + int(positive_count * self.negative_ratio)) + loss = nn.functional.binary_cross_entropy( + pred, gt, reduction='none') + positive_loss = loss * positive.float() + negative_loss = loss * negative.float() + negative_loss, _ = torch.topk(negative_loss.view(-1), negative_count) + + balance_loss = (positive_loss.sum() + negative_loss.sum()) /\ + (positive_count + negative_count + self.eps) + return balance_loss + + +def focal_ctc_loss(ctc_loss,alpha=0.95,gamma=1): # 0.99,1 +# import pdb +# pdb.set_trace() + prob = torch.exp(-ctc_loss) + focal_loss = alpha*(1-prob).pow(gamma)*ctc_loss + return focal_loss.sum() + + +class focal_bin_cross_entropy(nn.Module): + def __init__(self,alpha=0.25,gamma=2,eps=1e-6): + super(focal_bin_cross_entropy,self).__init__() + self.alpha = alpha + self.gamma = gamma + self.eps = eps + def forward(self,pred,gt): + loss = -self.alpha*(1-pred).pow(self.gamma)*gt*torch.log(pred+self.eps)-\ + (1-self.alpha)*pred.pow(self.gamma)*(1-gt)*torch.log(1-pred+self.eps) + return loss + +class FocalCrossEntropyLoss(nn.Module): + + def __init__(self, negative_ratio=3.0, eps=1e-6): + super(FocalCrossEntropyLoss, self).__init__() + self.negative_ratio = negative_ratio + self.eps = eps + self.focal_bin_loss = focal_bin_cross_entropy() + + def forward(self, + pred: torch.Tensor, + gt: torch.Tensor, + mask: torch.Tensor): + ''' + Args: + pred: shape :math:`(N, H, W)`, the prediction of network + gt: shape :math:`(N, H, W)`, the target + mask: shape :math:`(N, H, W)`, the mask indicates positive regions + ''' + loss = self.focal_bin_loss(pred*mask,gt*mask) + return loss.mean() + +class MaskL1Loss(nn.Module): + def __init__(self): + super(MaskL1Loss, self).__init__() + + def forward(self, pred: torch.Tensor, + gt: torch.Tensor, + mask: torch.Tensor): + mask_sum = mask.sum() + if mask_sum.item() == 0: + return mask_sum, dict(l1_loss=mask_sum) + else: + loss = (torch.abs(pred - gt) * mask).sum() / mask_sum + return loss, dict(loss_l1=loss) + +def ohem_single(score, gt_text, training_mask): + pos_num = (int)(np.sum(gt_text > 0.5)) - (int)(np.sum((gt_text > 0.5) & (training_mask <= 0.5))) + + if pos_num == 0: + # selected_mask = gt_text.copy() * 0 # may be not good + selected_mask = training_mask + selected_mask = selected_mask.reshape(1, selected_mask.shape[0], selected_mask.shape[1]).astype('float32') + return selected_mask + + neg_num = (int)(np.sum(gt_text <= 0.5)) + neg_num = (int)(min(pos_num * 3, neg_num)) + + if neg_num == 0: + selected_mask = training_mask + selected_mask = selected_mask.reshape(1, selected_mask.shape[0], selected_mask.shape[1]).astype('float32') + return selected_mask + + neg_score = score[gt_text <= 0.5] + neg_score_sorted = np.sort(-neg_score) + threshold = -neg_score_sorted[neg_num - 1] + + selected_mask = ((score >= threshold) | (gt_text > 0.5)) & (training_mask > 0.5) + selected_mask = selected_mask.reshape(1, selected_mask.shape[0], selected_mask.shape[1]).astype('float32') + return selected_mask + +def ohem_batch(scores, gt_texts, training_masks): + scores = scores.data.cpu().numpy() + gt_texts = gt_texts.data.cpu().numpy() + training_masks = training_masks.data.cpu().numpy() + + selected_masks = [] + for i in range(scores.shape[0]): + selected_masks.append(ohem_single(scores[i, :, :], gt_texts[i, :, :], training_masks[i, :, :])) + + selected_masks = np.concatenate(selected_masks, 0) + selected_masks = torch.from_numpy(selected_masks).float() + + return selected_masks \ No newline at end of file diff --git a/ptocr/model/loss/centerloss.py b/ptocr/model/loss/centerloss.py new file mode 100644 index 0000000..20383f3 --- /dev/null +++ b/ptocr/model/loss/centerloss.py @@ -0,0 +1,95 @@ +import torch +import torch.nn as nn + +class CenterLoss(nn.Module): + """Center loss. + + Reference: + Wen et al. A Discriminative Feature Learning Approach for Deep Face Recognition. ECCV 2016. + + Args: + num_classes (int): number of classes. + feat_dim (int): feature dimension. + """ + def __init__(self, num_classes=10, feat_dim=2, use_gpu=True): + super(CenterLoss, self).__init__() + self.num_classes = num_classes + self.feat_dim = feat_dim + self.use_gpu = use_gpu + + if self.use_gpu: + self.centers = nn.Parameter(torch.randn(self.num_classes, self.feat_dim).cuda()) + else: + self.centers = nn.Parameter(torch.randn(self.num_classes, self.feat_dim)) + + nn.init.normal_(self.centers, mean=0, std=1) +# nn.init.kaiming_normal_(tensor) +# nn.init.constant_(tensor,0.5) + + def forward(self, x, labels): + """ + Args: + x: feature matrix with shape (batch_size, feat_dim). + labels: ground truth labels with shape (batch_size). + """ + batch_size = x.size(0) + distmat = torch.pow(x, 2).sum(dim=1, keepdim=True).expand(batch_size, self.num_classes) + \ + torch.pow(self.centers, 2).sum(dim=1, keepdim=True).expand(self.num_classes, batch_size).t() + distmat.addmm_(1, -2, x, self.centers.t()) + + classes = torch.arange(self.num_classes).long() + if self.use_gpu: classes = classes.cuda() + labels = labels.unsqueeze(1).expand(batch_size, self.num_classes) + mask = labels.eq(classes.expand(batch_size, self.num_classes)) + + dist = distmat * mask.float() + loss = dist.clamp(min=1e-12, max=1e+12).sum() / batch_size + + return loss + + + +from torch.autograd.function import Function + +class CenterLoss1(nn.Module): + def __init__(self, num_classes, feat_dim, size_average=True): + super(CenterLoss1, self).__init__() + self.centers = nn.Parameter(torch.randn(num_classes, feat_dim)) + self.centerlossfunc = CenterlossFunc.apply + self.feat_dim = feat_dim + self.size_average = size_average + + def forward(self, feat,label): + batch_size = feat.size(0) + feat = feat.view(batch_size, -1) + # To check the dim of centers and features + if feat.size(1) != self.feat_dim: + raise ValueError("Center's dim: {0} should be equal to input feature's \ + dim: {1}".format(self.feat_dim,feat.size(1))) + batch_size_tensor = feat.new_empty(1).fill_(batch_size if self.size_average else 1) + loss = self.centerlossfunc(feat, label, self.centers, batch_size_tensor) + loss = loss.clamp(min=1e-12, max=1e+12) + return loss + + +class CenterlossFunc(Function): + @staticmethod + def forward(ctx, feature, label, centers, batch_size): + ctx.save_for_backward(feature, label, centers, batch_size) + centers_batch = centers.index_select(0, label.long()) + return (feature - centers_batch).pow(2).sum() / 2.0 / batch_size + + @staticmethod + def backward(ctx, grad_output): + feature, label, centers, batch_size = ctx.saved_tensors + centers_batch = centers.index_select(0, label.long()) + diff = centers_batch - feature + # init every iteration + counts = centers.new_ones(centers.size(0)) + ones = centers.new_ones(label.size(0)) + grad_centers = centers.new_zeros(centers.size()) + + counts = counts.scatter_add_(0, label.long(), ones) + grad_centers.scatter_add_(0, label.unsqueeze(1).expand(feature.size()).long(), diff) + grad_centers = grad_centers/counts.view(-1, 1) + return - grad_output * diff / batch_size, None, grad_centers / batch_size, None \ No newline at end of file diff --git a/ptocr/model/loss/ctc_loss.py b/ptocr/model/loss/ctc_loss.py new file mode 100644 index 0000000..cde7a8f --- /dev/null +++ b/ptocr/model/loss/ctc_loss.py @@ -0,0 +1,22 @@ +import torch.nn as nn +class CTCLoss(nn.Module): + def __init__(self,config): + super(CTCLoss,self).__init__() + self.config = config + if config['loss']['ctc_type'] == 'warpctc': + from warpctc_pytorch import CTCLoss as PytorchCTCLoss + self.criterion = PytorchCTCLoss() + else: + from torch.nn import CTCLoss as PytorchCTCLoss + self.criterion = PytorchCTCLoss(reduction = 'none') + + def forward(self,pre_batch,gt_batch): + preds,preds_size = pre_batch['preds'],pre_batch['preds_size'] + labels,labels_len = gt_batch['labels'],gt_batch['labels_len'] + if self.config['loss']['ctc_type'] != 'warpctc': + preds = preds.log_softmax(2).requires_grad_() # torch.ctcloss + loss = self.criterion(preds, labels, preds_size, labels_len) + if self.config['loss']['use_ctc_weight']: + loss = gt_batch['ctc_loss_weight']*loss.cuda() + loss = loss.sum() + return loss/self.config['trainload']['batch_size'] \ No newline at end of file diff --git a/ptocr/model/loss/db_loss.py b/ptocr/model/loss/db_loss.py new file mode 100644 index 0000000..d8ed9e7 --- /dev/null +++ b/ptocr/model/loss/db_loss.py @@ -0,0 +1,30 @@ +#-*- coding:utf-8 _*- +""" +@author:fxw +@file: db_loss.py +@time: 2020/08/10 +""" +import torch +import torch.nn as nn +from .basical_loss import MaskL1Loss,BalanceCrossEntropyLoss,DiceLoss,FocalCrossEntropyLoss +class DBLoss(nn.Module): + def __init__(self, l1_scale=10, bce_scale=1,eps=1e-6): + super(DBLoss, self).__init__() + self.dice_loss = DiceLoss(eps) + self.l1_loss = MaskL1Loss() + self.bce_loss = BalanceCrossEntropyLoss() + self.l1_scale = l1_scale + self.bce_scale = bce_scale + + def forward(self, pred_bach, gt_batch): + bce_loss = self.bce_loss(pred_bach['binary'][:,0], gt_batch['gt'], gt_batch['mask']) + metrics = dict(loss_bce=bce_loss) + if 'thresh' in pred_bach: + l1_loss, l1_metric = self.l1_loss(pred_bach['thresh'][:,0], gt_batch['thresh_map'], gt_batch['thresh_mask']) + dice_loss = self.dice_loss(pred_bach['thresh_binary'][:,0], gt_batch['gt'], gt_batch['mask']) + metrics['loss_thresh'] = dice_loss + loss = dice_loss + self.l1_scale * l1_loss + bce_loss * self.bce_scale + metrics.update(**l1_metric) + else: + loss = bce_loss + return loss, metrics \ No newline at end of file diff --git a/ptocr/model/loss/fc_loss.py b/ptocr/model/loss/fc_loss.py new file mode 100644 index 0000000..293b7e2 --- /dev/null +++ b/ptocr/model/loss/fc_loss.py @@ -0,0 +1,14 @@ +import torch +import torch.nn as nn +from .basical_loss import CrossEntropyLoss + +class FCLoss(nn.Module): + def __init__(self,ignore_index = -1): + super(FCLoss, self).__init__() + self.cross_entropy_loss = CrossEntropyLoss(ignore_index = ignore_index) + + + def forward(self, pred_bach, gt_batch): + loss = self.cross_entropy_loss(pred_bach['pred'],gt_batch['gt']) + metrics = dict(loss_fc=loss) + return loss, metrics \ No newline at end of file diff --git a/ptocr/model/loss/pan_loss.py b/ptocr/model/loss/pan_loss.py new file mode 100644 index 0000000..fc755f5 --- /dev/null +++ b/ptocr/model/loss/pan_loss.py @@ -0,0 +1,66 @@ +#-*- coding:utf-8 _*- +""" +@author:fxw +@file: pan_loss.py +@time: 2020/08/10 +""" +import torch +import torch.nn as nn +from torch.autograd import Variable +from .basical_loss import DiceLoss,Agg_loss,Dis_loss,ohem_batch + +class PANLoss(nn.Module): + def __init__(self,kernel_rate=0.5,agg_dis_rate=0.25,eps=1e-6): + super(PANLoss,self).__init__() + self.kernel_rate = kernel_rate + self.agg_dis_rate = agg_dis_rate + self.dice_loss = DiceLoss(eps) + self.agg_loss = Agg_loss() + self.dis_loss = Dis_loss() + + def GetKernelLoss(self,pre_text,pre_kernel,gt_kernel,train_mask): + mask0 = pre_text.data.cpu().numpy() + mask1 = train_mask.data.cpu().numpy() + selected_masks = ((mask0 > 0.5) & (mask1 > 0.5)).astype('float32') + selected_masks = torch.from_numpy(selected_masks).float() + selected_masks = Variable(selected_masks) + if torch.cuda.is_available(): + selected_masks = selected_masks.cuda() + loss_kernel = self.dice_loss(pre_kernel, gt_kernel, selected_masks) + return loss_kernel + + def GetTextLoss(self,pre_text,gt_text,train_mask): + selected_masks = ohem_batch(pre_text, gt_text, train_mask) + selected_masks = Variable(selected_masks) + if torch.cuda.is_available(): + selected_masks = selected_masks.cuda() + loss_text = self.dice_loss(pre_text, gt_text, selected_masks) + return loss_text + + def forward(self,pred_bach,gt_batch): + pre_text = torch.sigmoid(pred_bach['pre_text']) + pre_kernel = torch.sigmoid(pred_bach['pre_kernel']) + gt_text = gt_batch['gt_text'] + gt_text_key = gt_batch['gt_text_key'] + gt_kernel = gt_batch['gt_kernel'] + gt_kernel_key = gt_batch['gt_kernel_key'] + train_mask = gt_batch['train_mask'] + similarity_vector = pred_bach['similarity_vector'] + + pre_text_select = (pre_text > 0.5).float() + pre_kernel_select = (pre_kernel > 0.5).float() + gt_text_key = gt_text_key*pre_text_select + gt_kernel_key = gt_kernel_key*pre_kernel_select + + + loss_kernel = self.GetKernelLoss(pre_text,pre_kernel,gt_kernel,train_mask) + loss_text = self.GetTextLoss(pre_text,gt_text,train_mask) + loss_agg = self.agg_loss.cal_agg_batch(similarity_vector,gt_kernel_key, gt_text_key,train_mask) + loss_dis = self.dis_loss.cal_Ldis_batch(similarity_vector,gt_kernel_key,train_mask) + loss = loss_text + self.kernel_rate*loss_kernel + self.agg_dis_rate*(loss_agg + loss_dis) + metrics = dict(loss_text=loss_text) + metrics['loss_kernel'] = loss_kernel + metrics['loss_agg'] = loss_agg + metrics['loss_dis'] = loss_dis + return loss,metrics + diff --git a/ptocr/model/loss/pse_loss.py b/ptocr/model/loss/pse_loss.py new file mode 100644 index 0000000..073ca4f --- /dev/null +++ b/ptocr/model/loss/pse_loss.py @@ -0,0 +1,52 @@ +#-*- coding:utf-8 _*- +""" +@author:fxw +@file: pse_loss.py +@time: 2020/08/10 +""" +import torch +import torch.nn as nn +from torch.autograd import Variable +from .basical_loss import DiceLoss,ohem_batch + +class PSELoss(nn.Module): + def __init__(self,text_tatio=0.7,eps=1e-6): + super(PSELoss,self).__init__() + self.text_tatio = text_tatio + self.dice_loss = DiceLoss(eps) + + def GetKernelLoss(self, pre_text, pre_kernel, gt_kernel, train_mask): + mask0 = pre_text.data.cpu().numpy() + mask1 = train_mask.data.cpu().numpy() + selected_masks = ((mask0 > 0.5) & (mask1 > 0.5)).astype('float32') + selected_masks = torch.from_numpy(selected_masks).float() + selected_masks = Variable(selected_masks) + if torch.cuda.is_available(): + selected_masks = selected_masks.cuda() + loss_kernels = [] + for i in range(pre_kernel.shape[1]): + loss_kernel = self.dice_loss(torch.sigmoid(pre_kernel[:,i]), gt_kernel[:,i], selected_masks) + loss_kernels.append(loss_kernel) + return sum(loss_kernels)/len(loss_kernels) + + def GetTextLoss(self, pre_text, gt_text, train_mask): + selected_masks = ohem_batch(pre_text, gt_text, train_mask) + selected_masks = Variable(selected_masks) + if torch.cuda.is_available(): + selected_masks = selected_masks.cuda() + loss_text = self.dice_loss(pre_text, gt_text, selected_masks) + return loss_text + + def forward(self, pred_bach,gt_batch): + pre_text = torch.sigmoid(pred_bach['pre_text']) + pre_kernel = pred_bach['pre_kernel'] + gt_text = gt_batch['gt_text'] + gt_kernel = gt_batch['gt_kernel'] + train_mask = gt_batch['train_mask'] + + loss_text = self.GetTextLoss(pre_text,gt_text,train_mask) + loss_kernel = self.GetKernelLoss(pre_text,pre_kernel,gt_kernel,train_mask) + loss = self.text_tatio*loss_text + (1 - self.text_tatio)*loss_kernel + metrics = dict(loss_text=loss_text) + metrics['loss_kernel'] = loss_kernel + return loss,metrics \ No newline at end of file diff --git a/ptocr/model/loss/sast_loss.py b/ptocr/model/loss/sast_loss.py new file mode 100644 index 0000000..0cc1e4e --- /dev/null +++ b/ptocr/model/loss/sast_loss.py @@ -0,0 +1,109 @@ +#-*- coding:utf-8 _*- +""" +@author:fxw +@file: sast_loss.py +@time: 2020/08/18 +""" + +import torch +import torch.nn as nn +from torch.autograd import Variable +from .basical_loss import DiceLoss,BalanceCrossEntropyLoss,ohem_batch + +class SASTLoss(nn.Module): + def __init__(self,tvo_lw,tco_lw,score_lw,border_lw): + super(SASTLoss,self).__init__() + self.dict_loss = DiceLoss() + self.bce_loss = BalanceCrossEntropyLoss() + self.tvo_lw, self.tco_lw = tvo_lw,tco_lw #1.5, 1.5 + self.score_lw, self.border_lw = score_lw,border_lw #1.0, 1.0 + + def forward(self, predicts,labels): + + f_score = predicts['f_score'] + f_border = predicts['f_border'] + f_tvo = predicts['f_tvo'] + f_tco = predicts['f_tco'] + + l_score = labels['input_score'] + l_border = labels['input_border'] + l_mask = labels['input_mask'] + l_tvo = labels['input_tvo'] + l_tco = labels['input_tco'] + + batch_size,_,w,h = f_score.shape + + +# #score_loss add ohem +# selected_masks = ohem_batch(f_score.squeeze(), l_score.squeeze(), l_mask.squeeze()) +# selected_masks = Variable(selected_masks).unsqueeze(1) +# if torch.cuda.is_available(): +# selected_masks = selected_masks.cuda() +# score_loss = self.dict_loss(f_score,l_score,selected_masks) + + #score_loss no ohem + intersection = torch.sum(f_score * l_score * l_mask) + union = torch.sum(f_score * l_mask) + torch.sum(l_score * l_mask) + score_loss = 1.0 - 2 * intersection / (union + 1e-5) + + # border loss + l_border_split, l_border_norm = l_border[:,0:4,:,:], l_border[:,-1:,:,:] + f_border_split = f_border + l_border_norm_split = l_border_norm.expand((batch_size,4,w,h)) + l_border_score = l_score.expand((batch_size,4,w,h)) + l_border_mask = l_mask.expand((batch_size,4,w,h)) + border_diff = l_border_split - f_border_split + abs_border_diff = torch.abs(border_diff) + border_sign = abs_border_diff < 1.0 + border_sign = border_sign.float() + border_sign.stop_gradient = True + border_in_loss = 0.5 * abs_border_diff * abs_border_diff * border_sign + \ + (abs_border_diff - 0.5) * (1.0 - border_sign) + border_out_loss = l_border_norm_split * border_in_loss + border_loss = torch.sum(border_out_loss * l_border_score * l_border_mask) / \ + (torch.sum(l_border_score * l_border_mask) + 1e-5) + + # tvo_loss + l_tvo_split, l_tvo_norm = l_tvo[:,0:8,:,:],l_tvo[:,-1:,:,:] + f_tvo_split = f_tvo + l_tvo_norm_split = l_tvo_norm.expand((batch_size,8,w,h)) + l_tvo_score = l_score.expand((batch_size,8,w,h)) + l_tvo_mask = l_mask.expand((batch_size,8,w,h)) + # + tvo_geo_diff = l_tvo_split - f_tvo_split + abs_tvo_geo_diff = torch.abs(tvo_geo_diff) + tvo_sign = abs_tvo_geo_diff < 1.0 + tvo_sign = tvo_sign.float() + tvo_sign.stop_gradient = True + tvo_in_loss = 0.5 * abs_tvo_geo_diff * abs_tvo_geo_diff * tvo_sign + \ + (abs_tvo_geo_diff - 0.5) * (1.0 - tvo_sign) + tvo_out_loss = l_tvo_norm_split * tvo_in_loss + tvo_loss = torch.sum(tvo_out_loss * l_tvo_score * l_tvo_mask) / \ + (torch.sum(l_tvo_score * l_tvo_mask) + 1e-5) + + # tco_loss + l_tco_split, l_tco_norm = l_tco[:,0:2,:,:],l_tco[:,-1:,:,:] + f_tco_split = f_tco + l_tco_norm_split = l_tco_norm.expand((batch_size,2,w,h)) + l_tco_score = l_score.expand((batch_size,2,w,h)) + l_tco_mask = l_mask.expand((batch_size,2,w,h)) + # + tco_geo_diff = l_tco_split - f_tco_split + abs_tco_geo_diff = torch.abs(tco_geo_diff) + tco_sign = abs_tco_geo_diff < 1.0 + tco_sign = tco_sign.float() + tco_sign.stop_gradient = True + tco_in_loss = 0.5 * abs_tco_geo_diff * abs_tco_geo_diff * tco_sign + \ + (abs_tco_geo_diff - 0.5) * (1.0 - tco_sign) + tco_out_loss = l_tco_norm_split * tco_in_loss + tco_loss = torch.sum(tco_out_loss * l_tco_score * l_tco_mask) / \ + (torch.sum(l_tco_score * l_tco_mask) + 1e-5) + + # total loss + + total_loss = score_loss * self.score_lw + border_loss * self.border_lw + \ + tvo_loss * self.tvo_lw + tco_loss * self.tco_lw + + metrics = {'loss_total': total_loss, "loss_score": score_loss, \ + "loss_border": border_loss, 'loss_tvo': tvo_loss, 'loss_tco': tco_loss} + return total_loss,metrics diff --git a/ptocr/model/segout/__init__.py b/ptocr/model/segout/__init__.py new file mode 100644 index 0000000..35de2ad --- /dev/null +++ b/ptocr/model/segout/__init__.py @@ -0,0 +1,6 @@ +#-*- coding:utf-8 _*- +""" +@author:fxw +@file: __init__.py.py +@time: 2020/08/07 +""" diff --git a/ptocr/model/segout/__pycache__/__init__.cpython-35.pyc b/ptocr/model/segout/__pycache__/__init__.cpython-35.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c884d2aec2855c2199aa0c50607772c8605985a4 GIT binary patch literal 226 zcmWgR<>mUqrW+r@z`*brh~a<{$Z`PUVlE(&!oUy(BpDfkHJPeRxf~KpOEU6{tkNpV zxg63mb5gAo;^Q;(GE3s)^$IG1h|8fQGZ!doWME{VZ(yNsV9w>I$#{!BK0YNsIX-?R zLlG0uR50<&$Jr_-v^ce>I3_JIFTJ9)JT)^WpfWilu_!m7C_gJTxuh7#FUc=T&hU2* qiYX|`PcDkd%}+_qi78G^&o3>BL9+W6hYe7wG$+-L4diwpW&i+EYCqxt literal 0 HcmV?d00001 diff --git a/ptocr/model/segout/__pycache__/__init__.cpython-36.pyc b/ptocr/model/segout/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cda4725a473e7364c95acb962af6d2e4b518376a GIT binary patch literal 180 zcmXr!<>k6Nb$@&i0|UcjAcg}*Aj<)Wi#dQq3PTh_3S%&XCR3FumqTJ{Nk)E=Ra!+k zmqS`+PO6nce0*kJW=VX!UO^=gaXFM^<^n~H42%r)4J`Bx%(?tD8E>)2$EV~c$H%W^ zC}IMd2_}9i=;;@fq)A6j`gQ74+N5uw)Fj=c zNkA8nYq7%NAUJ*0eG<>BacTC3uU~ba46|JCg{iKhr=LVtkIH#UlyOj217h-)%SA@rfRw-Q*zD;=N;b7hM4)y4%u5?nB z=8_cAWf!)5ngmy-v59?*H4OV9_AC4=IwQL9XyVZYkvj8u9yRF97sjVbyvVdgXOM>R zitZvHY^z-3tO+N3EYV(UUntS*TwU&h6mNGI+l!aro^86Q&-kvoeCD!VkL|vIHq7ob zo%Ct4BFrkm4t0jhU~i4Um=6?7tcxKXz9jO5?N=VlY>3*%oBD{r;h-09z+v6+>Uo~U zb}#(>SXX0h!lW!-hIxFVjSa`~OC9odSemdXtNYhFaT?F6>?IOwp;wjR-0E<#5msZP z?YPX-urhI0WX0%zUSg+ZS&jF?&xVkh>yP9G&$B!$bZo+`u$7L}upA<(*{Yk-*(!>hdSQSEzRoQJ1ecma_cY~P0K8GZQLU^*{OET zcs9#VT>q8MM&ruLX|BC+yX)7-*A8(({X1wo-|BqmRe^ihjB4_J%<&?U!355X8MIGCn#5o zIchc;f4=|a@vJHnb383mogdebbo@3z8LnWiWcx!9>TRhG{sQ%$T32i8p3pmL)7$cR zHBdY1^wttK++X-R;i&M#VPJkA!=8YkKqsgZMd!c+0S6R4iV0Zt0#&p}Rf8r1x-1zm zHgKsGzg$Q+M7kx?b9tQz0U=PoQkBr_upTdQ0Rq<<#@Yu*K8MtFV8&7BI^ZKr@%-!Y z=*Ro-{_`I{zx(*|KX}ZKusGu6iD^eJL(R`mO9;8 zcJy+>C0r`}G%+}9#Myrb0~ekHZ9OvEf=G%UVGeMH|Cu5L*oN~x;BF1Nm;krnWZ*6& zfN=FT+{kmlnlu&QF2fx}>+OYh#EwN~#j&2?b2ix4GRD{n4X4$b_Nv zelX|q@i6JJ5Fd^_!`sn>p@x!%K)|VsZj7SG3g;}Rkf*SU&#?m+2u7cX3+p7}(7Tly zgQI$PuxfUAwY}(yhTV08!LBLb=^9B~)ivK8&I{>j=p9IyN}E}k#}zt(9mnW9^&#k$ zvuGjdg82+kpa-qG{QV(XvukA2k$K362Xzk=Rr3=*|NU#iklKCgk_R;OFbalX>K4lrjPmy!hd12A>wA>o*j?=wWO2cIJocW%M)w=pCW z88ZjI`5As(*XSghnKFS78v6B28rL{y4ts4N*R>2_e^ROD^0miaHa5-->;reRFKhN!Z#S*EjeT PyWBEx1%QdZTJ!!3RrAWc literal 0 HcmV?d00001 diff --git a/ptocr/model/segout/__pycache__/det_DB_segout.cpython-36.pyc b/ptocr/model/segout/__pycache__/det_DB_segout.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4bd465740bd3885db9178f44e59efe7983867e9a GIT binary patch literal 2929 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 literal 0 HcmV?d00001 diff --git a/ptocr/model/segout/__pycache__/det_PAN_segout.cpython-36.pyc b/ptocr/model/segout/__pycache__/det_PAN_segout.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8022601a1c1b5d7110c9bf0fea0af14b25548621 GIT binary patch literal 1045 zcmZWn&u`N(6tv!(Vj42oexM z1xx6J1x#4NPOQ`pY)ak}Q4`KJ5sqY&+R_Rf!A^-^UmSYP@=7gUDA>P55UB-RuWzl1TMA)D+(zJjqjLRF`G)I5TbE8kF!{FZdjHX z=OoY0d&0P9aTcn0A$`l(g-nK~6=#`LJQ{~ECxxj;NmvxJ00J7ER~ycMl0OIS&Q#}- z>ZG}lNoP%C=Ra0e10LUnzo>|!ExOp>s=a-O>R;&?hW`oX2rwm?kdiLx6$J{Q=q0-X zDke;Q)>a8@3vvTQ4B0ax*mrt$@3YWXf(FNszlj5yZrH}}fZsZWGi;K_jFjp6$5)WWgJOo3i2cJWq(0%%p+H~=Fo3~B0&r}UU6*db9 z4kBk^DnVqAa}Z{h;Ch$yud^^&pHzaV=MZn28$BbLB%I#^@~J`q3UO7sX9F5yqraZj0b&{uPp1qr8k&g3hO^frf0#oJQKeSc8d%TAG8Y?N# J*vzB5>@T@~`w;*D literal 0 HcmV?d00001 diff --git a/ptocr/model/segout/__pycache__/det_PSE_segout.cpython-35.pyc b/ptocr/model/segout/__pycache__/det_PSE_segout.cpython-35.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5e9c4274ba00cb8240fdc66e8c6361feae80b436 GIT binary patch literal 1091 zcmZWo%Wl&^6uo0RPGZ_pDB`iATW{Jb0#Q{JiU^MtrE0_`vNX8%BysQ~%s4cS+EsXb z3qQg~c*`okz=|`ImImQ??#$eKXYRS@IQQ4ry`LXiJg|s<(aPgs-NsNaU}B;kETh<> zl+nndo<*@uu|ug%nL|B?*qgXUu}fc&=#sE#RHL~~Jy##<`t{kq*A6G;u#gA+>6zE= zC#g8tjz!6jyRWzsgJM$djL*GxnPdpG_F8+pd;7b4&%6Z?(9r@2$!Aq#GFD+WPDP~M z*H7(gp`KtN02f$cfzp8fPSnT3ApQpQSX_ezbVLELy44kfm!cF=S;z%3qD38-MfTOz zdUi4Nte(P@co|&~&C#qyb0SvSG;-(y-eqM|d|uN@90he9`)GlyNowP}RZUICLP`g_ zs=;}ZCne{VmFJb)De}*)xN4jxc_@>a@a@V`BJEcjNuCSIqhW|SRaHGoL#2cQJ5A2b zhVviv`=oOayjMc1pdaRg>Eui#!SOkc$?RAbBQ&b8N4L@NUFReimqjFltcXP#EcFQf zlSeiI)Wc93F|jQ+ySGBy&UFWWS<`7+oDPP{VL-(QmeHKiB{K{fxJ(#A!Do9qviH26LM literal 0 HcmV?d00001 diff --git a/ptocr/model/segout/__pycache__/det_PSE_segout.cpython-36.pyc b/ptocr/model/segout/__pycache__/det_PSE_segout.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0d1925d025cd01e2fcae7655609befbd7f38c19b GIT binary patch literal 999 zcmZWnJ8#=C5GJXo)!66@MY?qgpv5-ap+OK72GZBi1_8VY1qfB7ZMk~56xF1LGC4)2 z{*eBhu3S6$FLdgioHPbXfk*0icl_@A_`KT<-u)W!kBpGtWM|m0AH%GMU<8SXpn_#| z!6GIsVP{ruM>ZvIiExB_MT9Ha!dY99E7*jD-s&(I$F-i9>S%g#9*n1HCXb#8srlLD zHLv8XtaX3+Eg0)Ghl9~@G#m^M2g4V^2JR!L8^A+CYW#Xx#rZOm3A%3wg2PcAfRQAk z0FVjqnoKEzyXr*N7FJ|m6PrX1K=RGKiJZNXS|++w8$t=$c#xi0SP2HM?#Q? zBVqhcX%VY*B}2>DmCUB5mllOoJekL^W|i@hEUqe90RbJ(+aJzN5 ziOe$2@8E<~-N&Mcs(lWqN~?%ZdsgcSJ>zjM$^^>$@G6sezbF(cXL=`Po|na&x=3_d e7F$x>4=tEB_wJ&d=KbR>+?Uu%g~o>7_vl}`_3bSH literal 0 HcmV?d00001 diff --git a/ptocr/model/segout/__pycache__/det_SAST_segout.cpython-35.pyc b/ptocr/model/segout/__pycache__/det_SAST_segout.cpython-35.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1a37c5ebc4218a338479438910086431420be352 GIT binary patch literal 3114 zcmd5;-EZ4e6hC(C#BtMhYqvouunqAQKKhaNVN?~$2z0zqrUhQ2NLRsjT31P2uAQ~3 zT4^E(p7|Si;veCU@GDRG7vP28xwaFht2T*&gcJXK&OP_@+}}C(-kP7cPk!GOzuH89 zQ0_4x*YSo=0WMJssFABvPooxuMAqr3K&=9~_#4zSsBcosB<+D~l3S!7(WppXfsTqa zLd6n2a7*M`%3$RT77S%_ZDp`?1{CVltj?W%V%Iyv=rE8w-P04h-t~Ll&K=i_MDu?0 z8xeX3!7y4oeqz@nzYj}oqqecWac_O|o;}6_Xn(8&t6FR~g2Cg@29Lbn&|yRN6jC8v zgWzGkSRhcRpNYEJdVfMmwpgsKWw5nbHRCeR;!CgNZaP}p5q~Yb+;R99Ai`gxbD|Nv zrqhT>i_n>JB>PXfaAa}~VGx^i^@k^wH8q);2@Nl`Dk@V_p0$Gi;qsBGakr(c0(!pG z*2=ntDQlaKSk6q(j%a5cTGLiDBhIlGo7M9E_$gd4t~0P_B*U0}6PHi?=uk*($llmM zLA4T_;qcg#lC{_p!XNmN5K`5i?~2d~q$eEg<7Pag)@IeMS#>*J=!)+G>3TA4Jge6( z*|#p~+hC6odc7{bWg(niCk%!7js9#lcG};Do($XF&fws5c;fl(hfkctj_f~_!I9@g zA@nGa&f(XMNA2S%aAdn5xL&WF@V5Odk7WgG=QmsekhZL?s^48b`JOFJ7`K)=$YF}} z!+vAAfj9gGfW1EgYBbX5LRTu72o6qE6z0{!RE-Qe0x9#>V%8O^q305v7gSe?YR#(U zoQhEj)a8KI_N%s>$JFEk!y>~qKx`^M#ih_c=m)+lud{`HAeR|#0IG((iBGJZ#-)@E zv6XGDe1)qic$F)ax`90DNSBq6Ta&h!{q$7Q&#q6snbt>8gL#8a-xDIXg}{Cddn{Lk z_wB^I?!@=f$Q9k;5HqW0gA?M)5!1|L?@JTTl|_aUgGxV~na2#W4A_(Ebf`$)D)|-z$6k_g=DH$!9Hc2D zr_|z{vXxi1mEz>vq)O5zRgyNTg6yJLov~-aU^SEXFq;=Ye8BJ_APsf73OP2_MyL-?fY~@#F}Q_V zsh6_I&c4$KudPiYT;Aq!?l9bCP<#HIU}j?Qbf>}0CI4bD&o#Y+&%FpI1`Z46DL?$> z9Kmn64!HUvF;bh$HwTka8NtYhFLG@uVK(1wl2vj#itVryM&gjKEsFHHg4%QGc%P@j z5yb~nMLWBZoA;E2JYjyaTTlaX*aqZD*w*QyKo{T@*o8w$ol#t9(nXPcq6@{Mb6guW z+C4`-NIE1?4^jaVSO>{~RN~9nzI+{H$Tt{1V)z(P)g`-5Zm?{rNJ~gn@cRc>HE3DK z(sO;B-B&T~3v?6mc}3by{EC{L{?3;3uK34HCCz* zks~3wQcoQDBe?ZH;D6va5*IjGTsU*$z1j8JNh(39#IEKy@4fljdGF0{c5QCX{q@J6 z+5HM3zmu8A0C@w7xC;=F771ve^=ON#TI=d9JS|hz> zgT)RUusFHJ0ZS=x!Q$qYQlM>-T6yoPyWtJe?Sx-zKiYLS+EFjOb|wf@wsmvsOD4ij zGDuf;zH>Lys1GfTON~qQOIPZvSKKjt9BGa^$D!N{=tHg_K~@qBXgCG%;l<4&Y`)= zkG>j`Ze>JnlW#xWr|H~~rq%BJNaMdieXM(YNV^N&#Sx|C!PC5T30j|K_U?(Ggl(WL zcNal(vz}i=Qeg0!+k)Z2LKX|v|?IarHV5#|w& z0Z21VxW6rJ5q0`W6!7ENvVgFNumn&u_!+1qeI)HXL!>haH$RWv6yR}(9tq#|cCjv+&Q^+_3braA zV5>@#o8Vn;A=n6tvXIor2tZl9gl0Sq&mt?1vEy|FJa`#PmD?00Xi;Ay-LPcp_!n_(!A;z>J{*J3Iq@CL2AEBh_xvC7(kPc;fJea_HXD%%@Hh zX|n0b57s7*x(zT%q`TU_zHfj=FGzZP&LH;F tD2X%PvOZIPIO+z score: + continue + + if points.shape[0] > 2: + box = self.unclip(points, self.unclip_ratio) + if len(box) > 1: + continue + else: + continue + box = box.reshape(-1, 2) + _, sside = self.get_mini_boxes(box.reshape((-1, 1, 2))) + if sside < self.min_size + 2: + continue + + if not isinstance(dest_width, int): + dest_width = dest_width.item() + dest_height = dest_height.item() + + box[:, 0] = np.clip( + np.round(box[:, 0] / width * dest_width), 0, dest_width) + box[:, 1] = np.clip( + np.round(box[:, 1] / height * dest_height), 0, dest_height) + boxes.append(box.tolist()) + scores.append(score) + return boxes, scores + + def boxes_from_bitmap(self, pred, _bitmap, dest_width, dest_height): + ''' + _bitmap: single map with shape (1, H, W), + whose values are binarized as {0, 1} + ''' + + bitmap = _bitmap + height, width = bitmap.shape + + outs = cv2.findContours((bitmap * 255).astype(np.uint8), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE) + if len(outs) == 3: + img, contours, _ = outs[0], outs[1], outs[2] + elif len(outs) == 2: + contours, _ = outs[0], outs[1] + + num_contours = min(len(contours), self.max_candidates) + boxes = np.zeros((num_contours, 4, 2), dtype=np.int16) + scores = np.zeros((num_contours,), dtype=np.float32) + + for index in range(num_contours): + contour = contours[index] + points, sside = self.get_mini_boxes(contour) + if sside < self.min_size: + continue + points = np.array(points) + score = self.box_score_fast(pred, points.reshape(-1, 2)) + if self.box_thresh > score: + continue + + box = self.unclip(points, self.unclip_ratio).reshape(-1, 1, 2) + box, sside = self.get_mini_boxes(box) + if sside < self.min_size + 2: + continue + box = np.array(box) + if not isinstance(dest_width, int): + dest_width = dest_width.item() + dest_height = dest_height.item() + + box[:, 0] = np.clip( + np.round(box[:, 0] / width * dest_width), 0, dest_width) + box[:, 1] = np.clip( + np.round(box[:, 1] / height * dest_height), 0, dest_height) + boxes[index, :, :] = box.astype(np.int16) + scores[index] = score + return boxes, scores + + def unclip(self, box, unclip_ratio=2): + poly = Polygon(box) + distance = poly.area * unclip_ratio / poly.length + offset = pyclipper.PyclipperOffset() + offset.AddPath(box, pyclipper.JT_ROUND, pyclipper.ET_CLOSEDPOLYGON) + expanded = np.array(offset.Execute(distance)) + return expanded + + def get_mini_boxes(self, contour): + bounding_box = cv2.minAreaRect(contour) + points = sorted(list(cv2.boxPoints(bounding_box)), key=lambda x: x[0]) + + index_1, index_2, index_3, index_4 = 0, 1, 2, 3 + if points[1][1] > points[0][1]: + index_1 = 0 + index_4 = 1 + else: + index_1 = 1 + index_4 = 0 + if points[3][1] > points[2][1]: + index_2 = 2 + index_3 = 3 + else: + index_2 = 3 + index_3 = 2 + + box = [ + points[index_1], points[index_2], points[index_3], points[index_4] + ] + return box, min(bounding_box[1]) + + def box_score_fast(self, bitmap, _box): + h, w = bitmap.shape[:2] + box = _box.copy() + xmin = np.clip(np.floor(box[:, 0].min()).astype(np.int), 0, w - 1) + xmax = np.clip(np.ceil(box[:, 0].max()).astype(np.int), 0, w - 1) + ymin = np.clip(np.floor(box[:, 1].min()).astype(np.int), 0, h - 1) + ymax = np.clip(np.ceil(box[:, 1].max()).astype(np.int), 0, h - 1) + + mask = np.zeros((ymax - ymin + 1, xmax - xmin + 1), dtype=np.uint8) + box[:, 0] = box[:, 0] - xmin + box[:, 1] = box[:, 1] - ymin + cv2.fillPoly(mask, box.reshape(1, -1, 2).astype(np.int32), 1) + return cv2.mean(bitmap[ymin:ymax + 1, xmin:xmax + 1], mask)[0] + + def __call__(self, pred, ratio_list): + pred = pred[:, 0, :, :] + segmentation = pred > self.thresh + + boxes_batch = [] + score_batch = [] + for batch_index in range(pred.shape[0]): + height, width = pred.shape[-2:] + if (self.is_poly): + tmp_boxes, tmp_scores = self.polygons_from_bitmap( + pred[batch_index], segmentation[batch_index], width, height) + + boxes = [] + score = [] + for k in range(len(tmp_boxes)): + if tmp_scores[k] > self.box_thresh: + boxes.append(tmp_boxes[k]) + score.append(tmp_scores[k]) + if len(boxes) > 0: + ratio_w, ratio_h = ratio_list[batch_index] + for i in range(len(boxes)): + boxes[i] = np.array(boxes[i]) + boxes[i][:, 0] = boxes[i][:, 0] * ratio_w + boxes[i][:, 1] = boxes[i][:, 1] * ratio_h + + boxes_batch.append(boxes) + score_batch.append(score) + else: + +# tmp_boxes, tmp_scores = self.boxes_from_bitmap( +# pred[batch_index], segmentation[batch_index], width, height) + + + tmp_boxes = cpp_boxes_from_bitmap(pred[batch_index], segmentation[batch_index],self.box_thresh,self.unclip_ratio) + boxes = [] + score = [] + for k in range(len(tmp_boxes)): +# if tmp_scores[k] > self.box_thresh: + boxes.append(tmp_boxes[k]) +# score.append(tmp_scores[k]) + if len(boxes) > 0: + boxes = np.array(boxes) + + ratio_w, ratio_h = ratio_list[batch_index] + boxes[:, :, 0] = boxes[:, :, 0] * ratio_w + boxes[:, :, 1] = boxes[:, :, 1] * ratio_h + + boxes_batch.append(boxes) + score_batch.append(score) + return boxes_batch, score_batch \ No newline at end of file diff --git a/ptocr/postprocess/PANpostprocess.py b/ptocr/postprocess/PANpostprocess.py new file mode 100644 index 0000000..eb9dce8 --- /dev/null +++ b/ptocr/postprocess/PANpostprocess.py @@ -0,0 +1,135 @@ +#-*- coding:utf-8 _*- +""" +@author:fxw +@file: PSEpostprocess.py +@time: 2020/08/13 +""" + +import numpy as np +import cv2 +import torch +from .piexlmerge import pan + +class PANPostProcess(object): + + def __init__(self, config): + self.min_text_area = config['postprocess']['min_text_area'] + self.is_poly = config['postprocess']['is_poly'] + self.min_score = config['postprocess']['min_score'] + self.config = config + + def polygons_from_bitmap(self, pred): + + boxes = [] + scores = [] + pred,label_points,label_values = pan(pred,self.config) + + for label_value, label_point in label_points.items(): + if label_value not in label_values: + continue + score_i = label_point[0] + label_point = label_point[2:] + points = np.array(label_point, dtype=int).reshape(-1, 2) + + if points.shape[0] < self.min_text_area : + continue + + if score_i < self.min_score: + continue + + binary = np.zeros(pred.shape, dtype='uint8') + binary[pred == label_value] = 1 + + find_out = cv2.findContours(binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) + if(len(find_out)==2): + contours, _ = find_out + else: + _, contours, _ = find_out + contour = contours[0] + # epsilon = 0.01 * cv2.arcLength(contour, True) + # box = cv2.approxPolyDP(contour, epsilon, True) + box = contour + + if box.shape[0] <= 2: + continue + + box = box * self.config['postprocess']['scale'] + box = box.astype('int32') + boxes.append(box[:,0]) + scores.append(score_i) + return boxes, scores + + def boxes_from_bitmap(self, pred): + boxes = [] + scores = [] + pred,label_points,label_values = pan(pred,self.config) + + for label_value, label_point in label_points.items(): + if label_value not in label_values: + continue + score_i = label_point[0] + label_point = label_point[2:] + points = np.array(label_point, dtype=int).reshape(-1, 2) + + if points.shape[0] < self.min_text_area : + continue + + if score_i < self.min_score: + continue + box = self.get_mini_boxes(points)*self.config['postprocess']['scale'] + boxes.append(box) + scores.append(score_i) + return boxes, scores + + def get_mini_boxes(self, contour): + bounding_box = cv2.minAreaRect(contour) + points = sorted(list(cv2.boxPoints(bounding_box)), key=lambda x: x[0]) + + index_1, index_2, index_3, index_4 = 0, 1, 2, 3 + if points[1][1] > points[0][1]: + index_1 = 0 + index_4 = 1 + else: + index_1 = 1 + index_4 = 0 + if points[3][1] > points[2][1]: + index_2 = 2 + index_3 = 3 + else: + index_2 = 3 + index_3 = 2 + + box = [ + points[index_1], points[index_2], points[index_3], points[index_4] + ] + return np.array(box) + + def __call__(self, pred, ratio_list): + boxes_batch = [] + score_batch = [] + for batch_index in range(pred.shape[0]): + pred_single = pred[batch_index].unsqueeze(0) + if (self.is_poly): + boxes, score = self.polygons_from_bitmap(pred_single) + new_bboxes =[] + if len(boxes) > 0: + ratio_w, ratio_h = ratio_list[batch_index] + for bbox in boxes: + bbox[:, 0] = bbox[:, 0] * ratio_w + bbox[:, 1] = bbox[:, 1] * ratio_h + new_bboxes.append(bbox) + + boxes_batch.append(new_bboxes) + score_batch.append(score) + else: + boxes, score = self.boxes_from_bitmap(pred_single) + if len(boxes) > 0: + boxes = np.array(boxes) + ratio_w, ratio_h = ratio_list[batch_index] + boxes[:, :, 0] = boxes[:, :, 0] * ratio_w + boxes[:, :, 1] = boxes[:, :, 1] * ratio_h + + boxes_batch.append(boxes) + score_batch.append(score) + return boxes_batch, score_batch + diff --git a/ptocr/postprocess/PSEpostprocess.py b/ptocr/postprocess/PSEpostprocess.py new file mode 100644 index 0000000..3843ff5 --- /dev/null +++ b/ptocr/postprocess/PSEpostprocess.py @@ -0,0 +1,135 @@ +#-*- coding:utf-8 _*- +""" +@author:fxw +@file: PSEpostprocess.py +@time: 2020/08/13 +""" + +import numpy as np +import cv2 +import torch +from .piexlmerge import pse + +class PSEPostProcess(object): + + def __init__(self, config): + self.min_text_area = config['postprocess']['min_text_area'] + self.is_poly = config['postprocess']['is_poly'] + self.min_score = config['postprocess']['min_score'] + self.config = config + + def polygons_from_bitmap(self, pred): + + boxes = [] + scores = [] + pred,label_points,label_values = pse(pred,self.config) + + for label_value, label_point in label_points.items(): + if label_value not in label_values: + continue + score_i = label_point[0] + label_point = label_point[2:] + points = np.array(label_point, dtype=int).reshape(-1, 2) + + if points.shape[0] < self.min_text_area : + continue + + if score_i < self.min_score: + continue + + binary = np.zeros(pred.shape, dtype='uint8') + binary[pred == label_value] = 1 + + find_out = cv2.findContours(binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) + if(len(find_out)==2): + contours, _ = find_out + else: + _, contours, _ = find_out + contour = contours[0] + # epsilon = 0.01 * cv2.arcLength(contour, True) + # box = cv2.approxPolyDP(contour, epsilon, True) + box = contour + + if box.shape[0] <= 2: + continue + + box = box * self.config['postprocess']['scale'] + box = box.astype('int32') + boxes.append(box[:,0]) + scores.append(score_i) + return boxes, scores + + def boxes_from_bitmap(self, pred): + boxes = [] + scores = [] + pred,label_points,label_values = pse(pred,self.config) + + for label_value, label_point in label_points.items(): + if label_value not in label_values: + continue + score_i = label_point[0] + label_point = label_point[2:] + points = np.array(label_point, dtype=int).reshape(-1, 2) + + if points.shape[0] < self.min_text_area : + continue + + if score_i < self.min_score: + continue + box = self.get_mini_boxes(points)*self.config['postprocess']['scale'] + boxes.append(box) + scores.append(score_i) + return boxes, scores + + def get_mini_boxes(self, contour): + bounding_box = cv2.minAreaRect(contour) + points = sorted(list(cv2.boxPoints(bounding_box)), key=lambda x: x[0]) + + index_1, index_2, index_3, index_4 = 0, 1, 2, 3 + if points[1][1] > points[0][1]: + index_1 = 0 + index_4 = 1 + else: + index_1 = 1 + index_4 = 0 + if points[3][1] > points[2][1]: + index_2 = 2 + index_3 = 3 + else: + index_2 = 3 + index_3 = 2 + + box = [ + points[index_1], points[index_2], points[index_3], points[index_4] + ] + return np.array(box) + + def __call__(self, pred, ratio_list): + boxes_batch = [] + score_batch = [] + for batch_index in range(pred.shape[0]): + pred_single = pred[batch_index].unsqueeze(0) + if (self.is_poly): + boxes, score = self.polygons_from_bitmap(pred_single) + new_bboxes =[] + if len(boxes) > 0: + ratio_w, ratio_h = ratio_list[batch_index] + for bbox in boxes: + bbox[:, 0] = bbox[:, 0] * ratio_w + bbox[:, 1] = bbox[:, 1] * ratio_h + new_bboxes.append(bbox) + + boxes_batch.append(new_bboxes) + score_batch.append(score) + else: + boxes, score = self.boxes_from_bitmap(pred_single) + if len(boxes) > 0: + boxes = np.array(boxes) + ratio_w, ratio_h = ratio_list[batch_index] + boxes[:, :, 0] = boxes[:, :, 0] * ratio_w + boxes[:, :, 1] = boxes[:, :, 1] * ratio_h + + boxes_batch.append(boxes) + score_batch.append(score) + return boxes_batch, score_batch + diff --git a/ptocr/postprocess/SASTpostprocess.py b/ptocr/postprocess/SASTpostprocess.py new file mode 100644 index 0000000..b10b1f4 --- /dev/null +++ b/ptocr/postprocess/SASTpostprocess.py @@ -0,0 +1,294 @@ +#-*- coding:utf-8 _*- +""" +@author:fxw +@file: 1232.py +@time: 2020/08/19 +""" + +import sys +import numpy as np +from .locality_aware_nms import nms_locality +from .lanms import merge_quadrangle_n9 +import cv2 +import time + + +class SASTPostProcess(object): + """ + The post process for SAST. + """ + + def __init__(self, config): + self.score_thresh = config['postprocess']['score_thresh'] + self.nms_thresh = config['postprocess']['nms_thresh'] + self.sample_pts_num = config['postprocess']['sample_pts_num'] + self.shrink_ratio_of_width = config['postprocess']['shrink_ratio_of_width'] + self.expand_scale = config['postprocess']['expand_scale'] + self.tcl_map_thresh = config['postprocess']['tcl_map_thresh'] + + # c++ la-nms is faster, but only support python 3.5 + self.is_python35 = True +# if sys.version_info.major == 3 and sys.version_info.minor == 6: +# self.is_python35 = True + + def point_pair2poly(self, point_pair_list): + """ + Transfer vertical point_pairs into poly point in clockwise. + """ + # constract poly + point_num = len(point_pair_list) * 2 + point_list = [0] * point_num + for idx, point_pair in enumerate(point_pair_list): + point_list[idx] = point_pair[0] + point_list[point_num - 1 - idx] = point_pair[1] + return np.array(point_list).reshape(-1, 2) + + def shrink_quad_along_width(self, quad, begin_width_ratio=0., end_width_ratio=1.): + """ + Generate shrink_quad_along_width. + """ + ratio_pair = np.array([[begin_width_ratio], [end_width_ratio]], dtype=np.float32) + p0_1 = quad[0] + (quad[1] - quad[0]) * ratio_pair + p3_2 = quad[3] + (quad[2] - quad[3]) * ratio_pair + return np.array([p0_1[0], p0_1[1], p3_2[1], p3_2[0]]) + + def expand_poly_along_width(self, poly, shrink_ratio_of_width=0.3): + """ + expand poly along width. + """ + point_num = poly.shape[0] + left_quad = np.array([poly[0], poly[1], poly[-2], poly[-1]], dtype=np.float32) + left_ratio = -shrink_ratio_of_width * np.linalg.norm(left_quad[0] - left_quad[3]) / \ + (np.linalg.norm(left_quad[0] - left_quad[1]) + 1e-6) + left_quad_expand = self.shrink_quad_along_width(left_quad, left_ratio, 1.0) + right_quad = np.array([poly[point_num // 2 - 2], poly[point_num // 2 - 1], + poly[point_num // 2], poly[point_num // 2 + 1]], dtype=np.float32) + right_ratio = 1.0 + \ + shrink_ratio_of_width * np.linalg.norm(right_quad[0] - right_quad[3]) / \ + (np.linalg.norm(right_quad[0] - right_quad[1]) + 1e-6) + right_quad_expand = self.shrink_quad_along_width(right_quad, 0.0, right_ratio) + poly[0] = left_quad_expand[0] + poly[-1] = left_quad_expand[-1] + poly[point_num // 2 - 1] = right_quad_expand[1] + poly[point_num // 2] = right_quad_expand[2] + return poly + + def restore_quad(self, tcl_map, tcl_map_thresh, tvo_map): + """Restore quad.""" + xy_text = np.argwhere(tcl_map[:, :, 0] > tcl_map_thresh) + xy_text = xy_text[:, ::-1] # (n, 2) + + # Sort the text boxes via the y axis + xy_text = xy_text[np.argsort(xy_text[:, 1])] + + scores = tcl_map[xy_text[:, 1], xy_text[:, 0], 0] + scores = scores[:, np.newaxis] + + # Restore + point_num = int(tvo_map.shape[-1] / 2) + assert point_num == 4 + tvo_map = tvo_map[xy_text[:, 1], xy_text[:, 0], :] + xy_text_tile = np.tile(xy_text, (1, point_num)) # (n, point_num * 2) + quads = xy_text_tile - tvo_map + + return scores, quads, xy_text + + def quad_area(self, quad): + """ + compute area of a quad. + """ + edge = [ + (quad[1][0] - quad[0][0]) * (quad[1][1] + quad[0][1]), + (quad[2][0] - quad[1][0]) * (quad[2][1] + quad[1][1]), + (quad[3][0] - quad[2][0]) * (quad[3][1] + quad[2][1]), + (quad[0][0] - quad[3][0]) * (quad[0][1] + quad[3][1]) + ] + return np.sum(edge) / 2. + + def nms(self, dets): + if self.is_python35: + dets = merge_quadrangle_n9(dets, self.nms_thresh) + else: + dets = nms_locality(dets, self.nms_thresh) + return dets + + def cluster_by_quads_tco(self, tcl_map, tcl_map_thresh, quads, tco_map): + """ + Cluster pixels in tcl_map based on quads. + """ + instance_count = quads.shape[0] + 1 # contain background + instance_label_map = np.zeros(tcl_map.shape[:2], dtype=np.int32) + if instance_count == 1: + return instance_count, instance_label_map + + # predict text center + xy_text = np.argwhere(tcl_map[:, :, 0] > tcl_map_thresh) + n = xy_text.shape[0] + xy_text = xy_text[:, ::-1] # (n, 2) + tco = tco_map[xy_text[:, 1], xy_text[:, 0], :] # (n, 2) + pred_tc = xy_text - tco + + # get gt text center + m = quads.shape[0] + gt_tc = np.mean(quads, axis=1) # (m, 2) + + pred_tc_tile = np.tile(pred_tc[:, np.newaxis, :], (1, m, 1)) # (n, m, 2) + gt_tc_tile = np.tile(gt_tc[np.newaxis, :, :], (n, 1, 1)) # (n, m, 2) + dist_mat = np.linalg.norm(pred_tc_tile - gt_tc_tile, axis=2) # (n, m) + xy_text_assign = np.argmin(dist_mat, axis=1) + 1 # (n,) + + instance_label_map[xy_text[:, 1], xy_text[:, 0]] = xy_text_assign + return instance_count, instance_label_map + + def estimate_sample_pts_num(self, quad, xy_text): + """ + Estimate sample points number. + """ + eh = (np.linalg.norm(quad[0] - quad[3]) + np.linalg.norm(quad[1] - quad[2])) / 2.0 + ew = (np.linalg.norm(quad[0] - quad[1]) + np.linalg.norm(quad[2] - quad[3])) / 2.0 + + dense_sample_pts_num = max(2, int(ew)) + dense_xy_center_line = xy_text[np.linspace(0, xy_text.shape[0] - 1, dense_sample_pts_num, + endpoint=True, dtype=np.float32).astype(np.int32)] + + dense_xy_center_line_diff = dense_xy_center_line[1:] - dense_xy_center_line[:-1] + estimate_arc_len = np.sum(np.linalg.norm(dense_xy_center_line_diff, axis=1)) + + sample_pts_num = max(2, int(estimate_arc_len / eh)) + return sample_pts_num + + def sort_coord(self,coord): + points = sorted(list(coord), key=lambda x: x[0]) + + index_1, index_2, index_3, index_4 = 0, 1, 2, 3 + if points[1][1] > points[0][1]: + index_1 = 0 + index_4 = 1 + else: + index_1 = 1 + index_4 = 0 + if points[3][1] > points[2][1]: + index_2 = 2 + index_3 = 3 + else: + index_2 = 3 + index_3 = 2 + + box = [ + points[index_1], points[index_2], points[index_3], points[index_4] + ] + return np.array(box) + + def detect_sast(self, tcl_map, tvo_map, tbo_map, tco_map, ratio_w, ratio_h, src_w, src_h, + shrink_ratio_of_width=0.3, tcl_map_thresh=0.5, offset_expand=1.0, out_strid=4.0): + """ + first resize the tcl_map, tvo_map and tbo_map to the input_size, then restore the polys + """ + # restore quad + scores, quads, xy_text = self.restore_quad(tcl_map, tcl_map_thresh, tvo_map) + dets = np.hstack((quads, scores)).astype(np.float32, copy=False) + dets = self.nms(dets) + if dets.shape[0] == 0: + return [] + quads = dets[:, :-1].reshape(-1, 4, 2) + + # Compute quad area + quad_areas = [] + for quad in quads: + quad_areas.append(-self.quad_area(quad)) + + # instance segmentation +# instance_count, instance_label_map = cv2.connectedComponents(tcl_map.astype(np.uint8), connectivity=8) + instance_count, instance_label_map = self.cluster_by_quads_tco(tcl_map, tcl_map_thresh, quads, tco_map) + + # restore single poly with tcl instance. + poly_list = [] + for instance_idx in range(1, instance_count): + xy_text = np.argwhere(instance_label_map == instance_idx)[:, ::-1] + quad = quads[instance_idx - 1] + q_area = quad_areas[instance_idx - 1] + if q_area < 5: + continue + + # + len1 = float(np.linalg.norm(quad[0] - quad[1])) + len2 = float(np.linalg.norm(quad[1] - quad[2])) + min_len = min(len1, len2) + if min_len < 3: + continue + + # filter small CC + if xy_text.shape[0] <= 0: + continue + + # filter low confidence instance + xy_text_scores = tcl_map[xy_text[:, 1], xy_text[:, 0], 0] + if np.sum(xy_text_scores) / quad_areas[instance_idx - 1] < 0.1: +# if np.sum(xy_text_scores) / quad_areas[instance_idx - 1] < 0.15: + continue + + # sort xy_text + left_center_pt = np.array([[(quad[0, 0] + quad[-1, 0]) / 2.0, + (quad[0, 1] + quad[-1, 1]) / 2.0]]) # (1, 2) + right_center_pt = np.array([[(quad[1, 0] + quad[2, 0]) / 2.0, + (quad[1, 1] + quad[2, 1]) / 2.0]]) # (1, 2) + proj_unit_vec = (right_center_pt - left_center_pt) / \ + (np.linalg.norm(right_center_pt - left_center_pt) + 1e-6) + proj_value = np.sum(xy_text * proj_unit_vec, axis=1) + xy_text = xy_text[np.argsort(proj_value)] + + # Sample pts in tcl map + if self.sample_pts_num == 0: + sample_pts_num = self.estimate_sample_pts_num(quad, xy_text) + else: + sample_pts_num = self.sample_pts_num + xy_center_line = xy_text[np.linspace(0, xy_text.shape[0] - 1, sample_pts_num, + endpoint=True, dtype=np.float32).astype(np.int32)] + + point_pair_list = [] + for x, y in xy_center_line: + # get corresponding offset + offset = tbo_map[y, x, :].reshape(2, 2) + if offset_expand != 1.0: + offset_length = np.linalg.norm(offset, axis=1, keepdims=True) + expand_length = np.clip(offset_length * (offset_expand - 1), a_min=0.5, a_max=3.0) + offset_detal = offset / offset_length * expand_length + offset = offset + offset_detal + # original point + ori_yx = np.array([y, x], dtype=np.float32) + point_pair = (ori_yx + offset)[:, ::-1] * out_strid / np.array([ratio_w, ratio_h]).reshape(-1, 2) + point_pair_list.append(point_pair) + + # ndarry: (x, 2), expand poly along width + detected_poly = self.point_pair2poly(point_pair_list) + detected_poly = self.expand_poly_along_width(detected_poly, shrink_ratio_of_width) + detected_poly[:, 0] = np.clip(detected_poly[:, 0], a_min=0, a_max=src_w) + detected_poly[:, 1] = np.clip(detected_poly[:, 1], a_min=0, a_max=src_h) +# detected_poly = self.sort_coord(cv2.boxPoints(cv2.minAreaRect(detected_poly.astype(np.int)))) + poly_list.append(detected_poly) + + return poly_list + + def __call__(self, outs_dict, ratio_list): + score_list = outs_dict['f_score'] + border_list = outs_dict['f_border'] + tvo_list = outs_dict['f_tvo'] + tco_list = outs_dict['f_tco'] + img_num = len(ratio_list) + poly_lists = [] + + for ino in range(img_num): + p_score = score_list[ino].transpose((1, 2, 0)) + p_border = border_list[ino].transpose((1, 2, 0)) + p_tvo = tvo_list[ino].transpose((1, 2, 0)) + p_tco = tco_list[ino].transpose((1, 2, 0)) + + ratio_h, ratio_w, src_h, src_w = ratio_list[ino] + + poly_list = self.detect_sast(p_score, p_tvo, p_border, p_tco, ratio_w, ratio_h, src_w, src_h,shrink_ratio_of_width=self.shrink_ratio_of_width,tcl_map_thresh=self.tcl_map_thresh, offset_expand=self.expand_scale) + + poly_lists.append(poly_list) + + return poly_lists,None + + diff --git a/ptocr/postprocess/__init__.py b/ptocr/postprocess/__init__.py new file mode 100644 index 0000000..8529416 --- /dev/null +++ b/ptocr/postprocess/__init__.py @@ -0,0 +1,6 @@ +#-*- coding:utf-8 _*- +""" +@author:fxw +@file: __init__.py.py +@time: 2020/08/11 +""" diff --git a/ptocr/postprocess/__pycache__/DBpostprocess.cpython-36.pyc b/ptocr/postprocess/__pycache__/DBpostprocess.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2b23210239e68bcd305668adb6f59cf6064f0fd3 GIT binary patch literal 5942 zcmbVQ&2JmW72oe&E-8wV=x;Y+61O!ARhI2E30&8eZMpe?LkYHnhH~y)>WIT%(bR2bE9b>*SluVYFcW} zV>fMPFth9Q3eAG5ysa>c*`F!Q4&0I6EHdq(Qg;sDbl3d-bSH{$w1c&61c~zq5&zZ{{mMTytyqJ=zIsw4d5|L$!7k*Ve;! zJBWjR8v0w^pmrzh`*C>ar(x8uU0%OaUy(yw8dgX@$|>DNN6nM|Q8X^7&9(-&5{69>tTaB-&IxLoe}L$BrcS;+h} zNQ50G-T>DiO8fm*Hyn5|ZA3V|u>~5_H?bYDN8a zxc#LP&!hRDmsYL}(x??*IkBKCv%(=9&kOru>Um2TlhAyMT0Z_Co}Rjsl?ijpBu4yb zl!YXZ_79LEu4N`yGnJbiwW@UV)aaNa6)hH$J<`GHX2qxK zu*+Shai!xt!R4$gPt{L8%1TTJSAmz9G196^Hiw$aOMDJ>JuCBar^wuqn$7e14m1Q8 z>+CA=ul5?4r^HpX79n}oNE>Nc1#NS@vTMY@L;r-|LY=tFd>(Wjd<$uXFN{=XqDSmC z`WmPUO4CYQP1IW4jgQVuiL2WD7$$-`Tr1taG<=WNkc)-!ukeJ5y zzrOs(pWolw{PClhFy)9eG$tH6BG^3o8o#<0pQ2Whvb-d#MCy9pKtpAJ{k$;c5{2IS z^qMHO!#=wk_0woSPK4zr>A@fnT7MwS{ji_DB?=GjJ$m5XfA`@dv2^#H+wV5K+nbvY zHa_wmzWe^>{d-c=M8S_+_k;d+x+6;d01z}Jz+B%HHmNnvLzGFglU5W5UK<~gS}Lqu zlckOdy&LpJc{@lwXmpsPt5jU!hB)IS_4}z6j6obQVUI6MxGYFg?{Ubm(4s8w1mX5hDy$sAg*C35IgcqZURZMBiEw}% z`9Z0ku*Vf)2ZJQ+Mt!*kY+YD`2&+tR&a_{lV-l2;kOdNPa#Eq?RSD2wSEkI!1@t8E zfGC!s8mfzAXa=yRqMcPM$Xz7Os3O-i!k`lRHP9-0HLavNsssL2j9W!-Sv`Dp8l7JB z(PN|{p&=ZVNM%6SBvKut6for*(J6CMhdaf7b`6PC zU!iu26W{5P1}>LuIIy*fmU&*4vsWjxGjoh^>*H^rKdYu^fOTgj)^&h&z#EBmloQsS zN4mhP%wa%wrq5uVF0t-Juf)3PF~$tm0ZD;%lQHN8)>Wlt!71@MK0p4J{9;>2?-B>T z-&FR#0`krAvP8b(;oumS{y*gVpV2Q~gcKf0+#GO485xHkH0ZO!!LhnNYKk;0>ut>cHPI;pG_Q zUt|`yV5QYYy%>{~6b3A;FDw`qvP^}+0a=Ve5R3WENpYjyPJ&d}w;9{?!Q{O6$a}Ey z)5f}}+GB+qeY5MKg#LG9wH^A zQyk&lIf@jthV*shsX^z};mr97=VsUMZ887WFKH~2a=0>maMLcu%FS-n^1I0`6!L+X zR^JT{^6!vv<|y-Hl~RbWDM{(#Z56 zV8I1LMK!1>M2{OxKdZn0%JMZdv2;R*U1)`=Musw%+e_{s2fv91YR0K zCp=_G04sGWa5)fQ&>Eg-+~Rg#l4cyv4@KY51FMlsC25K@g>^Mvqq21J7o_2XM;r|f za+o61BTN{wD3%hKn?f>8;0nSnjGsS@@6Zg=G?L8}4qisO`)ULoSMwT-MsaBt+WeO#AxE#TE+#v)Q_cq>(bbOplRb>)+l zj09i~yh?}S=g)buV?*rFA|uTbYO-HOo1cHEU~G|4Tl$du;@oK&!Sh3>Co%7;&N8{UVQ)D6cHQP=* zn`+Y~uiV>gJ9uBXGuko7s{qu_9GjmdNLn6#|C{3NQvTF*k6NY6DW95dQS~(<(k=WR zm8A1YK0{2dj`R}i3(uUvrYm2uJZUsaNwD3+Yn^7^iUh# zqsaF=_8gmqv^Rc~Y`Rp;gVx+zB$rUAyNJhWa_{k^02K~r^pl>tU?97m_gf6g15x!N zoWtCrkh;RM$l9U|FNwls#Q(Coths8*J`WB9?qe0jObab9;;T*o=%lw~h2c!)k>(37w*;eAhVRpMktzG-w1R&0wmR?+<>8<<>xe1p>s-A%KxffMAj2d#{>J z4kaJb16EhPe)ZG!UVZPU=gz`{_pg8ckHKHv(zJimW-bf$hxkRGpb(m`30)Xn-PieU z_@*$0)wO!IZ|fTBcGu|@`~vF4=lU++i+=HAO*o?PnI;OMcVhY_VLa06?&zlXuyGjg z4dnZ+qi5d3R;L@jUwdc!&i2jWvG*|U^w8YCy?y)E?RRgz^Pcw-tI&2{>L}}aTJHCv zpgU+bx}EqK?K8blw!`4bVMEA9zugUk{(DX8F}d=$c?wFX;Vm@Z5IWw%5awr^ZwX7- zxZ9#8Tv7Z?_Z@LjltdY|f>;n0JafgOsN!A}OJW&!Pn;1exR=CPv5I?Htci8p7sMs8 zDYnqFBHj?^L0J?R@XnXh#YZ1Jdi=>Cia(KqW*9}ID_#xPqWo9jhTUOR8aLX1h#&U#) z1s8YH60Iv+R$7jl0~rSKo(!YC)FT;W^-9#}4YBLPI12iQz4UCfCp-OvKsMseAQ-fQ zXB`pmrRDHw*yxKOg5bim5;wa+uQAL9d0a2Z0$xhGR1~T3s3=iUMv;~}Q7}A)&h+p6 zM%_rQDD1XUr#a}iI_6Pi9Ia;5S)y+wVTpSMi*!bn*h#`U#h-5_@8^UPoiGiAVbT@`DPV|qopIrac7+YgI zu~}qhV%^hFCxMv>^l)8Etaa_4_N4YBEjISe#5~dWNi+2mIy3g@_gEiTWuWF!E0ndT zp^Q7&PAE{P9|yxmM@BW&1|W2g6&3AT6Q||iStkmWPSF@INtYy@C`q-K|BI_`r)D?o zr^OIr3?aTSHTuKUZb;cUP75UX#xSfql6EXzoK6~aJ5ih#6@gU1Q&lFL9dR@v&zJAc8V^0_96^V;QB~gjB zeTWAlGTVCFKnq~-9Kyj_pwpzYP`FM)S<%)sh)oxCnsh;@i0kwTijG#=sfINeuZpt;PucJr{ zt?r-^-`U17V&PacXu7l;wmW?_sydG8Vu&Ni2Qnjg>X?#bQfqiScn9U3V0&IlYu~h3 z%pTF9)vR@W3xA_G=Oy(GJ-@oc*S|by(kszLC!U1Gg8DvmGtu{9Q8sXnVtqe@F>JmP zQjrDW(H7$=>=BF5C~0U}`*wnp0}dejhPJMq;9SnpF$&Rzv!-p5fVF)mv2Y>_C+65u z^Chrz=V0H~HJU4H8MCcf`Wm=q)>06K#K`6>@LCG5tYtz!wHBxwU>K_@0FGU(rkFU~ zALs(ia72mUyo`EL?ZtFuOe0LmR*N>N-@@pvT>TUbD}-RJtFbyvwQj0xO3!pjM^au! z(SH6PfA__I{qk?`x6#1$^@FSe1ogi{c27Q)cd5K?6mhX&XTGt)}`r#jQ17Y&MXs6 zJ$n#;6AOrHC^XB{uS2)b!$#EfE4rbd!@UAzD(fqH^))*G4I}25L>4YLAODF4V+grd z`sY9+E{zrm(l`Ku0GpuBlV`&D+-yN*h)U>{KV#|%9T;HQX=>gwsHfnu*%G1*vHT!5{;Nxr2ss^@Uj9Q1~Vaae1}uu&Vd zY7M0f`EzzplxC3CQ!s=IT@UMq(g`y62Gcq*f@};k=ZJ%F<$@pMobMbEDZe zb^6;jMjP{D$+>fxkf%g)`4GQ|fDAAV4J8*3HPUmvUyk8nPg)GV`cB=F3*eTl(Cp#T zC|JC*)gp{v(I^uRc9*7M+p0Ty#VG5eGxKW%SCeprF7l>wKj)`pK22plg+U&SGHIZ| zRDKRgp^d-|M$;je^uUns;%wryK4L>j`XfwWkt+2WEW%mN%(4p;m2-(ghDtd1Ih}DK zZ|RIZ+=Ee~2vjHn6}-tx$0e-FhB-5YgO!!hauQF?fqFI@^%X{fV6tUm&li}BcxP7g zxk67DBPp&5p1b?SWFe`n1KW7~MbNxtQO(A?R86W08D8~PuUUP|fJx2v2?+M>Tu(pj z9!4>I@?qyF?2;E=%Rr{K+lWF@8}wPRqu1HlomoQ*r_UlVW8|xs7|xWI+oMnpqSOY+ z-PxA!Lfom<3mbj;Td2zSsX!pF$=meA0XX(L{rZwR75C`@dDZee^b`oFFt5_-M{%Ry z41?z2upg((dAr-#4LL-U52=UV_f2&23qu)-Aa165&$rt#YJNE*F+%zt6II<62n>L& zv0s^>Xd{X`?f(4HTxqK93wDngLlImy2NC9t$oJ5!3M{UxyJig$!76~%H9ee&Yx*_V z-(};9KDsi0GF~^}DNmCQ79Zq)M}+|Dw1-K)VNQj@;XnY?h`Oc`jIoKE?E{`ueZZ|C zd8UsQ8j4hyXpFKQ|0&PJ71-!@S_P&c+kqtVTVNQOs z0ysSUXeFe!#pi(8v761~CMFqi%vDT^0N&E*cCN7BjgV(Sj8{|PyNVX08p5~TP|j=V zW$ckcVglib)A#T(B5@ZH1qWm7ChpXA`P;-yo*=ZBLzJmjn!U!6{5Bq?E?mH9*l1!d zVpncqBjRZ1cZl*44WPV2#&lLg-w5|oBYftsh_D}p!Rge)gbH12hJBny1g8Da-%z}# zse$OUT7DIFn|C2-$Yy}xSLGfko1j#bL)5jL>-2m-16#C=LbLAbcZ~{EbJbYKZ~1jD z;bf-MW8}t#^f7k{e~v~BAXYgAVIYHM0uT-1erki?AT>xgsdiw=?+9=R zJny?Gp>NDd4jqNA0M?wdE1a<<3S%3b1=JkU$b@tqE+v=TBS8-s@d9Th3LMTBcbb$s zpo{Kg7a!rr#wuYS0Lo@$4jpYKKutfIKlJXsZlkv=8u$N_`r@aJ-kv|kTG&)&Gb&s=3>b>kqTGbiP70vwuCm28Pn9~X(2-y0 z^hJ0Syfdk6PbzmNmAk3AJ9y>Iyy>#P!06Ey3Qb>u318PO-7zebdClB1M(4iyy}zQq zn)a&)S*7~0NzMdaZ{tUi1jE=r!>1sB1m1@SU(5W|950M3W3q8ZQb`uzhr{BdMUjL7 zWvYVPPO5+lSUms`SydB$zz^CGW>STXgr9(N6}A*$14vqJeOV@T zC+NldtglvQt?1R>psU zpVlT%!GDn{mnM{q|Bd90WNV%LH`)iTV?WEB;5g4KTTITc00_ha`q ztb3V~?x4S!TurWF-`+%u^*ZX;;on{b?=sDSj2^A%EZ(r5)RU{&}Fv)g2|{h0JbTL!j6hg|!%;3^M2P z?OGPR){t7S#k=g()sO%to=zW;RX|<9wiouPKZo1&kdhryKK}Q}>ue5&3QTttZc_e( zJ=2$DmsI5S^S_|$AM(7H)dal6S?v|>P46?e@w*>sUUX<(&=`F@&dKLg`)oUj9>qN7z&y6HE#ou<3i;eA?(9hP-b);EeYt&(Q*XTA+TMI_hQp$xCAe?Rd{$ z$ntervz%=S0`o@KcLuT(93RQ^7z5w;!Z>Wgg9tcLIp-*r)Kj>nQT+E{kLYuQ=Byxk zLA(N^f8Mkxg0WU`UqKF$GKijTn3Os9xK>5$n!aTi<|=y8&q2AYSIjaZ9;&axeW(~T z;xe3wGEzz_`dz(>h^UM#<#nTCj5g=piJ5ifK@e#=B_P;)_ysBmSCG9K(-%V6H_9QB zC52PSp%0Sca14Qi1)#fo^rvu6;S2)d69gA#f^G4v>4O3I(&W%eg49iVWHX1Is~XiwfO{C2v@ z=7cTM(Dd~goZazNRFZuX=Q1Y*z(0!bm6g323IBfL}y&X~-2 z{fKA`vAa}b;rubxrmxF2ivJ{;KWasUC9Y{1?t)wPir!gw#Vxx9cgtOJ*SsGyU%f0p z2UluF$4J#c7O<%l1pCXAe_03`&j5!3Qg;$AJO1sU+n~C|y_!$ADOcSK0ad&EKpGzO ab*oCk6Nb$@&i0|UcjAcg}*Aj<)Wi#dQq3PTh_3S%&XCR3FumqTJ{Nk)E=Ra!+k zmqS`+PO6nce0*kJW=VX!UO^=gaXFM^<^n~H42%r)4J`Bx4Y~X@8E>)2$EV~c$H%W^ zC}IMd2_}BY>**JiRG8Q@yjj zJJUT|-D~fzx=(9k!vPivkU%*jA@K)r3x|j!7dW+qgv7zx8|MfKey@7Iyh+3b@zJAs zRrTss)vNba@4YoW?fv;@AGh9r!7%=6Onff-xA0GX3LuT9A(`Y|*5qi-uGzDi7Bi$J z?MFt_mX55Tb)+k+Xe-i_Q)pdTlhbIcaz@Ug_2esZUY>o#np5(kT#$?C)q-icw8PbL zFcVk--=3Ar@?3|@^YQ}Vi}EGFm!;_Nj&;P2*mnJh;at99G%g*U_1=%$em6`Hg#XZ2 zfrxs^s&@j0jr9gADtF@UK_`ydoG|#T!T(*f$uU4`Wcpr@`LeOiq(dj~0P_x+U)5hwRej2yc zm42M0eHFKZB)QTZ$iJoK-y-?<4^$QO^iLK53~PZcFb}^vv)DYK%Pg*GK3_41SaV7- zr3y|f#vLmLN?0ScKnb@dlwgjOU_l8`%1HE43ZvDgbXo<~!}Fg}K`V?q12rfU?2VTxad;lsWFDXtxfVBfAZ{)2Ip9YEC+i@i(Vp-fbjS~u3Bte0?&q}+00Tc5Vqm=` z6})5FSTxR$=+{oo5FO!Q(dOZ@7U$g{>};ih6um%of>M}Tmh1XT5Z3t0r!G44aIm(e z*M1r7CL}z=x&?buh+N}~5uPqaT1tKdkixN_f*rbqpQPqSd(# zr?#cJXwV6}uiZ7C8W~QTaPW=PkEE|;8M4Ub*R?cG$gQ58sJ_e@>_=s>df z7IKzi+o#Y3HU~W7KjFL9Mrk1prj4q>3%+`_U@1CmqhR6Kd_MKox?z&G<6i&9P1?CU z=1?R9&a^YHX$Mbktd5CK{(-OI6g=t|6<>9dSfz!PgolVSe!m|?vao%n{DZ=NxD}|N z;iyIIU)bpuqETVTN+J+hJ3-Jd%=iJiVI-5G5fG_bBA{)&Hp$J~#3q@sVF`qi8tmF* z7x^5kbM+dqBkO+72!4o}{|zI^Ol<&{v_DK5z=}{ox)%u&#B*VWocZ5q4C*ZHM?o3x zHkPz@Hxjp62DpR&&=_8w6n{&z_c<1!&e4vJqWGgO5O|RQnW}o3fB+bnfqI1y7YSS< z@FfDT5+E*~V@n&rna^@nma+G%|AxYS66nDD7m1<&u!>Eb zNl(xhDfKeup1q~NKi+)y%U{rUa`PFm`2KQJCqHe9`ShK^pWI(RY+uE#ka`(M6p?Kt zcnFiZlR1*_xXDGd=8mCOQoe0=5Xq5(xgYYplDpE%tH4?rGKAbiB*!}1W!&RNK9$#w z+5YcSo2YIZ$L!v3^Xbgarln1PsStc0)8;3SL}Xt7d&Jxo@6AabPDZ19O1$BG8Z_UgZ|VcCC0Nfi*}NuAt!$? zIXUJ^V7@ZoxRyHDrFO*gdRCWg$HM%4W&=k=9lt%mKa)8b{Oo z98#>vDpor$P)dPB7ZR;T^P{!Dk~G%qvMQZjN&&QkzUFv<&G?R?d4SBNxkSmzSPqrp z2rImDUdy6_v8IORAxH388OrmR(c8ScMN*YKb13mai1rTuq@6Q~H&c2A_27EnSH8F( z?*|D2;fh!n`{EsOxgFy@OT?QZX~%f$CP?*jw-xVo<%$TpVK0pQG!Sh+2@-)px#05E z)vH&(GP!I$ZRwROB8>OS?p8RlZ4!2R{)*`Mdr1=dk$4y&tt#hxLAn*=v~Cy$z7nw- zQR#X8kgV$v(;-)(ugqr2c?PJz-9!u?Y8%PZmPMPS8Ubey=Dl-Jr$%g<&mC}bg{z#7j&#^hA6lGfB za+=X2eu33E)htLicny#$8T~_Q?Xe|LI^1GQ<{T5cwsF}USJ$!s=piEUQkkY>()ZCp z&WWTNdFSoSxNY29%PkayY^SQOpGoW~nGF4C@FU?TR?9RY; z%e6X(EoV|QXx3xu>zFOvR;%ZSQL7c+g&&09CGb0dlP8!B3+nxtdb>2y z0YJ6Z07AV%;7tOgaAe?wZJa48t(J`2tyW=csp%UPb?rypgVjzD!vWNRF4#*6P>QRo z8hvH;It}R&)K)cOGV*&ttEK3{E3+&rAScXz!uHpCvE1thH%w4y8CbY^&2;DRtGZ|C M=e_GKx}39r0+*Z1(f|Me literal 0 HcmV?d00001 diff --git a/ptocr/postprocess/dbprocess/Makefile b/ptocr/postprocess/dbprocess/Makefile new file mode 100644 index 0000000..19c5e82 --- /dev/null +++ b/ptocr/postprocess/dbprocess/Makefile @@ -0,0 +1,15 @@ +CXXFLAGS = -I include -std=c++11 -O3 $(shell python3-config --cflags) + +DEPS = $(shell find include -xtype f) +CXX_SOURCES = cppdbprocess.cpp include/clipper/clipper.cpp +OPENCV = `pkg-config --cflags --libs opencv` + +LIB_SO = cppdbprocess.so + +$(LIB_SO): $(CXX_SOURCES) $(DEPS) + $(CXX) -o $@ $(CXXFLAGS) $(LDFLAGS) $(CXX_SOURCES) --shared -fPIC $(OPENCV) + +clean: + rm -rf $(LIB_SO) + + diff --git a/ptocr/postprocess/dbprocess/__init__.py b/ptocr/postprocess/dbprocess/__init__.py new file mode 100644 index 0000000..1125432 --- /dev/null +++ b/ptocr/postprocess/dbprocess/__init__.py @@ -0,0 +1,32 @@ + +import os +import cv2 +import torch +import time +import subprocess +import numpy as np + + + +BASE_DIR = os.path.dirname(os.path.realpath(__file__)) + +if subprocess.call(['make', '-C', BASE_DIR]) != 0: # return value + raise RuntimeError('Cannot compile pse: {}'.format(BASE_DIR)) + + +def cpp_boxes_from_bitmap(pred,bitmap,box_thresh=0.6,det_db_unclip_ratio=2.0): + + from .cppdbprocess import db_cpp + bitmap = bitmap.astype(np.uint8) + bboxes = db_cpp(pred,bitmap,box_thresh,det_db_unclip_ratio) + + return bboxes + + + + + + + + + diff --git a/ptocr/postprocess/dbprocess/__pycache__/__init__.cpython-36.pyc b/ptocr/postprocess/dbprocess/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..53b601c544faf6ff285e274d296181a88d60be36 GIT binary patch literal 692 zcmYjP&2H2%5VjpBn`VDnpm5%65OgJasStv~(gT+s+FLGGHukbfli0Ey#Fp%ZufT~(TB=So-)BcN-#3nPI37oTe*R`J2trrrK~Pv9LmCUf5yvG;ZW$-I z-;;#&>?K~$G@(8FNgz-$D8e3hM6|_QoD2mmMx1a@3_p8YP`L2)Qj9q*CSurCe8+r; z4|w!$A_fb=2fkmfl6$DEX?!W)WFiwk1Cf^P8!x3qMlr)T`S>8t(@QFwLOXhN0k=b1&iEPsdCXmer0y0g;^y}xUdZL znmQ|U#dK<#lcuJ5X!)6sBT*Gh1p| zv0K|mpkhJ*L`HaoCq(bTIwsl!(%lz|_Z*SNdD)j&&bLx$D@Sc!2^X2>{~?`UHb+LasdT-Pa-g<7O#|0-l`2{*!65`K7pe!@O}I&(Bj%hHX`n!3|G(OPO3ETyhe j+fULT^8xI34&j?h@}?AT;eSNtF+hm#AG%NW@x=QFF^RcH literal 0 HcmV?d00001 diff --git a/ptocr/postprocess/dbprocess/cppdbprocess.cpp b/ptocr/postprocess/dbprocess/cppdbprocess.cpp new file mode 100644 index 0000000..56258e5 --- /dev/null +++ b/ptocr/postprocess/dbprocess/cppdbprocess.cpp @@ -0,0 +1,369 @@ +// Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "include/postprocess_op.h" +#include "include/pybind11/pybind11.h" +#include "include/pybind11/numpy.h" +#include "include/pybind11/stl.h" +#include "include/pybind11/stl_bind.h" + +#include +#include +#include +#include + +using namespace std; +using namespace cv; + +namespace py = pybind11; + + +namespace cppdbprocess { + +void PostProcessor::GetContourArea(const std::vector> &box, + float unclip_ratio, float &distance) { + int pts_num = 4; + float area = 0.0f; + float dist = 0.0f; + for (int i = 0; i < pts_num; i++) { + area += box[i][0] * box[(i + 1) % pts_num][1] - + box[i][1] * box[(i + 1) % pts_num][0]; + dist += sqrtf((box[i][0] - box[(i + 1) % pts_num][0]) * + (box[i][0] - box[(i + 1) % pts_num][0]) + + (box[i][1] - box[(i + 1) % pts_num][1]) * + (box[i][1] - box[(i + 1) % pts_num][1])); + } + area = fabs(float(area / 2.0)); + + distance = area * unclip_ratio / dist; +} + +cv::RotatedRect PostProcessor::UnClip(std::vector> box, + const float &unclip_ratio) { + float distance = 1.0; + + GetContourArea(box, unclip_ratio, distance); + + ClipperLib::ClipperOffset offset; + ClipperLib::Path p; + p << ClipperLib::IntPoint(int(box[0][0]), int(box[0][1])) + << ClipperLib::IntPoint(int(box[1][0]), int(box[1][1])) + << ClipperLib::IntPoint(int(box[2][0]), int(box[2][1])) + << ClipperLib::IntPoint(int(box[3][0]), int(box[3][1])); + offset.AddPath(p, ClipperLib::jtRound, ClipperLib::etClosedPolygon); + + ClipperLib::Paths soln; + offset.Execute(soln, distance); + std::vector points; + + for (int j = 0; j < soln.size(); j++) { + for (int i = 0; i < soln[soln.size() - 1].size(); i++) { + points.emplace_back(soln[j][i].X, soln[j][i].Y); + } + } + cv::RotatedRect res; + if (points.size() <= 0) { + res = cv::RotatedRect(cv::Point2f(0, 0), cv::Size2f(1, 1), 0); + } else { + res = cv::minAreaRect(points); + } + return res; +} + +float **PostProcessor::Mat2Vec(cv::Mat mat) { + auto **array = new float *[mat.rows]; + for (int i = 0; i < mat.rows; ++i) + array[i] = new float[mat.cols]; + for (int i = 0; i < mat.rows; ++i) { + for (int j = 0; j < mat.cols; ++j) { + array[i][j] = mat.at(i, j); + } + } + + return array; +} + +std::vector> +PostProcessor::OrderPointsClockwise(std::vector> pts) { + std::vector> box = pts; + std::sort(box.begin(), box.end(), XsortInt); + + std::vector> leftmost = {box[0], box[1]}; + std::vector> rightmost = {box[2], box[3]}; + + if (leftmost[0][1] > leftmost[1][1]) + std::swap(leftmost[0], leftmost[1]); + + if (rightmost[0][1] > rightmost[1][1]) + std::swap(rightmost[0], rightmost[1]); + + std::vector> rect = {leftmost[0], rightmost[0], rightmost[1], + leftmost[1]}; + return rect; +} + +std::vector> PostProcessor::Mat2Vector(cv::Mat mat) { + std::vector> img_vec; + std::vector tmp; + + for (int i = 0; i < mat.rows; ++i) { + tmp.clear(); + for (int j = 0; j < mat.cols; ++j) { + tmp.push_back(mat.at(i, j)); + } + img_vec.push_back(tmp); + } + return img_vec; +} + +bool PostProcessor::XsortFp32(std::vector a, std::vector b) { + if (a[0] != b[0]) + return a[0] < b[0]; + return false; +} + +bool PostProcessor::XsortInt(std::vector a, std::vector b) { + if (a[0] != b[0]) + return a[0] < b[0]; + return false; +} + +std::vector> PostProcessor::GetMiniBoxes(cv::RotatedRect box, + float &ssid) { + ssid = std::max(box.size.width, box.size.height); + + cv::Mat points; + cv::boxPoints(box, points); + + auto array = Mat2Vector(points); + std::sort(array.begin(), array.end(), XsortFp32); + + std::vector idx1 = array[0], idx2 = array[1], idx3 = array[2], + idx4 = array[3]; + if (array[3][1] <= array[2][1]) { + idx2 = array[3]; + idx3 = array[2]; + } else { + idx2 = array[2]; + idx3 = array[3]; + } + if (array[1][1] <= array[0][1]) { + idx1 = array[1]; + idx4 = array[0]; + } else { + idx1 = array[0]; + idx4 = array[1]; + } + + array[0] = idx1; + array[1] = idx2; + array[2] = idx3; + array[3] = idx4; + + return array; +} + +float PostProcessor::BoxScoreFast(std::vector> box_array, + cv::Mat pred) { + auto array = box_array; + int width = pred.cols; + int height = pred.rows; + + float box_x[4] = {array[0][0], array[1][0], array[2][0], array[3][0]}; + float box_y[4] = {array[0][1], array[1][1], array[2][1], array[3][1]}; + + int xmin = clamp(int(std::floor(*(std::min_element(box_x, box_x + 4)))), 0, + width - 1); + int xmax = clamp(int(std::ceil(*(std::max_element(box_x, box_x + 4)))), 0, + width - 1); + int ymin = clamp(int(std::floor(*(std::min_element(box_y, box_y + 4)))), 0, + height - 1); + int ymax = clamp(int(std::ceil(*(std::max_element(box_y, box_y + 4)))), 0, + height - 1); + + cv::Mat mask; + mask = cv::Mat::zeros(ymax - ymin + 1, xmax - xmin + 1, CV_8UC1); + + cv::Point root_point[4]; + root_point[0] = cv::Point(int(array[0][0]) - xmin, int(array[0][1]) - ymin); + root_point[1] = cv::Point(int(array[1][0]) - xmin, int(array[1][1]) - ymin); + root_point[2] = cv::Point(int(array[2][0]) - xmin, int(array[2][1]) - ymin); + root_point[3] = cv::Point(int(array[3][0]) - xmin, int(array[3][1]) - ymin); + const cv::Point *ppt[1] = {root_point}; + int npt[] = {4}; + cv::fillPoly(mask, ppt, npt, 1, cv::Scalar(1)); + + cv::Mat croppedImg; + pred(cv::Rect(xmin, ymin, xmax - xmin + 1, ymax - ymin + 1)) + .copyTo(croppedImg); + auto score = cv::mean(croppedImg, mask)[0]; + return score; +} + +std::vector>> +PostProcessor::BoxesFromBitmap(const cv::Mat pred, const cv::Mat bitmap, + const float &box_thresh, + const float &det_db_unclip_ratio) { + const int min_size = 3; + const int max_candidates = 1000; + + int width = bitmap.cols; + int height = bitmap.rows; + + + + std::vector> contours; + std::vector hierarchy; + + cv::findContours(bitmap, contours, hierarchy, cv::RETR_LIST, + cv::CHAIN_APPROX_SIMPLE); + + int num_contours = + contours.size() >= max_candidates ? max_candidates : contours.size(); + + std::vector>> boxes; + + for (int _i = 0; _i < num_contours; _i++) { + if (contours[_i].size() <= 2) { + continue; + } + float ssid; + cv::RotatedRect box = cv::minAreaRect(contours[_i]); + auto array = GetMiniBoxes(box, ssid); + + auto box_for_unclip = array; + // end get_mini_box + + if (ssid < min_size) { + continue; + } + + double score; + score = BoxScoreFast(array, pred); +// cout<> intcliparray; + + for (int num_pt = 0; num_pt < 4; num_pt++) { + std::vector a{int(clampf(roundf(cliparray[num_pt][0] / float(width) * + float(dest_width)), + 0, float(dest_width))), + int(clampf(roundf(cliparray[num_pt][1] / + float(height) * float(dest_height)), + 0, float(dest_height)))}; + + intcliparray.push_back(a); + } + boxes.push_back(intcliparray); + + + } // end for + + return boxes; +} + + +std::vector>> DBProcess(py::array_t pred, + py::array_t bitmap, + float box_thresh, + float det_db_unclip_ratio){ + + auto buf_pred = pred.request(); + auto buf_bitmap= bitmap.request(); + + auto ptr_pred = static_cast(buf_pred.ptr); + auto ptr_bitmap = static_cast(buf_bitmap.ptr); + + cv::Mat pred_mat; + cv::Mat bitmap_mat; + + + + vector data_shape=buf_pred.shape; + + std::vector>> boxes; + + + pred_mat = Mat::zeros(data_shape[0], data_shape[1], CV_32FC1); + for (int x = 0; x < pred_mat.rows; ++x) { + for (int y = 0; y < pred_mat.cols; ++y) { + pred_mat.at(x, y) = ptr_pred[x * data_shape[1] + y]; + + }} + bitmap_mat = Mat::zeros(data_shape[0], data_shape[1], CV_8UC1); + for (int x = 0; x < bitmap_mat.rows; ++x) { + for (int y = 0; y < bitmap_mat.cols; ++y) { + bitmap_mat.at(x, y) = ptr_bitmap[x * data_shape[1] + y]; + }} + PostProcessor *post_processor_ = new PostProcessor(); + boxes = post_processor_->BoxesFromBitmap(pred_mat,bitmap_mat,box_thresh,det_db_unclip_ratio); + delete post_processor_; + return boxes; + +} + +std::vector>> +PostProcessor::FilterTagDetRes(std::vector>> boxes, + float ratio_h, float ratio_w, cv::Mat srcimg) { + int oriimg_h = srcimg.rows; + int oriimg_w = srcimg.cols; + + std::vector>> root_points; + for (int n = 0; n < boxes.size(); n++) { + boxes[n] = OrderPointsClockwise(boxes[n]); + for (int m = 0; m < boxes[0].size(); m++) { + boxes[n][m][0] /= ratio_w; + boxes[n][m][1] /= ratio_h; + + boxes[n][m][0] = int(_min(_max(boxes[n][m][0], 0), oriimg_w - 1)); + boxes[n][m][1] = int(_min(_max(boxes[n][m][1], 0), oriimg_h - 1)); + } + } + + for (int n = 0; n < boxes.size(); n++) { + int rect_width, rect_height; + rect_width = int(sqrt(pow(boxes[n][0][0] - boxes[n][1][0], 2) + + pow(boxes[n][0][1] - boxes[n][1][1], 2))); + rect_height = int(sqrt(pow(boxes[n][0][0] - boxes[n][3][0], 2) + + pow(boxes[n][0][1] - boxes[n][3][1], 2))); + if (rect_width <= 10 || rect_height <= 10) + continue; + root_points.push_back(boxes[n]); + } + return root_points; +} + +} // namespace PaddleOCR + +PYBIND11_MODULE(cppdbprocess, m){ + m.def("db_cpp", &cppdbprocess::DBProcess, " re-implementation db algorithm(cpp)", py::arg("pred"), py::arg("bitmap"), py::arg("box_thresh"), py::arg("det_db_unclip_ratio")); +} + diff --git a/ptocr/postprocess/dbprocess/cppdbprocess.so b/ptocr/postprocess/dbprocess/cppdbprocess.so new file mode 100644 index 0000000000000000000000000000000000000000..5381addd0c1041f3ae647efcefa2112aa24d9cb2 GIT binary patch literal 299392 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 +#include +#include +#include +#include +#include +#include +#include +#include + +namespace ClipperLib { + +enum ClipType { ctIntersection, ctUnion, ctDifference, ctXor }; +enum PolyType { ptSubject, ptClip }; +// By far the most widely used winding rules for polygon filling are +// EvenOdd & NonZero (GDI, GDI+, XLib, OpenGL, Cairo, AGG, Quartz, SVG, Gr32) +// Others rules include Positive, Negative and ABS_GTR_EQ_TWO (only in OpenGL) +// see http://glprogramming.com/red/chapter11.html +enum PolyFillType { pftEvenOdd, pftNonZero, pftPositive, pftNegative }; + +#ifdef use_int32 +typedef int cInt; +static cInt const loRange = 0x7FFF; +static cInt const hiRange = 0x7FFF; +#else +typedef signed long long cInt; +static cInt const loRange = 0x3FFFFFFF; +static cInt const hiRange = 0x3FFFFFFFFFFFFFFFLL; +typedef signed long long long64; // used by Int128 class +typedef unsigned long long ulong64; + +#endif + +struct IntPoint { + cInt X; + cInt Y; +#ifdef use_xyz + cInt Z; + IntPoint(cInt x = 0, cInt y = 0, cInt z = 0) : X(x), Y(y), Z(z){}; +#else + IntPoint(cInt x = 0, cInt y = 0) : X(x), Y(y){}; +#endif + + friend inline bool operator==(const IntPoint &a, const IntPoint &b) { + return a.X == b.X && a.Y == b.Y; + } + friend inline bool operator!=(const IntPoint &a, const IntPoint &b) { + return a.X != b.X || a.Y != b.Y; + } +}; +//------------------------------------------------------------------------------ + +typedef std::vector Path; +typedef std::vector Paths; + +inline Path &operator<<(Path &poly, const IntPoint &p) { + poly.push_back(p); + return poly; +} +inline Paths &operator<<(Paths &polys, const Path &p) { + polys.push_back(p); + return polys; +} + +std::ostream &operator<<(std::ostream &s, const IntPoint &p); +std::ostream &operator<<(std::ostream &s, const Path &p); +std::ostream &operator<<(std::ostream &s, const Paths &p); + +struct DoublePoint { + double X; + double Y; + DoublePoint(double x = 0, double y = 0) : X(x), Y(y) {} + DoublePoint(IntPoint ip) : X((double)ip.X), Y((double)ip.Y) {} +}; +//------------------------------------------------------------------------------ + +#ifdef use_xyz +typedef void (*ZFillCallback)(IntPoint &e1bot, IntPoint &e1top, IntPoint &e2bot, + IntPoint &e2top, IntPoint &pt); +#endif + +enum InitOptions { + ioReverseSolution = 1, + ioStrictlySimple = 2, + ioPreserveCollinear = 4 +}; +enum JoinType { jtSquare, jtRound, jtMiter }; +enum EndType { + etClosedPolygon, + etClosedLine, + etOpenButt, + etOpenSquare, + etOpenRound +}; + +class PolyNode; +typedef std::vector PolyNodes; + +class PolyNode { +public: + PolyNode(); + virtual ~PolyNode(){}; + Path Contour; + PolyNodes Childs; + PolyNode *Parent; + PolyNode *GetNext() const; + bool IsHole() const; + bool IsOpen() const; + int ChildCount() const; + +private: + // PolyNode& operator =(PolyNode& other); + unsigned Index; // node index in Parent.Childs + bool m_IsOpen; + JoinType m_jointype; + EndType m_endtype; + PolyNode *GetNextSiblingUp() const; + void AddChild(PolyNode &child); + friend class Clipper; // to access Index + friend class ClipperOffset; +}; + +class PolyTree : public PolyNode { +public: + ~PolyTree() { Clear(); }; + PolyNode *GetFirst() const; + void Clear(); + int Total() const; + +private: + // PolyTree& operator =(PolyTree& other); + PolyNodes AllNodes; + friend class Clipper; // to access AllNodes +}; + +bool Orientation(const Path &poly); +double Area(const Path &poly); +int PointInPolygon(const IntPoint &pt, const Path &path); + +void SimplifyPolygon(const Path &in_poly, Paths &out_polys, + PolyFillType fillType = pftEvenOdd); +void SimplifyPolygons(const Paths &in_polys, Paths &out_polys, + PolyFillType fillType = pftEvenOdd); +void SimplifyPolygons(Paths &polys, PolyFillType fillType = pftEvenOdd); + +void CleanPolygon(const Path &in_poly, Path &out_poly, double distance = 1.415); +void CleanPolygon(Path &poly, double distance = 1.415); +void CleanPolygons(const Paths &in_polys, Paths &out_polys, + double distance = 1.415); +void CleanPolygons(Paths &polys, double distance = 1.415); + +void MinkowskiSum(const Path &pattern, const Path &path, Paths &solution, + bool pathIsClosed); +void MinkowskiSum(const Path &pattern, const Paths &paths, Paths &solution, + bool pathIsClosed); +void MinkowskiDiff(const Path &poly1, const Path &poly2, Paths &solution); + +void PolyTreeToPaths(const PolyTree &polytree, Paths &paths); +void ClosedPathsFromPolyTree(const PolyTree &polytree, Paths &paths); +void OpenPathsFromPolyTree(PolyTree &polytree, Paths &paths); + +void ReversePath(Path &p); +void ReversePaths(Paths &p); + +struct IntRect { + cInt left; + cInt top; + cInt right; + cInt bottom; +}; + +// enums that are used internally ... +enum EdgeSide { esLeft = 1, esRight = 2 }; + +// forward declarations (for stuff used internally) ... +struct TEdge; +struct IntersectNode; +struct LocalMinimum; +struct OutPt; +struct OutRec; +struct Join; + +typedef std::vector PolyOutList; +typedef std::vector EdgeList; +typedef std::vector JoinList; +typedef std::vector IntersectList; + +//------------------------------------------------------------------------------ + +// ClipperBase is the ancestor to the Clipper class. It should not be +// instantiated directly. This class simply abstracts the conversion of sets of +// polygon coordinates into edge objects that are stored in a LocalMinima list. +class ClipperBase { +public: + ClipperBase(); + virtual ~ClipperBase(); + virtual bool AddPath(const Path &pg, PolyType PolyTyp, bool Closed); + bool AddPaths(const Paths &ppg, PolyType PolyTyp, bool Closed); + virtual void Clear(); + IntRect GetBounds(); + bool PreserveCollinear() { return m_PreserveCollinear; }; + void PreserveCollinear(bool value) { m_PreserveCollinear = value; }; + +protected: + void DisposeLocalMinimaList(); + TEdge *AddBoundsToLML(TEdge *e, bool IsClosed); + virtual void Reset(); + TEdge *ProcessBound(TEdge *E, bool IsClockwise); + void InsertScanbeam(const cInt Y); + bool PopScanbeam(cInt &Y); + bool LocalMinimaPending(); + bool PopLocalMinima(cInt Y, const LocalMinimum *&locMin); + OutRec *CreateOutRec(); + void DisposeAllOutRecs(); + void DisposeOutRec(PolyOutList::size_type index); + void SwapPositionsInAEL(TEdge *edge1, TEdge *edge2); + void DeleteFromAEL(TEdge *e); + void UpdateEdgeIntoAEL(TEdge *&e); + + typedef std::vector MinimaList; + MinimaList::iterator m_CurrentLM; + MinimaList m_MinimaList; + + bool m_UseFullRange; + EdgeList m_edges; + bool m_PreserveCollinear; + bool m_HasOpenPaths; + PolyOutList m_PolyOuts; + TEdge *m_ActiveEdges; + + typedef std::priority_queue ScanbeamList; + ScanbeamList m_Scanbeam; +}; +//------------------------------------------------------------------------------ + +class Clipper : public virtual ClipperBase { +public: + Clipper(int initOptions = 0); + bool Execute(ClipType clipType, Paths &solution, + PolyFillType fillType = pftEvenOdd); + bool Execute(ClipType clipType, Paths &solution, PolyFillType subjFillType, + PolyFillType clipFillType); + bool Execute(ClipType clipType, PolyTree &polytree, + PolyFillType fillType = pftEvenOdd); + bool Execute(ClipType clipType, PolyTree &polytree, PolyFillType subjFillType, + PolyFillType clipFillType); + bool ReverseSolution() { return m_ReverseOutput; }; + void ReverseSolution(bool value) { m_ReverseOutput = value; }; + bool StrictlySimple() { return m_StrictSimple; }; + void StrictlySimple(bool value) { m_StrictSimple = value; }; +// set the callback function for z value filling on intersections (otherwise Z +// is 0) +#ifdef use_xyz + void ZFillFunction(ZFillCallback zFillFunc); +#endif +protected: + virtual bool ExecuteInternal(); + +private: + JoinList m_Joins; + JoinList m_GhostJoins; + IntersectList m_IntersectList; + ClipType m_ClipType; + typedef std::list MaximaList; + MaximaList m_Maxima; + TEdge *m_SortedEdges; + bool m_ExecuteLocked; + PolyFillType m_ClipFillType; + PolyFillType m_SubjFillType; + bool m_ReverseOutput; + bool m_UsingPolyTree; + bool m_StrictSimple; +#ifdef use_xyz + ZFillCallback m_ZFill; // custom callback +#endif + void SetWindingCount(TEdge &edge); + bool IsEvenOddFillType(const TEdge &edge) const; + bool IsEvenOddAltFillType(const TEdge &edge) const; + void InsertLocalMinimaIntoAEL(const cInt botY); + void InsertEdgeIntoAEL(TEdge *edge, TEdge *startEdge); + void AddEdgeToSEL(TEdge *edge); + bool PopEdgeFromSEL(TEdge *&edge); + void CopyAELToSEL(); + void DeleteFromSEL(TEdge *e); + void SwapPositionsInSEL(TEdge *edge1, TEdge *edge2); + bool IsContributing(const TEdge &edge) const; + bool IsTopHorz(const cInt XPos); + void DoMaxima(TEdge *e); + void ProcessHorizontals(); + void ProcessHorizontal(TEdge *horzEdge); + void AddLocalMaxPoly(TEdge *e1, TEdge *e2, const IntPoint &pt); + OutPt *AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &pt); + OutRec *GetOutRec(int idx); + void AppendPolygon(TEdge *e1, TEdge *e2); + void IntersectEdges(TEdge *e1, TEdge *e2, IntPoint &pt); + OutPt *AddOutPt(TEdge *e, const IntPoint &pt); + OutPt *GetLastOutPt(TEdge *e); + bool ProcessIntersections(const cInt topY); + void BuildIntersectList(const cInt topY); + void ProcessIntersectList(); + void ProcessEdgesAtTopOfScanbeam(const cInt topY); + void BuildResult(Paths &polys); + void BuildResult2(PolyTree &polytree); + void SetHoleState(TEdge *e, OutRec *outrec); + void DisposeIntersectNodes(); + bool FixupIntersectionOrder(); + void FixupOutPolygon(OutRec &outrec); + void FixupOutPolyline(OutRec &outrec); + bool IsHole(TEdge *e); + bool FindOwnerFromSplitRecs(OutRec &outRec, OutRec *&currOrfl); + void FixHoleLinkage(OutRec &outrec); + void AddJoin(OutPt *op1, OutPt *op2, const IntPoint offPt); + void ClearJoins(); + void ClearGhostJoins(); + void AddGhostJoin(OutPt *op, const IntPoint offPt); + bool JoinPoints(Join *j, OutRec *outRec1, OutRec *outRec2); + void JoinCommonEdges(); + void DoSimplePolygons(); + void FixupFirstLefts1(OutRec *OldOutRec, OutRec *NewOutRec); + void FixupFirstLefts2(OutRec *InnerOutRec, OutRec *OuterOutRec); + void FixupFirstLefts3(OutRec *OldOutRec, OutRec *NewOutRec); +#ifdef use_xyz + void SetZ(IntPoint &pt, TEdge &e1, TEdge &e2); +#endif +}; +//------------------------------------------------------------------------------ + +class ClipperOffset { +public: + ClipperOffset(double miterLimit = 2.0, double roundPrecision = 0.25); + ~ClipperOffset(); + void AddPath(const Path &path, JoinType joinType, EndType endType); + void AddPaths(const Paths &paths, JoinType joinType, EndType endType); + void Execute(Paths &solution, double delta); + void Execute(PolyTree &solution, double delta); + void Clear(); + double MiterLimit; + double ArcTolerance; + +private: + Paths m_destPolys; + Path m_srcPoly; + Path m_destPoly; + std::vector m_normals; + double m_delta, m_sinA, m_sin, m_cos; + double m_miterLim, m_StepsPerRad; + IntPoint m_lowest; + PolyNode m_polyNodes; + + void FixOrientations(); + void DoOffset(double delta); + void OffsetPoint(int j, int &k, JoinType jointype); + void DoSquare(int j, int k); + void DoMiter(int j, int k, double r); + void DoRound(int j, int k); +}; +//------------------------------------------------------------------------------ + +class clipperException : public std::exception { +public: + clipperException(const char *description) : m_descr(description) {} + virtual ~clipperException() throw() {} + virtual const char *what() const throw() { return m_descr.c_str(); } + +private: + std::string m_descr; +}; +//------------------------------------------------------------------------------ + +} // ClipperLib namespace + +#endif // clipper_hpp diff --git a/ptocr/postprocess/dbprocess/include/clipper/clipper.cpp b/ptocr/postprocess/dbprocess/include/clipper/clipper.cpp new file mode 100644 index 0000000..4993ba9 --- /dev/null +++ b/ptocr/postprocess/dbprocess/include/clipper/clipper.cpp @@ -0,0 +1,4622 @@ +/******************************************************************************* +* * +* Author : Angus Johnson * +* Version : 6.4.0 * +* Date : 2 July 2015 * +* Website : http://www.angusj.com * +* Copyright : Angus Johnson 2010-2015 * +* * +* License: * +* Use, modification & distribution is subject to Boost Software License Ver 1. * +* http://www.boost.org/LICENSE_1_0.txt * +* * +* Attributions: * +* The code in this library is an extension of Bala Vatti's clipping algorithm: * +* "A generic solution to polygon clipping" * +* Communications of the ACM, Vol 35, Issue 7 (July 1992) pp 56-63. * +* http://portal.acm.org/citation.cfm?id=129906 * +* * +* Computer graphics and geometric modeling: implementation and algorithms * +* By Max K. Agoston * +* Springer; 1 edition (January 4, 2005) * +* http://books.google.com/books?q=vatti+clipping+agoston * +* * +* See also: * +* "Polygon Offsetting by Computing Winding Numbers" * +* Paper no. DETC2005-85513 pp. 565-575 * +* ASME 2005 International Design Engineering Technical Conferences * +* and Computers and Information in Engineering Conference (IDETC/CIE2005) * +* September 24-28, 2005 , Long Beach, California, USA * +* http://www.me.berkeley.edu/~mcmains/pubs/DAC05OffsetPolygon.pdf * +* * +*******************************************************************************/ + +/******************************************************************************* +* * +* This is a translation of the Delphi Clipper library and the naming style * +* used has retained a Delphi flavour. * +* * +*******************************************************************************/ + +#include "clipper.hpp" +#include +#include +#include +#include +#include +#include +#include +#include + +namespace ClipperLib { + +static double const pi = 3.141592653589793238; +static double const two_pi = pi *2; +static double const def_arc_tolerance = 0.25; + +enum Direction { dRightToLeft, dLeftToRight }; + +static int const Unassigned = -1; //edge not currently 'owning' a solution +static int const Skip = -2; //edge that would otherwise close a path + +#define HORIZONTAL (-1.0E+40) +#define TOLERANCE (1.0e-20) +#define NEAR_ZERO(val) (((val) > -TOLERANCE) && ((val) < TOLERANCE)) + +struct TEdge { + IntPoint Bot; + IntPoint Curr; //current (updated for every new scanbeam) + IntPoint Top; + double Dx; + PolyType PolyTyp; + EdgeSide Side; //side only refers to current side of solution poly + int WindDelta; //1 or -1 depending on winding direction + int WindCnt; + int WindCnt2; //winding count of the opposite polytype + int OutIdx; + TEdge *Next; + TEdge *Prev; + TEdge *NextInLML; + TEdge *NextInAEL; + TEdge *PrevInAEL; + TEdge *NextInSEL; + TEdge *PrevInSEL; +}; + +struct IntersectNode { + TEdge *Edge1; + TEdge *Edge2; + IntPoint Pt; +}; + +struct LocalMinimum { + cInt Y; + TEdge *LeftBound; + TEdge *RightBound; +}; + +struct OutPt; + +//OutRec: contains a path in the clipping solution. Edges in the AEL will +//carry a pointer to an OutRec when they are part of the clipping solution. +struct OutRec { + int Idx; + bool IsHole; + bool IsOpen; + OutRec *FirstLeft; //see comments in clipper.pas + PolyNode *PolyNd; + OutPt *Pts; + OutPt *BottomPt; +}; + +struct OutPt { + int Idx; + IntPoint Pt; + OutPt *Next; + OutPt *Prev; +}; + +struct Join { + OutPt *OutPt1; + OutPt *OutPt2; + IntPoint OffPt; +}; + +struct LocMinSorter +{ + inline bool operator()(const LocalMinimum& locMin1, const LocalMinimum& locMin2) + { + return locMin2.Y < locMin1.Y; + } +}; + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + +inline cInt Round(double val) +{ + if ((val < 0)) return static_cast(val - 0.5); + else return static_cast(val + 0.5); +} +//------------------------------------------------------------------------------ + +inline cInt Abs(cInt val) +{ + return val < 0 ? -val : val; +} + +//------------------------------------------------------------------------------ +// PolyTree methods ... +//------------------------------------------------------------------------------ + +void PolyTree::Clear() +{ + for (PolyNodes::size_type i = 0; i < AllNodes.size(); ++i) + delete AllNodes[i]; + AllNodes.resize(0); + Childs.resize(0); +} +//------------------------------------------------------------------------------ + +PolyNode* PolyTree::GetFirst() const +{ + if (!Childs.empty()) + return Childs[0]; + else + return 0; +} +//------------------------------------------------------------------------------ + +int PolyTree::Total() const +{ + int result = (int)AllNodes.size(); + //with negative offsets, ignore the hidden outer polygon ... + if (result > 0 && Childs[0] != AllNodes[0]) result--; + return result; +} + +//------------------------------------------------------------------------------ +// PolyNode methods ... +//------------------------------------------------------------------------------ + +PolyNode::PolyNode(): Childs(), Parent(0), Index(0), m_IsOpen(false) +{ +} +//------------------------------------------------------------------------------ + +int PolyNode::ChildCount() const +{ + return (int)Childs.size(); +} +//------------------------------------------------------------------------------ + +void PolyNode::AddChild(PolyNode& child) +{ + unsigned cnt = (unsigned)Childs.size(); + Childs.push_back(&child); + child.Parent = this; + child.Index = cnt; +} +//------------------------------------------------------------------------------ + +PolyNode* PolyNode::GetNext() const +{ + if (!Childs.empty()) + return Childs[0]; + else + return GetNextSiblingUp(); +} +//------------------------------------------------------------------------------ + +PolyNode* PolyNode::GetNextSiblingUp() const +{ + if (!Parent) //protects against PolyTree.GetNextSiblingUp() + return 0; + else if (Index == Parent->Childs.size() - 1) + return Parent->GetNextSiblingUp(); + else + return Parent->Childs[Index + 1]; +} +//------------------------------------------------------------------------------ + +bool PolyNode::IsHole() const +{ + bool result = true; + PolyNode* node = Parent; + while (node) + { + result = !result; + node = node->Parent; + } + return result; +} +//------------------------------------------------------------------------------ + +bool PolyNode::IsOpen() const +{ + return m_IsOpen; +} +//------------------------------------------------------------------------------ + +#ifndef use_int32 + +//------------------------------------------------------------------------------ +// Int128 class (enables safe math on signed 64bit integers) +// eg Int128 val1((long64)9223372036854775807); //ie 2^63 -1 +// Int128 val2((long64)9223372036854775807); +// Int128 val3 = val1 * val2; +// val3.AsString => "85070591730234615847396907784232501249" (8.5e+37) +//------------------------------------------------------------------------------ + +class Int128 +{ + public: + ulong64 lo; + long64 hi; + + Int128(long64 _lo = 0) + { + lo = (ulong64)_lo; + if (_lo < 0) hi = -1; else hi = 0; + } + + + Int128(const Int128 &val): lo(val.lo), hi(val.hi){} + + Int128(const long64& _hi, const ulong64& _lo): lo(_lo), hi(_hi){} + + Int128& operator = (const long64 &val) + { + lo = (ulong64)val; + if (val < 0) hi = -1; else hi = 0; + return *this; + } + + bool operator == (const Int128 &val) const + {return (hi == val.hi && lo == val.lo);} + + bool operator != (const Int128 &val) const + { return !(*this == val);} + + bool operator > (const Int128 &val) const + { + if (hi != val.hi) + return hi > val.hi; + else + return lo > val.lo; + } + + bool operator < (const Int128 &val) const + { + if (hi != val.hi) + return hi < val.hi; + else + return lo < val.lo; + } + + bool operator >= (const Int128 &val) const + { return !(*this < val);} + + bool operator <= (const Int128 &val) const + { return !(*this > val);} + + Int128& operator += (const Int128 &rhs) + { + hi += rhs.hi; + lo += rhs.lo; + if (lo < rhs.lo) hi++; + return *this; + } + + Int128 operator + (const Int128 &rhs) const + { + Int128 result(*this); + result+= rhs; + return result; + } + + Int128& operator -= (const Int128 &rhs) + { + *this += -rhs; + return *this; + } + + Int128 operator - (const Int128 &rhs) const + { + Int128 result(*this); + result -= rhs; + return result; + } + + Int128 operator-() const //unary negation + { + if (lo == 0) + return Int128(-hi, 0); + else + return Int128(~hi, ~lo + 1); + } + + operator double() const + { + const double shift64 = 18446744073709551616.0; //2^64 + if (hi < 0) + { + if (lo == 0) return (double)hi * shift64; + else return -(double)(~lo + ~hi * shift64); + } + else + return (double)(lo + hi * shift64); + } + +}; +//------------------------------------------------------------------------------ + +Int128 Int128Mul (long64 lhs, long64 rhs) +{ + bool negate = (lhs < 0) != (rhs < 0); + + if (lhs < 0) lhs = -lhs; + ulong64 int1Hi = ulong64(lhs) >> 32; + ulong64 int1Lo = ulong64(lhs & 0xFFFFFFFF); + + if (rhs < 0) rhs = -rhs; + ulong64 int2Hi = ulong64(rhs) >> 32; + ulong64 int2Lo = ulong64(rhs & 0xFFFFFFFF); + + //nb: see comments in clipper.pas + ulong64 a = int1Hi * int2Hi; + ulong64 b = int1Lo * int2Lo; + ulong64 c = int1Hi * int2Lo + int1Lo * int2Hi; + + Int128 tmp; + tmp.hi = long64(a + (c >> 32)); + tmp.lo = long64(c << 32); + tmp.lo += long64(b); + if (tmp.lo < b) tmp.hi++; + if (negate) tmp = -tmp; + return tmp; +}; +#endif + +//------------------------------------------------------------------------------ +// Miscellaneous global functions +//------------------------------------------------------------------------------ + +bool Orientation(const Path &poly) +{ + return Area(poly) >= 0; +} +//------------------------------------------------------------------------------ + +double Area(const Path &poly) +{ + int size = (int)poly.size(); + if (size < 3) return 0; + + double a = 0; + for (int i = 0, j = size -1; i < size; ++i) + { + a += ((double)poly[j].X + poly[i].X) * ((double)poly[j].Y - poly[i].Y); + j = i; + } + return -a * 0.5; +} +//------------------------------------------------------------------------------ + +double Area(const OutPt *op) +{ + const OutPt *startOp = op; + if (!op) return 0; + double a = 0; + do { + a += (double)(op->Prev->Pt.X + op->Pt.X) * (double)(op->Prev->Pt.Y - op->Pt.Y); + op = op->Next; + } while (op != startOp); + return a * 0.5; +} +//------------------------------------------------------------------------------ + +double Area(const OutRec &outRec) +{ + return Area(outRec.Pts); +} +//------------------------------------------------------------------------------ + +bool PointIsVertex(const IntPoint &Pt, OutPt *pp) +{ + OutPt *pp2 = pp; + do + { + if (pp2->Pt == Pt) return true; + pp2 = pp2->Next; + } + while (pp2 != pp); + return false; +} +//------------------------------------------------------------------------------ + +//See "The Point in Polygon Problem for Arbitrary Polygons" by Hormann & Agathos +//http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.88.5498&rep=rep1&type=pdf +int PointInPolygon(const IntPoint &pt, const Path &path) +{ + //returns 0 if false, +1 if true, -1 if pt ON polygon boundary + int result = 0; + size_t cnt = path.size(); + if (cnt < 3) return 0; + IntPoint ip = path[0]; + for(size_t i = 1; i <= cnt; ++i) + { + IntPoint ipNext = (i == cnt ? path[0] : path[i]); + if (ipNext.Y == pt.Y) + { + if ((ipNext.X == pt.X) || (ip.Y == pt.Y && + ((ipNext.X > pt.X) == (ip.X < pt.X)))) return -1; + } + if ((ip.Y < pt.Y) != (ipNext.Y < pt.Y)) + { + if (ip.X >= pt.X) + { + if (ipNext.X > pt.X) result = 1 - result; + else + { + double d = (double)(ip.X - pt.X) * (ipNext.Y - pt.Y) - + (double)(ipNext.X - pt.X) * (ip.Y - pt.Y); + if (!d) return -1; + if ((d > 0) == (ipNext.Y > ip.Y)) result = 1 - result; + } + } else + { + if (ipNext.X > pt.X) + { + double d = (double)(ip.X - pt.X) * (ipNext.Y - pt.Y) - + (double)(ipNext.X - pt.X) * (ip.Y - pt.Y); + if (!d) return -1; + if ((d > 0) == (ipNext.Y > ip.Y)) result = 1 - result; + } + } + } + ip = ipNext; + } + return result; +} +//------------------------------------------------------------------------------ + +int PointInPolygon (const IntPoint &pt, OutPt *op) +{ + //returns 0 if false, +1 if true, -1 if pt ON polygon boundary + int result = 0; + OutPt* startOp = op; + for(;;) + { + if (op->Next->Pt.Y == pt.Y) + { + if ((op->Next->Pt.X == pt.X) || (op->Pt.Y == pt.Y && + ((op->Next->Pt.X > pt.X) == (op->Pt.X < pt.X)))) return -1; + } + if ((op->Pt.Y < pt.Y) != (op->Next->Pt.Y < pt.Y)) + { + if (op->Pt.X >= pt.X) + { + if (op->Next->Pt.X > pt.X) result = 1 - result; + else + { + double d = (double)(op->Pt.X - pt.X) * (op->Next->Pt.Y - pt.Y) - + (double)(op->Next->Pt.X - pt.X) * (op->Pt.Y - pt.Y); + if (!d) return -1; + if ((d > 0) == (op->Next->Pt.Y > op->Pt.Y)) result = 1 - result; + } + } else + { + if (op->Next->Pt.X > pt.X) + { + double d = (double)(op->Pt.X - pt.X) * (op->Next->Pt.Y - pt.Y) - + (double)(op->Next->Pt.X - pt.X) * (op->Pt.Y - pt.Y); + if (!d) return -1; + if ((d > 0) == (op->Next->Pt.Y > op->Pt.Y)) result = 1 - result; + } + } + } + op = op->Next; + if (startOp == op) break; + } + return result; +} +//------------------------------------------------------------------------------ + +bool Poly2ContainsPoly1(OutPt *OutPt1, OutPt *OutPt2) +{ + OutPt* op = OutPt1; + do + { + //nb: PointInPolygon returns 0 if false, +1 if true, -1 if pt on polygon + int res = PointInPolygon(op->Pt, OutPt2); + if (res >= 0) return res > 0; + op = op->Next; + } + while (op != OutPt1); + return true; +} +//---------------------------------------------------------------------- + +bool SlopesEqual(const TEdge &e1, const TEdge &e2, bool UseFullInt64Range) +{ +#ifndef use_int32 + if (UseFullInt64Range) + return Int128Mul(e1.Top.Y - e1.Bot.Y, e2.Top.X - e2.Bot.X) == + Int128Mul(e1.Top.X - e1.Bot.X, e2.Top.Y - e2.Bot.Y); + else +#endif + return (e1.Top.Y - e1.Bot.Y) * (e2.Top.X - e2.Bot.X) == + (e1.Top.X - e1.Bot.X) * (e2.Top.Y - e2.Bot.Y); +} +//------------------------------------------------------------------------------ + +bool SlopesEqual(const IntPoint pt1, const IntPoint pt2, + const IntPoint pt3, bool UseFullInt64Range) +{ +#ifndef use_int32 + if (UseFullInt64Range) + return Int128Mul(pt1.Y-pt2.Y, pt2.X-pt3.X) == Int128Mul(pt1.X-pt2.X, pt2.Y-pt3.Y); + else +#endif + return (pt1.Y-pt2.Y)*(pt2.X-pt3.X) == (pt1.X-pt2.X)*(pt2.Y-pt3.Y); +} +//------------------------------------------------------------------------------ + +bool SlopesEqual(const IntPoint pt1, const IntPoint pt2, + const IntPoint pt3, const IntPoint pt4, bool UseFullInt64Range) +{ +#ifndef use_int32 + if (UseFullInt64Range) + return Int128Mul(pt1.Y-pt2.Y, pt3.X-pt4.X) == Int128Mul(pt1.X-pt2.X, pt3.Y-pt4.Y); + else +#endif + return (pt1.Y-pt2.Y)*(pt3.X-pt4.X) == (pt1.X-pt2.X)*(pt3.Y-pt4.Y); +} +//------------------------------------------------------------------------------ + +inline bool IsHorizontal(TEdge &e) +{ + return e.Dx == HORIZONTAL; +} +//------------------------------------------------------------------------------ + +inline double GetDx(const IntPoint pt1, const IntPoint pt2) +{ + return (pt1.Y == pt2.Y) ? + HORIZONTAL : (double)(pt2.X - pt1.X) / (pt2.Y - pt1.Y); +} +//--------------------------------------------------------------------------- + +inline void SetDx(TEdge &e) +{ + cInt dy = (e.Top.Y - e.Bot.Y); + if (dy == 0) e.Dx = HORIZONTAL; + else e.Dx = (double)(e.Top.X - e.Bot.X) / dy; +} +//--------------------------------------------------------------------------- + +inline void SwapSides(TEdge &Edge1, TEdge &Edge2) +{ + EdgeSide Side = Edge1.Side; + Edge1.Side = Edge2.Side; + Edge2.Side = Side; +} +//------------------------------------------------------------------------------ + +inline void SwapPolyIndexes(TEdge &Edge1, TEdge &Edge2) +{ + int OutIdx = Edge1.OutIdx; + Edge1.OutIdx = Edge2.OutIdx; + Edge2.OutIdx = OutIdx; +} +//------------------------------------------------------------------------------ + +inline cInt TopX(TEdge &edge, const cInt currentY) +{ + return ( currentY == edge.Top.Y ) ? + edge.Top.X : edge.Bot.X + Round(edge.Dx *(currentY - edge.Bot.Y)); +} +//------------------------------------------------------------------------------ + +void IntersectPoint(TEdge &Edge1, TEdge &Edge2, IntPoint &ip) +{ +#ifdef use_xyz + ip.Z = 0; +#endif + + double b1, b2; + if (Edge1.Dx == Edge2.Dx) + { + ip.Y = Edge1.Curr.Y; + ip.X = TopX(Edge1, ip.Y); + return; + } + else if (Edge1.Dx == 0) + { + ip.X = Edge1.Bot.X; + if (IsHorizontal(Edge2)) + ip.Y = Edge2.Bot.Y; + else + { + b2 = Edge2.Bot.Y - (Edge2.Bot.X / Edge2.Dx); + ip.Y = Round(ip.X / Edge2.Dx + b2); + } + } + else if (Edge2.Dx == 0) + { + ip.X = Edge2.Bot.X; + if (IsHorizontal(Edge1)) + ip.Y = Edge1.Bot.Y; + else + { + b1 = Edge1.Bot.Y - (Edge1.Bot.X / Edge1.Dx); + ip.Y = Round(ip.X / Edge1.Dx + b1); + } + } + else + { + b1 = Edge1.Bot.X - Edge1.Bot.Y * Edge1.Dx; + b2 = Edge2.Bot.X - Edge2.Bot.Y * Edge2.Dx; + double q = (b2-b1) / (Edge1.Dx - Edge2.Dx); + ip.Y = Round(q); + if (std::fabs(Edge1.Dx) < std::fabs(Edge2.Dx)) + ip.X = Round(Edge1.Dx * q + b1); + else + ip.X = Round(Edge2.Dx * q + b2); + } + + if (ip.Y < Edge1.Top.Y || ip.Y < Edge2.Top.Y) + { + if (Edge1.Top.Y > Edge2.Top.Y) + ip.Y = Edge1.Top.Y; + else + ip.Y = Edge2.Top.Y; + if (std::fabs(Edge1.Dx) < std::fabs(Edge2.Dx)) + ip.X = TopX(Edge1, ip.Y); + else + ip.X = TopX(Edge2, ip.Y); + } + //finally, don't allow 'ip' to be BELOW curr.Y (ie bottom of scanbeam) ... + if (ip.Y > Edge1.Curr.Y) + { + ip.Y = Edge1.Curr.Y; + //use the more vertical edge to derive X ... + if (std::fabs(Edge1.Dx) > std::fabs(Edge2.Dx)) + ip.X = TopX(Edge2, ip.Y); else + ip.X = TopX(Edge1, ip.Y); + } +} +//------------------------------------------------------------------------------ + +void ReversePolyPtLinks(OutPt *pp) +{ + if (!pp) return; + OutPt *pp1, *pp2; + pp1 = pp; + do { + pp2 = pp1->Next; + pp1->Next = pp1->Prev; + pp1->Prev = pp2; + pp1 = pp2; + } while( pp1 != pp ); +} +//------------------------------------------------------------------------------ + +void DisposeOutPts(OutPt*& pp) +{ + if (pp == 0) return; + pp->Prev->Next = 0; + while( pp ) + { + OutPt *tmpPp = pp; + pp = pp->Next; + delete tmpPp; + } +} +//------------------------------------------------------------------------------ + +inline void InitEdge(TEdge* e, TEdge* eNext, TEdge* ePrev, const IntPoint& Pt) +{ + std::memset(e, 0, sizeof(TEdge)); + e->Next = eNext; + e->Prev = ePrev; + e->Curr = Pt; + e->OutIdx = Unassigned; +} +//------------------------------------------------------------------------------ + +void InitEdge2(TEdge& e, PolyType Pt) +{ + if (e.Curr.Y >= e.Next->Curr.Y) + { + e.Bot = e.Curr; + e.Top = e.Next->Curr; + } else + { + e.Top = e.Curr; + e.Bot = e.Next->Curr; + } + SetDx(e); + e.PolyTyp = Pt; +} +//------------------------------------------------------------------------------ + +TEdge* RemoveEdge(TEdge* e) +{ + //removes e from double_linked_list (but without removing from memory) + e->Prev->Next = e->Next; + e->Next->Prev = e->Prev; + TEdge* result = e->Next; + e->Prev = 0; //flag as removed (see ClipperBase.Clear) + return result; +} +//------------------------------------------------------------------------------ + +inline void ReverseHorizontal(TEdge &e) +{ + //swap horizontal edges' Top and Bottom x's so they follow the natural + //progression of the bounds - ie so their xbots will align with the + //adjoining lower edge. [Helpful in the ProcessHorizontal() method.] + std::swap(e.Top.X, e.Bot.X); +#ifdef use_xyz + std::swap(e.Top.Z, e.Bot.Z); +#endif +} +//------------------------------------------------------------------------------ + +void SwapPoints(IntPoint &pt1, IntPoint &pt2) +{ + IntPoint tmp = pt1; + pt1 = pt2; + pt2 = tmp; +} +//------------------------------------------------------------------------------ + +bool GetOverlapSegment(IntPoint pt1a, IntPoint pt1b, IntPoint pt2a, + IntPoint pt2b, IntPoint &pt1, IntPoint &pt2) +{ + //precondition: segments are Collinear. + if (Abs(pt1a.X - pt1b.X) > Abs(pt1a.Y - pt1b.Y)) + { + if (pt1a.X > pt1b.X) SwapPoints(pt1a, pt1b); + if (pt2a.X > pt2b.X) SwapPoints(pt2a, pt2b); + if (pt1a.X > pt2a.X) pt1 = pt1a; else pt1 = pt2a; + if (pt1b.X < pt2b.X) pt2 = pt1b; else pt2 = pt2b; + return pt1.X < pt2.X; + } else + { + if (pt1a.Y < pt1b.Y) SwapPoints(pt1a, pt1b); + if (pt2a.Y < pt2b.Y) SwapPoints(pt2a, pt2b); + if (pt1a.Y < pt2a.Y) pt1 = pt1a; else pt1 = pt2a; + if (pt1b.Y > pt2b.Y) pt2 = pt1b; else pt2 = pt2b; + return pt1.Y > pt2.Y; + } +} +//------------------------------------------------------------------------------ + +bool FirstIsBottomPt(const OutPt* btmPt1, const OutPt* btmPt2) +{ + OutPt *p = btmPt1->Prev; + while ((p->Pt == btmPt1->Pt) && (p != btmPt1)) p = p->Prev; + double dx1p = std::fabs(GetDx(btmPt1->Pt, p->Pt)); + p = btmPt1->Next; + while ((p->Pt == btmPt1->Pt) && (p != btmPt1)) p = p->Next; + double dx1n = std::fabs(GetDx(btmPt1->Pt, p->Pt)); + + p = btmPt2->Prev; + while ((p->Pt == btmPt2->Pt) && (p != btmPt2)) p = p->Prev; + double dx2p = std::fabs(GetDx(btmPt2->Pt, p->Pt)); + p = btmPt2->Next; + while ((p->Pt == btmPt2->Pt) && (p != btmPt2)) p = p->Next; + double dx2n = std::fabs(GetDx(btmPt2->Pt, p->Pt)); + + if (std::max(dx1p, dx1n) == std::max(dx2p, dx2n) && + std::min(dx1p, dx1n) == std::min(dx2p, dx2n)) + return Area(btmPt1) > 0; //if otherwise identical use orientation + else + return (dx1p >= dx2p && dx1p >= dx2n) || (dx1n >= dx2p && dx1n >= dx2n); +} +//------------------------------------------------------------------------------ + +OutPt* GetBottomPt(OutPt *pp) +{ + OutPt* dups = 0; + OutPt* p = pp->Next; + while (p != pp) + { + if (p->Pt.Y > pp->Pt.Y) + { + pp = p; + dups = 0; + } + else if (p->Pt.Y == pp->Pt.Y && p->Pt.X <= pp->Pt.X) + { + if (p->Pt.X < pp->Pt.X) + { + dups = 0; + pp = p; + } else + { + if (p->Next != pp && p->Prev != pp) dups = p; + } + } + p = p->Next; + } + if (dups) + { + //there appears to be at least 2 vertices at BottomPt so ... + while (dups != p) + { + if (!FirstIsBottomPt(p, dups)) pp = dups; + dups = dups->Next; + while (dups->Pt != pp->Pt) dups = dups->Next; + } + } + return pp; +} +//------------------------------------------------------------------------------ + +bool Pt2IsBetweenPt1AndPt3(const IntPoint pt1, + const IntPoint pt2, const IntPoint pt3) +{ + if ((pt1 == pt3) || (pt1 == pt2) || (pt3 == pt2)) + return false; + else if (pt1.X != pt3.X) + return (pt2.X > pt1.X) == (pt2.X < pt3.X); + else + return (pt2.Y > pt1.Y) == (pt2.Y < pt3.Y); +} +//------------------------------------------------------------------------------ + +bool HorzSegmentsOverlap(cInt seg1a, cInt seg1b, cInt seg2a, cInt seg2b) +{ + if (seg1a > seg1b) std::swap(seg1a, seg1b); + if (seg2a > seg2b) std::swap(seg2a, seg2b); + return (seg1a < seg2b) && (seg2a < seg1b); +} + +//------------------------------------------------------------------------------ +// ClipperBase class methods ... +//------------------------------------------------------------------------------ + +ClipperBase::ClipperBase() //constructor +{ + m_CurrentLM = m_MinimaList.begin(); //begin() == end() here + m_UseFullRange = false; +} +//------------------------------------------------------------------------------ + +ClipperBase::~ClipperBase() //destructor +{ + Clear(); +} +//------------------------------------------------------------------------------ + +void RangeTest(const IntPoint& Pt, bool& useFullRange) +{ + if (useFullRange) + { + if (Pt.X > hiRange || Pt.Y > hiRange || -Pt.X > hiRange || -Pt.Y > hiRange) + throw clipperException("Coordinate outside allowed range"); + } + else if (Pt.X > loRange|| Pt.Y > loRange || -Pt.X > loRange || -Pt.Y > loRange) + { + useFullRange = true; + RangeTest(Pt, useFullRange); + } +} +//------------------------------------------------------------------------------ + +TEdge* FindNextLocMin(TEdge* E) +{ + for (;;) + { + while (E->Bot != E->Prev->Bot || E->Curr == E->Top) E = E->Next; + if (!IsHorizontal(*E) && !IsHorizontal(*E->Prev)) break; + while (IsHorizontal(*E->Prev)) E = E->Prev; + TEdge* E2 = E; + while (IsHorizontal(*E)) E = E->Next; + if (E->Top.Y == E->Prev->Bot.Y) continue; //ie just an intermediate horz. + if (E2->Prev->Bot.X < E->Bot.X) E = E2; + break; + } + return E; +} +//------------------------------------------------------------------------------ + +TEdge* ClipperBase::ProcessBound(TEdge* E, bool NextIsForward) +{ + TEdge *Result = E; + TEdge *Horz = 0; + + if (E->OutIdx == Skip) + { + //if edges still remain in the current bound beyond the skip edge then + //create another LocMin and call ProcessBound once more + if (NextIsForward) + { + while (E->Top.Y == E->Next->Bot.Y) E = E->Next; + //don't include top horizontals when parsing a bound a second time, + //they will be contained in the opposite bound ... + while (E != Result && IsHorizontal(*E)) E = E->Prev; + } + else + { + while (E->Top.Y == E->Prev->Bot.Y) E = E->Prev; + while (E != Result && IsHorizontal(*E)) E = E->Next; + } + + if (E == Result) + { + if (NextIsForward) Result = E->Next; + else Result = E->Prev; + } + else + { + //there are more edges in the bound beyond result starting with E + if (NextIsForward) + E = Result->Next; + else + E = Result->Prev; + MinimaList::value_type locMin; + locMin.Y = E->Bot.Y; + locMin.LeftBound = 0; + locMin.RightBound = E; + E->WindDelta = 0; + Result = ProcessBound(E, NextIsForward); + m_MinimaList.push_back(locMin); + } + return Result; + } + + TEdge *EStart; + + if (IsHorizontal(*E)) + { + //We need to be careful with open paths because this may not be a + //true local minima (ie E may be following a skip edge). + //Also, consecutive horz. edges may start heading left before going right. + if (NextIsForward) + EStart = E->Prev; + else + EStart = E->Next; + if (IsHorizontal(*EStart)) //ie an adjoining horizontal skip edge + { + if (EStart->Bot.X != E->Bot.X && EStart->Top.X != E->Bot.X) + ReverseHorizontal(*E); + } + else if (EStart->Bot.X != E->Bot.X) + ReverseHorizontal(*E); + } + + EStart = E; + if (NextIsForward) + { + while (Result->Top.Y == Result->Next->Bot.Y && Result->Next->OutIdx != Skip) + Result = Result->Next; + if (IsHorizontal(*Result) && Result->Next->OutIdx != Skip) + { + //nb: at the top of a bound, horizontals are added to the bound + //only when the preceding edge attaches to the horizontal's left vertex + //unless a Skip edge is encountered when that becomes the top divide + Horz = Result; + while (IsHorizontal(*Horz->Prev)) Horz = Horz->Prev; + if (Horz->Prev->Top.X > Result->Next->Top.X) Result = Horz->Prev; + } + while (E != Result) + { + E->NextInLML = E->Next; + if (IsHorizontal(*E) && E != EStart && + E->Bot.X != E->Prev->Top.X) ReverseHorizontal(*E); + E = E->Next; + } + if (IsHorizontal(*E) && E != EStart && E->Bot.X != E->Prev->Top.X) + ReverseHorizontal(*E); + Result = Result->Next; //move to the edge just beyond current bound + } else + { + while (Result->Top.Y == Result->Prev->Bot.Y && Result->Prev->OutIdx != Skip) + Result = Result->Prev; + if (IsHorizontal(*Result) && Result->Prev->OutIdx != Skip) + { + Horz = Result; + while (IsHorizontal(*Horz->Next)) Horz = Horz->Next; + if (Horz->Next->Top.X == Result->Prev->Top.X || + Horz->Next->Top.X > Result->Prev->Top.X) Result = Horz->Next; + } + + while (E != Result) + { + E->NextInLML = E->Prev; + if (IsHorizontal(*E) && E != EStart && E->Bot.X != E->Next->Top.X) + ReverseHorizontal(*E); + E = E->Prev; + } + if (IsHorizontal(*E) && E != EStart && E->Bot.X != E->Next->Top.X) + ReverseHorizontal(*E); + Result = Result->Prev; //move to the edge just beyond current bound + } + + return Result; +} +//------------------------------------------------------------------------------ + +bool ClipperBase::AddPath(const Path &pg, PolyType PolyTyp, bool Closed) +{ +#ifdef use_lines + if (!Closed && PolyTyp == ptClip) + throw clipperException("AddPath: Open paths must be subject."); +#else + if (!Closed) + throw clipperException("AddPath: Open paths have been disabled."); +#endif + + int highI = (int)pg.size() -1; + if (Closed) while (highI > 0 && (pg[highI] == pg[0])) --highI; + while (highI > 0 && (pg[highI] == pg[highI -1])) --highI; + if ((Closed && highI < 2) || (!Closed && highI < 1)) return false; + + //create a new edge array ... + TEdge *edges = new TEdge [highI +1]; + + bool IsFlat = true; + //1. Basic (first) edge initialization ... + try + { + edges[1].Curr = pg[1]; + RangeTest(pg[0], m_UseFullRange); + RangeTest(pg[highI], m_UseFullRange); + InitEdge(&edges[0], &edges[1], &edges[highI], pg[0]); + InitEdge(&edges[highI], &edges[0], &edges[highI-1], pg[highI]); + for (int i = highI - 1; i >= 1; --i) + { + RangeTest(pg[i], m_UseFullRange); + InitEdge(&edges[i], &edges[i+1], &edges[i-1], pg[i]); + } + } + catch(...) + { + delete [] edges; + throw; //range test fails + } + TEdge *eStart = &edges[0]; + + //2. Remove duplicate vertices, and (when closed) collinear edges ... + TEdge *E = eStart, *eLoopStop = eStart; + for (;;) + { + //nb: allows matching start and end points when not Closed ... + if (E->Curr == E->Next->Curr && (Closed || E->Next != eStart)) + { + if (E == E->Next) break; + if (E == eStart) eStart = E->Next; + E = RemoveEdge(E); + eLoopStop = E; + continue; + } + if (E->Prev == E->Next) + break; //only two vertices + else if (Closed && + SlopesEqual(E->Prev->Curr, E->Curr, E->Next->Curr, m_UseFullRange) && + (!m_PreserveCollinear || + !Pt2IsBetweenPt1AndPt3(E->Prev->Curr, E->Curr, E->Next->Curr))) + { + //Collinear edges are allowed for open paths but in closed paths + //the default is to merge adjacent collinear edges into a single edge. + //However, if the PreserveCollinear property is enabled, only overlapping + //collinear edges (ie spikes) will be removed from closed paths. + if (E == eStart) eStart = E->Next; + E = RemoveEdge(E); + E = E->Prev; + eLoopStop = E; + continue; + } + E = E->Next; + if ((E == eLoopStop) || (!Closed && E->Next == eStart)) break; + } + + if ((!Closed && (E == E->Next)) || (Closed && (E->Prev == E->Next))) + { + delete [] edges; + return false; + } + + if (!Closed) + { + m_HasOpenPaths = true; + eStart->Prev->OutIdx = Skip; + } + + //3. Do second stage of edge initialization ... + E = eStart; + do + { + InitEdge2(*E, PolyTyp); + E = E->Next; + if (IsFlat && E->Curr.Y != eStart->Curr.Y) IsFlat = false; + } + while (E != eStart); + + //4. Finally, add edge bounds to LocalMinima list ... + + //Totally flat paths must be handled differently when adding them + //to LocalMinima list to avoid endless loops etc ... + if (IsFlat) + { + if (Closed) + { + delete [] edges; + return false; + } + E->Prev->OutIdx = Skip; + MinimaList::value_type locMin; + locMin.Y = E->Bot.Y; + locMin.LeftBound = 0; + locMin.RightBound = E; + locMin.RightBound->Side = esRight; + locMin.RightBound->WindDelta = 0; + for (;;) + { + if (E->Bot.X != E->Prev->Top.X) ReverseHorizontal(*E); + if (E->Next->OutIdx == Skip) break; + E->NextInLML = E->Next; + E = E->Next; + } + m_MinimaList.push_back(locMin); + m_edges.push_back(edges); + return true; + } + + m_edges.push_back(edges); + bool leftBoundIsForward; + TEdge* EMin = 0; + + //workaround to avoid an endless loop in the while loop below when + //open paths have matching start and end points ... + if (E->Prev->Bot == E->Prev->Top) E = E->Next; + + for (;;) + { + E = FindNextLocMin(E); + if (E == EMin) break; + else if (!EMin) EMin = E; + + //E and E.Prev now share a local minima (left aligned if horizontal). + //Compare their slopes to find which starts which bound ... + MinimaList::value_type locMin; + locMin.Y = E->Bot.Y; + if (E->Dx < E->Prev->Dx) + { + locMin.LeftBound = E->Prev; + locMin.RightBound = E; + leftBoundIsForward = false; //Q.nextInLML = Q.prev + } else + { + locMin.LeftBound = E; + locMin.RightBound = E->Prev; + leftBoundIsForward = true; //Q.nextInLML = Q.next + } + + if (!Closed) locMin.LeftBound->WindDelta = 0; + else if (locMin.LeftBound->Next == locMin.RightBound) + locMin.LeftBound->WindDelta = -1; + else locMin.LeftBound->WindDelta = 1; + locMin.RightBound->WindDelta = -locMin.LeftBound->WindDelta; + + E = ProcessBound(locMin.LeftBound, leftBoundIsForward); + if (E->OutIdx == Skip) E = ProcessBound(E, leftBoundIsForward); + + TEdge* E2 = ProcessBound(locMin.RightBound, !leftBoundIsForward); + if (E2->OutIdx == Skip) E2 = ProcessBound(E2, !leftBoundIsForward); + + if (locMin.LeftBound->OutIdx == Skip) + locMin.LeftBound = 0; + else if (locMin.RightBound->OutIdx == Skip) + locMin.RightBound = 0; + m_MinimaList.push_back(locMin); + if (!leftBoundIsForward) E = E2; + } + return true; +} +//------------------------------------------------------------------------------ + +bool ClipperBase::AddPaths(const Paths &ppg, PolyType PolyTyp, bool Closed) +{ + bool result = false; + for (Paths::size_type i = 0; i < ppg.size(); ++i) + if (AddPath(ppg[i], PolyTyp, Closed)) result = true; + return result; +} +//------------------------------------------------------------------------------ + +void ClipperBase::Clear() +{ + DisposeLocalMinimaList(); + for (EdgeList::size_type i = 0; i < m_edges.size(); ++i) + { + TEdge* edges = m_edges[i]; + delete [] edges; + } + m_edges.clear(); + m_UseFullRange = false; + m_HasOpenPaths = false; +} +//------------------------------------------------------------------------------ + +void ClipperBase::Reset() +{ + m_CurrentLM = m_MinimaList.begin(); + if (m_CurrentLM == m_MinimaList.end()) return; //ie nothing to process + std::sort(m_MinimaList.begin(), m_MinimaList.end(), LocMinSorter()); + + m_Scanbeam = ScanbeamList(); //clears/resets priority_queue + //reset all edges ... + for (MinimaList::iterator lm = m_MinimaList.begin(); lm != m_MinimaList.end(); ++lm) + { + InsertScanbeam(lm->Y); + TEdge* e = lm->LeftBound; + if (e) + { + e->Curr = e->Bot; + e->Side = esLeft; + e->OutIdx = Unassigned; + } + + e = lm->RightBound; + if (e) + { + e->Curr = e->Bot; + e->Side = esRight; + e->OutIdx = Unassigned; + } + } + m_ActiveEdges = 0; + m_CurrentLM = m_MinimaList.begin(); +} +//------------------------------------------------------------------------------ + +void ClipperBase::DisposeLocalMinimaList() +{ + m_MinimaList.clear(); + m_CurrentLM = m_MinimaList.begin(); +} +//------------------------------------------------------------------------------ + +bool ClipperBase::PopLocalMinima(cInt Y, const LocalMinimum *&locMin) +{ + if (m_CurrentLM == m_MinimaList.end() || (*m_CurrentLM).Y != Y) return false; + locMin = &(*m_CurrentLM); + ++m_CurrentLM; + return true; +} +//------------------------------------------------------------------------------ + +IntRect ClipperBase::GetBounds() +{ + IntRect result; + MinimaList::iterator lm = m_MinimaList.begin(); + if (lm == m_MinimaList.end()) + { + result.left = result.top = result.right = result.bottom = 0; + return result; + } + result.left = lm->LeftBound->Bot.X; + result.top = lm->LeftBound->Bot.Y; + result.right = lm->LeftBound->Bot.X; + result.bottom = lm->LeftBound->Bot.Y; + while (lm != m_MinimaList.end()) + { + //todo - needs fixing for open paths + result.bottom = std::max(result.bottom, lm->LeftBound->Bot.Y); + TEdge* e = lm->LeftBound; + for (;;) { + TEdge* bottomE = e; + while (e->NextInLML) + { + if (e->Bot.X < result.left) result.left = e->Bot.X; + if (e->Bot.X > result.right) result.right = e->Bot.X; + e = e->NextInLML; + } + result.left = std::min(result.left, e->Bot.X); + result.right = std::max(result.right, e->Bot.X); + result.left = std::min(result.left, e->Top.X); + result.right = std::max(result.right, e->Top.X); + result.top = std::min(result.top, e->Top.Y); + if (bottomE == lm->LeftBound) e = lm->RightBound; + else break; + } + ++lm; + } + return result; +} +//------------------------------------------------------------------------------ + +void ClipperBase::InsertScanbeam(const cInt Y) +{ + m_Scanbeam.push(Y); +} +//------------------------------------------------------------------------------ + +bool ClipperBase::PopScanbeam(cInt &Y) +{ + if (m_Scanbeam.empty()) return false; + Y = m_Scanbeam.top(); + m_Scanbeam.pop(); + while (!m_Scanbeam.empty() && Y == m_Scanbeam.top()) { m_Scanbeam.pop(); } // Pop duplicates. + return true; +} +//------------------------------------------------------------------------------ + +void ClipperBase::DisposeAllOutRecs(){ + for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) + DisposeOutRec(i); + m_PolyOuts.clear(); +} +//------------------------------------------------------------------------------ + +void ClipperBase::DisposeOutRec(PolyOutList::size_type index) +{ + OutRec *outRec = m_PolyOuts[index]; + if (outRec->Pts) DisposeOutPts(outRec->Pts); + delete outRec; + m_PolyOuts[index] = 0; +} +//------------------------------------------------------------------------------ + +void ClipperBase::DeleteFromAEL(TEdge *e) +{ + TEdge* AelPrev = e->PrevInAEL; + TEdge* AelNext = e->NextInAEL; + if (!AelPrev && !AelNext && (e != m_ActiveEdges)) return; //already deleted + if (AelPrev) AelPrev->NextInAEL = AelNext; + else m_ActiveEdges = AelNext; + if (AelNext) AelNext->PrevInAEL = AelPrev; + e->NextInAEL = 0; + e->PrevInAEL = 0; +} +//------------------------------------------------------------------------------ + +OutRec* ClipperBase::CreateOutRec() +{ + OutRec* result = new OutRec; + result->IsHole = false; + result->IsOpen = false; + result->FirstLeft = 0; + result->Pts = 0; + result->BottomPt = 0; + result->PolyNd = 0; + m_PolyOuts.push_back(result); + result->Idx = (int)m_PolyOuts.size() - 1; + return result; +} +//------------------------------------------------------------------------------ + +void ClipperBase::SwapPositionsInAEL(TEdge *Edge1, TEdge *Edge2) +{ + //check that one or other edge hasn't already been removed from AEL ... + if (Edge1->NextInAEL == Edge1->PrevInAEL || + Edge2->NextInAEL == Edge2->PrevInAEL) return; + + if (Edge1->NextInAEL == Edge2) + { + TEdge* Next = Edge2->NextInAEL; + if (Next) Next->PrevInAEL = Edge1; + TEdge* Prev = Edge1->PrevInAEL; + if (Prev) Prev->NextInAEL = Edge2; + Edge2->PrevInAEL = Prev; + Edge2->NextInAEL = Edge1; + Edge1->PrevInAEL = Edge2; + Edge1->NextInAEL = Next; + } + else if (Edge2->NextInAEL == Edge1) + { + TEdge* Next = Edge1->NextInAEL; + if (Next) Next->PrevInAEL = Edge2; + TEdge* Prev = Edge2->PrevInAEL; + if (Prev) Prev->NextInAEL = Edge1; + Edge1->PrevInAEL = Prev; + Edge1->NextInAEL = Edge2; + Edge2->PrevInAEL = Edge1; + Edge2->NextInAEL = Next; + } + else + { + TEdge* Next = Edge1->NextInAEL; + TEdge* Prev = Edge1->PrevInAEL; + Edge1->NextInAEL = Edge2->NextInAEL; + if (Edge1->NextInAEL) Edge1->NextInAEL->PrevInAEL = Edge1; + Edge1->PrevInAEL = Edge2->PrevInAEL; + if (Edge1->PrevInAEL) Edge1->PrevInAEL->NextInAEL = Edge1; + Edge2->NextInAEL = Next; + if (Edge2->NextInAEL) Edge2->NextInAEL->PrevInAEL = Edge2; + Edge2->PrevInAEL = Prev; + if (Edge2->PrevInAEL) Edge2->PrevInAEL->NextInAEL = Edge2; + } + + if (!Edge1->PrevInAEL) m_ActiveEdges = Edge1; + else if (!Edge2->PrevInAEL) m_ActiveEdges = Edge2; +} +//------------------------------------------------------------------------------ + +void ClipperBase::UpdateEdgeIntoAEL(TEdge *&e) +{ + if (!e->NextInLML) + throw clipperException("UpdateEdgeIntoAEL: invalid call"); + + e->NextInLML->OutIdx = e->OutIdx; + TEdge* AelPrev = e->PrevInAEL; + TEdge* AelNext = e->NextInAEL; + if (AelPrev) AelPrev->NextInAEL = e->NextInLML; + else m_ActiveEdges = e->NextInLML; + if (AelNext) AelNext->PrevInAEL = e->NextInLML; + e->NextInLML->Side = e->Side; + e->NextInLML->WindDelta = e->WindDelta; + e->NextInLML->WindCnt = e->WindCnt; + e->NextInLML->WindCnt2 = e->WindCnt2; + e = e->NextInLML; + e->Curr = e->Bot; + e->PrevInAEL = AelPrev; + e->NextInAEL = AelNext; + if (!IsHorizontal(*e)) InsertScanbeam(e->Top.Y); +} +//------------------------------------------------------------------------------ + +bool ClipperBase::LocalMinimaPending() +{ + return (m_CurrentLM != m_MinimaList.end()); +} + +//------------------------------------------------------------------------------ +// TClipper methods ... +//------------------------------------------------------------------------------ + +Clipper::Clipper(int initOptions) : ClipperBase() //constructor +{ + m_ExecuteLocked = false; + m_UseFullRange = false; + m_ReverseOutput = ((initOptions & ioReverseSolution) != 0); + m_StrictSimple = ((initOptions & ioStrictlySimple) != 0); + m_PreserveCollinear = ((initOptions & ioPreserveCollinear) != 0); + m_HasOpenPaths = false; +#ifdef use_xyz + m_ZFill = 0; +#endif +} +//------------------------------------------------------------------------------ + +#ifdef use_xyz +void Clipper::ZFillFunction(ZFillCallback zFillFunc) +{ + m_ZFill = zFillFunc; +} +//------------------------------------------------------------------------------ +#endif + +bool Clipper::Execute(ClipType clipType, Paths &solution, PolyFillType fillType) +{ + return Execute(clipType, solution, fillType, fillType); +} +//------------------------------------------------------------------------------ + +bool Clipper::Execute(ClipType clipType, PolyTree &polytree, PolyFillType fillType) +{ + return Execute(clipType, polytree, fillType, fillType); +} +//------------------------------------------------------------------------------ + +bool Clipper::Execute(ClipType clipType, Paths &solution, + PolyFillType subjFillType, PolyFillType clipFillType) +{ + if( m_ExecuteLocked ) return false; + if (m_HasOpenPaths) + throw clipperException("Error: PolyTree struct is needed for open path clipping."); + m_ExecuteLocked = true; + solution.resize(0); + m_SubjFillType = subjFillType; + m_ClipFillType = clipFillType; + m_ClipType = clipType; + m_UsingPolyTree = false; + bool succeeded = ExecuteInternal(); + if (succeeded) BuildResult(solution); + DisposeAllOutRecs(); + m_ExecuteLocked = false; + return succeeded; +} +//------------------------------------------------------------------------------ + +bool Clipper::Execute(ClipType clipType, PolyTree& polytree, + PolyFillType subjFillType, PolyFillType clipFillType) +{ + if( m_ExecuteLocked ) return false; + m_ExecuteLocked = true; + m_SubjFillType = subjFillType; + m_ClipFillType = clipFillType; + m_ClipType = clipType; + m_UsingPolyTree = true; + bool succeeded = ExecuteInternal(); + if (succeeded) BuildResult2(polytree); + DisposeAllOutRecs(); + m_ExecuteLocked = false; + return succeeded; +} +//------------------------------------------------------------------------------ + +void Clipper::FixHoleLinkage(OutRec &outrec) +{ + //skip OutRecs that (a) contain outermost polygons or + //(b) already have the correct owner/child linkage ... + if (!outrec.FirstLeft || + (outrec.IsHole != outrec.FirstLeft->IsHole && + outrec.FirstLeft->Pts)) return; + + OutRec* orfl = outrec.FirstLeft; + while (orfl && ((orfl->IsHole == outrec.IsHole) || !orfl->Pts)) + orfl = orfl->FirstLeft; + outrec.FirstLeft = orfl; +} +//------------------------------------------------------------------------------ + +bool Clipper::ExecuteInternal() +{ + bool succeeded = true; + try { + Reset(); + m_Maxima = MaximaList(); + m_SortedEdges = 0; + + succeeded = true; + cInt botY, topY; + if (!PopScanbeam(botY)) return false; + InsertLocalMinimaIntoAEL(botY); + while (PopScanbeam(topY) || LocalMinimaPending()) + { + ProcessHorizontals(); + ClearGhostJoins(); + if (!ProcessIntersections(topY)) + { + succeeded = false; + break; + } + ProcessEdgesAtTopOfScanbeam(topY); + botY = topY; + InsertLocalMinimaIntoAEL(botY); + } + } + catch(...) + { + succeeded = false; + } + + if (succeeded) + { + //fix orientations ... + for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) + { + OutRec *outRec = m_PolyOuts[i]; + if (!outRec->Pts || outRec->IsOpen) continue; + if ((outRec->IsHole ^ m_ReverseOutput) == (Area(*outRec) > 0)) + ReversePolyPtLinks(outRec->Pts); + } + + if (!m_Joins.empty()) JoinCommonEdges(); + + //unfortunately FixupOutPolygon() must be done after JoinCommonEdges() + for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) + { + OutRec *outRec = m_PolyOuts[i]; + if (!outRec->Pts) continue; + if (outRec->IsOpen) + FixupOutPolyline(*outRec); + else + FixupOutPolygon(*outRec); + } + + if (m_StrictSimple) DoSimplePolygons(); + } + + ClearJoins(); + ClearGhostJoins(); + return succeeded; +} +//------------------------------------------------------------------------------ + +void Clipper::SetWindingCount(TEdge &edge) +{ + TEdge *e = edge.PrevInAEL; + //find the edge of the same polytype that immediately preceeds 'edge' in AEL + while (e && ((e->PolyTyp != edge.PolyTyp) || (e->WindDelta == 0))) e = e->PrevInAEL; + if (!e) + { + if (edge.WindDelta == 0) + { + PolyFillType pft = (edge.PolyTyp == ptSubject ? m_SubjFillType : m_ClipFillType); + edge.WindCnt = (pft == pftNegative ? -1 : 1); + } + else + edge.WindCnt = edge.WindDelta; + edge.WindCnt2 = 0; + e = m_ActiveEdges; //ie get ready to calc WindCnt2 + } + else if (edge.WindDelta == 0 && m_ClipType != ctUnion) + { + edge.WindCnt = 1; + edge.WindCnt2 = e->WindCnt2; + e = e->NextInAEL; //ie get ready to calc WindCnt2 + } + else if (IsEvenOddFillType(edge)) + { + //EvenOdd filling ... + if (edge.WindDelta == 0) + { + //are we inside a subj polygon ... + bool Inside = true; + TEdge *e2 = e->PrevInAEL; + while (e2) + { + if (e2->PolyTyp == e->PolyTyp && e2->WindDelta != 0) + Inside = !Inside; + e2 = e2->PrevInAEL; + } + edge.WindCnt = (Inside ? 0 : 1); + } + else + { + edge.WindCnt = edge.WindDelta; + } + edge.WindCnt2 = e->WindCnt2; + e = e->NextInAEL; //ie get ready to calc WindCnt2 + } + else + { + //nonZero, Positive or Negative filling ... + if (e->WindCnt * e->WindDelta < 0) + { + //prev edge is 'decreasing' WindCount (WC) toward zero + //so we're outside the previous polygon ... + if (Abs(e->WindCnt) > 1) + { + //outside prev poly but still inside another. + //when reversing direction of prev poly use the same WC + if (e->WindDelta * edge.WindDelta < 0) edge.WindCnt = e->WindCnt; + //otherwise continue to 'decrease' WC ... + else edge.WindCnt = e->WindCnt + edge.WindDelta; + } + else + //now outside all polys of same polytype so set own WC ... + edge.WindCnt = (edge.WindDelta == 0 ? 1 : edge.WindDelta); + } else + { + //prev edge is 'increasing' WindCount (WC) away from zero + //so we're inside the previous polygon ... + if (edge.WindDelta == 0) + edge.WindCnt = (e->WindCnt < 0 ? e->WindCnt - 1 : e->WindCnt + 1); + //if wind direction is reversing prev then use same WC + else if (e->WindDelta * edge.WindDelta < 0) edge.WindCnt = e->WindCnt; + //otherwise add to WC ... + else edge.WindCnt = e->WindCnt + edge.WindDelta; + } + edge.WindCnt2 = e->WindCnt2; + e = e->NextInAEL; //ie get ready to calc WindCnt2 + } + + //update WindCnt2 ... + if (IsEvenOddAltFillType(edge)) + { + //EvenOdd filling ... + while (e != &edge) + { + if (e->WindDelta != 0) + edge.WindCnt2 = (edge.WindCnt2 == 0 ? 1 : 0); + e = e->NextInAEL; + } + } else + { + //nonZero, Positive or Negative filling ... + while ( e != &edge ) + { + edge.WindCnt2 += e->WindDelta; + e = e->NextInAEL; + } + } +} +//------------------------------------------------------------------------------ + +bool Clipper::IsEvenOddFillType(const TEdge& edge) const +{ + if (edge.PolyTyp == ptSubject) + return m_SubjFillType == pftEvenOdd; else + return m_ClipFillType == pftEvenOdd; +} +//------------------------------------------------------------------------------ + +bool Clipper::IsEvenOddAltFillType(const TEdge& edge) const +{ + if (edge.PolyTyp == ptSubject) + return m_ClipFillType == pftEvenOdd; else + return m_SubjFillType == pftEvenOdd; +} +//------------------------------------------------------------------------------ + +bool Clipper::IsContributing(const TEdge& edge) const +{ + PolyFillType pft, pft2; + if (edge.PolyTyp == ptSubject) + { + pft = m_SubjFillType; + pft2 = m_ClipFillType; + } else + { + pft = m_ClipFillType; + pft2 = m_SubjFillType; + } + + switch(pft) + { + case pftEvenOdd: + //return false if a subj line has been flagged as inside a subj polygon + if (edge.WindDelta == 0 && edge.WindCnt != 1) return false; + break; + case pftNonZero: + if (Abs(edge.WindCnt) != 1) return false; + break; + case pftPositive: + if (edge.WindCnt != 1) return false; + break; + default: //pftNegative + if (edge.WindCnt != -1) return false; + } + + switch(m_ClipType) + { + case ctIntersection: + switch(pft2) + { + case pftEvenOdd: + case pftNonZero: + return (edge.WindCnt2 != 0); + case pftPositive: + return (edge.WindCnt2 > 0); + default: + return (edge.WindCnt2 < 0); + } + break; + case ctUnion: + switch(pft2) + { + case pftEvenOdd: + case pftNonZero: + return (edge.WindCnt2 == 0); + case pftPositive: + return (edge.WindCnt2 <= 0); + default: + return (edge.WindCnt2 >= 0); + } + break; + case ctDifference: + if (edge.PolyTyp == ptSubject) + switch(pft2) + { + case pftEvenOdd: + case pftNonZero: + return (edge.WindCnt2 == 0); + case pftPositive: + return (edge.WindCnt2 <= 0); + default: + return (edge.WindCnt2 >= 0); + } + else + switch(pft2) + { + case pftEvenOdd: + case pftNonZero: + return (edge.WindCnt2 != 0); + case pftPositive: + return (edge.WindCnt2 > 0); + default: + return (edge.WindCnt2 < 0); + } + break; + case ctXor: + if (edge.WindDelta == 0) //XOr always contributing unless open + switch(pft2) + { + case pftEvenOdd: + case pftNonZero: + return (edge.WindCnt2 == 0); + case pftPositive: + return (edge.WindCnt2 <= 0); + default: + return (edge.WindCnt2 >= 0); + } + else + return true; + break; + default: + return true; + } +} +//------------------------------------------------------------------------------ + +OutPt* Clipper::AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &Pt) +{ + OutPt* result; + TEdge *e, *prevE; + if (IsHorizontal(*e2) || ( e1->Dx > e2->Dx )) + { + result = AddOutPt(e1, Pt); + e2->OutIdx = e1->OutIdx; + e1->Side = esLeft; + e2->Side = esRight; + e = e1; + if (e->PrevInAEL == e2) + prevE = e2->PrevInAEL; + else + prevE = e->PrevInAEL; + } else + { + result = AddOutPt(e2, Pt); + e1->OutIdx = e2->OutIdx; + e1->Side = esRight; + e2->Side = esLeft; + e = e2; + if (e->PrevInAEL == e1) + prevE = e1->PrevInAEL; + else + prevE = e->PrevInAEL; + } + + if (prevE && prevE->OutIdx >= 0) + { + cInt xPrev = TopX(*prevE, Pt.Y); + cInt xE = TopX(*e, Pt.Y); + if (xPrev == xE && (e->WindDelta != 0) && (prevE->WindDelta != 0) && + SlopesEqual(IntPoint(xPrev, Pt.Y), prevE->Top, IntPoint(xE, Pt.Y), e->Top, m_UseFullRange)) + { + OutPt* outPt = AddOutPt(prevE, Pt); + AddJoin(result, outPt, e->Top); + } + } + return result; +} +//------------------------------------------------------------------------------ + +void Clipper::AddLocalMaxPoly(TEdge *e1, TEdge *e2, const IntPoint &Pt) +{ + AddOutPt( e1, Pt ); + if (e2->WindDelta == 0) AddOutPt(e2, Pt); + if( e1->OutIdx == e2->OutIdx ) + { + e1->OutIdx = Unassigned; + e2->OutIdx = Unassigned; + } + else if (e1->OutIdx < e2->OutIdx) + AppendPolygon(e1, e2); + else + AppendPolygon(e2, e1); +} +//------------------------------------------------------------------------------ + +void Clipper::AddEdgeToSEL(TEdge *edge) +{ + //SEL pointers in PEdge are reused to build a list of horizontal edges. + //However, we don't need to worry about order with horizontal edge processing. + if( !m_SortedEdges ) + { + m_SortedEdges = edge; + edge->PrevInSEL = 0; + edge->NextInSEL = 0; + } + else + { + edge->NextInSEL = m_SortedEdges; + edge->PrevInSEL = 0; + m_SortedEdges->PrevInSEL = edge; + m_SortedEdges = edge; + } +} +//------------------------------------------------------------------------------ + +bool Clipper::PopEdgeFromSEL(TEdge *&edge) +{ + if (!m_SortedEdges) return false; + edge = m_SortedEdges; + DeleteFromSEL(m_SortedEdges); + return true; +} +//------------------------------------------------------------------------------ + +void Clipper::CopyAELToSEL() +{ + TEdge* e = m_ActiveEdges; + m_SortedEdges = e; + while ( e ) + { + e->PrevInSEL = e->PrevInAEL; + e->NextInSEL = e->NextInAEL; + e = e->NextInAEL; + } +} +//------------------------------------------------------------------------------ + +void Clipper::AddJoin(OutPt *op1, OutPt *op2, const IntPoint OffPt) +{ + Join* j = new Join; + j->OutPt1 = op1; + j->OutPt2 = op2; + j->OffPt = OffPt; + m_Joins.push_back(j); +} +//------------------------------------------------------------------------------ + +void Clipper::ClearJoins() +{ + for (JoinList::size_type i = 0; i < m_Joins.size(); i++) + delete m_Joins[i]; + m_Joins.resize(0); +} +//------------------------------------------------------------------------------ + +void Clipper::ClearGhostJoins() +{ + for (JoinList::size_type i = 0; i < m_GhostJoins.size(); i++) + delete m_GhostJoins[i]; + m_GhostJoins.resize(0); +} +//------------------------------------------------------------------------------ + +void Clipper::AddGhostJoin(OutPt *op, const IntPoint OffPt) +{ + Join* j = new Join; + j->OutPt1 = op; + j->OutPt2 = 0; + j->OffPt = OffPt; + m_GhostJoins.push_back(j); +} +//------------------------------------------------------------------------------ + +void Clipper::InsertLocalMinimaIntoAEL(const cInt botY) +{ + const LocalMinimum *lm; + while (PopLocalMinima(botY, lm)) + { + TEdge* lb = lm->LeftBound; + TEdge* rb = lm->RightBound; + + OutPt *Op1 = 0; + if (!lb) + { + //nb: don't insert LB into either AEL or SEL + InsertEdgeIntoAEL(rb, 0); + SetWindingCount(*rb); + if (IsContributing(*rb)) + Op1 = AddOutPt(rb, rb->Bot); + } + else if (!rb) + { + InsertEdgeIntoAEL(lb, 0); + SetWindingCount(*lb); + if (IsContributing(*lb)) + Op1 = AddOutPt(lb, lb->Bot); + InsertScanbeam(lb->Top.Y); + } + else + { + InsertEdgeIntoAEL(lb, 0); + InsertEdgeIntoAEL(rb, lb); + SetWindingCount( *lb ); + rb->WindCnt = lb->WindCnt; + rb->WindCnt2 = lb->WindCnt2; + if (IsContributing(*lb)) + Op1 = AddLocalMinPoly(lb, rb, lb->Bot); + InsertScanbeam(lb->Top.Y); + } + + if (rb) + { + if (IsHorizontal(*rb)) + { + AddEdgeToSEL(rb); + if (rb->NextInLML) + InsertScanbeam(rb->NextInLML->Top.Y); + } + else InsertScanbeam( rb->Top.Y ); + } + + if (!lb || !rb) continue; + + //if any output polygons share an edge, they'll need joining later ... + if (Op1 && IsHorizontal(*rb) && + m_GhostJoins.size() > 0 && (rb->WindDelta != 0)) + { + for (JoinList::size_type i = 0; i < m_GhostJoins.size(); ++i) + { + Join* jr = m_GhostJoins[i]; + //if the horizontal Rb and a 'ghost' horizontal overlap, then convert + //the 'ghost' join to a real join ready for later ... + if (HorzSegmentsOverlap(jr->OutPt1->Pt.X, jr->OffPt.X, rb->Bot.X, rb->Top.X)) + AddJoin(jr->OutPt1, Op1, jr->OffPt); + } + } + + if (lb->OutIdx >= 0 && lb->PrevInAEL && + lb->PrevInAEL->Curr.X == lb->Bot.X && + lb->PrevInAEL->OutIdx >= 0 && + SlopesEqual(lb->PrevInAEL->Bot, lb->PrevInAEL->Top, lb->Curr, lb->Top, m_UseFullRange) && + (lb->WindDelta != 0) && (lb->PrevInAEL->WindDelta != 0)) + { + OutPt *Op2 = AddOutPt(lb->PrevInAEL, lb->Bot); + AddJoin(Op1, Op2, lb->Top); + } + + if(lb->NextInAEL != rb) + { + + if (rb->OutIdx >= 0 && rb->PrevInAEL->OutIdx >= 0 && + SlopesEqual(rb->PrevInAEL->Curr, rb->PrevInAEL->Top, rb->Curr, rb->Top, m_UseFullRange) && + (rb->WindDelta != 0) && (rb->PrevInAEL->WindDelta != 0)) + { + OutPt *Op2 = AddOutPt(rb->PrevInAEL, rb->Bot); + AddJoin(Op1, Op2, rb->Top); + } + + TEdge* e = lb->NextInAEL; + if (e) + { + while( e != rb ) + { + //nb: For calculating winding counts etc, IntersectEdges() assumes + //that param1 will be to the Right of param2 ABOVE the intersection ... + IntersectEdges(rb , e , lb->Curr); //order important here + e = e->NextInAEL; + } + } + } + + } +} +//------------------------------------------------------------------------------ + +void Clipper::DeleteFromSEL(TEdge *e) +{ + TEdge* SelPrev = e->PrevInSEL; + TEdge* SelNext = e->NextInSEL; + if( !SelPrev && !SelNext && (e != m_SortedEdges) ) return; //already deleted + if( SelPrev ) SelPrev->NextInSEL = SelNext; + else m_SortedEdges = SelNext; + if( SelNext ) SelNext->PrevInSEL = SelPrev; + e->NextInSEL = 0; + e->PrevInSEL = 0; +} +//------------------------------------------------------------------------------ + +#ifdef use_xyz +void Clipper::SetZ(IntPoint& pt, TEdge& e1, TEdge& e2) +{ + if (pt.Z != 0 || !m_ZFill) return; + else if (pt == e1.Bot) pt.Z = e1.Bot.Z; + else if (pt == e1.Top) pt.Z = e1.Top.Z; + else if (pt == e2.Bot) pt.Z = e2.Bot.Z; + else if (pt == e2.Top) pt.Z = e2.Top.Z; + else (*m_ZFill)(e1.Bot, e1.Top, e2.Bot, e2.Top, pt); +} +//------------------------------------------------------------------------------ +#endif + +void Clipper::IntersectEdges(TEdge *e1, TEdge *e2, IntPoint &Pt) +{ + bool e1Contributing = ( e1->OutIdx >= 0 ); + bool e2Contributing = ( e2->OutIdx >= 0 ); + +#ifdef use_xyz + SetZ(Pt, *e1, *e2); +#endif + +#ifdef use_lines + //if either edge is on an OPEN path ... + if (e1->WindDelta == 0 || e2->WindDelta == 0) + { + //ignore subject-subject open path intersections UNLESS they + //are both open paths, AND they are both 'contributing maximas' ... + if (e1->WindDelta == 0 && e2->WindDelta == 0) return; + + //if intersecting a subj line with a subj poly ... + else if (e1->PolyTyp == e2->PolyTyp && + e1->WindDelta != e2->WindDelta && m_ClipType == ctUnion) + { + if (e1->WindDelta == 0) + { + if (e2Contributing) + { + AddOutPt(e1, Pt); + if (e1Contributing) e1->OutIdx = Unassigned; + } + } + else + { + if (e1Contributing) + { + AddOutPt(e2, Pt); + if (e2Contributing) e2->OutIdx = Unassigned; + } + } + } + else if (e1->PolyTyp != e2->PolyTyp) + { + //toggle subj open path OutIdx on/off when Abs(clip.WndCnt) == 1 ... + if ((e1->WindDelta == 0) && abs(e2->WindCnt) == 1 && + (m_ClipType != ctUnion || e2->WindCnt2 == 0)) + { + AddOutPt(e1, Pt); + if (e1Contributing) e1->OutIdx = Unassigned; + } + else if ((e2->WindDelta == 0) && (abs(e1->WindCnt) == 1) && + (m_ClipType != ctUnion || e1->WindCnt2 == 0)) + { + AddOutPt(e2, Pt); + if (e2Contributing) e2->OutIdx = Unassigned; + } + } + return; + } +#endif + + //update winding counts... + //assumes that e1 will be to the Right of e2 ABOVE the intersection + if ( e1->PolyTyp == e2->PolyTyp ) + { + if ( IsEvenOddFillType( *e1) ) + { + int oldE1WindCnt = e1->WindCnt; + e1->WindCnt = e2->WindCnt; + e2->WindCnt = oldE1WindCnt; + } else + { + if (e1->WindCnt + e2->WindDelta == 0 ) e1->WindCnt = -e1->WindCnt; + else e1->WindCnt += e2->WindDelta; + if ( e2->WindCnt - e1->WindDelta == 0 ) e2->WindCnt = -e2->WindCnt; + else e2->WindCnt -= e1->WindDelta; + } + } else + { + if (!IsEvenOddFillType(*e2)) e1->WindCnt2 += e2->WindDelta; + else e1->WindCnt2 = ( e1->WindCnt2 == 0 ) ? 1 : 0; + if (!IsEvenOddFillType(*e1)) e2->WindCnt2 -= e1->WindDelta; + else e2->WindCnt2 = ( e2->WindCnt2 == 0 ) ? 1 : 0; + } + + PolyFillType e1FillType, e2FillType, e1FillType2, e2FillType2; + if (e1->PolyTyp == ptSubject) + { + e1FillType = m_SubjFillType; + e1FillType2 = m_ClipFillType; + } else + { + e1FillType = m_ClipFillType; + e1FillType2 = m_SubjFillType; + } + if (e2->PolyTyp == ptSubject) + { + e2FillType = m_SubjFillType; + e2FillType2 = m_ClipFillType; + } else + { + e2FillType = m_ClipFillType; + e2FillType2 = m_SubjFillType; + } + + cInt e1Wc, e2Wc; + switch (e1FillType) + { + case pftPositive: e1Wc = e1->WindCnt; break; + case pftNegative: e1Wc = -e1->WindCnt; break; + default: e1Wc = Abs(e1->WindCnt); + } + switch(e2FillType) + { + case pftPositive: e2Wc = e2->WindCnt; break; + case pftNegative: e2Wc = -e2->WindCnt; break; + default: e2Wc = Abs(e2->WindCnt); + } + + if ( e1Contributing && e2Contributing ) + { + if ((e1Wc != 0 && e1Wc != 1) || (e2Wc != 0 && e2Wc != 1) || + (e1->PolyTyp != e2->PolyTyp && m_ClipType != ctXor) ) + { + AddLocalMaxPoly(e1, e2, Pt); + } + else + { + AddOutPt(e1, Pt); + AddOutPt(e2, Pt); + SwapSides( *e1 , *e2 ); + SwapPolyIndexes( *e1 , *e2 ); + } + } + else if ( e1Contributing ) + { + if (e2Wc == 0 || e2Wc == 1) + { + AddOutPt(e1, Pt); + SwapSides(*e1, *e2); + SwapPolyIndexes(*e1, *e2); + } + } + else if ( e2Contributing ) + { + if (e1Wc == 0 || e1Wc == 1) + { + AddOutPt(e2, Pt); + SwapSides(*e1, *e2); + SwapPolyIndexes(*e1, *e2); + } + } + else if ( (e1Wc == 0 || e1Wc == 1) && (e2Wc == 0 || e2Wc == 1)) + { + //neither edge is currently contributing ... + + cInt e1Wc2, e2Wc2; + switch (e1FillType2) + { + case pftPositive: e1Wc2 = e1->WindCnt2; break; + case pftNegative : e1Wc2 = -e1->WindCnt2; break; + default: e1Wc2 = Abs(e1->WindCnt2); + } + switch (e2FillType2) + { + case pftPositive: e2Wc2 = e2->WindCnt2; break; + case pftNegative: e2Wc2 = -e2->WindCnt2; break; + default: e2Wc2 = Abs(e2->WindCnt2); + } + + if (e1->PolyTyp != e2->PolyTyp) + { + AddLocalMinPoly(e1, e2, Pt); + } + else if (e1Wc == 1 && e2Wc == 1) + switch( m_ClipType ) { + case ctIntersection: + if (e1Wc2 > 0 && e2Wc2 > 0) + AddLocalMinPoly(e1, e2, Pt); + break; + case ctUnion: + if ( e1Wc2 <= 0 && e2Wc2 <= 0 ) + AddLocalMinPoly(e1, e2, Pt); + break; + case ctDifference: + if (((e1->PolyTyp == ptClip) && (e1Wc2 > 0) && (e2Wc2 > 0)) || + ((e1->PolyTyp == ptSubject) && (e1Wc2 <= 0) && (e2Wc2 <= 0))) + AddLocalMinPoly(e1, e2, Pt); + break; + case ctXor: + AddLocalMinPoly(e1, e2, Pt); + } + else + SwapSides( *e1, *e2 ); + } +} +//------------------------------------------------------------------------------ + +void Clipper::SetHoleState(TEdge *e, OutRec *outrec) +{ + TEdge *e2 = e->PrevInAEL; + TEdge *eTmp = 0; + while (e2) + { + if (e2->OutIdx >= 0 && e2->WindDelta != 0) + { + if (!eTmp) eTmp = e2; + else if (eTmp->OutIdx == e2->OutIdx) eTmp = 0; + } + e2 = e2->PrevInAEL; + } + if (!eTmp) + { + outrec->FirstLeft = 0; + outrec->IsHole = false; + } + else + { + outrec->FirstLeft = m_PolyOuts[eTmp->OutIdx]; + outrec->IsHole = !outrec->FirstLeft->IsHole; + } +} +//------------------------------------------------------------------------------ + +OutRec* GetLowermostRec(OutRec *outRec1, OutRec *outRec2) +{ + //work out which polygon fragment has the correct hole state ... + if (!outRec1->BottomPt) + outRec1->BottomPt = GetBottomPt(outRec1->Pts); + if (!outRec2->BottomPt) + outRec2->BottomPt = GetBottomPt(outRec2->Pts); + OutPt *OutPt1 = outRec1->BottomPt; + OutPt *OutPt2 = outRec2->BottomPt; + if (OutPt1->Pt.Y > OutPt2->Pt.Y) return outRec1; + else if (OutPt1->Pt.Y < OutPt2->Pt.Y) return outRec2; + else if (OutPt1->Pt.X < OutPt2->Pt.X) return outRec1; + else if (OutPt1->Pt.X > OutPt2->Pt.X) return outRec2; + else if (OutPt1->Next == OutPt1) return outRec2; + else if (OutPt2->Next == OutPt2) return outRec1; + else if (FirstIsBottomPt(OutPt1, OutPt2)) return outRec1; + else return outRec2; +} +//------------------------------------------------------------------------------ + +bool OutRec1RightOfOutRec2(OutRec* outRec1, OutRec* outRec2) +{ + do + { + outRec1 = outRec1->FirstLeft; + if (outRec1 == outRec2) return true; + } while (outRec1); + return false; +} +//------------------------------------------------------------------------------ + +OutRec* Clipper::GetOutRec(int Idx) +{ + OutRec* outrec = m_PolyOuts[Idx]; + while (outrec != m_PolyOuts[outrec->Idx]) + outrec = m_PolyOuts[outrec->Idx]; + return outrec; +} +//------------------------------------------------------------------------------ + +void Clipper::AppendPolygon(TEdge *e1, TEdge *e2) +{ + //get the start and ends of both output polygons ... + OutRec *outRec1 = m_PolyOuts[e1->OutIdx]; + OutRec *outRec2 = m_PolyOuts[e2->OutIdx]; + + OutRec *holeStateRec; + if (OutRec1RightOfOutRec2(outRec1, outRec2)) + holeStateRec = outRec2; + else if (OutRec1RightOfOutRec2(outRec2, outRec1)) + holeStateRec = outRec1; + else + holeStateRec = GetLowermostRec(outRec1, outRec2); + + //get the start and ends of both output polygons and + //join e2 poly onto e1 poly and delete pointers to e2 ... + + OutPt* p1_lft = outRec1->Pts; + OutPt* p1_rt = p1_lft->Prev; + OutPt* p2_lft = outRec2->Pts; + OutPt* p2_rt = p2_lft->Prev; + + //join e2 poly onto e1 poly and delete pointers to e2 ... + if( e1->Side == esLeft ) + { + if( e2->Side == esLeft ) + { + //z y x a b c + ReversePolyPtLinks(p2_lft); + p2_lft->Next = p1_lft; + p1_lft->Prev = p2_lft; + p1_rt->Next = p2_rt; + p2_rt->Prev = p1_rt; + outRec1->Pts = p2_rt; + } else + { + //x y z a b c + p2_rt->Next = p1_lft; + p1_lft->Prev = p2_rt; + p2_lft->Prev = p1_rt; + p1_rt->Next = p2_lft; + outRec1->Pts = p2_lft; + } + } else + { + if( e2->Side == esRight ) + { + //a b c z y x + ReversePolyPtLinks(p2_lft); + p1_rt->Next = p2_rt; + p2_rt->Prev = p1_rt; + p2_lft->Next = p1_lft; + p1_lft->Prev = p2_lft; + } else + { + //a b c x y z + p1_rt->Next = p2_lft; + p2_lft->Prev = p1_rt; + p1_lft->Prev = p2_rt; + p2_rt->Next = p1_lft; + } + } + + outRec1->BottomPt = 0; + if (holeStateRec == outRec2) + { + if (outRec2->FirstLeft != outRec1) + outRec1->FirstLeft = outRec2->FirstLeft; + outRec1->IsHole = outRec2->IsHole; + } + outRec2->Pts = 0; + outRec2->BottomPt = 0; + outRec2->FirstLeft = outRec1; + + int OKIdx = e1->OutIdx; + int ObsoleteIdx = e2->OutIdx; + + e1->OutIdx = Unassigned; //nb: safe because we only get here via AddLocalMaxPoly + e2->OutIdx = Unassigned; + + TEdge* e = m_ActiveEdges; + while( e ) + { + if( e->OutIdx == ObsoleteIdx ) + { + e->OutIdx = OKIdx; + e->Side = e1->Side; + break; + } + e = e->NextInAEL; + } + + outRec2->Idx = outRec1->Idx; +} +//------------------------------------------------------------------------------ + +OutPt* Clipper::AddOutPt(TEdge *e, const IntPoint &pt) +{ + if( e->OutIdx < 0 ) + { + OutRec *outRec = CreateOutRec(); + outRec->IsOpen = (e->WindDelta == 0); + OutPt* newOp = new OutPt; + outRec->Pts = newOp; + newOp->Idx = outRec->Idx; + newOp->Pt = pt; + newOp->Next = newOp; + newOp->Prev = newOp; + if (!outRec->IsOpen) + SetHoleState(e, outRec); + e->OutIdx = outRec->Idx; + return newOp; + } else + { + OutRec *outRec = m_PolyOuts[e->OutIdx]; + //OutRec.Pts is the 'Left-most' point & OutRec.Pts.Prev is the 'Right-most' + OutPt* op = outRec->Pts; + + bool ToFront = (e->Side == esLeft); + if (ToFront && (pt == op->Pt)) return op; + else if (!ToFront && (pt == op->Prev->Pt)) return op->Prev; + + OutPt* newOp = new OutPt; + newOp->Idx = outRec->Idx; + newOp->Pt = pt; + newOp->Next = op; + newOp->Prev = op->Prev; + newOp->Prev->Next = newOp; + op->Prev = newOp; + if (ToFront) outRec->Pts = newOp; + return newOp; + } +} +//------------------------------------------------------------------------------ + +OutPt* Clipper::GetLastOutPt(TEdge *e) +{ + OutRec *outRec = m_PolyOuts[e->OutIdx]; + if (e->Side == esLeft) + return outRec->Pts; + else + return outRec->Pts->Prev; +} +//------------------------------------------------------------------------------ + +void Clipper::ProcessHorizontals() +{ + TEdge* horzEdge; + while (PopEdgeFromSEL(horzEdge)) + ProcessHorizontal(horzEdge); +} +//------------------------------------------------------------------------------ + +inline bool IsMinima(TEdge *e) +{ + return e && (e->Prev->NextInLML != e) && (e->Next->NextInLML != e); +} +//------------------------------------------------------------------------------ + +inline bool IsMaxima(TEdge *e, const cInt Y) +{ + return e && e->Top.Y == Y && !e->NextInLML; +} +//------------------------------------------------------------------------------ + +inline bool IsIntermediate(TEdge *e, const cInt Y) +{ + return e->Top.Y == Y && e->NextInLML; +} +//------------------------------------------------------------------------------ + +TEdge *GetMaximaPair(TEdge *e) +{ + if ((e->Next->Top == e->Top) && !e->Next->NextInLML) + return e->Next; + else if ((e->Prev->Top == e->Top) && !e->Prev->NextInLML) + return e->Prev; + else return 0; +} +//------------------------------------------------------------------------------ + +TEdge *GetMaximaPairEx(TEdge *e) +{ + //as GetMaximaPair() but returns 0 if MaxPair isn't in AEL (unless it's horizontal) + TEdge* result = GetMaximaPair(e); + if (result && (result->OutIdx == Skip || + (result->NextInAEL == result->PrevInAEL && !IsHorizontal(*result)))) return 0; + return result; +} +//------------------------------------------------------------------------------ + +void Clipper::SwapPositionsInSEL(TEdge *Edge1, TEdge *Edge2) +{ + if( !( Edge1->NextInSEL ) && !( Edge1->PrevInSEL ) ) return; + if( !( Edge2->NextInSEL ) && !( Edge2->PrevInSEL ) ) return; + + if( Edge1->NextInSEL == Edge2 ) + { + TEdge* Next = Edge2->NextInSEL; + if( Next ) Next->PrevInSEL = Edge1; + TEdge* Prev = Edge1->PrevInSEL; + if( Prev ) Prev->NextInSEL = Edge2; + Edge2->PrevInSEL = Prev; + Edge2->NextInSEL = Edge1; + Edge1->PrevInSEL = Edge2; + Edge1->NextInSEL = Next; + } + else if( Edge2->NextInSEL == Edge1 ) + { + TEdge* Next = Edge1->NextInSEL; + if( Next ) Next->PrevInSEL = Edge2; + TEdge* Prev = Edge2->PrevInSEL; + if( Prev ) Prev->NextInSEL = Edge1; + Edge1->PrevInSEL = Prev; + Edge1->NextInSEL = Edge2; + Edge2->PrevInSEL = Edge1; + Edge2->NextInSEL = Next; + } + else + { + TEdge* Next = Edge1->NextInSEL; + TEdge* Prev = Edge1->PrevInSEL; + Edge1->NextInSEL = Edge2->NextInSEL; + if( Edge1->NextInSEL ) Edge1->NextInSEL->PrevInSEL = Edge1; + Edge1->PrevInSEL = Edge2->PrevInSEL; + if( Edge1->PrevInSEL ) Edge1->PrevInSEL->NextInSEL = Edge1; + Edge2->NextInSEL = Next; + if( Edge2->NextInSEL ) Edge2->NextInSEL->PrevInSEL = Edge2; + Edge2->PrevInSEL = Prev; + if( Edge2->PrevInSEL ) Edge2->PrevInSEL->NextInSEL = Edge2; + } + + if( !Edge1->PrevInSEL ) m_SortedEdges = Edge1; + else if( !Edge2->PrevInSEL ) m_SortedEdges = Edge2; +} +//------------------------------------------------------------------------------ + +TEdge* GetNextInAEL(TEdge *e, Direction dir) +{ + return dir == dLeftToRight ? e->NextInAEL : e->PrevInAEL; +} +//------------------------------------------------------------------------------ + +void GetHorzDirection(TEdge& HorzEdge, Direction& Dir, cInt& Left, cInt& Right) +{ + if (HorzEdge.Bot.X < HorzEdge.Top.X) + { + Left = HorzEdge.Bot.X; + Right = HorzEdge.Top.X; + Dir = dLeftToRight; + } else + { + Left = HorzEdge.Top.X; + Right = HorzEdge.Bot.X; + Dir = dRightToLeft; + } +} +//------------------------------------------------------------------------ + +/******************************************************************************* +* Notes: Horizontal edges (HEs) at scanline intersections (ie at the Top or * +* Bottom of a scanbeam) are processed as if layered. The order in which HEs * +* are processed doesn't matter. HEs intersect with other HE Bot.Xs only [#] * +* (or they could intersect with Top.Xs only, ie EITHER Bot.Xs OR Top.Xs), * +* and with other non-horizontal edges [*]. Once these intersections are * +* processed, intermediate HEs then 'promote' the Edge above (NextInLML) into * +* the AEL. These 'promoted' edges may in turn intersect [%] with other HEs. * +*******************************************************************************/ + +void Clipper::ProcessHorizontal(TEdge *horzEdge) +{ + Direction dir; + cInt horzLeft, horzRight; + bool IsOpen = (horzEdge->WindDelta == 0); + + GetHorzDirection(*horzEdge, dir, horzLeft, horzRight); + + TEdge* eLastHorz = horzEdge, *eMaxPair = 0; + while (eLastHorz->NextInLML && IsHorizontal(*eLastHorz->NextInLML)) + eLastHorz = eLastHorz->NextInLML; + if (!eLastHorz->NextInLML) + eMaxPair = GetMaximaPair(eLastHorz); + + MaximaList::const_iterator maxIt; + MaximaList::const_reverse_iterator maxRit; + if (m_Maxima.size() > 0) + { + //get the first maxima in range (X) ... + if (dir == dLeftToRight) + { + maxIt = m_Maxima.begin(); + while (maxIt != m_Maxima.end() && *maxIt <= horzEdge->Bot.X) maxIt++; + if (maxIt != m_Maxima.end() && *maxIt >= eLastHorz->Top.X) + maxIt = m_Maxima.end(); + } + else + { + maxRit = m_Maxima.rbegin(); + while (maxRit != m_Maxima.rend() && *maxRit > horzEdge->Bot.X) maxRit++; + if (maxRit != m_Maxima.rend() && *maxRit <= eLastHorz->Top.X) + maxRit = m_Maxima.rend(); + } + } + + OutPt* op1 = 0; + + for (;;) //loop through consec. horizontal edges + { + + bool IsLastHorz = (horzEdge == eLastHorz); + TEdge* e = GetNextInAEL(horzEdge, dir); + while(e) + { + + //this code block inserts extra coords into horizontal edges (in output + //polygons) whereever maxima touch these horizontal edges. This helps + //'simplifying' polygons (ie if the Simplify property is set). + if (m_Maxima.size() > 0) + { + if (dir == dLeftToRight) + { + while (maxIt != m_Maxima.end() && *maxIt < e->Curr.X) + { + if (horzEdge->OutIdx >= 0 && !IsOpen) + AddOutPt(horzEdge, IntPoint(*maxIt, horzEdge->Bot.Y)); + maxIt++; + } + } + else + { + while (maxRit != m_Maxima.rend() && *maxRit > e->Curr.X) + { + if (horzEdge->OutIdx >= 0 && !IsOpen) + AddOutPt(horzEdge, IntPoint(*maxRit, horzEdge->Bot.Y)); + maxRit++; + } + } + }; + + if ((dir == dLeftToRight && e->Curr.X > horzRight) || + (dir == dRightToLeft && e->Curr.X < horzLeft)) break; + + //Also break if we've got to the end of an intermediate horizontal edge ... + //nb: Smaller Dx's are to the right of larger Dx's ABOVE the horizontal. + if (e->Curr.X == horzEdge->Top.X && horzEdge->NextInLML && + e->Dx < horzEdge->NextInLML->Dx) break; + + if (horzEdge->OutIdx >= 0 && !IsOpen) //note: may be done multiple times + { + op1 = AddOutPt(horzEdge, e->Curr); + TEdge* eNextHorz = m_SortedEdges; + while (eNextHorz) + { + if (eNextHorz->OutIdx >= 0 && + HorzSegmentsOverlap(horzEdge->Bot.X, + horzEdge->Top.X, eNextHorz->Bot.X, eNextHorz->Top.X)) + { + OutPt* op2 = GetLastOutPt(eNextHorz); + AddJoin(op2, op1, eNextHorz->Top); + } + eNextHorz = eNextHorz->NextInSEL; + } + AddGhostJoin(op1, horzEdge->Bot); + } + + //OK, so far we're still in range of the horizontal Edge but make sure + //we're at the last of consec. horizontals when matching with eMaxPair + if(e == eMaxPair && IsLastHorz) + { + if (horzEdge->OutIdx >= 0) + AddLocalMaxPoly(horzEdge, eMaxPair, horzEdge->Top); + DeleteFromAEL(horzEdge); + DeleteFromAEL(eMaxPair); + return; + } + + if(dir == dLeftToRight) + { + IntPoint Pt = IntPoint(e->Curr.X, horzEdge->Curr.Y); + IntersectEdges(horzEdge, e, Pt); + } + else + { + IntPoint Pt = IntPoint(e->Curr.X, horzEdge->Curr.Y); + IntersectEdges( e, horzEdge, Pt); + } + TEdge* eNext = GetNextInAEL(e, dir); + SwapPositionsInAEL( horzEdge, e ); + e = eNext; + } //end while(e) + + //Break out of loop if HorzEdge.NextInLML is not also horizontal ... + if (!horzEdge->NextInLML || !IsHorizontal(*horzEdge->NextInLML)) break; + + UpdateEdgeIntoAEL(horzEdge); + if (horzEdge->OutIdx >= 0) AddOutPt(horzEdge, horzEdge->Bot); + GetHorzDirection(*horzEdge, dir, horzLeft, horzRight); + + } //end for (;;) + + if (horzEdge->OutIdx >= 0 && !op1) + { + op1 = GetLastOutPt(horzEdge); + TEdge* eNextHorz = m_SortedEdges; + while (eNextHorz) + { + if (eNextHorz->OutIdx >= 0 && + HorzSegmentsOverlap(horzEdge->Bot.X, + horzEdge->Top.X, eNextHorz->Bot.X, eNextHorz->Top.X)) + { + OutPt* op2 = GetLastOutPt(eNextHorz); + AddJoin(op2, op1, eNextHorz->Top); + } + eNextHorz = eNextHorz->NextInSEL; + } + AddGhostJoin(op1, horzEdge->Top); + } + + if (horzEdge->NextInLML) + { + if(horzEdge->OutIdx >= 0) + { + op1 = AddOutPt( horzEdge, horzEdge->Top); + UpdateEdgeIntoAEL(horzEdge); + if (horzEdge->WindDelta == 0) return; + //nb: HorzEdge is no longer horizontal here + TEdge* ePrev = horzEdge->PrevInAEL; + TEdge* eNext = horzEdge->NextInAEL; + if (ePrev && ePrev->Curr.X == horzEdge->Bot.X && + ePrev->Curr.Y == horzEdge->Bot.Y && ePrev->WindDelta != 0 && + (ePrev->OutIdx >= 0 && ePrev->Curr.Y > ePrev->Top.Y && + SlopesEqual(*horzEdge, *ePrev, m_UseFullRange))) + { + OutPt* op2 = AddOutPt(ePrev, horzEdge->Bot); + AddJoin(op1, op2, horzEdge->Top); + } + else if (eNext && eNext->Curr.X == horzEdge->Bot.X && + eNext->Curr.Y == horzEdge->Bot.Y && eNext->WindDelta != 0 && + eNext->OutIdx >= 0 && eNext->Curr.Y > eNext->Top.Y && + SlopesEqual(*horzEdge, *eNext, m_UseFullRange)) + { + OutPt* op2 = AddOutPt(eNext, horzEdge->Bot); + AddJoin(op1, op2, horzEdge->Top); + } + } + else + UpdateEdgeIntoAEL(horzEdge); + } + else + { + if (horzEdge->OutIdx >= 0) AddOutPt(horzEdge, horzEdge->Top); + DeleteFromAEL(horzEdge); + } +} +//------------------------------------------------------------------------------ + +bool Clipper::ProcessIntersections(const cInt topY) +{ + if( !m_ActiveEdges ) return true; + try { + BuildIntersectList(topY); + size_t IlSize = m_IntersectList.size(); + if (IlSize == 0) return true; + if (IlSize == 1 || FixupIntersectionOrder()) ProcessIntersectList(); + else return false; + } + catch(...) + { + m_SortedEdges = 0; + DisposeIntersectNodes(); + throw clipperException("ProcessIntersections error"); + } + m_SortedEdges = 0; + return true; +} +//------------------------------------------------------------------------------ + +void Clipper::DisposeIntersectNodes() +{ + for (size_t i = 0; i < m_IntersectList.size(); ++i ) + delete m_IntersectList[i]; + m_IntersectList.clear(); +} +//------------------------------------------------------------------------------ + +void Clipper::BuildIntersectList(const cInt topY) +{ + if ( !m_ActiveEdges ) return; + + //prepare for sorting ... + TEdge* e = m_ActiveEdges; + m_SortedEdges = e; + while( e ) + { + e->PrevInSEL = e->PrevInAEL; + e->NextInSEL = e->NextInAEL; + e->Curr.X = TopX( *e, topY ); + e = e->NextInAEL; + } + + //bubblesort ... + bool isModified; + do + { + isModified = false; + e = m_SortedEdges; + while( e->NextInSEL ) + { + TEdge *eNext = e->NextInSEL; + IntPoint Pt; + if(e->Curr.X > eNext->Curr.X) + { + IntersectPoint(*e, *eNext, Pt); + if (Pt.Y < topY) Pt = IntPoint(TopX(*e, topY), topY); + IntersectNode * newNode = new IntersectNode; + newNode->Edge1 = e; + newNode->Edge2 = eNext; + newNode->Pt = Pt; + m_IntersectList.push_back(newNode); + + SwapPositionsInSEL(e, eNext); + isModified = true; + } + else + e = eNext; + } + if( e->PrevInSEL ) e->PrevInSEL->NextInSEL = 0; + else break; + } + while ( isModified ); + m_SortedEdges = 0; //important +} +//------------------------------------------------------------------------------ + + +void Clipper::ProcessIntersectList() +{ + for (size_t i = 0; i < m_IntersectList.size(); ++i) + { + IntersectNode* iNode = m_IntersectList[i]; + { + IntersectEdges( iNode->Edge1, iNode->Edge2, iNode->Pt); + SwapPositionsInAEL( iNode->Edge1 , iNode->Edge2 ); + } + delete iNode; + } + m_IntersectList.clear(); +} +//------------------------------------------------------------------------------ + +bool IntersectListSort(IntersectNode* node1, IntersectNode* node2) +{ + return node2->Pt.Y < node1->Pt.Y; +} +//------------------------------------------------------------------------------ + +inline bool EdgesAdjacent(const IntersectNode &inode) +{ + return (inode.Edge1->NextInSEL == inode.Edge2) || + (inode.Edge1->PrevInSEL == inode.Edge2); +} +//------------------------------------------------------------------------------ + +bool Clipper::FixupIntersectionOrder() +{ + //pre-condition: intersections are sorted Bottom-most first. + //Now it's crucial that intersections are made only between adjacent edges, + //so to ensure this the order of intersections may need adjusting ... + CopyAELToSEL(); + std::sort(m_IntersectList.begin(), m_IntersectList.end(), IntersectListSort); + size_t cnt = m_IntersectList.size(); + for (size_t i = 0; i < cnt; ++i) + { + if (!EdgesAdjacent(*m_IntersectList[i])) + { + size_t j = i + 1; + while (j < cnt && !EdgesAdjacent(*m_IntersectList[j])) j++; + if (j == cnt) return false; + std::swap(m_IntersectList[i], m_IntersectList[j]); + } + SwapPositionsInSEL(m_IntersectList[i]->Edge1, m_IntersectList[i]->Edge2); + } + return true; +} +//------------------------------------------------------------------------------ + +void Clipper::DoMaxima(TEdge *e) +{ + TEdge* eMaxPair = GetMaximaPairEx(e); + if (!eMaxPair) + { + if (e->OutIdx >= 0) + AddOutPt(e, e->Top); + DeleteFromAEL(e); + return; + } + + TEdge* eNext = e->NextInAEL; + while(eNext && eNext != eMaxPair) + { + IntersectEdges(e, eNext, e->Top); + SwapPositionsInAEL(e, eNext); + eNext = e->NextInAEL; + } + + if(e->OutIdx == Unassigned && eMaxPair->OutIdx == Unassigned) + { + DeleteFromAEL(e); + DeleteFromAEL(eMaxPair); + } + else if( e->OutIdx >= 0 && eMaxPair->OutIdx >= 0 ) + { + if (e->OutIdx >= 0) AddLocalMaxPoly(e, eMaxPair, e->Top); + DeleteFromAEL(e); + DeleteFromAEL(eMaxPair); + } +#ifdef use_lines + else if (e->WindDelta == 0) + { + if (e->OutIdx >= 0) + { + AddOutPt(e, e->Top); + e->OutIdx = Unassigned; + } + DeleteFromAEL(e); + + if (eMaxPair->OutIdx >= 0) + { + AddOutPt(eMaxPair, e->Top); + eMaxPair->OutIdx = Unassigned; + } + DeleteFromAEL(eMaxPair); + } +#endif + else throw clipperException("DoMaxima error"); +} +//------------------------------------------------------------------------------ + +void Clipper::ProcessEdgesAtTopOfScanbeam(const cInt topY) +{ + TEdge* e = m_ActiveEdges; + while( e ) + { + //1. process maxima, treating them as if they're 'bent' horizontal edges, + // but exclude maxima with horizontal edges. nb: e can't be a horizontal. + bool IsMaximaEdge = IsMaxima(e, topY); + + if(IsMaximaEdge) + { + TEdge* eMaxPair = GetMaximaPairEx(e); + IsMaximaEdge = (!eMaxPair || !IsHorizontal(*eMaxPair)); + } + + if(IsMaximaEdge) + { + if (m_StrictSimple) m_Maxima.push_back(e->Top.X); + TEdge* ePrev = e->PrevInAEL; + DoMaxima(e); + if( !ePrev ) e = m_ActiveEdges; + else e = ePrev->NextInAEL; + } + else + { + //2. promote horizontal edges, otherwise update Curr.X and Curr.Y ... + if (IsIntermediate(e, topY) && IsHorizontal(*e->NextInLML)) + { + UpdateEdgeIntoAEL(e); + if (e->OutIdx >= 0) + AddOutPt(e, e->Bot); + AddEdgeToSEL(e); + } + else + { + e->Curr.X = TopX( *e, topY ); + e->Curr.Y = topY; + } + + //When StrictlySimple and 'e' is being touched by another edge, then + //make sure both edges have a vertex here ... + if (m_StrictSimple) + { + TEdge* ePrev = e->PrevInAEL; + if ((e->OutIdx >= 0) && (e->WindDelta != 0) && ePrev && (ePrev->OutIdx >= 0) && + (ePrev->Curr.X == e->Curr.X) && (ePrev->WindDelta != 0)) + { + IntPoint pt = e->Curr; +#ifdef use_xyz + SetZ(pt, *ePrev, *e); +#endif + OutPt* op = AddOutPt(ePrev, pt); + OutPt* op2 = AddOutPt(e, pt); + AddJoin(op, op2, pt); //StrictlySimple (type-3) join + } + } + + e = e->NextInAEL; + } + } + + //3. Process horizontals at the Top of the scanbeam ... + m_Maxima.sort(); + ProcessHorizontals(); + m_Maxima.clear(); + + //4. Promote intermediate vertices ... + e = m_ActiveEdges; + while(e) + { + if(IsIntermediate(e, topY)) + { + OutPt* op = 0; + if( e->OutIdx >= 0 ) + op = AddOutPt(e, e->Top); + UpdateEdgeIntoAEL(e); + + //if output polygons share an edge, they'll need joining later ... + TEdge* ePrev = e->PrevInAEL; + TEdge* eNext = e->NextInAEL; + if (ePrev && ePrev->Curr.X == e->Bot.X && + ePrev->Curr.Y == e->Bot.Y && op && + ePrev->OutIdx >= 0 && ePrev->Curr.Y > ePrev->Top.Y && + SlopesEqual(e->Curr, e->Top, ePrev->Curr, ePrev->Top, m_UseFullRange) && + (e->WindDelta != 0) && (ePrev->WindDelta != 0)) + { + OutPt* op2 = AddOutPt(ePrev, e->Bot); + AddJoin(op, op2, e->Top); + } + else if (eNext && eNext->Curr.X == e->Bot.X && + eNext->Curr.Y == e->Bot.Y && op && + eNext->OutIdx >= 0 && eNext->Curr.Y > eNext->Top.Y && + SlopesEqual(e->Curr, e->Top, eNext->Curr, eNext->Top, m_UseFullRange) && + (e->WindDelta != 0) && (eNext->WindDelta != 0)) + { + OutPt* op2 = AddOutPt(eNext, e->Bot); + AddJoin(op, op2, e->Top); + } + } + e = e->NextInAEL; + } +} +//------------------------------------------------------------------------------ + +void Clipper::FixupOutPolyline(OutRec &outrec) +{ + OutPt *pp = outrec.Pts; + OutPt *lastPP = pp->Prev; + while (pp != lastPP) + { + pp = pp->Next; + if (pp->Pt == pp->Prev->Pt) + { + if (pp == lastPP) lastPP = pp->Prev; + OutPt *tmpPP = pp->Prev; + tmpPP->Next = pp->Next; + pp->Next->Prev = tmpPP; + delete pp; + pp = tmpPP; + } + } + + if (pp == pp->Prev) + { + DisposeOutPts(pp); + outrec.Pts = 0; + return; + } +} +//------------------------------------------------------------------------------ + +void Clipper::FixupOutPolygon(OutRec &outrec) +{ + //FixupOutPolygon() - removes duplicate points and simplifies consecutive + //parallel edges by removing the middle vertex. + OutPt *lastOK = 0; + outrec.BottomPt = 0; + OutPt *pp = outrec.Pts; + bool preserveCol = m_PreserveCollinear || m_StrictSimple; + + for (;;) + { + if (pp->Prev == pp || pp->Prev == pp->Next) + { + DisposeOutPts(pp); + outrec.Pts = 0; + return; + } + + //test for duplicate points and collinear edges ... + if ((pp->Pt == pp->Next->Pt) || (pp->Pt == pp->Prev->Pt) || + (SlopesEqual(pp->Prev->Pt, pp->Pt, pp->Next->Pt, m_UseFullRange) && + (!preserveCol || !Pt2IsBetweenPt1AndPt3(pp->Prev->Pt, pp->Pt, pp->Next->Pt)))) + { + lastOK = 0; + OutPt *tmp = pp; + pp->Prev->Next = pp->Next; + pp->Next->Prev = pp->Prev; + pp = pp->Prev; + delete tmp; + } + else if (pp == lastOK) break; + else + { + if (!lastOK) lastOK = pp; + pp = pp->Next; + } + } + outrec.Pts = pp; +} +//------------------------------------------------------------------------------ + +int PointCount(OutPt *Pts) +{ + if (!Pts) return 0; + int result = 0; + OutPt* p = Pts; + do + { + result++; + p = p->Next; + } + while (p != Pts); + return result; +} +//------------------------------------------------------------------------------ + +void Clipper::BuildResult(Paths &polys) +{ + polys.reserve(m_PolyOuts.size()); + for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) + { + if (!m_PolyOuts[i]->Pts) continue; + Path pg; + OutPt* p = m_PolyOuts[i]->Pts->Prev; + int cnt = PointCount(p); + if (cnt < 2) continue; + pg.reserve(cnt); + for (int i = 0; i < cnt; ++i) + { + pg.push_back(p->Pt); + p = p->Prev; + } + polys.push_back(pg); + } +} +//------------------------------------------------------------------------------ + +void Clipper::BuildResult2(PolyTree& polytree) +{ + polytree.Clear(); + polytree.AllNodes.reserve(m_PolyOuts.size()); + //add each output polygon/contour to polytree ... + for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); i++) + { + OutRec* outRec = m_PolyOuts[i]; + int cnt = PointCount(outRec->Pts); + if ((outRec->IsOpen && cnt < 2) || (!outRec->IsOpen && cnt < 3)) continue; + FixHoleLinkage(*outRec); + PolyNode* pn = new PolyNode(); + //nb: polytree takes ownership of all the PolyNodes + polytree.AllNodes.push_back(pn); + outRec->PolyNd = pn; + pn->Parent = 0; + pn->Index = 0; + pn->Contour.reserve(cnt); + OutPt *op = outRec->Pts->Prev; + for (int j = 0; j < cnt; j++) + { + pn->Contour.push_back(op->Pt); + op = op->Prev; + } + } + + //fixup PolyNode links etc ... + polytree.Childs.reserve(m_PolyOuts.size()); + for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); i++) + { + OutRec* outRec = m_PolyOuts[i]; + if (!outRec->PolyNd) continue; + if (outRec->IsOpen) + { + outRec->PolyNd->m_IsOpen = true; + polytree.AddChild(*outRec->PolyNd); + } + else if (outRec->FirstLeft && outRec->FirstLeft->PolyNd) + outRec->FirstLeft->PolyNd->AddChild(*outRec->PolyNd); + else + polytree.AddChild(*outRec->PolyNd); + } +} +//------------------------------------------------------------------------------ + +void SwapIntersectNodes(IntersectNode &int1, IntersectNode &int2) +{ + //just swap the contents (because fIntersectNodes is a single-linked-list) + IntersectNode inode = int1; //gets a copy of Int1 + int1.Edge1 = int2.Edge1; + int1.Edge2 = int2.Edge2; + int1.Pt = int2.Pt; + int2.Edge1 = inode.Edge1; + int2.Edge2 = inode.Edge2; + int2.Pt = inode.Pt; +} +//------------------------------------------------------------------------------ + +inline bool E2InsertsBeforeE1(TEdge &e1, TEdge &e2) +{ + if (e2.Curr.X == e1.Curr.X) + { + if (e2.Top.Y > e1.Top.Y) + return e2.Top.X < TopX(e1, e2.Top.Y); + else return e1.Top.X > TopX(e2, e1.Top.Y); + } + else return e2.Curr.X < e1.Curr.X; +} +//------------------------------------------------------------------------------ + +bool GetOverlap(const cInt a1, const cInt a2, const cInt b1, const cInt b2, + cInt& Left, cInt& Right) +{ + if (a1 < a2) + { + if (b1 < b2) {Left = std::max(a1,b1); Right = std::min(a2,b2);} + else {Left = std::max(a1,b2); Right = std::min(a2,b1);} + } + else + { + if (b1 < b2) {Left = std::max(a2,b1); Right = std::min(a1,b2);} + else {Left = std::max(a2,b2); Right = std::min(a1,b1);} + } + return Left < Right; +} +//------------------------------------------------------------------------------ + +inline void UpdateOutPtIdxs(OutRec& outrec) +{ + OutPt* op = outrec.Pts; + do + { + op->Idx = outrec.Idx; + op = op->Prev; + } + while(op != outrec.Pts); +} +//------------------------------------------------------------------------------ + +void Clipper::InsertEdgeIntoAEL(TEdge *edge, TEdge* startEdge) +{ + if(!m_ActiveEdges) + { + edge->PrevInAEL = 0; + edge->NextInAEL = 0; + m_ActiveEdges = edge; + } + else if(!startEdge && E2InsertsBeforeE1(*m_ActiveEdges, *edge)) + { + edge->PrevInAEL = 0; + edge->NextInAEL = m_ActiveEdges; + m_ActiveEdges->PrevInAEL = edge; + m_ActiveEdges = edge; + } + else + { + if(!startEdge) startEdge = m_ActiveEdges; + while(startEdge->NextInAEL && + !E2InsertsBeforeE1(*startEdge->NextInAEL , *edge)) + startEdge = startEdge->NextInAEL; + edge->NextInAEL = startEdge->NextInAEL; + if(startEdge->NextInAEL) startEdge->NextInAEL->PrevInAEL = edge; + edge->PrevInAEL = startEdge; + startEdge->NextInAEL = edge; + } +} +//---------------------------------------------------------------------- + +OutPt* DupOutPt(OutPt* outPt, bool InsertAfter) +{ + OutPt* result = new OutPt; + result->Pt = outPt->Pt; + result->Idx = outPt->Idx; + if (InsertAfter) + { + result->Next = outPt->Next; + result->Prev = outPt; + outPt->Next->Prev = result; + outPt->Next = result; + } + else + { + result->Prev = outPt->Prev; + result->Next = outPt; + outPt->Prev->Next = result; + outPt->Prev = result; + } + return result; +} +//------------------------------------------------------------------------------ + +bool JoinHorz(OutPt* op1, OutPt* op1b, OutPt* op2, OutPt* op2b, + const IntPoint Pt, bool DiscardLeft) +{ + Direction Dir1 = (op1->Pt.X > op1b->Pt.X ? dRightToLeft : dLeftToRight); + Direction Dir2 = (op2->Pt.X > op2b->Pt.X ? dRightToLeft : dLeftToRight); + if (Dir1 == Dir2) return false; + + //When DiscardLeft, we want Op1b to be on the Left of Op1, otherwise we + //want Op1b to be on the Right. (And likewise with Op2 and Op2b.) + //So, to facilitate this while inserting Op1b and Op2b ... + //when DiscardLeft, make sure we're AT or RIGHT of Pt before adding Op1b, + //otherwise make sure we're AT or LEFT of Pt. (Likewise with Op2b.) + if (Dir1 == dLeftToRight) + { + while (op1->Next->Pt.X <= Pt.X && + op1->Next->Pt.X >= op1->Pt.X && op1->Next->Pt.Y == Pt.Y) + op1 = op1->Next; + if (DiscardLeft && (op1->Pt.X != Pt.X)) op1 = op1->Next; + op1b = DupOutPt(op1, !DiscardLeft); + if (op1b->Pt != Pt) + { + op1 = op1b; + op1->Pt = Pt; + op1b = DupOutPt(op1, !DiscardLeft); + } + } + else + { + while (op1->Next->Pt.X >= Pt.X && + op1->Next->Pt.X <= op1->Pt.X && op1->Next->Pt.Y == Pt.Y) + op1 = op1->Next; + if (!DiscardLeft && (op1->Pt.X != Pt.X)) op1 = op1->Next; + op1b = DupOutPt(op1, DiscardLeft); + if (op1b->Pt != Pt) + { + op1 = op1b; + op1->Pt = Pt; + op1b = DupOutPt(op1, DiscardLeft); + } + } + + if (Dir2 == dLeftToRight) + { + while (op2->Next->Pt.X <= Pt.X && + op2->Next->Pt.X >= op2->Pt.X && op2->Next->Pt.Y == Pt.Y) + op2 = op2->Next; + if (DiscardLeft && (op2->Pt.X != Pt.X)) op2 = op2->Next; + op2b = DupOutPt(op2, !DiscardLeft); + if (op2b->Pt != Pt) + { + op2 = op2b; + op2->Pt = Pt; + op2b = DupOutPt(op2, !DiscardLeft); + }; + } else + { + while (op2->Next->Pt.X >= Pt.X && + op2->Next->Pt.X <= op2->Pt.X && op2->Next->Pt.Y == Pt.Y) + op2 = op2->Next; + if (!DiscardLeft && (op2->Pt.X != Pt.X)) op2 = op2->Next; + op2b = DupOutPt(op2, DiscardLeft); + if (op2b->Pt != Pt) + { + op2 = op2b; + op2->Pt = Pt; + op2b = DupOutPt(op2, DiscardLeft); + }; + }; + + if ((Dir1 == dLeftToRight) == DiscardLeft) + { + op1->Prev = op2; + op2->Next = op1; + op1b->Next = op2b; + op2b->Prev = op1b; + } + else + { + op1->Next = op2; + op2->Prev = op1; + op1b->Prev = op2b; + op2b->Next = op1b; + } + return true; +} +//------------------------------------------------------------------------------ + +bool Clipper::JoinPoints(Join *j, OutRec* outRec1, OutRec* outRec2) +{ + OutPt *op1 = j->OutPt1, *op1b; + OutPt *op2 = j->OutPt2, *op2b; + + //There are 3 kinds of joins for output polygons ... + //1. Horizontal joins where Join.OutPt1 & Join.OutPt2 are vertices anywhere + //along (horizontal) collinear edges (& Join.OffPt is on the same horizontal). + //2. Non-horizontal joins where Join.OutPt1 & Join.OutPt2 are at the same + //location at the Bottom of the overlapping segment (& Join.OffPt is above). + //3. StrictSimple joins where edges touch but are not collinear and where + //Join.OutPt1, Join.OutPt2 & Join.OffPt all share the same point. + bool isHorizontal = (j->OutPt1->Pt.Y == j->OffPt.Y); + + if (isHorizontal && (j->OffPt == j->OutPt1->Pt) && + (j->OffPt == j->OutPt2->Pt)) + { + //Strictly Simple join ... + if (outRec1 != outRec2) return false; + op1b = j->OutPt1->Next; + while (op1b != op1 && (op1b->Pt == j->OffPt)) + op1b = op1b->Next; + bool reverse1 = (op1b->Pt.Y > j->OffPt.Y); + op2b = j->OutPt2->Next; + while (op2b != op2 && (op2b->Pt == j->OffPt)) + op2b = op2b->Next; + bool reverse2 = (op2b->Pt.Y > j->OffPt.Y); + if (reverse1 == reverse2) return false; + if (reverse1) + { + op1b = DupOutPt(op1, false); + op2b = DupOutPt(op2, true); + op1->Prev = op2; + op2->Next = op1; + op1b->Next = op2b; + op2b->Prev = op1b; + j->OutPt1 = op1; + j->OutPt2 = op1b; + return true; + } else + { + op1b = DupOutPt(op1, true); + op2b = DupOutPt(op2, false); + op1->Next = op2; + op2->Prev = op1; + op1b->Prev = op2b; + op2b->Next = op1b; + j->OutPt1 = op1; + j->OutPt2 = op1b; + return true; + } + } + else if (isHorizontal) + { + //treat horizontal joins differently to non-horizontal joins since with + //them we're not yet sure where the overlapping is. OutPt1.Pt & OutPt2.Pt + //may be anywhere along the horizontal edge. + op1b = op1; + while (op1->Prev->Pt.Y == op1->Pt.Y && op1->Prev != op1b && op1->Prev != op2) + op1 = op1->Prev; + while (op1b->Next->Pt.Y == op1b->Pt.Y && op1b->Next != op1 && op1b->Next != op2) + op1b = op1b->Next; + if (op1b->Next == op1 || op1b->Next == op2) return false; //a flat 'polygon' + + op2b = op2; + while (op2->Prev->Pt.Y == op2->Pt.Y && op2->Prev != op2b && op2->Prev != op1b) + op2 = op2->Prev; + while (op2b->Next->Pt.Y == op2b->Pt.Y && op2b->Next != op2 && op2b->Next != op1) + op2b = op2b->Next; + if (op2b->Next == op2 || op2b->Next == op1) return false; //a flat 'polygon' + + cInt Left, Right; + //Op1 --> Op1b & Op2 --> Op2b are the extremites of the horizontal edges + if (!GetOverlap(op1->Pt.X, op1b->Pt.X, op2->Pt.X, op2b->Pt.X, Left, Right)) + return false; + + //DiscardLeftSide: when overlapping edges are joined, a spike will created + //which needs to be cleaned up. However, we don't want Op1 or Op2 caught up + //on the discard Side as either may still be needed for other joins ... + IntPoint Pt; + bool DiscardLeftSide; + if (op1->Pt.X >= Left && op1->Pt.X <= Right) + { + Pt = op1->Pt; DiscardLeftSide = (op1->Pt.X > op1b->Pt.X); + } + else if (op2->Pt.X >= Left&& op2->Pt.X <= Right) + { + Pt = op2->Pt; DiscardLeftSide = (op2->Pt.X > op2b->Pt.X); + } + else if (op1b->Pt.X >= Left && op1b->Pt.X <= Right) + { + Pt = op1b->Pt; DiscardLeftSide = op1b->Pt.X > op1->Pt.X; + } + else + { + Pt = op2b->Pt; DiscardLeftSide = (op2b->Pt.X > op2->Pt.X); + } + j->OutPt1 = op1; j->OutPt2 = op2; + return JoinHorz(op1, op1b, op2, op2b, Pt, DiscardLeftSide); + } else + { + //nb: For non-horizontal joins ... + // 1. Jr.OutPt1.Pt.Y == Jr.OutPt2.Pt.Y + // 2. Jr.OutPt1.Pt > Jr.OffPt.Y + + //make sure the polygons are correctly oriented ... + op1b = op1->Next; + while ((op1b->Pt == op1->Pt) && (op1b != op1)) op1b = op1b->Next; + bool Reverse1 = ((op1b->Pt.Y > op1->Pt.Y) || + !SlopesEqual(op1->Pt, op1b->Pt, j->OffPt, m_UseFullRange)); + if (Reverse1) + { + op1b = op1->Prev; + while ((op1b->Pt == op1->Pt) && (op1b != op1)) op1b = op1b->Prev; + if ((op1b->Pt.Y > op1->Pt.Y) || + !SlopesEqual(op1->Pt, op1b->Pt, j->OffPt, m_UseFullRange)) return false; + }; + op2b = op2->Next; + while ((op2b->Pt == op2->Pt) && (op2b != op2))op2b = op2b->Next; + bool Reverse2 = ((op2b->Pt.Y > op2->Pt.Y) || + !SlopesEqual(op2->Pt, op2b->Pt, j->OffPt, m_UseFullRange)); + if (Reverse2) + { + op2b = op2->Prev; + while ((op2b->Pt == op2->Pt) && (op2b != op2)) op2b = op2b->Prev; + if ((op2b->Pt.Y > op2->Pt.Y) || + !SlopesEqual(op2->Pt, op2b->Pt, j->OffPt, m_UseFullRange)) return false; + } + + if ((op1b == op1) || (op2b == op2) || (op1b == op2b) || + ((outRec1 == outRec2) && (Reverse1 == Reverse2))) return false; + + if (Reverse1) + { + op1b = DupOutPt(op1, false); + op2b = DupOutPt(op2, true); + op1->Prev = op2; + op2->Next = op1; + op1b->Next = op2b; + op2b->Prev = op1b; + j->OutPt1 = op1; + j->OutPt2 = op1b; + return true; + } else + { + op1b = DupOutPt(op1, true); + op2b = DupOutPt(op2, false); + op1->Next = op2; + op2->Prev = op1; + op1b->Prev = op2b; + op2b->Next = op1b; + j->OutPt1 = op1; + j->OutPt2 = op1b; + return true; + } + } +} +//---------------------------------------------------------------------- + +static OutRec* ParseFirstLeft(OutRec* FirstLeft) +{ + while (FirstLeft && !FirstLeft->Pts) + FirstLeft = FirstLeft->FirstLeft; + return FirstLeft; +} +//------------------------------------------------------------------------------ + +void Clipper::FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec) +{ + //tests if NewOutRec contains the polygon before reassigning FirstLeft + for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) + { + OutRec* outRec = m_PolyOuts[i]; + OutRec* firstLeft = ParseFirstLeft(outRec->FirstLeft); + if (outRec->Pts && firstLeft == OldOutRec) + { + if (Poly2ContainsPoly1(outRec->Pts, NewOutRec->Pts)) + outRec->FirstLeft = NewOutRec; + } + } +} +//---------------------------------------------------------------------- + +void Clipper::FixupFirstLefts2(OutRec* InnerOutRec, OutRec* OuterOutRec) +{ + //A polygon has split into two such that one is now the inner of the other. + //It's possible that these polygons now wrap around other polygons, so check + //every polygon that's also contained by OuterOutRec's FirstLeft container + //(including 0) to see if they've become inner to the new inner polygon ... + OutRec* orfl = OuterOutRec->FirstLeft; + for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) + { + OutRec* outRec = m_PolyOuts[i]; + + if (!outRec->Pts || outRec == OuterOutRec || outRec == InnerOutRec) + continue; + OutRec* firstLeft = ParseFirstLeft(outRec->FirstLeft); + if (firstLeft != orfl && firstLeft != InnerOutRec && firstLeft != OuterOutRec) + continue; + if (Poly2ContainsPoly1(outRec->Pts, InnerOutRec->Pts)) + outRec->FirstLeft = InnerOutRec; + else if (Poly2ContainsPoly1(outRec->Pts, OuterOutRec->Pts)) + outRec->FirstLeft = OuterOutRec; + else if (outRec->FirstLeft == InnerOutRec || outRec->FirstLeft == OuterOutRec) + outRec->FirstLeft = orfl; + } +} +//---------------------------------------------------------------------- +void Clipper::FixupFirstLefts3(OutRec* OldOutRec, OutRec* NewOutRec) +{ + //reassigns FirstLeft WITHOUT testing if NewOutRec contains the polygon + for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) + { + OutRec* outRec = m_PolyOuts[i]; + OutRec* firstLeft = ParseFirstLeft(outRec->FirstLeft); + if (outRec->Pts && outRec->FirstLeft == OldOutRec) + outRec->FirstLeft = NewOutRec; + } +} +//---------------------------------------------------------------------- + +void Clipper::JoinCommonEdges() +{ + for (JoinList::size_type i = 0; i < m_Joins.size(); i++) + { + Join* join = m_Joins[i]; + + OutRec *outRec1 = GetOutRec(join->OutPt1->Idx); + OutRec *outRec2 = GetOutRec(join->OutPt2->Idx); + + if (!outRec1->Pts || !outRec2->Pts) continue; + if (outRec1->IsOpen || outRec2->IsOpen) continue; + + //get the polygon fragment with the correct hole state (FirstLeft) + //before calling JoinPoints() ... + OutRec *holeStateRec; + if (outRec1 == outRec2) holeStateRec = outRec1; + else if (OutRec1RightOfOutRec2(outRec1, outRec2)) holeStateRec = outRec2; + else if (OutRec1RightOfOutRec2(outRec2, outRec1)) holeStateRec = outRec1; + else holeStateRec = GetLowermostRec(outRec1, outRec2); + + if (!JoinPoints(join, outRec1, outRec2)) continue; + + if (outRec1 == outRec2) + { + //instead of joining two polygons, we've just created a new one by + //splitting one polygon into two. + outRec1->Pts = join->OutPt1; + outRec1->BottomPt = 0; + outRec2 = CreateOutRec(); + outRec2->Pts = join->OutPt2; + + //update all OutRec2.Pts Idx's ... + UpdateOutPtIdxs(*outRec2); + + if (Poly2ContainsPoly1(outRec2->Pts, outRec1->Pts)) + { + //outRec1 contains outRec2 ... + outRec2->IsHole = !outRec1->IsHole; + outRec2->FirstLeft = outRec1; + + if (m_UsingPolyTree) FixupFirstLefts2(outRec2, outRec1); + + if ((outRec2->IsHole ^ m_ReverseOutput) == (Area(*outRec2) > 0)) + ReversePolyPtLinks(outRec2->Pts); + + } else if (Poly2ContainsPoly1(outRec1->Pts, outRec2->Pts)) + { + //outRec2 contains outRec1 ... + outRec2->IsHole = outRec1->IsHole; + outRec1->IsHole = !outRec2->IsHole; + outRec2->FirstLeft = outRec1->FirstLeft; + outRec1->FirstLeft = outRec2; + + if (m_UsingPolyTree) FixupFirstLefts2(outRec1, outRec2); + + if ((outRec1->IsHole ^ m_ReverseOutput) == (Area(*outRec1) > 0)) + ReversePolyPtLinks(outRec1->Pts); + } + else + { + //the 2 polygons are completely separate ... + outRec2->IsHole = outRec1->IsHole; + outRec2->FirstLeft = outRec1->FirstLeft; + + //fixup FirstLeft pointers that may need reassigning to OutRec2 + if (m_UsingPolyTree) FixupFirstLefts1(outRec1, outRec2); + } + + } else + { + //joined 2 polygons together ... + + outRec2->Pts = 0; + outRec2->BottomPt = 0; + outRec2->Idx = outRec1->Idx; + + outRec1->IsHole = holeStateRec->IsHole; + if (holeStateRec == outRec2) + outRec1->FirstLeft = outRec2->FirstLeft; + outRec2->FirstLeft = outRec1; + + if (m_UsingPolyTree) FixupFirstLefts3(outRec2, outRec1); + } + } +} + +//------------------------------------------------------------------------------ +// ClipperOffset support functions ... +//------------------------------------------------------------------------------ + +DoublePoint GetUnitNormal(const IntPoint &pt1, const IntPoint &pt2) +{ + if(pt2.X == pt1.X && pt2.Y == pt1.Y) + return DoublePoint(0, 0); + + double Dx = (double)(pt2.X - pt1.X); + double dy = (double)(pt2.Y - pt1.Y); + double f = 1 *1.0/ std::sqrt( Dx*Dx + dy*dy ); + Dx *= f; + dy *= f; + return DoublePoint(dy, -Dx); +} + +//------------------------------------------------------------------------------ +// ClipperOffset class +//------------------------------------------------------------------------------ + +ClipperOffset::ClipperOffset(double miterLimit, double arcTolerance) +{ + this->MiterLimit = miterLimit; + this->ArcTolerance = arcTolerance; + m_lowest.X = -1; +} +//------------------------------------------------------------------------------ + +ClipperOffset::~ClipperOffset() +{ + Clear(); +} +//------------------------------------------------------------------------------ + +void ClipperOffset::Clear() +{ + for (int i = 0; i < m_polyNodes.ChildCount(); ++i) + delete m_polyNodes.Childs[i]; + m_polyNodes.Childs.clear(); + m_lowest.X = -1; +} +//------------------------------------------------------------------------------ + +void ClipperOffset::AddPath(const Path& path, JoinType joinType, EndType endType) +{ + int highI = (int)path.size() - 1; + if (highI < 0) return; + PolyNode* newNode = new PolyNode(); + newNode->m_jointype = joinType; + newNode->m_endtype = endType; + + //strip duplicate points from path and also get index to the lowest point ... + if (endType == etClosedLine || endType == etClosedPolygon) + while (highI > 0 && path[0] == path[highI]) highI--; + newNode->Contour.reserve(highI + 1); + newNode->Contour.push_back(path[0]); + int j = 0, k = 0; + for (int i = 1; i <= highI; i++) + if (newNode->Contour[j] != path[i]) + { + j++; + newNode->Contour.push_back(path[i]); + if (path[i].Y > newNode->Contour[k].Y || + (path[i].Y == newNode->Contour[k].Y && + path[i].X < newNode->Contour[k].X)) k = j; + } + if (endType == etClosedPolygon && j < 2) + { + delete newNode; + return; + } + m_polyNodes.AddChild(*newNode); + + //if this path's lowest pt is lower than all the others then update m_lowest + if (endType != etClosedPolygon) return; + if (m_lowest.X < 0) + m_lowest = IntPoint(m_polyNodes.ChildCount() - 1, k); + else + { + IntPoint ip = m_polyNodes.Childs[(int)m_lowest.X]->Contour[(int)m_lowest.Y]; + if (newNode->Contour[k].Y > ip.Y || + (newNode->Contour[k].Y == ip.Y && + newNode->Contour[k].X < ip.X)) + m_lowest = IntPoint(m_polyNodes.ChildCount() - 1, k); + } +} +//------------------------------------------------------------------------------ + +void ClipperOffset::AddPaths(const Paths& paths, JoinType joinType, EndType endType) +{ + for (Paths::size_type i = 0; i < paths.size(); ++i) + AddPath(paths[i], joinType, endType); +} +//------------------------------------------------------------------------------ + +void ClipperOffset::FixOrientations() +{ + //fixup orientations of all closed paths if the orientation of the + //closed path with the lowermost vertex is wrong ... + if (m_lowest.X >= 0 && + !Orientation(m_polyNodes.Childs[(int)m_lowest.X]->Contour)) + { + for (int i = 0; i < m_polyNodes.ChildCount(); ++i) + { + PolyNode& node = *m_polyNodes.Childs[i]; + if (node.m_endtype == etClosedPolygon || + (node.m_endtype == etClosedLine && Orientation(node.Contour))) + ReversePath(node.Contour); + } + } else + { + for (int i = 0; i < m_polyNodes.ChildCount(); ++i) + { + PolyNode& node = *m_polyNodes.Childs[i]; + if (node.m_endtype == etClosedLine && !Orientation(node.Contour)) + ReversePath(node.Contour); + } + } +} +//------------------------------------------------------------------------------ + +void ClipperOffset::Execute(Paths& solution, double delta) +{ + solution.clear(); + FixOrientations(); + DoOffset(delta); + + //now clean up 'corners' ... + Clipper clpr; + clpr.AddPaths(m_destPolys, ptSubject, true); + if (delta > 0) + { + clpr.Execute(ctUnion, solution, pftPositive, pftPositive); + } + else + { + IntRect r = clpr.GetBounds(); + Path outer(4); + outer[0] = IntPoint(r.left - 10, r.bottom + 10); + outer[1] = IntPoint(r.right + 10, r.bottom + 10); + outer[2] = IntPoint(r.right + 10, r.top - 10); + outer[3] = IntPoint(r.left - 10, r.top - 10); + + clpr.AddPath(outer, ptSubject, true); + clpr.ReverseSolution(true); + clpr.Execute(ctUnion, solution, pftNegative, pftNegative); + if (solution.size() > 0) solution.erase(solution.begin()); + } +} +//------------------------------------------------------------------------------ + +void ClipperOffset::Execute(PolyTree& solution, double delta) +{ + solution.Clear(); + FixOrientations(); + DoOffset(delta); + + //now clean up 'corners' ... + Clipper clpr; + clpr.AddPaths(m_destPolys, ptSubject, true); + if (delta > 0) + { + clpr.Execute(ctUnion, solution, pftPositive, pftPositive); + } + else + { + IntRect r = clpr.GetBounds(); + Path outer(4); + outer[0] = IntPoint(r.left - 10, r.bottom + 10); + outer[1] = IntPoint(r.right + 10, r.bottom + 10); + outer[2] = IntPoint(r.right + 10, r.top - 10); + outer[3] = IntPoint(r.left - 10, r.top - 10); + + clpr.AddPath(outer, ptSubject, true); + clpr.ReverseSolution(true); + clpr.Execute(ctUnion, solution, pftNegative, pftNegative); + //remove the outer PolyNode rectangle ... + if (solution.ChildCount() == 1 && solution.Childs[0]->ChildCount() > 0) + { + PolyNode* outerNode = solution.Childs[0]; + solution.Childs.reserve(outerNode->ChildCount()); + solution.Childs[0] = outerNode->Childs[0]; + solution.Childs[0]->Parent = outerNode->Parent; + for (int i = 1; i < outerNode->ChildCount(); ++i) + solution.AddChild(*outerNode->Childs[i]); + } + else + solution.Clear(); + } +} +//------------------------------------------------------------------------------ + +void ClipperOffset::DoOffset(double delta) +{ + m_destPolys.clear(); + m_delta = delta; + + //if Zero offset, just copy any CLOSED polygons to m_p and return ... + if (NEAR_ZERO(delta)) + { + m_destPolys.reserve(m_polyNodes.ChildCount()); + for (int i = 0; i < m_polyNodes.ChildCount(); i++) + { + PolyNode& node = *m_polyNodes.Childs[i]; + if (node.m_endtype == etClosedPolygon) + m_destPolys.push_back(node.Contour); + } + return; + } + + //see offset_triginometry3.svg in the documentation folder ... + if (MiterLimit > 2) m_miterLim = 2/(MiterLimit * MiterLimit); + else m_miterLim = 0.5; + + double y; + if (ArcTolerance <= 0.0) y = def_arc_tolerance; + else if (ArcTolerance > std::fabs(delta) * def_arc_tolerance) + y = std::fabs(delta) * def_arc_tolerance; + else y = ArcTolerance; + //see offset_triginometry2.svg in the documentation folder ... + double steps = pi / std::acos(1 - y / std::fabs(delta)); + if (steps > std::fabs(delta) * pi) + steps = std::fabs(delta) * pi; //ie excessive precision check + m_sin = std::sin(two_pi / steps); + m_cos = std::cos(two_pi / steps); + m_StepsPerRad = steps / two_pi; + if (delta < 0.0) m_sin = -m_sin; + + m_destPolys.reserve(m_polyNodes.ChildCount() * 2); + for (int i = 0; i < m_polyNodes.ChildCount(); i++) + { + PolyNode& node = *m_polyNodes.Childs[i]; + m_srcPoly = node.Contour; + + int len = (int)m_srcPoly.size(); + if (len == 0 || (delta <= 0 && (len < 3 || node.m_endtype != etClosedPolygon))) + continue; + + m_destPoly.clear(); + if (len == 1) + { + if (node.m_jointype == jtRound) + { + double X = 1.0, Y = 0.0; + for (cInt j = 1; j <= steps; j++) + { + m_destPoly.push_back(IntPoint( + Round(m_srcPoly[0].X + X * delta), + Round(m_srcPoly[0].Y + Y * delta))); + double X2 = X; + X = X * m_cos - m_sin * Y; + Y = X2 * m_sin + Y * m_cos; + } + } + else + { + double X = -1.0, Y = -1.0; + for (int j = 0; j < 4; ++j) + { + m_destPoly.push_back(IntPoint( + Round(m_srcPoly[0].X + X * delta), + Round(m_srcPoly[0].Y + Y * delta))); + if (X < 0) X = 1; + else if (Y < 0) Y = 1; + else X = -1; + } + } + m_destPolys.push_back(m_destPoly); + continue; + } + //build m_normals ... + m_normals.clear(); + m_normals.reserve(len); + for (int j = 0; j < len - 1; ++j) + m_normals.push_back(GetUnitNormal(m_srcPoly[j], m_srcPoly[j + 1])); + if (node.m_endtype == etClosedLine || node.m_endtype == etClosedPolygon) + m_normals.push_back(GetUnitNormal(m_srcPoly[len - 1], m_srcPoly[0])); + else + m_normals.push_back(DoublePoint(m_normals[len - 2])); + + if (node.m_endtype == etClosedPolygon) + { + int k = len - 1; + for (int j = 0; j < len; ++j) + OffsetPoint(j, k, node.m_jointype); + m_destPolys.push_back(m_destPoly); + } + else if (node.m_endtype == etClosedLine) + { + int k = len - 1; + for (int j = 0; j < len; ++j) + OffsetPoint(j, k, node.m_jointype); + m_destPolys.push_back(m_destPoly); + m_destPoly.clear(); + //re-build m_normals ... + DoublePoint n = m_normals[len -1]; + for (int j = len - 1; j > 0; j--) + m_normals[j] = DoublePoint(-m_normals[j - 1].X, -m_normals[j - 1].Y); + m_normals[0] = DoublePoint(-n.X, -n.Y); + k = 0; + for (int j = len - 1; j >= 0; j--) + OffsetPoint(j, k, node.m_jointype); + m_destPolys.push_back(m_destPoly); + } + else + { + int k = 0; + for (int j = 1; j < len - 1; ++j) + OffsetPoint(j, k, node.m_jointype); + + IntPoint pt1; + if (node.m_endtype == etOpenButt) + { + int j = len - 1; + pt1 = IntPoint((cInt)Round(m_srcPoly[j].X + m_normals[j].X * + delta), (cInt)Round(m_srcPoly[j].Y + m_normals[j].Y * delta)); + m_destPoly.push_back(pt1); + pt1 = IntPoint((cInt)Round(m_srcPoly[j].X - m_normals[j].X * + delta), (cInt)Round(m_srcPoly[j].Y - m_normals[j].Y * delta)); + m_destPoly.push_back(pt1); + } + else + { + int j = len - 1; + k = len - 2; + m_sinA = 0; + m_normals[j] = DoublePoint(-m_normals[j].X, -m_normals[j].Y); + if (node.m_endtype == etOpenSquare) + DoSquare(j, k); + else + DoRound(j, k); + } + + //re-build m_normals ... + for (int j = len - 1; j > 0; j--) + m_normals[j] = DoublePoint(-m_normals[j - 1].X, -m_normals[j - 1].Y); + m_normals[0] = DoublePoint(-m_normals[1].X, -m_normals[1].Y); + + k = len - 1; + for (int j = k - 1; j > 0; --j) OffsetPoint(j, k, node.m_jointype); + + if (node.m_endtype == etOpenButt) + { + pt1 = IntPoint((cInt)Round(m_srcPoly[0].X - m_normals[0].X * delta), + (cInt)Round(m_srcPoly[0].Y - m_normals[0].Y * delta)); + m_destPoly.push_back(pt1); + pt1 = IntPoint((cInt)Round(m_srcPoly[0].X + m_normals[0].X * delta), + (cInt)Round(m_srcPoly[0].Y + m_normals[0].Y * delta)); + m_destPoly.push_back(pt1); + } + else + { + k = 1; + m_sinA = 0; + if (node.m_endtype == etOpenSquare) + DoSquare(0, 1); + else + DoRound(0, 1); + } + m_destPolys.push_back(m_destPoly); + } + } +} +//------------------------------------------------------------------------------ + +void ClipperOffset::OffsetPoint(int j, int& k, JoinType jointype) +{ + //cross product ... + m_sinA = (m_normals[k].X * m_normals[j].Y - m_normals[j].X * m_normals[k].Y); + if (std::fabs(m_sinA * m_delta) < 1.0) + { + //dot product ... + double cosA = (m_normals[k].X * m_normals[j].X + m_normals[j].Y * m_normals[k].Y ); + if (cosA > 0) // angle => 0 degrees + { + m_destPoly.push_back(IntPoint(Round(m_srcPoly[j].X + m_normals[k].X * m_delta), + Round(m_srcPoly[j].Y + m_normals[k].Y * m_delta))); + return; + } + //else angle => 180 degrees + } + else if (m_sinA > 1.0) m_sinA = 1.0; + else if (m_sinA < -1.0) m_sinA = -1.0; + + if (m_sinA * m_delta < 0) + { + m_destPoly.push_back(IntPoint(Round(m_srcPoly[j].X + m_normals[k].X * m_delta), + Round(m_srcPoly[j].Y + m_normals[k].Y * m_delta))); + m_destPoly.push_back(m_srcPoly[j]); + m_destPoly.push_back(IntPoint(Round(m_srcPoly[j].X + m_normals[j].X * m_delta), + Round(m_srcPoly[j].Y + m_normals[j].Y * m_delta))); + } + else + switch (jointype) + { + case jtMiter: + { + double r = 1 + (m_normals[j].X * m_normals[k].X + + m_normals[j].Y * m_normals[k].Y); + if (r >= m_miterLim) DoMiter(j, k, r); else DoSquare(j, k); + break; + } + case jtSquare: DoSquare(j, k); break; + case jtRound: DoRound(j, k); break; + } + k = j; +} +//------------------------------------------------------------------------------ + +void ClipperOffset::DoSquare(int j, int k) +{ + double dx = std::tan(std::atan2(m_sinA, + m_normals[k].X * m_normals[j].X + m_normals[k].Y * m_normals[j].Y) / 4); + m_destPoly.push_back(IntPoint( + Round(m_srcPoly[j].X + m_delta * (m_normals[k].X - m_normals[k].Y * dx)), + Round(m_srcPoly[j].Y + m_delta * (m_normals[k].Y + m_normals[k].X * dx)))); + m_destPoly.push_back(IntPoint( + Round(m_srcPoly[j].X + m_delta * (m_normals[j].X + m_normals[j].Y * dx)), + Round(m_srcPoly[j].Y + m_delta * (m_normals[j].Y - m_normals[j].X * dx)))); +} +//------------------------------------------------------------------------------ + +void ClipperOffset::DoMiter(int j, int k, double r) +{ + double q = m_delta / r; + m_destPoly.push_back(IntPoint(Round(m_srcPoly[j].X + (m_normals[k].X + m_normals[j].X) * q), + Round(m_srcPoly[j].Y + (m_normals[k].Y + m_normals[j].Y) * q))); +} +//------------------------------------------------------------------------------ + +void ClipperOffset::DoRound(int j, int k) +{ + double a = std::atan2(m_sinA, + m_normals[k].X * m_normals[j].X + m_normals[k].Y * m_normals[j].Y); + int steps = std::max((int)Round(m_StepsPerRad * std::fabs(a)), 1); + + double X = m_normals[k].X, Y = m_normals[k].Y, X2; + for (int i = 0; i < steps; ++i) + { + m_destPoly.push_back(IntPoint( + Round(m_srcPoly[j].X + X * m_delta), + Round(m_srcPoly[j].Y + Y * m_delta))); + X2 = X; + X = X * m_cos - m_sin * Y; + Y = X2 * m_sin + Y * m_cos; + } + m_destPoly.push_back(IntPoint( + Round(m_srcPoly[j].X + m_normals[j].X * m_delta), + Round(m_srcPoly[j].Y + m_normals[j].Y * m_delta))); +} + +//------------------------------------------------------------------------------ +// Miscellaneous public functions +//------------------------------------------------------------------------------ + +void Clipper::DoSimplePolygons() +{ + PolyOutList::size_type i = 0; + while (i < m_PolyOuts.size()) + { + OutRec* outrec = m_PolyOuts[i++]; + OutPt* op = outrec->Pts; + if (!op || outrec->IsOpen) continue; + do //for each Pt in Polygon until duplicate found do ... + { + OutPt* op2 = op->Next; + while (op2 != outrec->Pts) + { + if ((op->Pt == op2->Pt) && op2->Next != op && op2->Prev != op) + { + //split the polygon into two ... + OutPt* op3 = op->Prev; + OutPt* op4 = op2->Prev; + op->Prev = op4; + op4->Next = op; + op2->Prev = op3; + op3->Next = op2; + + outrec->Pts = op; + OutRec* outrec2 = CreateOutRec(); + outrec2->Pts = op2; + UpdateOutPtIdxs(*outrec2); + if (Poly2ContainsPoly1(outrec2->Pts, outrec->Pts)) + { + //OutRec2 is contained by OutRec1 ... + outrec2->IsHole = !outrec->IsHole; + outrec2->FirstLeft = outrec; + if (m_UsingPolyTree) FixupFirstLefts2(outrec2, outrec); + } + else + if (Poly2ContainsPoly1(outrec->Pts, outrec2->Pts)) + { + //OutRec1 is contained by OutRec2 ... + outrec2->IsHole = outrec->IsHole; + outrec->IsHole = !outrec2->IsHole; + outrec2->FirstLeft = outrec->FirstLeft; + outrec->FirstLeft = outrec2; + if (m_UsingPolyTree) FixupFirstLefts2(outrec, outrec2); + } + else + { + //the 2 polygons are separate ... + outrec2->IsHole = outrec->IsHole; + outrec2->FirstLeft = outrec->FirstLeft; + if (m_UsingPolyTree) FixupFirstLefts1(outrec, outrec2); + } + op2 = op; //ie get ready for the Next iteration + } + op2 = op2->Next; + } + op = op->Next; + } + while (op != outrec->Pts); + } +} +//------------------------------------------------------------------------------ + +void ReversePath(Path& p) +{ + std::reverse(p.begin(), p.end()); +} +//------------------------------------------------------------------------------ + +void ReversePaths(Paths& p) +{ + for (Paths::size_type i = 0; i < p.size(); ++i) + ReversePath(p[i]); +} +//------------------------------------------------------------------------------ + +void SimplifyPolygon(const Path &in_poly, Paths &out_polys, PolyFillType fillType) +{ + Clipper c; + c.StrictlySimple(true); + c.AddPath(in_poly, ptSubject, true); + c.Execute(ctUnion, out_polys, fillType, fillType); +} +//------------------------------------------------------------------------------ + +void SimplifyPolygons(const Paths &in_polys, Paths &out_polys, PolyFillType fillType) +{ + Clipper c; + c.StrictlySimple(true); + c.AddPaths(in_polys, ptSubject, true); + c.Execute(ctUnion, out_polys, fillType, fillType); +} +//------------------------------------------------------------------------------ + +void SimplifyPolygons(Paths &polys, PolyFillType fillType) +{ + SimplifyPolygons(polys, polys, fillType); +} +//------------------------------------------------------------------------------ + +inline double DistanceSqrd(const IntPoint& pt1, const IntPoint& pt2) +{ + double Dx = ((double)pt1.X - pt2.X); + double dy = ((double)pt1.Y - pt2.Y); + return (Dx*Dx + dy*dy); +} +//------------------------------------------------------------------------------ + +double DistanceFromLineSqrd( + const IntPoint& pt, const IntPoint& ln1, const IntPoint& ln2) +{ + //The equation of a line in general form (Ax + By + C = 0) + //given 2 points (x,y) & (x,y) is ... + //(y - y)x + (x - x)y + (y - y)x - (x - x)y = 0 + //A = (y - y); B = (x - x); C = (y - y)x - (x - x)y + //perpendicular distance of point (x,y) = (Ax + By + C)/Sqrt(A + B) + //see http://en.wikipedia.org/wiki/Perpendicular_distance + double A = double(ln1.Y - ln2.Y); + double B = double(ln2.X - ln1.X); + double C = A * ln1.X + B * ln1.Y; + C = A * pt.X + B * pt.Y - C; + return (C * C) / (A * A + B * B); +} +//--------------------------------------------------------------------------- + +bool SlopesNearCollinear(const IntPoint& pt1, + const IntPoint& pt2, const IntPoint& pt3, double distSqrd) +{ + //this function is more accurate when the point that's geometrically + //between the other 2 points is the one that's tested for distance. + //ie makes it more likely to pick up 'spikes' ... + if (Abs(pt1.X - pt2.X) > Abs(pt1.Y - pt2.Y)) + { + if ((pt1.X > pt2.X) == (pt1.X < pt3.X)) + return DistanceFromLineSqrd(pt1, pt2, pt3) < distSqrd; + else if ((pt2.X > pt1.X) == (pt2.X < pt3.X)) + return DistanceFromLineSqrd(pt2, pt1, pt3) < distSqrd; + else + return DistanceFromLineSqrd(pt3, pt1, pt2) < distSqrd; + } + else + { + if ((pt1.Y > pt2.Y) == (pt1.Y < pt3.Y)) + return DistanceFromLineSqrd(pt1, pt2, pt3) < distSqrd; + else if ((pt2.Y > pt1.Y) == (pt2.Y < pt3.Y)) + return DistanceFromLineSqrd(pt2, pt1, pt3) < distSqrd; + else + return DistanceFromLineSqrd(pt3, pt1, pt2) < distSqrd; + } +} +//------------------------------------------------------------------------------ + +bool PointsAreClose(IntPoint pt1, IntPoint pt2, double distSqrd) +{ + double Dx = (double)pt1.X - pt2.X; + double dy = (double)pt1.Y - pt2.Y; + return ((Dx * Dx) + (dy * dy) <= distSqrd); +} +//------------------------------------------------------------------------------ + +OutPt* ExcludeOp(OutPt* op) +{ + OutPt* result = op->Prev; + result->Next = op->Next; + op->Next->Prev = result; + result->Idx = 0; + return result; +} +//------------------------------------------------------------------------------ + +void CleanPolygon(const Path& in_poly, Path& out_poly, double distance) +{ + //distance = proximity in units/pixels below which vertices + //will be stripped. Default ~= sqrt(2). + + size_t size = in_poly.size(); + + if (size == 0) + { + out_poly.clear(); + return; + } + + OutPt* outPts = new OutPt[size]; + for (size_t i = 0; i < size; ++i) + { + outPts[i].Pt = in_poly[i]; + outPts[i].Next = &outPts[(i + 1) % size]; + outPts[i].Next->Prev = &outPts[i]; + outPts[i].Idx = 0; + } + + double distSqrd = distance * distance; + OutPt* op = &outPts[0]; + while (op->Idx == 0 && op->Next != op->Prev) + { + if (PointsAreClose(op->Pt, op->Prev->Pt, distSqrd)) + { + op = ExcludeOp(op); + size--; + } + else if (PointsAreClose(op->Prev->Pt, op->Next->Pt, distSqrd)) + { + ExcludeOp(op->Next); + op = ExcludeOp(op); + size -= 2; + } + else if (SlopesNearCollinear(op->Prev->Pt, op->Pt, op->Next->Pt, distSqrd)) + { + op = ExcludeOp(op); + size--; + } + else + { + op->Idx = 1; + op = op->Next; + } + } + + if (size < 3) size = 0; + out_poly.resize(size); + for (size_t i = 0; i < size; ++i) + { + out_poly[i] = op->Pt; + op = op->Next; + } + delete [] outPts; +} +//------------------------------------------------------------------------------ + +void CleanPolygon(Path& poly, double distance) +{ + CleanPolygon(poly, poly, distance); +} +//------------------------------------------------------------------------------ + +void CleanPolygons(const Paths& in_polys, Paths& out_polys, double distance) +{ + out_polys.resize(in_polys.size()); + for (Paths::size_type i = 0; i < in_polys.size(); ++i) + CleanPolygon(in_polys[i], out_polys[i], distance); +} +//------------------------------------------------------------------------------ + +void CleanPolygons(Paths& polys, double distance) +{ + CleanPolygons(polys, polys, distance); +} +//------------------------------------------------------------------------------ + +void Minkowski(const Path& poly, const Path& path, + Paths& solution, bool isSum, bool isClosed) +{ + int delta = (isClosed ? 1 : 0); + size_t polyCnt = poly.size(); + size_t pathCnt = path.size(); + Paths pp; + pp.reserve(pathCnt); + if (isSum) + for (size_t i = 0; i < pathCnt; ++i) + { + Path p; + p.reserve(polyCnt); + for (size_t j = 0; j < poly.size(); ++j) + p.push_back(IntPoint(path[i].X + poly[j].X, path[i].Y + poly[j].Y)); + pp.push_back(p); + } + else + for (size_t i = 0; i < pathCnt; ++i) + { + Path p; + p.reserve(polyCnt); + for (size_t j = 0; j < poly.size(); ++j) + p.push_back(IntPoint(path[i].X - poly[j].X, path[i].Y - poly[j].Y)); + pp.push_back(p); + } + + solution.clear(); + solution.reserve((pathCnt + delta) * (polyCnt + 1)); + for (size_t i = 0; i < pathCnt - 1 + delta; ++i) + for (size_t j = 0; j < polyCnt; ++j) + { + Path quad; + quad.reserve(4); + quad.push_back(pp[i % pathCnt][j % polyCnt]); + quad.push_back(pp[(i + 1) % pathCnt][j % polyCnt]); + quad.push_back(pp[(i + 1) % pathCnt][(j + 1) % polyCnt]); + quad.push_back(pp[i % pathCnt][(j + 1) % polyCnt]); + if (!Orientation(quad)) ReversePath(quad); + solution.push_back(quad); + } +} +//------------------------------------------------------------------------------ + +void MinkowskiSum(const Path& pattern, const Path& path, Paths& solution, bool pathIsClosed) +{ + Minkowski(pattern, path, solution, true, pathIsClosed); + Clipper c; + c.AddPaths(solution, ptSubject, true); + c.Execute(ctUnion, solution, pftNonZero, pftNonZero); +} +//------------------------------------------------------------------------------ + +void TranslatePath(const Path& input, Path& output, const IntPoint delta) +{ + //precondition: input != output + output.resize(input.size()); + for (size_t i = 0; i < input.size(); ++i) + output[i] = IntPoint(input[i].X + delta.X, input[i].Y + delta.Y); +} +//------------------------------------------------------------------------------ + +void MinkowskiSum(const Path& pattern, const Paths& paths, Paths& solution, bool pathIsClosed) +{ + Clipper c; + for (size_t i = 0; i < paths.size(); ++i) + { + Paths tmp; + Minkowski(pattern, paths[i], tmp, true, pathIsClosed); + c.AddPaths(tmp, ptSubject, true); + if (pathIsClosed) + { + Path tmp2; + TranslatePath(paths[i], tmp2, pattern[0]); + c.AddPath(tmp2, ptClip, true); + } + } + c.Execute(ctUnion, solution, pftNonZero, pftNonZero); +} +//------------------------------------------------------------------------------ + +void MinkowskiDiff(const Path& poly1, const Path& poly2, Paths& solution) +{ + Minkowski(poly1, poly2, solution, false, true); + Clipper c; + c.AddPaths(solution, ptSubject, true); + c.Execute(ctUnion, solution, pftNonZero, pftNonZero); +} +//------------------------------------------------------------------------------ + +enum NodeType {ntAny, ntOpen, ntClosed}; + +void AddPolyNodeToPaths(const PolyNode& polynode, NodeType nodetype, Paths& paths) +{ + bool match = true; + if (nodetype == ntClosed) match = !polynode.IsOpen(); + else if (nodetype == ntOpen) return; + + if (!polynode.Contour.empty() && match) + paths.push_back(polynode.Contour); + for (int i = 0; i < polynode.ChildCount(); ++i) + AddPolyNodeToPaths(*polynode.Childs[i], nodetype, paths); +} +//------------------------------------------------------------------------------ + +void PolyTreeToPaths(const PolyTree& polytree, Paths& paths) +{ + paths.resize(0); + paths.reserve(polytree.Total()); + AddPolyNodeToPaths(polytree, ntAny, paths); +} +//------------------------------------------------------------------------------ + +void ClosedPathsFromPolyTree(const PolyTree& polytree, Paths& paths) +{ + paths.resize(0); + paths.reserve(polytree.Total()); + AddPolyNodeToPaths(polytree, ntClosed, paths); +} +//------------------------------------------------------------------------------ + +void OpenPathsFromPolyTree(PolyTree& polytree, Paths& paths) +{ + paths.resize(0); + paths.reserve(polytree.Total()); + //Open paths are top level only, so ... + for (int i = 0; i < polytree.ChildCount(); ++i) + if (polytree.Childs[i]->IsOpen()) + paths.push_back(polytree.Childs[i]->Contour); +} +//------------------------------------------------------------------------------ + +std::ostream& operator <<(std::ostream &s, const IntPoint &p) +{ + s << "(" << p.X << "," << p.Y << ")"; + return s; +} +//------------------------------------------------------------------------------ + +std::ostream& operator <<(std::ostream &s, const Path &p) +{ + if (p.empty()) return s; + Path::size_type last = p.size() -1; + for (Path::size_type i = 0; i < last; i++) + s << "(" << p[i].X << "," << p[i].Y << "), "; + s << "(" << p[last].X << "," << p[last].Y << ")\n"; + return s; +} +//------------------------------------------------------------------------------ + +std::ostream& operator <<(std::ostream &s, const Paths &p) +{ + for (Paths::size_type i = 0; i < p.size(); i++) + s << p[i]; + s << "\n"; + return s; +} +//------------------------------------------------------------------------------ + +} //ClipperLib namespace diff --git a/ptocr/postprocess/dbprocess/include/clipper/clipper.hpp b/ptocr/postprocess/dbprocess/include/clipper/clipper.hpp new file mode 100644 index 0000000..1c38371 --- /dev/null +++ b/ptocr/postprocess/dbprocess/include/clipper/clipper.hpp @@ -0,0 +1,404 @@ +/******************************************************************************* +* * +* Author : Angus Johnson * +* Version : 6.4.0 * +* Date : 2 July 2015 * +* Website : http://www.angusj.com * +* Copyright : Angus Johnson 2010-2015 * +* * +* License: * +* Use, modification & distribution is subject to Boost Software License Ver 1. * +* http://www.boost.org/LICENSE_1_0.txt * +* * +* Attributions: * +* The code in this library is an extension of Bala Vatti's clipping algorithm: * +* "A generic solution to polygon clipping" * +* Communications of the ACM, Vol 35, Issue 7 (July 1992) pp 56-63. * +* http://portal.acm.org/citation.cfm?id=129906 * +* * +* Computer graphics and geometric modeling: implementation and algorithms * +* By Max K. Agoston * +* Springer; 1 edition (January 4, 2005) * +* http://books.google.com/books?q=vatti+clipping+agoston * +* * +* See also: * +* "Polygon Offsetting by Computing Winding Numbers" * +* Paper no. DETC2005-85513 pp. 565-575 * +* ASME 2005 International Design Engineering Technical Conferences * +* and Computers and Information in Engineering Conference (IDETC/CIE2005) * +* September 24-28, 2005 , Long Beach, California, USA * +* http://www.me.berkeley.edu/~mcmains/pubs/DAC05OffsetPolygon.pdf * +* * +*******************************************************************************/ + +#ifndef clipper_hpp +#define clipper_hpp + +#define CLIPPER_VERSION "6.2.6" + +//use_int32: When enabled 32bit ints are used instead of 64bit ints. This +//improve performance but coordinate values are limited to the range +/- 46340 +//#define use_int32 + +//use_xyz: adds a Z member to IntPoint. Adds a minor cost to perfomance. +//#define use_xyz + +//use_lines: Enables line clipping. Adds a very minor cost to performance. +#define use_lines + +//use_deprecated: Enables temporary support for the obsolete functions +//#define use_deprecated + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace ClipperLib { + +enum ClipType { ctIntersection, ctUnion, ctDifference, ctXor }; +enum PolyType { ptSubject, ptClip }; +//By far the most widely used winding rules for polygon filling are +//EvenOdd & NonZero (GDI, GDI+, XLib, OpenGL, Cairo, AGG, Quartz, SVG, Gr32) +//Others rules include Positive, Negative and ABS_GTR_EQ_TWO (only in OpenGL) +//see http://glprogramming.com/red/chapter11.html +enum PolyFillType { pftEvenOdd, pftNonZero, pftPositive, pftNegative }; + +#ifdef use_int32 + typedef int cInt; + static cInt const loRange = 0x7FFF; + static cInt const hiRange = 0x7FFF; +#else + typedef signed long long cInt; + static cInt const loRange = 0x3FFFFFFF; + static cInt const hiRange = 0x3FFFFFFFFFFFFFFFLL; + typedef signed long long long64; //used by Int128 class + typedef unsigned long long ulong64; + +#endif + +struct IntPoint { + cInt X; + cInt Y; +#ifdef use_xyz + cInt Z; + IntPoint(cInt x = 0, cInt y = 0, cInt z = 0): X(x), Y(y), Z(z) {}; +#else + IntPoint(cInt x = 0, cInt y = 0): X(x), Y(y) {}; +#endif + + friend inline bool operator== (const IntPoint& a, const IntPoint& b) + { + return a.X == b.X && a.Y == b.Y; + } + friend inline bool operator!= (const IntPoint& a, const IntPoint& b) + { + return a.X != b.X || a.Y != b.Y; + } +}; +//------------------------------------------------------------------------------ + +typedef std::vector< IntPoint > Path; +typedef std::vector< Path > Paths; + +inline Path& operator <<(Path& poly, const IntPoint& p) {poly.push_back(p); return poly;} +inline Paths& operator <<(Paths& polys, const Path& p) {polys.push_back(p); return polys;} + +std::ostream& operator <<(std::ostream &s, const IntPoint &p); +std::ostream& operator <<(std::ostream &s, const Path &p); +std::ostream& operator <<(std::ostream &s, const Paths &p); + +struct DoublePoint +{ + double X; + double Y; + DoublePoint(double x = 0, double y = 0) : X(x), Y(y) {} + DoublePoint(IntPoint ip) : X((double)ip.X), Y((double)ip.Y) {} +}; +//------------------------------------------------------------------------------ + +#ifdef use_xyz +typedef void (*ZFillCallback)(IntPoint& e1bot, IntPoint& e1top, IntPoint& e2bot, IntPoint& e2top, IntPoint& pt); +#endif + +enum InitOptions {ioReverseSolution = 1, ioStrictlySimple = 2, ioPreserveCollinear = 4}; +enum JoinType {jtSquare, jtRound, jtMiter}; +enum EndType {etClosedPolygon, etClosedLine, etOpenButt, etOpenSquare, etOpenRound}; + +class PolyNode; +typedef std::vector< PolyNode* > PolyNodes; + +class PolyNode +{ +public: + PolyNode(); + virtual ~PolyNode(){}; + Path Contour; + PolyNodes Childs; + PolyNode* Parent; + PolyNode* GetNext() const; + bool IsHole() const; + bool IsOpen() const; + int ChildCount() const; +private: + unsigned Index; //node index in Parent.Childs + bool m_IsOpen; + JoinType m_jointype; + EndType m_endtype; + PolyNode* GetNextSiblingUp() const; + void AddChild(PolyNode& child); + friend class Clipper; //to access Index + friend class ClipperOffset; +}; + +class PolyTree: public PolyNode +{ +public: + ~PolyTree(){Clear();}; + PolyNode* GetFirst() const; + void Clear(); + int Total() const; +private: + PolyNodes AllNodes; + friend class Clipper; //to access AllNodes +}; + +bool Orientation(const Path &poly); +double Area(const Path &poly); +int PointInPolygon(const IntPoint &pt, const Path &path); + +void SimplifyPolygon(const Path &in_poly, Paths &out_polys, PolyFillType fillType = pftEvenOdd); +void SimplifyPolygons(const Paths &in_polys, Paths &out_polys, PolyFillType fillType = pftEvenOdd); +void SimplifyPolygons(Paths &polys, PolyFillType fillType = pftEvenOdd); + +void CleanPolygon(const Path& in_poly, Path& out_poly, double distance = 1.415); +void CleanPolygon(Path& poly, double distance = 1.415); +void CleanPolygons(const Paths& in_polys, Paths& out_polys, double distance = 1.415); +void CleanPolygons(Paths& polys, double distance = 1.415); + +void MinkowskiSum(const Path& pattern, const Path& path, Paths& solution, bool pathIsClosed); +void MinkowskiSum(const Path& pattern, const Paths& paths, Paths& solution, bool pathIsClosed); +void MinkowskiDiff(const Path& poly1, const Path& poly2, Paths& solution); + +void PolyTreeToPaths(const PolyTree& polytree, Paths& paths); +void ClosedPathsFromPolyTree(const PolyTree& polytree, Paths& paths); +void OpenPathsFromPolyTree(PolyTree& polytree, Paths& paths); + +void ReversePath(Path& p); +void ReversePaths(Paths& p); + +struct IntRect { cInt left; cInt top; cInt right; cInt bottom; }; + +//enums that are used internally ... +enum EdgeSide { esLeft = 1, esRight = 2}; + +//forward declarations (for stuff used internally) ... +struct TEdge; +struct IntersectNode; +struct LocalMinimum; +struct OutPt; +struct OutRec; +struct Join; + +typedef std::vector < OutRec* > PolyOutList; +typedef std::vector < TEdge* > EdgeList; +typedef std::vector < Join* > JoinList; +typedef std::vector < IntersectNode* > IntersectList; + +//------------------------------------------------------------------------------ + +//ClipperBase is the ancestor to the Clipper class. It should not be +//instantiated directly. This class simply abstracts the conversion of sets of +//polygon coordinates into edge objects that are stored in a LocalMinima list. +class ClipperBase +{ +public: + ClipperBase(); + virtual ~ClipperBase(); + virtual bool AddPath(const Path &pg, PolyType PolyTyp, bool Closed); + bool AddPaths(const Paths &ppg, PolyType PolyTyp, bool Closed); + virtual void Clear(); + IntRect GetBounds(); + bool PreserveCollinear() {return m_PreserveCollinear;}; + void PreserveCollinear(bool value) {m_PreserveCollinear = value;}; +protected: + void DisposeLocalMinimaList(); + TEdge* AddBoundsToLML(TEdge *e, bool IsClosed); + virtual void Reset(); + TEdge* ProcessBound(TEdge* E, bool IsClockwise); + void InsertScanbeam(const cInt Y); + bool PopScanbeam(cInt &Y); + bool LocalMinimaPending(); + bool PopLocalMinima(cInt Y, const LocalMinimum *&locMin); + OutRec* CreateOutRec(); + void DisposeAllOutRecs(); + void DisposeOutRec(PolyOutList::size_type index); + void SwapPositionsInAEL(TEdge *edge1, TEdge *edge2); + void DeleteFromAEL(TEdge *e); + void UpdateEdgeIntoAEL(TEdge *&e); + + typedef std::vector MinimaList; + MinimaList::iterator m_CurrentLM; + MinimaList m_MinimaList; + + bool m_UseFullRange; + EdgeList m_edges; + bool m_PreserveCollinear; + bool m_HasOpenPaths; + PolyOutList m_PolyOuts; + TEdge *m_ActiveEdges; + + typedef std::priority_queue ScanbeamList; + ScanbeamList m_Scanbeam; +}; +//------------------------------------------------------------------------------ + +class Clipper : public virtual ClipperBase +{ +public: + Clipper(int initOptions = 0); + bool Execute(ClipType clipType, + Paths &solution, + PolyFillType fillType = pftEvenOdd); + bool Execute(ClipType clipType, + Paths &solution, + PolyFillType subjFillType, + PolyFillType clipFillType); + bool Execute(ClipType clipType, + PolyTree &polytree, + PolyFillType fillType = pftEvenOdd); + bool Execute(ClipType clipType, + PolyTree &polytree, + PolyFillType subjFillType, + PolyFillType clipFillType); + bool ReverseSolution() { return m_ReverseOutput; }; + void ReverseSolution(bool value) {m_ReverseOutput = value;}; + bool StrictlySimple() {return m_StrictSimple;}; + void StrictlySimple(bool value) {m_StrictSimple = value;}; + //set the callback function for z value filling on intersections (otherwise Z is 0) +#ifdef use_xyz + void ZFillFunction(ZFillCallback zFillFunc); +#endif +protected: + virtual bool ExecuteInternal(); +private: + JoinList m_Joins; + JoinList m_GhostJoins; + IntersectList m_IntersectList; + ClipType m_ClipType; + typedef std::list MaximaList; + MaximaList m_Maxima; + TEdge *m_SortedEdges; + bool m_ExecuteLocked; + PolyFillType m_ClipFillType; + PolyFillType m_SubjFillType; + bool m_ReverseOutput; + bool m_UsingPolyTree; + bool m_StrictSimple; +#ifdef use_xyz + ZFillCallback m_ZFill; //custom callback +#endif + void SetWindingCount(TEdge& edge); + bool IsEvenOddFillType(const TEdge& edge) const; + bool IsEvenOddAltFillType(const TEdge& edge) const; + void InsertLocalMinimaIntoAEL(const cInt botY); + void InsertEdgeIntoAEL(TEdge *edge, TEdge* startEdge); + void AddEdgeToSEL(TEdge *edge); + bool PopEdgeFromSEL(TEdge *&edge); + void CopyAELToSEL(); + void DeleteFromSEL(TEdge *e); + void SwapPositionsInSEL(TEdge *edge1, TEdge *edge2); + bool IsContributing(const TEdge& edge) const; + bool IsTopHorz(const cInt XPos); + void DoMaxima(TEdge *e); + void ProcessHorizontals(); + void ProcessHorizontal(TEdge *horzEdge); + void AddLocalMaxPoly(TEdge *e1, TEdge *e2, const IntPoint &pt); + OutPt* AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &pt); + OutRec* GetOutRec(int idx); + void AppendPolygon(TEdge *e1, TEdge *e2); + void IntersectEdges(TEdge *e1, TEdge *e2, IntPoint &pt); + OutPt* AddOutPt(TEdge *e, const IntPoint &pt); + OutPt* GetLastOutPt(TEdge *e); + bool ProcessIntersections(const cInt topY); + void BuildIntersectList(const cInt topY); + void ProcessIntersectList(); + void ProcessEdgesAtTopOfScanbeam(const cInt topY); + void BuildResult(Paths& polys); + void BuildResult2(PolyTree& polytree); + void SetHoleState(TEdge *e, OutRec *outrec); + void DisposeIntersectNodes(); + bool FixupIntersectionOrder(); + void FixupOutPolygon(OutRec &outrec); + void FixupOutPolyline(OutRec &outrec); + bool IsHole(TEdge *e); + bool FindOwnerFromSplitRecs(OutRec &outRec, OutRec *&currOrfl); + void FixHoleLinkage(OutRec &outrec); + void AddJoin(OutPt *op1, OutPt *op2, const IntPoint offPt); + void ClearJoins(); + void ClearGhostJoins(); + void AddGhostJoin(OutPt *op, const IntPoint offPt); + bool JoinPoints(Join *j, OutRec* outRec1, OutRec* outRec2); + void JoinCommonEdges(); + void DoSimplePolygons(); + void FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec); + void FixupFirstLefts2(OutRec* InnerOutRec, OutRec* OuterOutRec); + void FixupFirstLefts3(OutRec* OldOutRec, OutRec* NewOutRec); +#ifdef use_xyz + void SetZ(IntPoint& pt, TEdge& e1, TEdge& e2); +#endif +}; +//------------------------------------------------------------------------------ + +class ClipperOffset +{ +public: + ClipperOffset(double miterLimit = 2.0, double roundPrecision = 0.25); + ~ClipperOffset(); + void AddPath(const Path& path, JoinType joinType, EndType endType); + void AddPaths(const Paths& paths, JoinType joinType, EndType endType); + void Execute(Paths& solution, double delta); + void Execute(PolyTree& solution, double delta); + void Clear(); + double MiterLimit; + double ArcTolerance; +private: + Paths m_destPolys; + Path m_srcPoly; + Path m_destPoly; + std::vector m_normals; + double m_delta, m_sinA, m_sin, m_cos; + double m_miterLim, m_StepsPerRad; + IntPoint m_lowest; + PolyNode m_polyNodes; + + void FixOrientations(); + void DoOffset(double delta); + void OffsetPoint(int j, int& k, JoinType jointype); + void DoSquare(int j, int k); + void DoMiter(int j, int k, double r); + void DoRound(int j, int k); +}; +//------------------------------------------------------------------------------ + +class clipperException : public std::exception +{ + public: + clipperException(const char* description): m_descr(description) {} + virtual ~clipperException() throw() {} + virtual const char* what() const throw() {return m_descr.c_str();} + private: + std::string m_descr; +}; +//------------------------------------------------------------------------------ + +} //ClipperLib namespace + +#endif //clipper_hpp + + diff --git a/ptocr/postprocess/dbprocess/include/postprocess_op.h b/ptocr/postprocess/dbprocess/include/postprocess_op.h new file mode 100644 index 0000000..3eee75d --- /dev/null +++ b/ptocr/postprocess/dbprocess/include/postprocess_op.h @@ -0,0 +1,91 @@ +// Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include "opencv2/core.hpp" +#include "opencv2/imgcodecs.hpp" +#include "opencv2/imgproc.hpp" +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "clipper.h" +// #include "utility.h" + +using namespace std; + +namespace cppdbprocess { + +class PostProcessor { +public: + void GetContourArea(const std::vector> &box, + float unclip_ratio, float &distance); + + cv::RotatedRect UnClip(std::vector> box, + const float &unclip_ratio); + + float **Mat2Vec(cv::Mat mat); + + std::vector> + OrderPointsClockwise(std::vector> pts); + + std::vector> GetMiniBoxes(cv::RotatedRect box, + float &ssid); + + float BoxScoreFast(std::vector> box_array, cv::Mat pred); + + std::vector>> + BoxesFromBitmap(const cv::Mat pred, const cv::Mat bitmap, + const float &box_thresh, const float &det_db_unclip_ratio); + + std::vector>> + FilterTagDetRes(std::vector>> boxes, + float ratio_h, float ratio_w, cv::Mat srcimg); + +private: + static bool XsortInt(std::vector a, std::vector b); + + static bool XsortFp32(std::vector a, std::vector b); + + std::vector> Mat2Vector(cv::Mat mat); + + inline int _max(int a, int b) { return a >= b ? a : b; } + + inline int _min(int a, int b) { return a >= b ? b : a; } + + template inline T clamp(T x, T min, T max) { + if (x > max) + return max; + if (x < min) + return min; + return x; + } + + inline float clampf(float x, float min, float max) { + if (x > max) + return max; + if (x < min) + return min; + return x; + } +}; + +} // namespace PaddleOCR diff --git a/ptocr/postprocess/dbprocess/include/pybind11/attr.h b/ptocr/postprocess/dbprocess/include/pybind11/attr.h new file mode 100644 index 0000000..8732cfe --- /dev/null +++ b/ptocr/postprocess/dbprocess/include/pybind11/attr.h @@ -0,0 +1,492 @@ +/* + pybind11/attr.h: Infrastructure for processing custom + type and function attributes + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "cast.h" + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) + +/// \addtogroup annotations +/// @{ + +/// Annotation for methods +struct is_method { handle class_; is_method(const handle &c) : class_(c) { } }; + +/// Annotation for operators +struct is_operator { }; + +/// Annotation for parent scope +struct scope { handle value; scope(const handle &s) : value(s) { } }; + +/// Annotation for documentation +struct doc { const char *value; doc(const char *value) : value(value) { } }; + +/// Annotation for function names +struct name { const char *value; name(const char *value) : value(value) { } }; + +/// Annotation indicating that a function is an overload associated with a given "sibling" +struct sibling { handle value; sibling(const handle &value) : value(value.ptr()) { } }; + +/// Annotation indicating that a class derives from another given type +template struct base { + PYBIND11_DEPRECATED("base() was deprecated in favor of specifying 'T' as a template argument to class_") + base() { } +}; + +/// Keep patient alive while nurse lives +template struct keep_alive { }; + +/// Annotation indicating that a class is involved in a multiple inheritance relationship +struct multiple_inheritance { }; + +/// Annotation which enables dynamic attributes, i.e. adds `__dict__` to a class +struct dynamic_attr { }; + +/// Annotation which enables the buffer protocol for a type +struct buffer_protocol { }; + +/// Annotation which requests that a special metaclass is created for a type +struct metaclass { + handle value; + + PYBIND11_DEPRECATED("py::metaclass() is no longer required. It's turned on by default now.") + metaclass() {} + + /// Override pybind11's default metaclass + explicit metaclass(handle value) : value(value) { } +}; + +/// Annotation that marks a class as local to the module: +struct module_local { const bool value; constexpr module_local(bool v = true) : value(v) { } }; + +/// Annotation to mark enums as an arithmetic type +struct arithmetic { }; + +/** \rst + A call policy which places one or more guard variables (``Ts...``) around the function call. + + For example, this definition: + + .. code-block:: cpp + + m.def("foo", foo, py::call_guard()); + + is equivalent to the following pseudocode: + + .. code-block:: cpp + + m.def("foo", [](args...) { + T scope_guard; + return foo(args...); // forwarded arguments + }); + \endrst */ +template struct call_guard; + +template <> struct call_guard<> { using type = detail::void_type; }; + +template +struct call_guard { + static_assert(std::is_default_constructible::value, + "The guard type must be default constructible"); + + using type = T; +}; + +template +struct call_guard { + struct type { + T guard{}; // Compose multiple guard types with left-to-right default-constructor order + typename call_guard::type next{}; + }; +}; + +/// @} annotations + +NAMESPACE_BEGIN(detail) +/* Forward declarations */ +enum op_id : int; +enum op_type : int; +struct undefined_t; +template struct op_; +inline void keep_alive_impl(size_t Nurse, size_t Patient, function_call &call, handle ret); + +/// Internal data structure which holds metadata about a keyword argument +struct argument_record { + const char *name; ///< Argument name + const char *descr; ///< Human-readable version of the argument value + handle value; ///< Associated Python object + bool convert : 1; ///< True if the argument is allowed to convert when loading + bool none : 1; ///< True if None is allowed when loading + + argument_record(const char *name, const char *descr, handle value, bool convert, bool none) + : name(name), descr(descr), value(value), convert(convert), none(none) { } +}; + +/// Internal data structure which holds metadata about a bound function (signature, overloads, etc.) +struct function_record { + function_record() + : is_constructor(false), is_new_style_constructor(false), is_stateless(false), + is_operator(false), has_args(false), has_kwargs(false), is_method(false) { } + + /// Function name + char *name = nullptr; /* why no C++ strings? They generate heavier code.. */ + + // User-specified documentation string + char *doc = nullptr; + + /// Human-readable version of the function signature + char *signature = nullptr; + + /// List of registered keyword arguments + std::vector args; + + /// Pointer to lambda function which converts arguments and performs the actual call + handle (*impl) (function_call &) = nullptr; + + /// Storage for the wrapped function pointer and captured data, if any + void *data[3] = { }; + + /// Pointer to custom destructor for 'data' (if needed) + void (*free_data) (function_record *ptr) = nullptr; + + /// Return value policy associated with this function + return_value_policy policy = return_value_policy::automatic; + + /// True if name == '__init__' + bool is_constructor : 1; + + /// True if this is a new-style `__init__` defined in `detail/init.h` + bool is_new_style_constructor : 1; + + /// True if this is a stateless function pointer + bool is_stateless : 1; + + /// True if this is an operator (__add__), etc. + bool is_operator : 1; + + /// True if the function has a '*args' argument + bool has_args : 1; + + /// True if the function has a '**kwargs' argument + bool has_kwargs : 1; + + /// True if this is a method + bool is_method : 1; + + /// Number of arguments (including py::args and/or py::kwargs, if present) + std::uint16_t nargs; + + /// Python method object + PyMethodDef *def = nullptr; + + /// Python handle to the parent scope (a class or a module) + handle scope; + + /// Python handle to the sibling function representing an overload chain + handle sibling; + + /// Pointer to next overload + function_record *next = nullptr; +}; + +/// Special data structure which (temporarily) holds metadata about a bound class +struct type_record { + PYBIND11_NOINLINE type_record() + : multiple_inheritance(false), dynamic_attr(false), buffer_protocol(false), module_local(false) { } + + /// Handle to the parent scope + handle scope; + + /// Name of the class + const char *name = nullptr; + + // Pointer to RTTI type_info data structure + const std::type_info *type = nullptr; + + /// How large is the underlying C++ type? + size_t type_size = 0; + + /// What is the alignment of the underlying C++ type? + size_t type_align = 0; + + /// How large is the type's holder? + size_t holder_size = 0; + + /// The global operator new can be overridden with a class-specific variant + void *(*operator_new)(size_t) = nullptr; + + /// Function pointer to class_<..>::init_instance + void (*init_instance)(instance *, const void *) = nullptr; + + /// Function pointer to class_<..>::dealloc + void (*dealloc)(detail::value_and_holder &) = nullptr; + + /// List of base classes of the newly created type + list bases; + + /// Optional docstring + const char *doc = nullptr; + + /// Custom metaclass (optional) + handle metaclass; + + /// Multiple inheritance marker + bool multiple_inheritance : 1; + + /// Does the class manage a __dict__? + bool dynamic_attr : 1; + + /// Does the class implement the buffer protocol? + bool buffer_protocol : 1; + + /// Is the default (unique_ptr) holder type used? + bool default_holder : 1; + + /// Is the class definition local to the module shared object? + bool module_local : 1; + + PYBIND11_NOINLINE void add_base(const std::type_info &base, void *(*caster)(void *)) { + auto base_info = detail::get_type_info(base, false); + if (!base_info) { + std::string tname(base.name()); + detail::clean_type_id(tname); + pybind11_fail("generic_type: type \"" + std::string(name) + + "\" referenced unknown base type \"" + tname + "\""); + } + + if (default_holder != base_info->default_holder) { + std::string tname(base.name()); + detail::clean_type_id(tname); + pybind11_fail("generic_type: type \"" + std::string(name) + "\" " + + (default_holder ? "does not have" : "has") + + " a non-default holder type while its base \"" + tname + "\" " + + (base_info->default_holder ? "does not" : "does")); + } + + bases.append((PyObject *) base_info->type); + + if (base_info->type->tp_dictoffset != 0) + dynamic_attr = true; + + if (caster) + base_info->implicit_casts.emplace_back(type, caster); + } +}; + +inline function_call::function_call(const function_record &f, handle p) : + func(f), parent(p) { + args.reserve(f.nargs); + args_convert.reserve(f.nargs); +} + +/// Tag for a new-style `__init__` defined in `detail/init.h` +struct is_new_style_constructor { }; + +/** + * Partial template specializations to process custom attributes provided to + * cpp_function_ and class_. These are either used to initialize the respective + * fields in the type_record and function_record data structures or executed at + * runtime to deal with custom call policies (e.g. keep_alive). + */ +template struct process_attribute; + +template struct process_attribute_default { + /// Default implementation: do nothing + static void init(const T &, function_record *) { } + static void init(const T &, type_record *) { } + static void precall(function_call &) { } + static void postcall(function_call &, handle) { } +}; + +/// Process an attribute specifying the function's name +template <> struct process_attribute : process_attribute_default { + static void init(const name &n, function_record *r) { r->name = const_cast(n.value); } +}; + +/// Process an attribute specifying the function's docstring +template <> struct process_attribute : process_attribute_default { + static void init(const doc &n, function_record *r) { r->doc = const_cast(n.value); } +}; + +/// Process an attribute specifying the function's docstring (provided as a C-style string) +template <> struct process_attribute : process_attribute_default { + static void init(const char *d, function_record *r) { r->doc = const_cast(d); } + static void init(const char *d, type_record *r) { r->doc = const_cast(d); } +}; +template <> struct process_attribute : process_attribute { }; + +/// Process an attribute indicating the function's return value policy +template <> struct process_attribute : process_attribute_default { + static void init(const return_value_policy &p, function_record *r) { r->policy = p; } +}; + +/// Process an attribute which indicates that this is an overloaded function associated with a given sibling +template <> struct process_attribute : process_attribute_default { + static void init(const sibling &s, function_record *r) { r->sibling = s.value; } +}; + +/// Process an attribute which indicates that this function is a method +template <> struct process_attribute : process_attribute_default { + static void init(const is_method &s, function_record *r) { r->is_method = true; r->scope = s.class_; } +}; + +/// Process an attribute which indicates the parent scope of a method +template <> struct process_attribute : process_attribute_default { + static void init(const scope &s, function_record *r) { r->scope = s.value; } +}; + +/// Process an attribute which indicates that this function is an operator +template <> struct process_attribute : process_attribute_default { + static void init(const is_operator &, function_record *r) { r->is_operator = true; } +}; + +template <> struct process_attribute : process_attribute_default { + static void init(const is_new_style_constructor &, function_record *r) { r->is_new_style_constructor = true; } +}; + +/// Process a keyword argument attribute (*without* a default value) +template <> struct process_attribute : process_attribute_default { + static void init(const arg &a, function_record *r) { + if (r->is_method && r->args.empty()) + r->args.emplace_back("self", nullptr, handle(), true /*convert*/, false /*none not allowed*/); + r->args.emplace_back(a.name, nullptr, handle(), !a.flag_noconvert, a.flag_none); + } +}; + +/// Process a keyword argument attribute (*with* a default value) +template <> struct process_attribute : process_attribute_default { + static void init(const arg_v &a, function_record *r) { + if (r->is_method && r->args.empty()) + r->args.emplace_back("self", nullptr /*descr*/, handle() /*parent*/, true /*convert*/, false /*none not allowed*/); + + if (!a.value) { +#if !defined(NDEBUG) + std::string descr("'"); + if (a.name) descr += std::string(a.name) + ": "; + descr += a.type + "'"; + if (r->is_method) { + if (r->name) + descr += " in method '" + (std::string) str(r->scope) + "." + (std::string) r->name + "'"; + else + descr += " in method of '" + (std::string) str(r->scope) + "'"; + } else if (r->name) { + descr += " in function '" + (std::string) r->name + "'"; + } + pybind11_fail("arg(): could not convert default argument " + + descr + " into a Python object (type not registered yet?)"); +#else + pybind11_fail("arg(): could not convert default argument " + "into a Python object (type not registered yet?). " + "Compile in debug mode for more information."); +#endif + } + r->args.emplace_back(a.name, a.descr, a.value.inc_ref(), !a.flag_noconvert, a.flag_none); + } +}; + +/// Process a parent class attribute. Single inheritance only (class_ itself already guarantees that) +template +struct process_attribute::value>> : process_attribute_default { + static void init(const handle &h, type_record *r) { r->bases.append(h); } +}; + +/// Process a parent class attribute (deprecated, does not support multiple inheritance) +template +struct process_attribute> : process_attribute_default> { + static void init(const base &, type_record *r) { r->add_base(typeid(T), nullptr); } +}; + +/// Process a multiple inheritance attribute +template <> +struct process_attribute : process_attribute_default { + static void init(const multiple_inheritance &, type_record *r) { r->multiple_inheritance = true; } +}; + +template <> +struct process_attribute : process_attribute_default { + static void init(const dynamic_attr &, type_record *r) { r->dynamic_attr = true; } +}; + +template <> +struct process_attribute : process_attribute_default { + static void init(const buffer_protocol &, type_record *r) { r->buffer_protocol = true; } +}; + +template <> +struct process_attribute : process_attribute_default { + static void init(const metaclass &m, type_record *r) { r->metaclass = m.value; } +}; + +template <> +struct process_attribute : process_attribute_default { + static void init(const module_local &l, type_record *r) { r->module_local = l.value; } +}; + +/// Process an 'arithmetic' attribute for enums (does nothing here) +template <> +struct process_attribute : process_attribute_default {}; + +template +struct process_attribute> : process_attribute_default> { }; + +/** + * Process a keep_alive call policy -- invokes keep_alive_impl during the + * pre-call handler if both Nurse, Patient != 0 and use the post-call handler + * otherwise + */ +template struct process_attribute> : public process_attribute_default> { + template = 0> + static void precall(function_call &call) { keep_alive_impl(Nurse, Patient, call, handle()); } + template = 0> + static void postcall(function_call &, handle) { } + template = 0> + static void precall(function_call &) { } + template = 0> + static void postcall(function_call &call, handle ret) { keep_alive_impl(Nurse, Patient, call, ret); } +}; + +/// Recursively iterate over variadic template arguments +template struct process_attributes { + static void init(const Args&... args, function_record *r) { + int unused[] = { 0, (process_attribute::type>::init(args, r), 0) ... }; + ignore_unused(unused); + } + static void init(const Args&... args, type_record *r) { + int unused[] = { 0, (process_attribute::type>::init(args, r), 0) ... }; + ignore_unused(unused); + } + static void precall(function_call &call) { + int unused[] = { 0, (process_attribute::type>::precall(call), 0) ... }; + ignore_unused(unused); + } + static void postcall(function_call &call, handle fn_ret) { + int unused[] = { 0, (process_attribute::type>::postcall(call, fn_ret), 0) ... }; + ignore_unused(unused); + } +}; + +template +using is_call_guard = is_instantiation; + +/// Extract the ``type`` from the first `call_guard` in `Extras...` (or `void_type` if none found) +template +using extract_guard_t = typename exactly_one_t, Extra...>::type; + +/// Check the number of named arguments at compile time +template ::value...), + size_t self = constexpr_sum(std::is_same::value...)> +constexpr bool expected_num_args(size_t nargs, bool has_args, bool has_kwargs) { + return named == 0 || (self + named + has_args + has_kwargs) == nargs; +} + +NAMESPACE_END(detail) +NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/ptocr/postprocess/dbprocess/include/pybind11/buffer_info.h b/ptocr/postprocess/dbprocess/include/pybind11/buffer_info.h new file mode 100644 index 0000000..9f072fa --- /dev/null +++ b/ptocr/postprocess/dbprocess/include/pybind11/buffer_info.h @@ -0,0 +1,108 @@ +/* + pybind11/buffer_info.h: Python buffer object interface + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "detail/common.h" + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) + +/// Information record describing a Python buffer object +struct buffer_info { + void *ptr = nullptr; // Pointer to the underlying storage + ssize_t itemsize = 0; // Size of individual items in bytes + ssize_t size = 0; // Total number of entries + std::string format; // For homogeneous buffers, this should be set to format_descriptor::format() + ssize_t ndim = 0; // Number of dimensions + std::vector shape; // Shape of the tensor (1 entry per dimension) + std::vector strides; // Number of entries between adjacent entries (for each per dimension) + + buffer_info() { } + + buffer_info(void *ptr, ssize_t itemsize, const std::string &format, ssize_t ndim, + detail::any_container shape_in, detail::any_container strides_in) + : ptr(ptr), itemsize(itemsize), size(1), format(format), ndim(ndim), + shape(std::move(shape_in)), strides(std::move(strides_in)) { + if (ndim != (ssize_t) shape.size() || ndim != (ssize_t) strides.size()) + pybind11_fail("buffer_info: ndim doesn't match shape and/or strides length"); + for (size_t i = 0; i < (size_t) ndim; ++i) + size *= shape[i]; + } + + template + buffer_info(T *ptr, detail::any_container shape_in, detail::any_container strides_in) + : buffer_info(private_ctr_tag(), ptr, sizeof(T), format_descriptor::format(), static_cast(shape_in->size()), std::move(shape_in), std::move(strides_in)) { } + + buffer_info(void *ptr, ssize_t itemsize, const std::string &format, ssize_t size) + : buffer_info(ptr, itemsize, format, 1, {size}, {itemsize}) { } + + template + buffer_info(T *ptr, ssize_t size) + : buffer_info(ptr, sizeof(T), format_descriptor::format(), size) { } + + explicit buffer_info(Py_buffer *view, bool ownview = true) + : buffer_info(view->buf, view->itemsize, view->format, view->ndim, + {view->shape, view->shape + view->ndim}, {view->strides, view->strides + view->ndim}) { + this->view = view; + this->ownview = ownview; + } + + buffer_info(const buffer_info &) = delete; + buffer_info& operator=(const buffer_info &) = delete; + + buffer_info(buffer_info &&other) { + (*this) = std::move(other); + } + + buffer_info& operator=(buffer_info &&rhs) { + ptr = rhs.ptr; + itemsize = rhs.itemsize; + size = rhs.size; + format = std::move(rhs.format); + ndim = rhs.ndim; + shape = std::move(rhs.shape); + strides = std::move(rhs.strides); + std::swap(view, rhs.view); + std::swap(ownview, rhs.ownview); + return *this; + } + + ~buffer_info() { + if (view && ownview) { PyBuffer_Release(view); delete view; } + } + +private: + struct private_ctr_tag { }; + + buffer_info(private_ctr_tag, void *ptr, ssize_t itemsize, const std::string &format, ssize_t ndim, + detail::any_container &&shape_in, detail::any_container &&strides_in) + : buffer_info(ptr, itemsize, format, ndim, std::move(shape_in), std::move(strides_in)) { } + + Py_buffer *view = nullptr; + bool ownview = false; +}; + +NAMESPACE_BEGIN(detail) + +template struct compare_buffer_info { + static bool compare(const buffer_info& b) { + return b.format == format_descriptor::format() && b.itemsize == (ssize_t) sizeof(T); + } +}; + +template struct compare_buffer_info::value>> { + static bool compare(const buffer_info& b) { + return (size_t) b.itemsize == sizeof(T) && (b.format == format_descriptor::value || + ((sizeof(T) == sizeof(long)) && b.format == (std::is_unsigned::value ? "L" : "l")) || + ((sizeof(T) == sizeof(size_t)) && b.format == (std::is_unsigned::value ? "N" : "n"))); + } +}; + +NAMESPACE_END(detail) +NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/ptocr/postprocess/dbprocess/include/pybind11/cast.h b/ptocr/postprocess/dbprocess/include/pybind11/cast.h new file mode 100644 index 0000000..80abb2b --- /dev/null +++ b/ptocr/postprocess/dbprocess/include/pybind11/cast.h @@ -0,0 +1,2128 @@ +/* + pybind11/cast.h: Partial template specializations to cast between + C++ and Python types + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "pytypes.h" +#include "detail/typeid.h" +#include "detail/descr.h" +#include "detail/internals.h" +#include +#include +#include +#include + +#if defined(PYBIND11_CPP17) +# if defined(__has_include) +# if __has_include() +# define PYBIND11_HAS_STRING_VIEW +# endif +# elif defined(_MSC_VER) +# define PYBIND11_HAS_STRING_VIEW +# endif +#endif +#ifdef PYBIND11_HAS_STRING_VIEW +#include +#endif + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +NAMESPACE_BEGIN(detail) + +/// A life support system for temporary objects created by `type_caster::load()`. +/// Adding a patient will keep it alive up until the enclosing function returns. +class loader_life_support { +public: + /// A new patient frame is created when a function is entered + loader_life_support() { + get_internals().loader_patient_stack.push_back(nullptr); + } + + /// ... and destroyed after it returns + ~loader_life_support() { + auto &stack = get_internals().loader_patient_stack; + if (stack.empty()) + pybind11_fail("loader_life_support: internal error"); + + auto ptr = stack.back(); + stack.pop_back(); + Py_CLEAR(ptr); + + // A heuristic to reduce the stack's capacity (e.g. after long recursive calls) + if (stack.capacity() > 16 && stack.size() != 0 && stack.capacity() / stack.size() > 2) + stack.shrink_to_fit(); + } + + /// This can only be used inside a pybind11-bound function, either by `argument_loader` + /// at argument preparation time or by `py::cast()` at execution time. + PYBIND11_NOINLINE static void add_patient(handle h) { + auto &stack = get_internals().loader_patient_stack; + if (stack.empty()) + throw cast_error("When called outside a bound function, py::cast() cannot " + "do Python -> C++ conversions which require the creation " + "of temporary values"); + + auto &list_ptr = stack.back(); + if (list_ptr == nullptr) { + list_ptr = PyList_New(1); + if (!list_ptr) + pybind11_fail("loader_life_support: error allocating list"); + PyList_SET_ITEM(list_ptr, 0, h.inc_ref().ptr()); + } else { + auto result = PyList_Append(list_ptr, h.ptr()); + if (result == -1) + pybind11_fail("loader_life_support: error adding patient"); + } + } +}; + +// Gets the cache entry for the given type, creating it if necessary. The return value is the pair +// returned by emplace, i.e. an iterator for the entry and a bool set to `true` if the entry was +// just created. +inline std::pair all_type_info_get_cache(PyTypeObject *type); + +// Populates a just-created cache entry. +PYBIND11_NOINLINE inline void all_type_info_populate(PyTypeObject *t, std::vector &bases) { + std::vector check; + for (handle parent : reinterpret_borrow(t->tp_bases)) + check.push_back((PyTypeObject *) parent.ptr()); + + auto const &type_dict = get_internals().registered_types_py; + for (size_t i = 0; i < check.size(); i++) { + auto type = check[i]; + // Ignore Python2 old-style class super type: + if (!PyType_Check((PyObject *) type)) continue; + + // Check `type` in the current set of registered python types: + auto it = type_dict.find(type); + if (it != type_dict.end()) { + // We found a cache entry for it, so it's either pybind-registered or has pre-computed + // pybind bases, but we have to make sure we haven't already seen the type(s) before: we + // want to follow Python/virtual C++ rules that there should only be one instance of a + // common base. + for (auto *tinfo : it->second) { + // NB: Could use a second set here, rather than doing a linear search, but since + // having a large number of immediate pybind11-registered types seems fairly + // unlikely, that probably isn't worthwhile. + bool found = false; + for (auto *known : bases) { + if (known == tinfo) { found = true; break; } + } + if (!found) bases.push_back(tinfo); + } + } + else if (type->tp_bases) { + // It's some python type, so keep follow its bases classes to look for one or more + // registered types + if (i + 1 == check.size()) { + // When we're at the end, we can pop off the current element to avoid growing + // `check` when adding just one base (which is typical--i.e. when there is no + // multiple inheritance) + check.pop_back(); + i--; + } + for (handle parent : reinterpret_borrow(type->tp_bases)) + check.push_back((PyTypeObject *) parent.ptr()); + } + } +} + +/** + * Extracts vector of type_info pointers of pybind-registered roots of the given Python type. Will + * be just 1 pybind type for the Python type of a pybind-registered class, or for any Python-side + * derived class that uses single inheritance. Will contain as many types as required for a Python + * class that uses multiple inheritance to inherit (directly or indirectly) from multiple + * pybind-registered classes. Will be empty if neither the type nor any base classes are + * pybind-registered. + * + * The value is cached for the lifetime of the Python type. + */ +inline const std::vector &all_type_info(PyTypeObject *type) { + auto ins = all_type_info_get_cache(type); + if (ins.second) + // New cache entry: populate it + all_type_info_populate(type, ins.first->second); + + return ins.first->second; +} + +/** + * Gets a single pybind11 type info for a python type. Returns nullptr if neither the type nor any + * ancestors are pybind11-registered. Throws an exception if there are multiple bases--use + * `all_type_info` instead if you want to support multiple bases. + */ +PYBIND11_NOINLINE inline detail::type_info* get_type_info(PyTypeObject *type) { + auto &bases = all_type_info(type); + if (bases.size() == 0) + return nullptr; + if (bases.size() > 1) + pybind11_fail("pybind11::detail::get_type_info: type has multiple pybind11-registered bases"); + return bases.front(); +} + +inline detail::type_info *get_local_type_info(const std::type_index &tp) { + auto &locals = registered_local_types_cpp(); + auto it = locals.find(tp); + if (it != locals.end()) + return it->second; + return nullptr; +} + +inline detail::type_info *get_global_type_info(const std::type_index &tp) { + auto &types = get_internals().registered_types_cpp; + auto it = types.find(tp); + if (it != types.end()) + return it->second; + return nullptr; +} + +/// Return the type info for a given C++ type; on lookup failure can either throw or return nullptr. +PYBIND11_NOINLINE inline detail::type_info *get_type_info(const std::type_index &tp, + bool throw_if_missing = false) { + if (auto ltype = get_local_type_info(tp)) + return ltype; + if (auto gtype = get_global_type_info(tp)) + return gtype; + + if (throw_if_missing) { + std::string tname = tp.name(); + detail::clean_type_id(tname); + pybind11_fail("pybind11::detail::get_type_info: unable to find type info for \"" + tname + "\""); + } + return nullptr; +} + +PYBIND11_NOINLINE inline handle get_type_handle(const std::type_info &tp, bool throw_if_missing) { + detail::type_info *type_info = get_type_info(tp, throw_if_missing); + return handle(type_info ? ((PyObject *) type_info->type) : nullptr); +} + +struct value_and_holder { + instance *inst; + size_t index; + const detail::type_info *type; + void **vh; + + // Main constructor for a found value/holder: + value_and_holder(instance *i, const detail::type_info *type, size_t vpos, size_t index) : + inst{i}, index{index}, type{type}, + vh{inst->simple_layout ? inst->simple_value_holder : &inst->nonsimple.values_and_holders[vpos]} + {} + + // Default constructor (used to signal a value-and-holder not found by get_value_and_holder()) + value_and_holder() : inst{nullptr} {} + + // Used for past-the-end iterator + value_and_holder(size_t index) : index{index} {} + + template V *&value_ptr() const { + return reinterpret_cast(vh[0]); + } + // True if this `value_and_holder` has a non-null value pointer + explicit operator bool() const { return value_ptr(); } + + template H &holder() const { + return reinterpret_cast(vh[1]); + } + bool holder_constructed() const { + return inst->simple_layout + ? inst->simple_holder_constructed + : inst->nonsimple.status[index] & instance::status_holder_constructed; + } + void set_holder_constructed(bool v = true) { + if (inst->simple_layout) + inst->simple_holder_constructed = v; + else if (v) + inst->nonsimple.status[index] |= instance::status_holder_constructed; + else + inst->nonsimple.status[index] &= (uint8_t) ~instance::status_holder_constructed; + } + bool instance_registered() const { + return inst->simple_layout + ? inst->simple_instance_registered + : inst->nonsimple.status[index] & instance::status_instance_registered; + } + void set_instance_registered(bool v = true) { + if (inst->simple_layout) + inst->simple_instance_registered = v; + else if (v) + inst->nonsimple.status[index] |= instance::status_instance_registered; + else + inst->nonsimple.status[index] &= (uint8_t) ~instance::status_instance_registered; + } +}; + +// Container for accessing and iterating over an instance's values/holders +struct values_and_holders { +private: + instance *inst; + using type_vec = std::vector; + const type_vec &tinfo; + +public: + values_and_holders(instance *inst) : inst{inst}, tinfo(all_type_info(Py_TYPE(inst))) {} + + struct iterator { + private: + instance *inst; + const type_vec *types; + value_and_holder curr; + friend struct values_and_holders; + iterator(instance *inst, const type_vec *tinfo) + : inst{inst}, types{tinfo}, + curr(inst /* instance */, + types->empty() ? nullptr : (*types)[0] /* type info */, + 0, /* vpos: (non-simple types only): the first vptr comes first */ + 0 /* index */) + {} + // Past-the-end iterator: + iterator(size_t end) : curr(end) {} + public: + bool operator==(const iterator &other) { return curr.index == other.curr.index; } + bool operator!=(const iterator &other) { return curr.index != other.curr.index; } + iterator &operator++() { + if (!inst->simple_layout) + curr.vh += 1 + (*types)[curr.index]->holder_size_in_ptrs; + ++curr.index; + curr.type = curr.index < types->size() ? (*types)[curr.index] : nullptr; + return *this; + } + value_and_holder &operator*() { return curr; } + value_and_holder *operator->() { return &curr; } + }; + + iterator begin() { return iterator(inst, &tinfo); } + iterator end() { return iterator(tinfo.size()); } + + iterator find(const type_info *find_type) { + auto it = begin(), endit = end(); + while (it != endit && it->type != find_type) ++it; + return it; + } + + size_t size() { return tinfo.size(); } +}; + +/** + * Extracts C++ value and holder pointer references from an instance (which may contain multiple + * values/holders for python-side multiple inheritance) that match the given type. Throws an error + * if the given type (or ValueType, if omitted) is not a pybind11 base of the given instance. If + * `find_type` is omitted (or explicitly specified as nullptr) the first value/holder are returned, + * regardless of type (and the resulting .type will be nullptr). + * + * The returned object should be short-lived: in particular, it must not outlive the called-upon + * instance. + */ +PYBIND11_NOINLINE inline value_and_holder instance::get_value_and_holder(const type_info *find_type /*= nullptr default in common.h*/, bool throw_if_missing /*= true in common.h*/) { + // Optimize common case: + if (!find_type || Py_TYPE(this) == find_type->type) + return value_and_holder(this, find_type, 0, 0); + + detail::values_and_holders vhs(this); + auto it = vhs.find(find_type); + if (it != vhs.end()) + return *it; + + if (!throw_if_missing) + return value_and_holder(); + +#if defined(NDEBUG) + pybind11_fail("pybind11::detail::instance::get_value_and_holder: " + "type is not a pybind11 base of the given instance " + "(compile in debug mode for type details)"); +#else + pybind11_fail("pybind11::detail::instance::get_value_and_holder: `" + + std::string(find_type->type->tp_name) + "' is not a pybind11 base of the given `" + + std::string(Py_TYPE(this)->tp_name) + "' instance"); +#endif +} + +PYBIND11_NOINLINE inline void instance::allocate_layout() { + auto &tinfo = all_type_info(Py_TYPE(this)); + + const size_t n_types = tinfo.size(); + + if (n_types == 0) + pybind11_fail("instance allocation failed: new instance has no pybind11-registered base types"); + + simple_layout = + n_types == 1 && tinfo.front()->holder_size_in_ptrs <= instance_simple_holder_in_ptrs(); + + // Simple path: no python-side multiple inheritance, and a small-enough holder + if (simple_layout) { + simple_value_holder[0] = nullptr; + simple_holder_constructed = false; + simple_instance_registered = false; + } + else { // multiple base types or a too-large holder + // Allocate space to hold: [v1*][h1][v2*][h2]...[bb...] where [vN*] is a value pointer, + // [hN] is the (uninitialized) holder instance for value N, and [bb...] is a set of bool + // values that tracks whether each associated holder has been initialized. Each [block] is + // padded, if necessary, to an integer multiple of sizeof(void *). + size_t space = 0; + for (auto t : tinfo) { + space += 1; // value pointer + space += t->holder_size_in_ptrs; // holder instance + } + size_t flags_at = space; + space += size_in_ptrs(n_types); // status bytes (holder_constructed and instance_registered) + + // Allocate space for flags, values, and holders, and initialize it to 0 (flags and values, + // in particular, need to be 0). Use Python's memory allocation functions: in Python 3.6 + // they default to using pymalloc, which is designed to be efficient for small allocations + // like the one we're doing here; in earlier versions (and for larger allocations) they are + // just wrappers around malloc. +#if PY_VERSION_HEX >= 0x03050000 + nonsimple.values_and_holders = (void **) PyMem_Calloc(space, sizeof(void *)); + if (!nonsimple.values_and_holders) throw std::bad_alloc(); +#else + nonsimple.values_and_holders = (void **) PyMem_New(void *, space); + if (!nonsimple.values_and_holders) throw std::bad_alloc(); + std::memset(nonsimple.values_and_holders, 0, space * sizeof(void *)); +#endif + nonsimple.status = reinterpret_cast(&nonsimple.values_and_holders[flags_at]); + } + owned = true; +} + +PYBIND11_NOINLINE inline void instance::deallocate_layout() { + if (!simple_layout) + PyMem_Free(nonsimple.values_and_holders); +} + +PYBIND11_NOINLINE inline bool isinstance_generic(handle obj, const std::type_info &tp) { + handle type = detail::get_type_handle(tp, false); + if (!type) + return false; + return isinstance(obj, type); +} + +PYBIND11_NOINLINE inline std::string error_string() { + if (!PyErr_Occurred()) { + PyErr_SetString(PyExc_RuntimeError, "Unknown internal error occurred"); + return "Unknown internal error occurred"; + } + + error_scope scope; // Preserve error state + + std::string errorString; + if (scope.type) { + errorString += handle(scope.type).attr("__name__").cast(); + errorString += ": "; + } + if (scope.value) + errorString += (std::string) str(scope.value); + + PyErr_NormalizeException(&scope.type, &scope.value, &scope.trace); + +#if PY_MAJOR_VERSION >= 3 + if (scope.trace != nullptr) + PyException_SetTraceback(scope.value, scope.trace); +#endif + +#if !defined(PYPY_VERSION) + if (scope.trace) { + PyTracebackObject *trace = (PyTracebackObject *) scope.trace; + + /* Get the deepest trace possible */ + while (trace->tb_next) + trace = trace->tb_next; + + PyFrameObject *frame = trace->tb_frame; + errorString += "\n\nAt:\n"; + while (frame) { + int lineno = PyFrame_GetLineNumber(frame); + errorString += + " " + handle(frame->f_code->co_filename).cast() + + "(" + std::to_string(lineno) + "): " + + handle(frame->f_code->co_name).cast() + "\n"; + frame = frame->f_back; + } + } +#endif + + return errorString; +} + +PYBIND11_NOINLINE inline handle get_object_handle(const void *ptr, const detail::type_info *type ) { + auto &instances = get_internals().registered_instances; + auto range = instances.equal_range(ptr); + for (auto it = range.first; it != range.second; ++it) { + for (auto vh : values_and_holders(it->second)) { + if (vh.type == type) + return handle((PyObject *) it->second); + } + } + return handle(); +} + +inline PyThreadState *get_thread_state_unchecked() { +#if defined(PYPY_VERSION) + return PyThreadState_GET(); +#elif PY_VERSION_HEX < 0x03000000 + return _PyThreadState_Current; +#elif PY_VERSION_HEX < 0x03050000 + return (PyThreadState*) _Py_atomic_load_relaxed(&_PyThreadState_Current); +#elif PY_VERSION_HEX < 0x03050200 + return (PyThreadState*) _PyThreadState_Current.value; +#else + return _PyThreadState_UncheckedGet(); +#endif +} + +// Forward declarations +inline void keep_alive_impl(handle nurse, handle patient); +inline PyObject *make_new_instance(PyTypeObject *type); + +class type_caster_generic { +public: + PYBIND11_NOINLINE type_caster_generic(const std::type_info &type_info) + : typeinfo(get_type_info(type_info)), cpptype(&type_info) { } + + type_caster_generic(const type_info *typeinfo) + : typeinfo(typeinfo), cpptype(typeinfo ? typeinfo->cpptype : nullptr) { } + + bool load(handle src, bool convert) { + return load_impl(src, convert); + } + + PYBIND11_NOINLINE static handle cast(const void *_src, return_value_policy policy, handle parent, + const detail::type_info *tinfo, + void *(*copy_constructor)(const void *), + void *(*move_constructor)(const void *), + const void *existing_holder = nullptr) { + if (!tinfo) // no type info: error will be set already + return handle(); + + void *src = const_cast(_src); + if (src == nullptr) + return none().release(); + + auto it_instances = get_internals().registered_instances.equal_range(src); + for (auto it_i = it_instances.first; it_i != it_instances.second; ++it_i) { + for (auto instance_type : detail::all_type_info(Py_TYPE(it_i->second))) { + if (instance_type && same_type(*instance_type->cpptype, *tinfo->cpptype)) + return handle((PyObject *) it_i->second).inc_ref(); + } + } + + auto inst = reinterpret_steal(make_new_instance(tinfo->type)); + auto wrapper = reinterpret_cast(inst.ptr()); + wrapper->owned = false; + void *&valueptr = values_and_holders(wrapper).begin()->value_ptr(); + + switch (policy) { + case return_value_policy::automatic: + case return_value_policy::take_ownership: + valueptr = src; + wrapper->owned = true; + break; + + case return_value_policy::automatic_reference: + case return_value_policy::reference: + valueptr = src; + wrapper->owned = false; + break; + + case return_value_policy::copy: + if (copy_constructor) + valueptr = copy_constructor(src); + else + throw cast_error("return_value_policy = copy, but the " + "object is non-copyable!"); + wrapper->owned = true; + break; + + case return_value_policy::move: + if (move_constructor) + valueptr = move_constructor(src); + else if (copy_constructor) + valueptr = copy_constructor(src); + else + throw cast_error("return_value_policy = move, but the " + "object is neither movable nor copyable!"); + wrapper->owned = true; + break; + + case return_value_policy::reference_internal: + valueptr = src; + wrapper->owned = false; + keep_alive_impl(inst, parent); + break; + + default: + throw cast_error("unhandled return_value_policy: should not happen!"); + } + + tinfo->init_instance(wrapper, existing_holder); + + return inst.release(); + } + + // Base methods for generic caster; there are overridden in copyable_holder_caster + void load_value(value_and_holder &&v_h) { + auto *&vptr = v_h.value_ptr(); + // Lazy allocation for unallocated values: + if (vptr == nullptr) { + auto *type = v_h.type ? v_h.type : typeinfo; + if (type->operator_new) { + vptr = type->operator_new(type->type_size); + } else { + #if defined(PYBIND11_CPP17) + if (type->type_align > __STDCPP_DEFAULT_NEW_ALIGNMENT__) + vptr = ::operator new(type->type_size, + (std::align_val_t) type->type_align); + else + #endif + vptr = ::operator new(type->type_size); + } + } + value = vptr; + } + bool try_implicit_casts(handle src, bool convert) { + for (auto &cast : typeinfo->implicit_casts) { + type_caster_generic sub_caster(*cast.first); + if (sub_caster.load(src, convert)) { + value = cast.second(sub_caster.value); + return true; + } + } + return false; + } + bool try_direct_conversions(handle src) { + for (auto &converter : *typeinfo->direct_conversions) { + if (converter(src.ptr(), value)) + return true; + } + return false; + } + void check_holder_compat() {} + + PYBIND11_NOINLINE static void *local_load(PyObject *src, const type_info *ti) { + auto caster = type_caster_generic(ti); + if (caster.load(src, false)) + return caster.value; + return nullptr; + } + + /// Try to load with foreign typeinfo, if available. Used when there is no + /// native typeinfo, or when the native one wasn't able to produce a value. + PYBIND11_NOINLINE bool try_load_foreign_module_local(handle src) { + constexpr auto *local_key = PYBIND11_MODULE_LOCAL_ID; + const auto pytype = src.get_type(); + if (!hasattr(pytype, local_key)) + return false; + + type_info *foreign_typeinfo = reinterpret_borrow(getattr(pytype, local_key)); + // Only consider this foreign loader if actually foreign and is a loader of the correct cpp type + if (foreign_typeinfo->module_local_load == &local_load + || (cpptype && !same_type(*cpptype, *foreign_typeinfo->cpptype))) + return false; + + if (auto result = foreign_typeinfo->module_local_load(src.ptr(), foreign_typeinfo)) { + value = result; + return true; + } + return false; + } + + // Implementation of `load`; this takes the type of `this` so that it can dispatch the relevant + // bits of code between here and copyable_holder_caster where the two classes need different + // logic (without having to resort to virtual inheritance). + template + PYBIND11_NOINLINE bool load_impl(handle src, bool convert) { + if (!src) return false; + if (!typeinfo) return try_load_foreign_module_local(src); + if (src.is_none()) { + // Defer accepting None to other overloads (if we aren't in convert mode): + if (!convert) return false; + value = nullptr; + return true; + } + + auto &this_ = static_cast(*this); + this_.check_holder_compat(); + + PyTypeObject *srctype = Py_TYPE(src.ptr()); + + // Case 1: If src is an exact type match for the target type then we can reinterpret_cast + // the instance's value pointer to the target type: + if (srctype == typeinfo->type) { + this_.load_value(reinterpret_cast(src.ptr())->get_value_and_holder()); + return true; + } + // Case 2: We have a derived class + else if (PyType_IsSubtype(srctype, typeinfo->type)) { + auto &bases = all_type_info(srctype); + bool no_cpp_mi = typeinfo->simple_type; + + // Case 2a: the python type is a Python-inherited derived class that inherits from just + // one simple (no MI) pybind11 class, or is an exact match, so the C++ instance is of + // the right type and we can use reinterpret_cast. + // (This is essentially the same as case 2b, but because not using multiple inheritance + // is extremely common, we handle it specially to avoid the loop iterator and type + // pointer lookup overhead) + if (bases.size() == 1 && (no_cpp_mi || bases.front()->type == typeinfo->type)) { + this_.load_value(reinterpret_cast(src.ptr())->get_value_and_holder()); + return true; + } + // Case 2b: the python type inherits from multiple C++ bases. Check the bases to see if + // we can find an exact match (or, for a simple C++ type, an inherited match); if so, we + // can safely reinterpret_cast to the relevant pointer. + else if (bases.size() > 1) { + for (auto base : bases) { + if (no_cpp_mi ? PyType_IsSubtype(base->type, typeinfo->type) : base->type == typeinfo->type) { + this_.load_value(reinterpret_cast(src.ptr())->get_value_and_holder(base)); + return true; + } + } + } + + // Case 2c: C++ multiple inheritance is involved and we couldn't find an exact type match + // in the registered bases, above, so try implicit casting (needed for proper C++ casting + // when MI is involved). + if (this_.try_implicit_casts(src, convert)) + return true; + } + + // Perform an implicit conversion + if (convert) { + for (auto &converter : typeinfo->implicit_conversions) { + auto temp = reinterpret_steal(converter(src.ptr(), typeinfo->type)); + if (load_impl(temp, false)) { + loader_life_support::add_patient(temp); + return true; + } + } + if (this_.try_direct_conversions(src)) + return true; + } + + // Failed to match local typeinfo. Try again with global. + if (typeinfo->module_local) { + if (auto gtype = get_global_type_info(*typeinfo->cpptype)) { + typeinfo = gtype; + return load(src, false); + } + } + + // Global typeinfo has precedence over foreign module_local + return try_load_foreign_module_local(src); + } + + + // Called to do type lookup and wrap the pointer and type in a pair when a dynamic_cast + // isn't needed or can't be used. If the type is unknown, sets the error and returns a pair + // with .second = nullptr. (p.first = nullptr is not an error: it becomes None). + PYBIND11_NOINLINE static std::pair src_and_type( + const void *src, const std::type_info &cast_type, const std::type_info *rtti_type = nullptr) { + if (auto *tpi = get_type_info(cast_type)) + return {src, const_cast(tpi)}; + + // Not found, set error: + std::string tname = rtti_type ? rtti_type->name() : cast_type.name(); + detail::clean_type_id(tname); + std::string msg = "Unregistered type : " + tname; + PyErr_SetString(PyExc_TypeError, msg.c_str()); + return {nullptr, nullptr}; + } + + const type_info *typeinfo = nullptr; + const std::type_info *cpptype = nullptr; + void *value = nullptr; +}; + +/** + * Determine suitable casting operator for pointer-or-lvalue-casting type casters. The type caster + * needs to provide `operator T*()` and `operator T&()` operators. + * + * If the type supports moving the value away via an `operator T&&() &&` method, it should use + * `movable_cast_op_type` instead. + */ +template +using cast_op_type = + conditional_t>::value, + typename std::add_pointer>::type, + typename std::add_lvalue_reference>::type>; + +/** + * Determine suitable casting operator for a type caster with a movable value. Such a type caster + * needs to provide `operator T*()`, `operator T&()`, and `operator T&&() &&`. The latter will be + * called in appropriate contexts where the value can be moved rather than copied. + * + * These operator are automatically provided when using the PYBIND11_TYPE_CASTER macro. + */ +template +using movable_cast_op_type = + conditional_t::type>::value, + typename std::add_pointer>::type, + conditional_t::value, + typename std::add_rvalue_reference>::type, + typename std::add_lvalue_reference>::type>>; + +// std::is_copy_constructible isn't quite enough: it lets std::vector (and similar) through when +// T is non-copyable, but code containing such a copy constructor fails to actually compile. +template struct is_copy_constructible : std::is_copy_constructible {}; + +// Specialization for types that appear to be copy constructible but also look like stl containers +// (we specifically check for: has `value_type` and `reference` with `reference = value_type&`): if +// so, copy constructability depends on whether the value_type is copy constructible. +template struct is_copy_constructible, + std::is_same + >::value>> : is_copy_constructible {}; + +#if !defined(PYBIND11_CPP17) +// Likewise for std::pair before C++17 (which mandates that the copy constructor not exist when the +// two types aren't themselves copy constructible). +template struct is_copy_constructible> + : all_of, is_copy_constructible> {}; +#endif + +NAMESPACE_END(detail) + +// polymorphic_type_hook::get(src, tinfo) determines whether the object pointed +// to by `src` actually is an instance of some class derived from `itype`. +// If so, it sets `tinfo` to point to the std::type_info representing that derived +// type, and returns a pointer to the start of the most-derived object of that type +// (in which `src` is a subobject; this will be the same address as `src` in most +// single inheritance cases). If not, or if `src` is nullptr, it simply returns `src` +// and leaves `tinfo` at its default value of nullptr. +// +// The default polymorphic_type_hook just returns src. A specialization for polymorphic +// types determines the runtime type of the passed object and adjusts the this-pointer +// appropriately via dynamic_cast. This is what enables a C++ Animal* to appear +// to Python as a Dog (if Dog inherits from Animal, Animal is polymorphic, Dog is +// registered with pybind11, and this Animal is in fact a Dog). +// +// You may specialize polymorphic_type_hook yourself for types that want to appear +// polymorphic to Python but do not use C++ RTTI. (This is a not uncommon pattern +// in performance-sensitive applications, used most notably in LLVM.) +template +struct polymorphic_type_hook +{ + static const void *get(const itype *src, const std::type_info*&) { return src; } +}; +template +struct polymorphic_type_hook::value>> +{ + static const void *get(const itype *src, const std::type_info*& type) { + type = src ? &typeid(*src) : nullptr; + return dynamic_cast(src); + } +}; + +NAMESPACE_BEGIN(detail) + +/// Generic type caster for objects stored on the heap +template class type_caster_base : public type_caster_generic { + using itype = intrinsic_t; + +public: + static constexpr auto name = _(); + + type_caster_base() : type_caster_base(typeid(type)) { } + explicit type_caster_base(const std::type_info &info) : type_caster_generic(info) { } + + static handle cast(const itype &src, return_value_policy policy, handle parent) { + if (policy == return_value_policy::automatic || policy == return_value_policy::automatic_reference) + policy = return_value_policy::copy; + return cast(&src, policy, parent); + } + + static handle cast(itype &&src, return_value_policy, handle parent) { + return cast(&src, return_value_policy::move, parent); + } + + // Returns a (pointer, type_info) pair taking care of necessary type lookup for a + // polymorphic type (using RTTI by default, but can be overridden by specializing + // polymorphic_type_hook). If the instance isn't derived, returns the base version. + static std::pair src_and_type(const itype *src) { + auto &cast_type = typeid(itype); + const std::type_info *instance_type = nullptr; + const void *vsrc = polymorphic_type_hook::get(src, instance_type); + if (instance_type && !same_type(cast_type, *instance_type)) { + // This is a base pointer to a derived type. If the derived type is registered + // with pybind11, we want to make the full derived object available. + // In the typical case where itype is polymorphic, we get the correct + // derived pointer (which may be != base pointer) by a dynamic_cast to + // most derived type. If itype is not polymorphic, we won't get here + // except via a user-provided specialization of polymorphic_type_hook, + // and the user has promised that no this-pointer adjustment is + // required in that case, so it's OK to use static_cast. + if (const auto *tpi = get_type_info(*instance_type)) + return {vsrc, tpi}; + } + // Otherwise we have either a nullptr, an `itype` pointer, or an unknown derived pointer, so + // don't do a cast + return type_caster_generic::src_and_type(src, cast_type, instance_type); + } + + static handle cast(const itype *src, return_value_policy policy, handle parent) { + auto st = src_and_type(src); + return type_caster_generic::cast( + st.first, policy, parent, st.second, + make_copy_constructor(src), make_move_constructor(src)); + } + + static handle cast_holder(const itype *src, const void *holder) { + auto st = src_and_type(src); + return type_caster_generic::cast( + st.first, return_value_policy::take_ownership, {}, st.second, + nullptr, nullptr, holder); + } + + template using cast_op_type = detail::cast_op_type; + + operator itype*() { return (type *) value; } + operator itype&() { if (!value) throw reference_cast_error(); return *((itype *) value); } + +protected: + using Constructor = void *(*)(const void *); + + /* Only enabled when the types are {copy,move}-constructible *and* when the type + does not have a private operator new implementation. */ + template ::value>> + static auto make_copy_constructor(const T *x) -> decltype(new T(*x), Constructor{}) { + return [](const void *arg) -> void * { + return new T(*reinterpret_cast(arg)); + }; + } + + template ::value>> + static auto make_move_constructor(const T *x) -> decltype(new T(std::move(*const_cast(x))), Constructor{}) { + return [](const void *arg) -> void * { + return new T(std::move(*const_cast(reinterpret_cast(arg)))); + }; + } + + static Constructor make_copy_constructor(...) { return nullptr; } + static Constructor make_move_constructor(...) { return nullptr; } +}; + +template class type_caster : public type_caster_base { }; +template using make_caster = type_caster>; + +// Shortcut for calling a caster's `cast_op_type` cast operator for casting a type_caster to a T +template typename make_caster::template cast_op_type cast_op(make_caster &caster) { + return caster.operator typename make_caster::template cast_op_type(); +} +template typename make_caster::template cast_op_type::type> +cast_op(make_caster &&caster) { + return std::move(caster).operator + typename make_caster::template cast_op_type::type>(); +} + +template class type_caster> { +private: + using caster_t = make_caster; + caster_t subcaster; + using subcaster_cast_op_type = typename caster_t::template cast_op_type; + static_assert(std::is_same::type &, subcaster_cast_op_type>::value, + "std::reference_wrapper caster requires T to have a caster with an `T &` operator"); +public: + bool load(handle src, bool convert) { return subcaster.load(src, convert); } + static constexpr auto name = caster_t::name; + static handle cast(const std::reference_wrapper &src, return_value_policy policy, handle parent) { + // It is definitely wrong to take ownership of this pointer, so mask that rvp + if (policy == return_value_policy::take_ownership || policy == return_value_policy::automatic) + policy = return_value_policy::automatic_reference; + return caster_t::cast(&src.get(), policy, parent); + } + template using cast_op_type = std::reference_wrapper; + operator std::reference_wrapper() { return subcaster.operator subcaster_cast_op_type&(); } +}; + +#define PYBIND11_TYPE_CASTER(type, py_name) \ + protected: \ + type value; \ + public: \ + static constexpr auto name = py_name; \ + template >::value, int> = 0> \ + static handle cast(T_ *src, return_value_policy policy, handle parent) { \ + if (!src) return none().release(); \ + if (policy == return_value_policy::take_ownership) { \ + auto h = cast(std::move(*src), policy, parent); delete src; return h; \ + } else { \ + return cast(*src, policy, parent); \ + } \ + } \ + operator type*() { return &value; } \ + operator type&() { return value; } \ + operator type&&() && { return std::move(value); } \ + template using cast_op_type = pybind11::detail::movable_cast_op_type + + +template using is_std_char_type = any_of< + std::is_same, /* std::string */ + std::is_same, /* std::u16string */ + std::is_same, /* std::u32string */ + std::is_same /* std::wstring */ +>; + +template +struct type_caster::value && !is_std_char_type::value>> { + using _py_type_0 = conditional_t; + using _py_type_1 = conditional_t::value, _py_type_0, typename std::make_unsigned<_py_type_0>::type>; + using py_type = conditional_t::value, double, _py_type_1>; +public: + + bool load(handle src, bool convert) { + py_type py_value; + + if (!src) + return false; + + if (std::is_floating_point::value) { + if (convert || PyFloat_Check(src.ptr())) + py_value = (py_type) PyFloat_AsDouble(src.ptr()); + else + return false; + } else if (PyFloat_Check(src.ptr())) { + return false; + } else if (std::is_unsigned::value) { + py_value = as_unsigned(src.ptr()); + } else { // signed integer: + py_value = sizeof(T) <= sizeof(long) + ? (py_type) PyLong_AsLong(src.ptr()) + : (py_type) PYBIND11_LONG_AS_LONGLONG(src.ptr()); + } + + bool py_err = py_value == (py_type) -1 && PyErr_Occurred(); + if (py_err || (std::is_integral::value && sizeof(py_type) != sizeof(T) && + (py_value < (py_type) std::numeric_limits::min() || + py_value > (py_type) std::numeric_limits::max()))) { + bool type_error = py_err && PyErr_ExceptionMatches( +#if PY_VERSION_HEX < 0x03000000 && !defined(PYPY_VERSION) + PyExc_SystemError +#else + PyExc_TypeError +#endif + ); + PyErr_Clear(); + if (type_error && convert && PyNumber_Check(src.ptr())) { + auto tmp = reinterpret_steal(std::is_floating_point::value + ? PyNumber_Float(src.ptr()) + : PyNumber_Long(src.ptr())); + PyErr_Clear(); + return load(tmp, false); + } + return false; + } + + value = (T) py_value; + return true; + } + + template + static typename std::enable_if::value, handle>::type + cast(U src, return_value_policy /* policy */, handle /* parent */) { + return PyFloat_FromDouble((double) src); + } + + template + static typename std::enable_if::value && std::is_signed::value && (sizeof(U) <= sizeof(long)), handle>::type + cast(U src, return_value_policy /* policy */, handle /* parent */) { + return PYBIND11_LONG_FROM_SIGNED((long) src); + } + + template + static typename std::enable_if::value && std::is_unsigned::value && (sizeof(U) <= sizeof(unsigned long)), handle>::type + cast(U src, return_value_policy /* policy */, handle /* parent */) { + return PYBIND11_LONG_FROM_UNSIGNED((unsigned long) src); + } + + template + static typename std::enable_if::value && std::is_signed::value && (sizeof(U) > sizeof(long)), handle>::type + cast(U src, return_value_policy /* policy */, handle /* parent */) { + return PyLong_FromLongLong((long long) src); + } + + template + static typename std::enable_if::value && std::is_unsigned::value && (sizeof(U) > sizeof(unsigned long)), handle>::type + cast(U src, return_value_policy /* policy */, handle /* parent */) { + return PyLong_FromUnsignedLongLong((unsigned long long) src); + } + + PYBIND11_TYPE_CASTER(T, _::value>("int", "float")); +}; + +template struct void_caster { +public: + bool load(handle src, bool) { + if (src && src.is_none()) + return true; + return false; + } + static handle cast(T, return_value_policy /* policy */, handle /* parent */) { + return none().inc_ref(); + } + PYBIND11_TYPE_CASTER(T, _("None")); +}; + +template <> class type_caster : public void_caster {}; + +template <> class type_caster : public type_caster { +public: + using type_caster::cast; + + bool load(handle h, bool) { + if (!h) { + return false; + } else if (h.is_none()) { + value = nullptr; + return true; + } + + /* Check if this is a capsule */ + if (isinstance(h)) { + value = reinterpret_borrow(h); + return true; + } + + /* Check if this is a C++ type */ + auto &bases = all_type_info((PyTypeObject *) h.get_type().ptr()); + if (bases.size() == 1) { // Only allowing loading from a single-value type + value = values_and_holders(reinterpret_cast(h.ptr())).begin()->value_ptr(); + return true; + } + + /* Fail */ + return false; + } + + static handle cast(const void *ptr, return_value_policy /* policy */, handle /* parent */) { + if (ptr) + return capsule(ptr).release(); + else + return none().inc_ref(); + } + + template using cast_op_type = void*&; + operator void *&() { return value; } + static constexpr auto name = _("capsule"); +private: + void *value = nullptr; +}; + +template <> class type_caster : public void_caster { }; + +template <> class type_caster { +public: + bool load(handle src, bool convert) { + if (!src) return false; + else if (src.ptr() == Py_True) { value = true; return true; } + else if (src.ptr() == Py_False) { value = false; return true; } + else if (convert || !strcmp("numpy.bool_", Py_TYPE(src.ptr())->tp_name)) { + // (allow non-implicit conversion for numpy booleans) + + Py_ssize_t res = -1; + if (src.is_none()) { + res = 0; // None is implicitly converted to False + } + #if defined(PYPY_VERSION) + // On PyPy, check that "__bool__" (or "__nonzero__" on Python 2.7) attr exists + else if (hasattr(src, PYBIND11_BOOL_ATTR)) { + res = PyObject_IsTrue(src.ptr()); + } + #else + // Alternate approach for CPython: this does the same as the above, but optimized + // using the CPython API so as to avoid an unneeded attribute lookup. + else if (auto tp_as_number = src.ptr()->ob_type->tp_as_number) { + if (PYBIND11_NB_BOOL(tp_as_number)) { + res = (*PYBIND11_NB_BOOL(tp_as_number))(src.ptr()); + } + } + #endif + if (res == 0 || res == 1) { + value = (bool) res; + return true; + } + } + return false; + } + static handle cast(bool src, return_value_policy /* policy */, handle /* parent */) { + return handle(src ? Py_True : Py_False).inc_ref(); + } + PYBIND11_TYPE_CASTER(bool, _("bool")); +}; + +// Helper class for UTF-{8,16,32} C++ stl strings: +template struct string_caster { + using CharT = typename StringType::value_type; + + // Simplify life by being able to assume standard char sizes (the standard only guarantees + // minimums, but Python requires exact sizes) + static_assert(!std::is_same::value || sizeof(CharT) == 1, "Unsupported char size != 1"); + static_assert(!std::is_same::value || sizeof(CharT) == 2, "Unsupported char16_t size != 2"); + static_assert(!std::is_same::value || sizeof(CharT) == 4, "Unsupported char32_t size != 4"); + // wchar_t can be either 16 bits (Windows) or 32 (everywhere else) + static_assert(!std::is_same::value || sizeof(CharT) == 2 || sizeof(CharT) == 4, + "Unsupported wchar_t size != 2/4"); + static constexpr size_t UTF_N = 8 * sizeof(CharT); + + bool load(handle src, bool) { +#if PY_MAJOR_VERSION < 3 + object temp; +#endif + handle load_src = src; + if (!src) { + return false; + } else if (!PyUnicode_Check(load_src.ptr())) { +#if PY_MAJOR_VERSION >= 3 + return load_bytes(load_src); +#else + if (sizeof(CharT) == 1) { + return load_bytes(load_src); + } + + // The below is a guaranteed failure in Python 3 when PyUnicode_Check returns false + if (!PYBIND11_BYTES_CHECK(load_src.ptr())) + return false; + + temp = reinterpret_steal(PyUnicode_FromObject(load_src.ptr())); + if (!temp) { PyErr_Clear(); return false; } + load_src = temp; +#endif + } + + object utfNbytes = reinterpret_steal(PyUnicode_AsEncodedString( + load_src.ptr(), UTF_N == 8 ? "utf-8" : UTF_N == 16 ? "utf-16" : "utf-32", nullptr)); + if (!utfNbytes) { PyErr_Clear(); return false; } + + const CharT *buffer = reinterpret_cast(PYBIND11_BYTES_AS_STRING(utfNbytes.ptr())); + size_t length = (size_t) PYBIND11_BYTES_SIZE(utfNbytes.ptr()) / sizeof(CharT); + if (UTF_N > 8) { buffer++; length--; } // Skip BOM for UTF-16/32 + value = StringType(buffer, length); + + // If we're loading a string_view we need to keep the encoded Python object alive: + if (IsView) + loader_life_support::add_patient(utfNbytes); + + return true; + } + + static handle cast(const StringType &src, return_value_policy /* policy */, handle /* parent */) { + const char *buffer = reinterpret_cast(src.data()); + ssize_t nbytes = ssize_t(src.size() * sizeof(CharT)); + handle s = decode_utfN(buffer, nbytes); + if (!s) throw error_already_set(); + return s; + } + + PYBIND11_TYPE_CASTER(StringType, _(PYBIND11_STRING_NAME)); + +private: + static handle decode_utfN(const char *buffer, ssize_t nbytes) { +#if !defined(PYPY_VERSION) + return + UTF_N == 8 ? PyUnicode_DecodeUTF8(buffer, nbytes, nullptr) : + UTF_N == 16 ? PyUnicode_DecodeUTF16(buffer, nbytes, nullptr, nullptr) : + PyUnicode_DecodeUTF32(buffer, nbytes, nullptr, nullptr); +#else + // PyPy seems to have multiple problems related to PyUnicode_UTF*: the UTF8 version + // sometimes segfaults for unknown reasons, while the UTF16 and 32 versions require a + // non-const char * arguments, which is also a nuisance, so bypass the whole thing by just + // passing the encoding as a string value, which works properly: + return PyUnicode_Decode(buffer, nbytes, UTF_N == 8 ? "utf-8" : UTF_N == 16 ? "utf-16" : "utf-32", nullptr); +#endif + } + + // When loading into a std::string or char*, accept a bytes object as-is (i.e. + // without any encoding/decoding attempt). For other C++ char sizes this is a no-op. + // which supports loading a unicode from a str, doesn't take this path. + template + bool load_bytes(enable_if_t src) { + if (PYBIND11_BYTES_CHECK(src.ptr())) { + // We were passed a Python 3 raw bytes; accept it into a std::string or char* + // without any encoding attempt. + const char *bytes = PYBIND11_BYTES_AS_STRING(src.ptr()); + if (bytes) { + value = StringType(bytes, (size_t) PYBIND11_BYTES_SIZE(src.ptr())); + return true; + } + } + + return false; + } + + template + bool load_bytes(enable_if_t) { return false; } +}; + +template +struct type_caster, enable_if_t::value>> + : string_caster> {}; + +#ifdef PYBIND11_HAS_STRING_VIEW +template +struct type_caster, enable_if_t::value>> + : string_caster, true> {}; +#endif + +// Type caster for C-style strings. We basically use a std::string type caster, but also add the +// ability to use None as a nullptr char* (which the string caster doesn't allow). +template struct type_caster::value>> { + using StringType = std::basic_string; + using StringCaster = type_caster; + StringCaster str_caster; + bool none = false; + CharT one_char = 0; +public: + bool load(handle src, bool convert) { + if (!src) return false; + if (src.is_none()) { + // Defer accepting None to other overloads (if we aren't in convert mode): + if (!convert) return false; + none = true; + return true; + } + return str_caster.load(src, convert); + } + + static handle cast(const CharT *src, return_value_policy policy, handle parent) { + if (src == nullptr) return pybind11::none().inc_ref(); + return StringCaster::cast(StringType(src), policy, parent); + } + + static handle cast(CharT src, return_value_policy policy, handle parent) { + if (std::is_same::value) { + handle s = PyUnicode_DecodeLatin1((const char *) &src, 1, nullptr); + if (!s) throw error_already_set(); + return s; + } + return StringCaster::cast(StringType(1, src), policy, parent); + } + + operator CharT*() { return none ? nullptr : const_cast(static_cast(str_caster).c_str()); } + operator CharT&() { + if (none) + throw value_error("Cannot convert None to a character"); + + auto &value = static_cast(str_caster); + size_t str_len = value.size(); + if (str_len == 0) + throw value_error("Cannot convert empty string to a character"); + + // If we're in UTF-8 mode, we have two possible failures: one for a unicode character that + // is too high, and one for multiple unicode characters (caught later), so we need to figure + // out how long the first encoded character is in bytes to distinguish between these two + // errors. We also allow want to allow unicode characters U+0080 through U+00FF, as those + // can fit into a single char value. + if (StringCaster::UTF_N == 8 && str_len > 1 && str_len <= 4) { + unsigned char v0 = static_cast(value[0]); + size_t char0_bytes = !(v0 & 0x80) ? 1 : // low bits only: 0-127 + (v0 & 0xE0) == 0xC0 ? 2 : // 0b110xxxxx - start of 2-byte sequence + (v0 & 0xF0) == 0xE0 ? 3 : // 0b1110xxxx - start of 3-byte sequence + 4; // 0b11110xxx - start of 4-byte sequence + + if (char0_bytes == str_len) { + // If we have a 128-255 value, we can decode it into a single char: + if (char0_bytes == 2 && (v0 & 0xFC) == 0xC0) { // 0x110000xx 0x10xxxxxx + one_char = static_cast(((v0 & 3) << 6) + (static_cast(value[1]) & 0x3F)); + return one_char; + } + // Otherwise we have a single character, but it's > U+00FF + throw value_error("Character code point not in range(0x100)"); + } + } + + // UTF-16 is much easier: we can only have a surrogate pair for values above U+FFFF, thus a + // surrogate pair with total length 2 instantly indicates a range error (but not a "your + // string was too long" error). + else if (StringCaster::UTF_N == 16 && str_len == 2) { + one_char = static_cast(value[0]); + if (one_char >= 0xD800 && one_char < 0xE000) + throw value_error("Character code point not in range(0x10000)"); + } + + if (str_len != 1) + throw value_error("Expected a character, but multi-character string found"); + + one_char = value[0]; + return one_char; + } + + static constexpr auto name = _(PYBIND11_STRING_NAME); + template using cast_op_type = pybind11::detail::cast_op_type<_T>; +}; + +// Base implementation for std::tuple and std::pair +template class Tuple, typename... Ts> class tuple_caster { + using type = Tuple; + static constexpr auto size = sizeof...(Ts); + using indices = make_index_sequence; +public: + + bool load(handle src, bool convert) { + if (!isinstance(src)) + return false; + const auto seq = reinterpret_borrow(src); + if (seq.size() != size) + return false; + return load_impl(seq, convert, indices{}); + } + + template + static handle cast(T &&src, return_value_policy policy, handle parent) { + return cast_impl(std::forward(src), policy, parent, indices{}); + } + + static constexpr auto name = _("Tuple[") + concat(make_caster::name...) + _("]"); + + template using cast_op_type = type; + + operator type() & { return implicit_cast(indices{}); } + operator type() && { return std::move(*this).implicit_cast(indices{}); } + +protected: + template + type implicit_cast(index_sequence) & { return type(cast_op(std::get(subcasters))...); } + template + type implicit_cast(index_sequence) && { return type(cast_op(std::move(std::get(subcasters)))...); } + + static constexpr bool load_impl(const sequence &, bool, index_sequence<>) { return true; } + + template + bool load_impl(const sequence &seq, bool convert, index_sequence) { + for (bool r : {std::get(subcasters).load(seq[Is], convert)...}) + if (!r) + return false; + return true; + } + + /* Implementation: Convert a C++ tuple into a Python tuple */ + template + static handle cast_impl(T &&src, return_value_policy policy, handle parent, index_sequence) { + std::array entries{{ + reinterpret_steal(make_caster::cast(std::get(std::forward(src)), policy, parent))... + }}; + for (const auto &entry: entries) + if (!entry) + return handle(); + tuple result(size); + int counter = 0; + for (auto & entry: entries) + PyTuple_SET_ITEM(result.ptr(), counter++, entry.release().ptr()); + return result.release(); + } + + Tuple...> subcasters; +}; + +template class type_caster> + : public tuple_caster {}; + +template class type_caster> + : public tuple_caster {}; + +/// Helper class which abstracts away certain actions. Users can provide specializations for +/// custom holders, but it's only necessary if the type has a non-standard interface. +template +struct holder_helper { + static auto get(const T &p) -> decltype(p.get()) { return p.get(); } +}; + +/// Type caster for holder types like std::shared_ptr, etc. +template +struct copyable_holder_caster : public type_caster_base { +public: + using base = type_caster_base; + static_assert(std::is_base_of>::value, + "Holder classes are only supported for custom types"); + using base::base; + using base::cast; + using base::typeinfo; + using base::value; + + bool load(handle src, bool convert) { + return base::template load_impl>(src, convert); + } + + explicit operator type*() { return this->value; } + explicit operator type&() { return *(this->value); } + explicit operator holder_type*() { return std::addressof(holder); } + + // Workaround for Intel compiler bug + // see pybind11 issue 94 + #if defined(__ICC) || defined(__INTEL_COMPILER) + operator holder_type&() { return holder; } + #else + explicit operator holder_type&() { return holder; } + #endif + + static handle cast(const holder_type &src, return_value_policy, handle) { + const auto *ptr = holder_helper::get(src); + return type_caster_base::cast_holder(ptr, &src); + } + +protected: + friend class type_caster_generic; + void check_holder_compat() { + if (typeinfo->default_holder) + throw cast_error("Unable to load a custom holder type from a default-holder instance"); + } + + bool load_value(value_and_holder &&v_h) { + if (v_h.holder_constructed()) { + value = v_h.value_ptr(); + holder = v_h.template holder(); + return true; + } else { + throw cast_error("Unable to cast from non-held to held instance (T& to Holder) " +#if defined(NDEBUG) + "(compile in debug mode for type information)"); +#else + "of type '" + type_id() + "''"); +#endif + } + } + + template ::value, int> = 0> + bool try_implicit_casts(handle, bool) { return false; } + + template ::value, int> = 0> + bool try_implicit_casts(handle src, bool convert) { + for (auto &cast : typeinfo->implicit_casts) { + copyable_holder_caster sub_caster(*cast.first); + if (sub_caster.load(src, convert)) { + value = cast.second(sub_caster.value); + holder = holder_type(sub_caster.holder, (type *) value); + return true; + } + } + return false; + } + + static bool try_direct_conversions(handle) { return false; } + + + holder_type holder; +}; + +/// Specialize for the common std::shared_ptr, so users don't need to +template +class type_caster> : public copyable_holder_caster> { }; + +template +struct move_only_holder_caster { + static_assert(std::is_base_of, type_caster>::value, + "Holder classes are only supported for custom types"); + + static handle cast(holder_type &&src, return_value_policy, handle) { + auto *ptr = holder_helper::get(src); + return type_caster_base::cast_holder(ptr, std::addressof(src)); + } + static constexpr auto name = type_caster_base::name; +}; + +template +class type_caster> + : public move_only_holder_caster> { }; + +template +using type_caster_holder = conditional_t::value, + copyable_holder_caster, + move_only_holder_caster>; + +template struct always_construct_holder { static constexpr bool value = Value; }; + +/// Create a specialization for custom holder types (silently ignores std::shared_ptr) +#define PYBIND11_DECLARE_HOLDER_TYPE(type, holder_type, ...) \ + namespace pybind11 { namespace detail { \ + template \ + struct always_construct_holder : always_construct_holder { }; \ + template \ + class type_caster::value>> \ + : public type_caster_holder { }; \ + }} + +// PYBIND11_DECLARE_HOLDER_TYPE holder types: +template struct is_holder_type : + std::is_base_of, detail::type_caster> {}; +// Specialization for always-supported unique_ptr holders: +template struct is_holder_type> : + std::true_type {}; + +template struct handle_type_name { static constexpr auto name = _(); }; +template <> struct handle_type_name { static constexpr auto name = _(PYBIND11_BYTES_NAME); }; +template <> struct handle_type_name { static constexpr auto name = _("*args"); }; +template <> struct handle_type_name { static constexpr auto name = _("**kwargs"); }; + +template +struct pyobject_caster { + template ::value, int> = 0> + bool load(handle src, bool /* convert */) { value = src; return static_cast(value); } + + template ::value, int> = 0> + bool load(handle src, bool /* convert */) { + if (!isinstance(src)) + return false; + value = reinterpret_borrow(src); + return true; + } + + static handle cast(const handle &src, return_value_policy /* policy */, handle /* parent */) { + return src.inc_ref(); + } + PYBIND11_TYPE_CASTER(type, handle_type_name::name); +}; + +template +class type_caster::value>> : public pyobject_caster { }; + +// Our conditions for enabling moving are quite restrictive: +// At compile time: +// - T needs to be a non-const, non-pointer, non-reference type +// - type_caster::operator T&() must exist +// - the type must be move constructible (obviously) +// At run-time: +// - if the type is non-copy-constructible, the object must be the sole owner of the type (i.e. it +// must have ref_count() == 1)h +// If any of the above are not satisfied, we fall back to copying. +template using move_is_plain_type = satisfies_none_of; +template struct move_always : std::false_type {}; +template struct move_always, + negation>, + std::is_move_constructible, + std::is_same>().operator T&()), T&> +>::value>> : std::true_type {}; +template struct move_if_unreferenced : std::false_type {}; +template struct move_if_unreferenced, + negation>, + std::is_move_constructible, + std::is_same>().operator T&()), T&> +>::value>> : std::true_type {}; +template using move_never = none_of, move_if_unreferenced>; + +// Detect whether returning a `type` from a cast on type's type_caster is going to result in a +// reference or pointer to a local variable of the type_caster. Basically, only +// non-reference/pointer `type`s and reference/pointers from a type_caster_generic are safe; +// everything else returns a reference/pointer to a local variable. +template using cast_is_temporary_value_reference = bool_constant< + (std::is_reference::value || std::is_pointer::value) && + !std::is_base_of>::value && + !std::is_same, void>::value +>; + +// When a value returned from a C++ function is being cast back to Python, we almost always want to +// force `policy = move`, regardless of the return value policy the function/method was declared +// with. +template struct return_value_policy_override { + static return_value_policy policy(return_value_policy p) { return p; } +}; + +template struct return_value_policy_override>::value, void>> { + static return_value_policy policy(return_value_policy p) { + return !std::is_lvalue_reference::value && + !std::is_pointer::value + ? return_value_policy::move : p; + } +}; + +// Basic python -> C++ casting; throws if casting fails +template type_caster &load_type(type_caster &conv, const handle &handle) { + if (!conv.load(handle, true)) { +#if defined(NDEBUG) + throw cast_error("Unable to cast Python instance to C++ type (compile in debug mode for details)"); +#else + throw cast_error("Unable to cast Python instance of type " + + (std::string) str(handle.get_type()) + " to C++ type '" + type_id() + "'"); +#endif + } + return conv; +} +// Wrapper around the above that also constructs and returns a type_caster +template make_caster load_type(const handle &handle) { + make_caster conv; + load_type(conv, handle); + return conv; +} + +NAMESPACE_END(detail) + +// pytype -> C++ type +template ::value, int> = 0> +T cast(const handle &handle) { + using namespace detail; + static_assert(!cast_is_temporary_value_reference::value, + "Unable to cast type to reference: value is local to type caster"); + return cast_op(load_type(handle)); +} + +// pytype -> pytype (calls converting constructor) +template ::value, int> = 0> +T cast(const handle &handle) { return T(reinterpret_borrow(handle)); } + +// C++ type -> py::object +template ::value, int> = 0> +object cast(const T &value, return_value_policy policy = return_value_policy::automatic_reference, + handle parent = handle()) { + if (policy == return_value_policy::automatic) + policy = std::is_pointer::value ? return_value_policy::take_ownership : return_value_policy::copy; + else if (policy == return_value_policy::automatic_reference) + policy = std::is_pointer::value ? return_value_policy::reference : return_value_policy::copy; + return reinterpret_steal(detail::make_caster::cast(value, policy, parent)); +} + +template T handle::cast() const { return pybind11::cast(*this); } +template <> inline void handle::cast() const { return; } + +template +detail::enable_if_t::value, T> move(object &&obj) { + if (obj.ref_count() > 1) +#if defined(NDEBUG) + throw cast_error("Unable to cast Python instance to C++ rvalue: instance has multiple references" + " (compile in debug mode for details)"); +#else + throw cast_error("Unable to move from Python " + (std::string) str(obj.get_type()) + + " instance to C++ " + type_id() + " instance: instance has multiple references"); +#endif + + // Move into a temporary and return that, because the reference may be a local value of `conv` + T ret = std::move(detail::load_type(obj).operator T&()); + return ret; +} + +// Calling cast() on an rvalue calls pybind::cast with the object rvalue, which does: +// - If we have to move (because T has no copy constructor), do it. This will fail if the moved +// object has multiple references, but trying to copy will fail to compile. +// - If both movable and copyable, check ref count: if 1, move; otherwise copy +// - Otherwise (not movable), copy. +template detail::enable_if_t::value, T> cast(object &&object) { + return move(std::move(object)); +} +template detail::enable_if_t::value, T> cast(object &&object) { + if (object.ref_count() > 1) + return cast(object); + else + return move(std::move(object)); +} +template detail::enable_if_t::value, T> cast(object &&object) { + return cast(object); +} + +template T object::cast() const & { return pybind11::cast(*this); } +template T object::cast() && { return pybind11::cast(std::move(*this)); } +template <> inline void object::cast() const & { return; } +template <> inline void object::cast() && { return; } + +NAMESPACE_BEGIN(detail) + +// Declared in pytypes.h: +template ::value, int>> +object object_or_cast(T &&o) { return pybind11::cast(std::forward(o)); } + +struct overload_unused {}; // Placeholder type for the unneeded (and dead code) static variable in the OVERLOAD_INT macro +template using overload_caster_t = conditional_t< + cast_is_temporary_value_reference::value, make_caster, overload_unused>; + +// Trampoline use: for reference/pointer types to value-converted values, we do a value cast, then +// store the result in the given variable. For other types, this is a no-op. +template enable_if_t::value, T> cast_ref(object &&o, make_caster &caster) { + return cast_op(load_type(caster, o)); +} +template enable_if_t::value, T> cast_ref(object &&, overload_unused &) { + pybind11_fail("Internal error: cast_ref fallback invoked"); } + +// Trampoline use: Having a pybind11::cast with an invalid reference type is going to static_assert, even +// though if it's in dead code, so we provide a "trampoline" to pybind11::cast that only does anything in +// cases where pybind11::cast is valid. +template enable_if_t::value, T> cast_safe(object &&o) { + return pybind11::cast(std::move(o)); } +template enable_if_t::value, T> cast_safe(object &&) { + pybind11_fail("Internal error: cast_safe fallback invoked"); } +template <> inline void cast_safe(object &&) {} + +NAMESPACE_END(detail) + +template +tuple make_tuple() { return tuple(0); } + +template tuple make_tuple(Args&&... args_) { + constexpr size_t size = sizeof...(Args); + std::array args { + { reinterpret_steal(detail::make_caster::cast( + std::forward(args_), policy, nullptr))... } + }; + for (size_t i = 0; i < args.size(); i++) { + if (!args[i]) { +#if defined(NDEBUG) + throw cast_error("make_tuple(): unable to convert arguments to Python object (compile in debug mode for details)"); +#else + std::array argtypes { {type_id()...} }; + throw cast_error("make_tuple(): unable to convert argument of type '" + + argtypes[i] + "' to Python object"); +#endif + } + } + tuple result(size); + int counter = 0; + for (auto &arg_value : args) + PyTuple_SET_ITEM(result.ptr(), counter++, arg_value.release().ptr()); + return result; +} + +/// \ingroup annotations +/// Annotation for arguments +struct arg { + /// Constructs an argument with the name of the argument; if null or omitted, this is a positional argument. + constexpr explicit arg(const char *name = nullptr) : name(name), flag_noconvert(false), flag_none(true) { } + /// Assign a value to this argument + template arg_v operator=(T &&value) const; + /// Indicate that the type should not be converted in the type caster + arg &noconvert(bool flag = true) { flag_noconvert = flag; return *this; } + /// Indicates that the argument should/shouldn't allow None (e.g. for nullable pointer args) + arg &none(bool flag = true) { flag_none = flag; return *this; } + + const char *name; ///< If non-null, this is a named kwargs argument + bool flag_noconvert : 1; ///< If set, do not allow conversion (requires a supporting type caster!) + bool flag_none : 1; ///< If set (the default), allow None to be passed to this argument +}; + +/// \ingroup annotations +/// Annotation for arguments with values +struct arg_v : arg { +private: + template + arg_v(arg &&base, T &&x, const char *descr = nullptr) + : arg(base), + value(reinterpret_steal( + detail::make_caster::cast(x, return_value_policy::automatic, {}) + )), + descr(descr) +#if !defined(NDEBUG) + , type(type_id()) +#endif + { } + +public: + /// Direct construction with name, default, and description + template + arg_v(const char *name, T &&x, const char *descr = nullptr) + : arg_v(arg(name), std::forward(x), descr) { } + + /// Called internally when invoking `py::arg("a") = value` + template + arg_v(const arg &base, T &&x, const char *descr = nullptr) + : arg_v(arg(base), std::forward(x), descr) { } + + /// Same as `arg::noconvert()`, but returns *this as arg_v&, not arg& + arg_v &noconvert(bool flag = true) { arg::noconvert(flag); return *this; } + + /// Same as `arg::nonone()`, but returns *this as arg_v&, not arg& + arg_v &none(bool flag = true) { arg::none(flag); return *this; } + + /// The default value + object value; + /// The (optional) description of the default value + const char *descr; +#if !defined(NDEBUG) + /// The C++ type name of the default value (only available when compiled in debug mode) + std::string type; +#endif +}; + +template +arg_v arg::operator=(T &&value) const { return {std::move(*this), std::forward(value)}; } + +/// Alias for backward compatibility -- to be removed in version 2.0 +template using arg_t = arg_v; + +inline namespace literals { +/** \rst + String literal version of `arg` + \endrst */ +constexpr arg operator"" _a(const char *name, size_t) { return arg(name); } +} + +NAMESPACE_BEGIN(detail) + +// forward declaration (definition in attr.h) +struct function_record; + +/// Internal data associated with a single function call +struct function_call { + function_call(const function_record &f, handle p); // Implementation in attr.h + + /// The function data: + const function_record &func; + + /// Arguments passed to the function: + std::vector args; + + /// The `convert` value the arguments should be loaded with + std::vector args_convert; + + /// Extra references for the optional `py::args` and/or `py::kwargs` arguments (which, if + /// present, are also in `args` but without a reference). + object args_ref, kwargs_ref; + + /// The parent, if any + handle parent; + + /// If this is a call to an initializer, this argument contains `self` + handle init_self; +}; + + +/// Helper class which loads arguments for C++ functions called from Python +template +class argument_loader { + using indices = make_index_sequence; + + template using argument_is_args = std::is_same, args>; + template using argument_is_kwargs = std::is_same, kwargs>; + // Get args/kwargs argument positions relative to the end of the argument list: + static constexpr auto args_pos = constexpr_first() - (int) sizeof...(Args), + kwargs_pos = constexpr_first() - (int) sizeof...(Args); + + static constexpr bool args_kwargs_are_last = kwargs_pos >= - 1 && args_pos >= kwargs_pos - 1; + + static_assert(args_kwargs_are_last, "py::args/py::kwargs are only permitted as the last argument(s) of a function"); + +public: + static constexpr bool has_kwargs = kwargs_pos < 0; + static constexpr bool has_args = args_pos < 0; + + static constexpr auto arg_names = concat(type_descr(make_caster::name)...); + + bool load_args(function_call &call) { + return load_impl_sequence(call, indices{}); + } + + template + enable_if_t::value, Return> call(Func &&f) && { + return std::move(*this).template call_impl(std::forward(f), indices{}, Guard{}); + } + + template + enable_if_t::value, void_type> call(Func &&f) && { + std::move(*this).template call_impl(std::forward(f), indices{}, Guard{}); + return void_type(); + } + +private: + + static bool load_impl_sequence(function_call &, index_sequence<>) { return true; } + + template + bool load_impl_sequence(function_call &call, index_sequence) { + for (bool r : {std::get(argcasters).load(call.args[Is], call.args_convert[Is])...}) + if (!r) + return false; + return true; + } + + template + Return call_impl(Func &&f, index_sequence, Guard &&) { + return std::forward(f)(cast_op(std::move(std::get(argcasters)))...); + } + + std::tuple...> argcasters; +}; + +/// Helper class which collects only positional arguments for a Python function call. +/// A fancier version below can collect any argument, but this one is optimal for simple calls. +template +class simple_collector { +public: + template + explicit simple_collector(Ts &&...values) + : m_args(pybind11::make_tuple(std::forward(values)...)) { } + + const tuple &args() const & { return m_args; } + dict kwargs() const { return {}; } + + tuple args() && { return std::move(m_args); } + + /// Call a Python function and pass the collected arguments + object call(PyObject *ptr) const { + PyObject *result = PyObject_CallObject(ptr, m_args.ptr()); + if (!result) + throw error_already_set(); + return reinterpret_steal(result); + } + +private: + tuple m_args; +}; + +/// Helper class which collects positional, keyword, * and ** arguments for a Python function call +template +class unpacking_collector { +public: + template + explicit unpacking_collector(Ts &&...values) { + // Tuples aren't (easily) resizable so a list is needed for collection, + // but the actual function call strictly requires a tuple. + auto args_list = list(); + int _[] = { 0, (process(args_list, std::forward(values)), 0)... }; + ignore_unused(_); + + m_args = std::move(args_list); + } + + const tuple &args() const & { return m_args; } + const dict &kwargs() const & { return m_kwargs; } + + tuple args() && { return std::move(m_args); } + dict kwargs() && { return std::move(m_kwargs); } + + /// Call a Python function and pass the collected arguments + object call(PyObject *ptr) const { + PyObject *result = PyObject_Call(ptr, m_args.ptr(), m_kwargs.ptr()); + if (!result) + throw error_already_set(); + return reinterpret_steal(result); + } + +private: + template + void process(list &args_list, T &&x) { + auto o = reinterpret_steal(detail::make_caster::cast(std::forward(x), policy, {})); + if (!o) { +#if defined(NDEBUG) + argument_cast_error(); +#else + argument_cast_error(std::to_string(args_list.size()), type_id()); +#endif + } + args_list.append(o); + } + + void process(list &args_list, detail::args_proxy ap) { + for (const auto &a : ap) + args_list.append(a); + } + + void process(list &/*args_list*/, arg_v a) { + if (!a.name) +#if defined(NDEBUG) + nameless_argument_error(); +#else + nameless_argument_error(a.type); +#endif + + if (m_kwargs.contains(a.name)) { +#if defined(NDEBUG) + multiple_values_error(); +#else + multiple_values_error(a.name); +#endif + } + if (!a.value) { +#if defined(NDEBUG) + argument_cast_error(); +#else + argument_cast_error(a.name, a.type); +#endif + } + m_kwargs[a.name] = a.value; + } + + void process(list &/*args_list*/, detail::kwargs_proxy kp) { + if (!kp) + return; + for (const auto &k : reinterpret_borrow(kp)) { + if (m_kwargs.contains(k.first)) { +#if defined(NDEBUG) + multiple_values_error(); +#else + multiple_values_error(str(k.first)); +#endif + } + m_kwargs[k.first] = k.second; + } + } + + [[noreturn]] static void nameless_argument_error() { + throw type_error("Got kwargs without a name; only named arguments " + "may be passed via py::arg() to a python function call. " + "(compile in debug mode for details)"); + } + [[noreturn]] static void nameless_argument_error(std::string type) { + throw type_error("Got kwargs without a name of type '" + type + "'; only named " + "arguments may be passed via py::arg() to a python function call. "); + } + [[noreturn]] static void multiple_values_error() { + throw type_error("Got multiple values for keyword argument " + "(compile in debug mode for details)"); + } + + [[noreturn]] static void multiple_values_error(std::string name) { + throw type_error("Got multiple values for keyword argument '" + name + "'"); + } + + [[noreturn]] static void argument_cast_error() { + throw cast_error("Unable to convert call argument to Python object " + "(compile in debug mode for details)"); + } + + [[noreturn]] static void argument_cast_error(std::string name, std::string type) { + throw cast_error("Unable to convert call argument '" + name + + "' of type '" + type + "' to Python object"); + } + +private: + tuple m_args; + dict m_kwargs; +}; + +/// Collect only positional arguments for a Python function call +template ...>::value>> +simple_collector collect_arguments(Args &&...args) { + return simple_collector(std::forward(args)...); +} + +/// Collect all arguments, including keywords and unpacking (only instantiated when needed) +template ...>::value>> +unpacking_collector collect_arguments(Args &&...args) { + // Following argument order rules for generalized unpacking according to PEP 448 + static_assert( + constexpr_last() < constexpr_first() + && constexpr_last() < constexpr_first(), + "Invalid function call: positional args must precede keywords and ** unpacking; " + "* unpacking must precede ** unpacking" + ); + return unpacking_collector(std::forward(args)...); +} + +template +template +object object_api::operator()(Args &&...args) const { + return detail::collect_arguments(std::forward(args)...).call(derived().ptr()); +} + +template +template +object object_api::call(Args &&...args) const { + return operator()(std::forward(args)...); +} + +NAMESPACE_END(detail) + +#define PYBIND11_MAKE_OPAQUE(...) \ + namespace pybind11 { namespace detail { \ + template<> class type_caster<__VA_ARGS__> : public type_caster_base<__VA_ARGS__> { }; \ + }} + +/// Lets you pass a type containing a `,` through a macro parameter without needing a separate +/// typedef, e.g.: `PYBIND11_OVERLOAD(PYBIND11_TYPE(ReturnType), PYBIND11_TYPE(Parent), f, arg)` +#define PYBIND11_TYPE(...) __VA_ARGS__ + +NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/ptocr/postprocess/dbprocess/include/pybind11/chrono.h b/ptocr/postprocess/dbprocess/include/pybind11/chrono.h new file mode 100644 index 0000000..95ada76 --- /dev/null +++ b/ptocr/postprocess/dbprocess/include/pybind11/chrono.h @@ -0,0 +1,162 @@ +/* + pybind11/chrono.h: Transparent conversion between std::chrono and python's datetime + + Copyright (c) 2016 Trent Houliston and + Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "pybind11.h" +#include +#include +#include +#include + +// Backport the PyDateTime_DELTA functions from Python3.3 if required +#ifndef PyDateTime_DELTA_GET_DAYS +#define PyDateTime_DELTA_GET_DAYS(o) (((PyDateTime_Delta*)o)->days) +#endif +#ifndef PyDateTime_DELTA_GET_SECONDS +#define PyDateTime_DELTA_GET_SECONDS(o) (((PyDateTime_Delta*)o)->seconds) +#endif +#ifndef PyDateTime_DELTA_GET_MICROSECONDS +#define PyDateTime_DELTA_GET_MICROSECONDS(o) (((PyDateTime_Delta*)o)->microseconds) +#endif + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +NAMESPACE_BEGIN(detail) + +template class duration_caster { +public: + typedef typename type::rep rep; + typedef typename type::period period; + + typedef std::chrono::duration> days; + + bool load(handle src, bool) { + using namespace std::chrono; + + // Lazy initialise the PyDateTime import + if (!PyDateTimeAPI) { PyDateTime_IMPORT; } + + if (!src) return false; + // If invoked with datetime.delta object + if (PyDelta_Check(src.ptr())) { + value = type(duration_cast>( + days(PyDateTime_DELTA_GET_DAYS(src.ptr())) + + seconds(PyDateTime_DELTA_GET_SECONDS(src.ptr())) + + microseconds(PyDateTime_DELTA_GET_MICROSECONDS(src.ptr())))); + return true; + } + // If invoked with a float we assume it is seconds and convert + else if (PyFloat_Check(src.ptr())) { + value = type(duration_cast>(duration(PyFloat_AsDouble(src.ptr())))); + return true; + } + else return false; + } + + // If this is a duration just return it back + static const std::chrono::duration& get_duration(const std::chrono::duration &src) { + return src; + } + + // If this is a time_point get the time_since_epoch + template static std::chrono::duration get_duration(const std::chrono::time_point> &src) { + return src.time_since_epoch(); + } + + static handle cast(const type &src, return_value_policy /* policy */, handle /* parent */) { + using namespace std::chrono; + + // Use overloaded function to get our duration from our source + // Works out if it is a duration or time_point and get the duration + auto d = get_duration(src); + + // Lazy initialise the PyDateTime import + if (!PyDateTimeAPI) { PyDateTime_IMPORT; } + + // Declare these special duration types so the conversions happen with the correct primitive types (int) + using dd_t = duration>; + using ss_t = duration>; + using us_t = duration; + + auto dd = duration_cast(d); + auto subd = d - dd; + auto ss = duration_cast(subd); + auto us = duration_cast(subd - ss); + return PyDelta_FromDSU(dd.count(), ss.count(), us.count()); + } + + PYBIND11_TYPE_CASTER(type, _("datetime.timedelta")); +}; + +// This is for casting times on the system clock into datetime.datetime instances +template class type_caster> { +public: + typedef std::chrono::time_point type; + bool load(handle src, bool) { + using namespace std::chrono; + + // Lazy initialise the PyDateTime import + if (!PyDateTimeAPI) { PyDateTime_IMPORT; } + + if (!src) return false; + if (PyDateTime_Check(src.ptr())) { + std::tm cal; + cal.tm_sec = PyDateTime_DATE_GET_SECOND(src.ptr()); + cal.tm_min = PyDateTime_DATE_GET_MINUTE(src.ptr()); + cal.tm_hour = PyDateTime_DATE_GET_HOUR(src.ptr()); + cal.tm_mday = PyDateTime_GET_DAY(src.ptr()); + cal.tm_mon = PyDateTime_GET_MONTH(src.ptr()) - 1; + cal.tm_year = PyDateTime_GET_YEAR(src.ptr()) - 1900; + cal.tm_isdst = -1; + + value = system_clock::from_time_t(std::mktime(&cal)) + microseconds(PyDateTime_DATE_GET_MICROSECOND(src.ptr())); + return true; + } + else return false; + } + + static handle cast(const std::chrono::time_point &src, return_value_policy /* policy */, handle /* parent */) { + using namespace std::chrono; + + // Lazy initialise the PyDateTime import + if (!PyDateTimeAPI) { PyDateTime_IMPORT; } + + std::time_t tt = system_clock::to_time_t(src); + // this function uses static memory so it's best to copy it out asap just in case + // otherwise other code that is using localtime may break this (not just python code) + std::tm localtime = *std::localtime(&tt); + + // Declare these special duration types so the conversions happen with the correct primitive types (int) + using us_t = duration; + + return PyDateTime_FromDateAndTime(localtime.tm_year + 1900, + localtime.tm_mon + 1, + localtime.tm_mday, + localtime.tm_hour, + localtime.tm_min, + localtime.tm_sec, + (duration_cast(src.time_since_epoch() % seconds(1))).count()); + } + PYBIND11_TYPE_CASTER(type, _("datetime.datetime")); +}; + +// Other clocks that are not the system clock are not measured as datetime.datetime objects +// since they are not measured on calendar time. So instead we just make them timedeltas +// Or if they have passed us a time as a float we convert that +template class type_caster> +: public duration_caster> { +}; + +template class type_caster> +: public duration_caster> { +}; + +NAMESPACE_END(detail) +NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/ptocr/postprocess/dbprocess/include/pybind11/class_support.h b/ptocr/postprocess/dbprocess/include/pybind11/class_support.h new file mode 100644 index 0000000..8e18c4c --- /dev/null +++ b/ptocr/postprocess/dbprocess/include/pybind11/class_support.h @@ -0,0 +1,603 @@ +/* + pybind11/class_support.h: Python C API implementation details for py::class_ + + Copyright (c) 2017 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "attr.h" + +NAMESPACE_BEGIN(pybind11) +NAMESPACE_BEGIN(detail) + +inline PyTypeObject *type_incref(PyTypeObject *type) { + Py_INCREF(type); + return type; +} + +#if !defined(PYPY_VERSION) + +/// `pybind11_static_property.__get__()`: Always pass the class instead of the instance. +extern "C" inline PyObject *pybind11_static_get(PyObject *self, PyObject * /*ob*/, PyObject *cls) { + return PyProperty_Type.tp_descr_get(self, cls, cls); +} + +/// `pybind11_static_property.__set__()`: Just like the above `__get__()`. +extern "C" inline int pybind11_static_set(PyObject *self, PyObject *obj, PyObject *value) { + PyObject *cls = PyType_Check(obj) ? obj : (PyObject *) Py_TYPE(obj); + return PyProperty_Type.tp_descr_set(self, cls, value); +} + +/** A `static_property` is the same as a `property` but the `__get__()` and `__set__()` + methods are modified to always use the object type instead of a concrete instance. + Return value: New reference. */ +inline PyTypeObject *make_static_property_type() { + constexpr auto *name = "pybind11_static_property"; + auto name_obj = reinterpret_steal(PYBIND11_FROM_STRING(name)); + + /* Danger zone: from now (and until PyType_Ready), make sure to + issue no Python C API calls which could potentially invoke the + garbage collector (the GC will call type_traverse(), which will in + turn find the newly constructed type in an invalid state) */ + auto heap_type = (PyHeapTypeObject *) PyType_Type.tp_alloc(&PyType_Type, 0); + if (!heap_type) + pybind11_fail("make_static_property_type(): error allocating type!"); + + heap_type->ht_name = name_obj.inc_ref().ptr(); +#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 3 + heap_type->ht_qualname = name_obj.inc_ref().ptr(); +#endif + + auto type = &heap_type->ht_type; + type->tp_name = name; + type->tp_base = type_incref(&PyProperty_Type); + type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE; + type->tp_descr_get = pybind11_static_get; + type->tp_descr_set = pybind11_static_set; + + if (PyType_Ready(type) < 0) + pybind11_fail("make_static_property_type(): failure in PyType_Ready()!"); + + setattr((PyObject *) type, "__module__", str("pybind11_builtins")); + + return type; +} + +#else // PYPY + +/** PyPy has some issues with the above C API, so we evaluate Python code instead. + This function will only be called once so performance isn't really a concern. + Return value: New reference. */ +inline PyTypeObject *make_static_property_type() { + auto d = dict(); + PyObject *result = PyRun_String(R"(\ + class pybind11_static_property(property): + def __get__(self, obj, cls): + return property.__get__(self, cls, cls) + + def __set__(self, obj, value): + cls = obj if isinstance(obj, type) else type(obj) + property.__set__(self, cls, value) + )", Py_file_input, d.ptr(), d.ptr() + ); + if (result == nullptr) + throw error_already_set(); + Py_DECREF(result); + return (PyTypeObject *) d["pybind11_static_property"].cast().release().ptr(); +} + +#endif // PYPY + +/** Types with static properties need to handle `Type.static_prop = x` in a specific way. + By default, Python replaces the `static_property` itself, but for wrapped C++ types + we need to call `static_property.__set__()` in order to propagate the new value to + the underlying C++ data structure. */ +extern "C" inline int pybind11_meta_setattro(PyObject* obj, PyObject* name, PyObject* value) { + // Use `_PyType_Lookup()` instead of `PyObject_GetAttr()` in order to get the raw + // descriptor (`property`) instead of calling `tp_descr_get` (`property.__get__()`). + PyObject *descr = _PyType_Lookup((PyTypeObject *) obj, name); + + // The following assignment combinations are possible: + // 1. `Type.static_prop = value` --> descr_set: `Type.static_prop.__set__(value)` + // 2. `Type.static_prop = other_static_prop` --> setattro: replace existing `static_prop` + // 3. `Type.regular_attribute = value` --> setattro: regular attribute assignment + const auto static_prop = (PyObject *) get_internals().static_property_type; + const auto call_descr_set = descr && PyObject_IsInstance(descr, static_prop) + && !PyObject_IsInstance(value, static_prop); + if (call_descr_set) { + // Call `static_property.__set__()` instead of replacing the `static_property`. +#if !defined(PYPY_VERSION) + return Py_TYPE(descr)->tp_descr_set(descr, obj, value); +#else + if (PyObject *result = PyObject_CallMethod(descr, "__set__", "OO", obj, value)) { + Py_DECREF(result); + return 0; + } else { + return -1; + } +#endif + } else { + // Replace existing attribute. + return PyType_Type.tp_setattro(obj, name, value); + } +} + +#if PY_MAJOR_VERSION >= 3 +/** + * Python 3's PyInstanceMethod_Type hides itself via its tp_descr_get, which prevents aliasing + * methods via cls.attr("m2") = cls.attr("m1"): instead the tp_descr_get returns a plain function, + * when called on a class, or a PyMethod, when called on an instance. Override that behaviour here + * to do a special case bypass for PyInstanceMethod_Types. + */ +extern "C" inline PyObject *pybind11_meta_getattro(PyObject *obj, PyObject *name) { + PyObject *descr = _PyType_Lookup((PyTypeObject *) obj, name); + if (descr && PyInstanceMethod_Check(descr)) { + Py_INCREF(descr); + return descr; + } + else { + return PyType_Type.tp_getattro(obj, name); + } +} +#endif + +/** This metaclass is assigned by default to all pybind11 types and is required in order + for static properties to function correctly. Users may override this using `py::metaclass`. + Return value: New reference. */ +inline PyTypeObject* make_default_metaclass() { + constexpr auto *name = "pybind11_type"; + auto name_obj = reinterpret_steal(PYBIND11_FROM_STRING(name)); + + /* Danger zone: from now (and until PyType_Ready), make sure to + issue no Python C API calls which could potentially invoke the + garbage collector (the GC will call type_traverse(), which will in + turn find the newly constructed type in an invalid state) */ + auto heap_type = (PyHeapTypeObject *) PyType_Type.tp_alloc(&PyType_Type, 0); + if (!heap_type) + pybind11_fail("make_default_metaclass(): error allocating metaclass!"); + + heap_type->ht_name = name_obj.inc_ref().ptr(); +#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 3 + heap_type->ht_qualname = name_obj.inc_ref().ptr(); +#endif + + auto type = &heap_type->ht_type; + type->tp_name = name; + type->tp_base = type_incref(&PyType_Type); + type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE; + + type->tp_setattro = pybind11_meta_setattro; +#if PY_MAJOR_VERSION >= 3 + type->tp_getattro = pybind11_meta_getattro; +#endif + + if (PyType_Ready(type) < 0) + pybind11_fail("make_default_metaclass(): failure in PyType_Ready()!"); + + setattr((PyObject *) type, "__module__", str("pybind11_builtins")); + + return type; +} + +/// For multiple inheritance types we need to recursively register/deregister base pointers for any +/// base classes with pointers that are difference from the instance value pointer so that we can +/// correctly recognize an offset base class pointer. This calls a function with any offset base ptrs. +inline void traverse_offset_bases(void *valueptr, const detail::type_info *tinfo, instance *self, + bool (*f)(void * /*parentptr*/, instance * /*self*/)) { + for (handle h : reinterpret_borrow(tinfo->type->tp_bases)) { + if (auto parent_tinfo = get_type_info((PyTypeObject *) h.ptr())) { + for (auto &c : parent_tinfo->implicit_casts) { + if (c.first == tinfo->cpptype) { + auto *parentptr = c.second(valueptr); + if (parentptr != valueptr) + f(parentptr, self); + traverse_offset_bases(parentptr, parent_tinfo, self, f); + break; + } + } + } + } +} + +inline bool register_instance_impl(void *ptr, instance *self) { + get_internals().registered_instances.emplace(ptr, self); + return true; // unused, but gives the same signature as the deregister func +} +inline bool deregister_instance_impl(void *ptr, instance *self) { + auto ®istered_instances = get_internals().registered_instances; + auto range = registered_instances.equal_range(ptr); + for (auto it = range.first; it != range.second; ++it) { + if (Py_TYPE(self) == Py_TYPE(it->second)) { + registered_instances.erase(it); + return true; + } + } + return false; +} + +inline void register_instance(instance *self, void *valptr, const type_info *tinfo) { + register_instance_impl(valptr, self); + if (!tinfo->simple_ancestors) + traverse_offset_bases(valptr, tinfo, self, register_instance_impl); +} + +inline bool deregister_instance(instance *self, void *valptr, const type_info *tinfo) { + bool ret = deregister_instance_impl(valptr, self); + if (!tinfo->simple_ancestors) + traverse_offset_bases(valptr, tinfo, self, deregister_instance_impl); + return ret; +} + +/// Instance creation function for all pybind11 types. It only allocates space for the C++ object +/// (or multiple objects, for Python-side inheritance from multiple pybind11 types), but doesn't +/// call the constructor -- an `__init__` function must do that (followed by an `init_instance` +/// to set up the holder and register the instance). +inline PyObject *make_new_instance(PyTypeObject *type, bool allocate_value /*= true (in cast.h)*/) { +#if defined(PYPY_VERSION) + // PyPy gets tp_basicsize wrong (issue 2482) under multiple inheritance when the first inherited + // object is a a plain Python type (i.e. not derived from an extension type). Fix it. + ssize_t instance_size = static_cast(sizeof(instance)); + if (type->tp_basicsize < instance_size) { + type->tp_basicsize = instance_size; + } +#endif + PyObject *self = type->tp_alloc(type, 0); + auto inst = reinterpret_cast(self); + // Allocate the value/holder internals: + inst->allocate_layout(); + + inst->owned = true; + // Allocate (if requested) the value pointers; otherwise leave them as nullptr + if (allocate_value) { + for (auto &v_h : values_and_holders(inst)) { + void *&vptr = v_h.value_ptr(); + vptr = v_h.type->operator_new(v_h.type->type_size); + } + } + + return self; +} + +/// Instance creation function for all pybind11 types. It only allocates space for the +/// C++ object, but doesn't call the constructor -- an `__init__` function must do that. +extern "C" inline PyObject *pybind11_object_new(PyTypeObject *type, PyObject *, PyObject *) { + return make_new_instance(type); +} + +/// An `__init__` function constructs the C++ object. Users should provide at least one +/// of these using `py::init` or directly with `.def(__init__, ...)`. Otherwise, the +/// following default function will be used which simply throws an exception. +extern "C" inline int pybind11_object_init(PyObject *self, PyObject *, PyObject *) { + PyTypeObject *type = Py_TYPE(self); + std::string msg; +#if defined(PYPY_VERSION) + msg += handle((PyObject *) type).attr("__module__").cast() + "."; +#endif + msg += type->tp_name; + msg += ": No constructor defined!"; + PyErr_SetString(PyExc_TypeError, msg.c_str()); + return -1; +} + +inline void add_patient(PyObject *nurse, PyObject *patient) { + auto &internals = get_internals(); + auto instance = reinterpret_cast(nurse); + instance->has_patients = true; + Py_INCREF(patient); + internals.patients[nurse].push_back(patient); +} + +inline void clear_patients(PyObject *self) { + auto instance = reinterpret_cast(self); + auto &internals = get_internals(); + auto pos = internals.patients.find(self); + assert(pos != internals.patients.end()); + // Clearing the patients can cause more Python code to run, which + // can invalidate the iterator. Extract the vector of patients + // from the unordered_map first. + auto patients = std::move(pos->second); + internals.patients.erase(pos); + instance->has_patients = false; + for (PyObject *&patient : patients) + Py_CLEAR(patient); +} + +/// Clears all internal data from the instance and removes it from registered instances in +/// preparation for deallocation. +inline void clear_instance(PyObject *self) { + auto instance = reinterpret_cast(self); + + // Deallocate any values/holders, if present: + for (auto &v_h : values_and_holders(instance)) { + if (v_h) { + + // We have to deregister before we call dealloc because, for virtual MI types, we still + // need to be able to get the parent pointers. + if (v_h.instance_registered() && !deregister_instance(instance, v_h.value_ptr(), v_h.type)) + pybind11_fail("pybind11_object_dealloc(): Tried to deallocate unregistered instance!"); + + if (instance->owned || v_h.holder_constructed()) + v_h.type->dealloc(v_h); + } + } + // Deallocate the value/holder layout internals: + instance->deallocate_layout(); + + if (instance->weakrefs) + PyObject_ClearWeakRefs(self); + + PyObject **dict_ptr = _PyObject_GetDictPtr(self); + if (dict_ptr) + Py_CLEAR(*dict_ptr); + + if (instance->has_patients) + clear_patients(self); +} + +/// Instance destructor function for all pybind11 types. It calls `type_info.dealloc` +/// to destroy the C++ object itself, while the rest is Python bookkeeping. +extern "C" inline void pybind11_object_dealloc(PyObject *self) { + clear_instance(self); + Py_TYPE(self)->tp_free(self); +} + +/** Create the type which can be used as a common base for all classes. This is + needed in order to satisfy Python's requirements for multiple inheritance. + Return value: New reference. */ +inline PyObject *make_object_base_type(PyTypeObject *metaclass) { + constexpr auto *name = "pybind11_object"; + auto name_obj = reinterpret_steal(PYBIND11_FROM_STRING(name)); + + /* Danger zone: from now (and until PyType_Ready), make sure to + issue no Python C API calls which could potentially invoke the + garbage collector (the GC will call type_traverse(), which will in + turn find the newly constructed type in an invalid state) */ + auto heap_type = (PyHeapTypeObject *) metaclass->tp_alloc(metaclass, 0); + if (!heap_type) + pybind11_fail("make_object_base_type(): error allocating type!"); + + heap_type->ht_name = name_obj.inc_ref().ptr(); +#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 3 + heap_type->ht_qualname = name_obj.inc_ref().ptr(); +#endif + + auto type = &heap_type->ht_type; + type->tp_name = name; + type->tp_base = type_incref(&PyBaseObject_Type); + type->tp_basicsize = static_cast(sizeof(instance)); + type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE; + + type->tp_new = pybind11_object_new; + type->tp_init = pybind11_object_init; + type->tp_dealloc = pybind11_object_dealloc; + + /* Support weak references (needed for the keep_alive feature) */ + type->tp_weaklistoffset = offsetof(instance, weakrefs); + + if (PyType_Ready(type) < 0) + pybind11_fail("PyType_Ready failed in make_object_base_type():" + error_string()); + + setattr((PyObject *) type, "__module__", str("pybind11_builtins")); + + assert(!PyType_HasFeature(type, Py_TPFLAGS_HAVE_GC)); + return (PyObject *) heap_type; +} + +/// dynamic_attr: Support for `d = instance.__dict__`. +extern "C" inline PyObject *pybind11_get_dict(PyObject *self, void *) { + PyObject *&dict = *_PyObject_GetDictPtr(self); + if (!dict) + dict = PyDict_New(); + Py_XINCREF(dict); + return dict; +} + +/// dynamic_attr: Support for `instance.__dict__ = dict()`. +extern "C" inline int pybind11_set_dict(PyObject *self, PyObject *new_dict, void *) { + if (!PyDict_Check(new_dict)) { + PyErr_Format(PyExc_TypeError, "__dict__ must be set to a dictionary, not a '%.200s'", + Py_TYPE(new_dict)->tp_name); + return -1; + } + PyObject *&dict = *_PyObject_GetDictPtr(self); + Py_INCREF(new_dict); + Py_CLEAR(dict); + dict = new_dict; + return 0; +} + +/// dynamic_attr: Allow the garbage collector to traverse the internal instance `__dict__`. +extern "C" inline int pybind11_traverse(PyObject *self, visitproc visit, void *arg) { + PyObject *&dict = *_PyObject_GetDictPtr(self); + Py_VISIT(dict); + return 0; +} + +/// dynamic_attr: Allow the GC to clear the dictionary. +extern "C" inline int pybind11_clear(PyObject *self) { + PyObject *&dict = *_PyObject_GetDictPtr(self); + Py_CLEAR(dict); + return 0; +} + +/// Give instances of this type a `__dict__` and opt into garbage collection. +inline void enable_dynamic_attributes(PyHeapTypeObject *heap_type) { + auto type = &heap_type->ht_type; +#if defined(PYPY_VERSION) + pybind11_fail(std::string(type->tp_name) + ": dynamic attributes are " + "currently not supported in " + "conjunction with PyPy!"); +#endif + type->tp_flags |= Py_TPFLAGS_HAVE_GC; + type->tp_dictoffset = type->tp_basicsize; // place dict at the end + type->tp_basicsize += (ssize_t)sizeof(PyObject *); // and allocate enough space for it + type->tp_traverse = pybind11_traverse; + type->tp_clear = pybind11_clear; + + static PyGetSetDef getset[] = { + {const_cast("__dict__"), pybind11_get_dict, pybind11_set_dict, nullptr, nullptr}, + {nullptr, nullptr, nullptr, nullptr, nullptr} + }; + type->tp_getset = getset; +} + +/// buffer_protocol: Fill in the view as specified by flags. +extern "C" inline int pybind11_getbuffer(PyObject *obj, Py_buffer *view, int flags) { + // Look for a `get_buffer` implementation in this type's info or any bases (following MRO). + type_info *tinfo = nullptr; + for (auto type : reinterpret_borrow(Py_TYPE(obj)->tp_mro)) { + tinfo = get_type_info((PyTypeObject *) type.ptr()); + if (tinfo && tinfo->get_buffer) + break; + } + if (view == nullptr || obj == nullptr || !tinfo || !tinfo->get_buffer) { + if (view) + view->obj = nullptr; + PyErr_SetString(PyExc_BufferError, "pybind11_getbuffer(): Internal error"); + return -1; + } + std::memset(view, 0, sizeof(Py_buffer)); + buffer_info *info = tinfo->get_buffer(obj, tinfo->get_buffer_data); + view->obj = obj; + view->ndim = 1; + view->internal = info; + view->buf = info->ptr; + view->itemsize = info->itemsize; + view->len = view->itemsize; + for (auto s : info->shape) + view->len *= s; + if ((flags & PyBUF_FORMAT) == PyBUF_FORMAT) + view->format = const_cast(info->format.c_str()); + if ((flags & PyBUF_STRIDES) == PyBUF_STRIDES) { + view->ndim = (int) info->ndim; + view->strides = &info->strides[0]; + view->shape = &info->shape[0]; + } + Py_INCREF(view->obj); + return 0; +} + +/// buffer_protocol: Release the resources of the buffer. +extern "C" inline void pybind11_releasebuffer(PyObject *, Py_buffer *view) { + delete (buffer_info *) view->internal; +} + +/// Give this type a buffer interface. +inline void enable_buffer_protocol(PyHeapTypeObject *heap_type) { + heap_type->ht_type.tp_as_buffer = &heap_type->as_buffer; +#if PY_MAJOR_VERSION < 3 + heap_type->ht_type.tp_flags |= Py_TPFLAGS_HAVE_NEWBUFFER; +#endif + + heap_type->as_buffer.bf_getbuffer = pybind11_getbuffer; + heap_type->as_buffer.bf_releasebuffer = pybind11_releasebuffer; +} + +/** Create a brand new Python type according to the `type_record` specification. + Return value: New reference. */ +inline PyObject* make_new_python_type(const type_record &rec) { + auto name = reinterpret_steal(PYBIND11_FROM_STRING(rec.name)); + +#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 3 + auto ht_qualname = name; + if (rec.scope && hasattr(rec.scope, "__qualname__")) { + ht_qualname = reinterpret_steal( + PyUnicode_FromFormat("%U.%U", rec.scope.attr("__qualname__").ptr(), name.ptr())); + } +#endif + + object module; + if (rec.scope) { + if (hasattr(rec.scope, "__module__")) + module = rec.scope.attr("__module__"); + else if (hasattr(rec.scope, "__name__")) + module = rec.scope.attr("__name__"); + } + +#if !defined(PYPY_VERSION) + const auto full_name = module ? str(module).cast() + "." + rec.name + : std::string(rec.name); +#else + const auto full_name = std::string(rec.name); +#endif + + char *tp_doc = nullptr; + if (rec.doc && options::show_user_defined_docstrings()) { + /* Allocate memory for docstring (using PyObject_MALLOC, since + Python will free this later on) */ + size_t size = strlen(rec.doc) + 1; + tp_doc = (char *) PyObject_MALLOC(size); + memcpy((void *) tp_doc, rec.doc, size); + } + + auto &internals = get_internals(); + auto bases = tuple(rec.bases); + auto base = (bases.size() == 0) ? internals.instance_base + : bases[0].ptr(); + + /* Danger zone: from now (and until PyType_Ready), make sure to + issue no Python C API calls which could potentially invoke the + garbage collector (the GC will call type_traverse(), which will in + turn find the newly constructed type in an invalid state) */ + auto metaclass = rec.metaclass.ptr() ? (PyTypeObject *) rec.metaclass.ptr() + : internals.default_metaclass; + + auto heap_type = (PyHeapTypeObject *) metaclass->tp_alloc(metaclass, 0); + if (!heap_type) + pybind11_fail(std::string(rec.name) + ": Unable to create type object!"); + + heap_type->ht_name = name.release().ptr(); +#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 3 + heap_type->ht_qualname = ht_qualname.release().ptr(); +#endif + + auto type = &heap_type->ht_type; + type->tp_name = strdup(full_name.c_str()); + type->tp_doc = tp_doc; + type->tp_base = type_incref((PyTypeObject *)base); + type->tp_basicsize = static_cast(sizeof(instance)); + if (bases.size() > 0) + type->tp_bases = bases.release().ptr(); + + /* Don't inherit base __init__ */ + type->tp_init = pybind11_object_init; + + /* Supported protocols */ + type->tp_as_number = &heap_type->as_number; + type->tp_as_sequence = &heap_type->as_sequence; + type->tp_as_mapping = &heap_type->as_mapping; + + /* Flags */ + type->tp_flags |= Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE; +#if PY_MAJOR_VERSION < 3 + type->tp_flags |= Py_TPFLAGS_CHECKTYPES; +#endif + + if (rec.dynamic_attr) + enable_dynamic_attributes(heap_type); + + if (rec.buffer_protocol) + enable_buffer_protocol(heap_type); + + if (PyType_Ready(type) < 0) + pybind11_fail(std::string(rec.name) + ": PyType_Ready failed (" + error_string() + ")!"); + + assert(rec.dynamic_attr ? PyType_HasFeature(type, Py_TPFLAGS_HAVE_GC) + : !PyType_HasFeature(type, Py_TPFLAGS_HAVE_GC)); + + /* Register type with the parent scope */ + if (rec.scope) + setattr(rec.scope, rec.name, (PyObject *) type); + + if (module) // Needed by pydoc + setattr((PyObject *) type, "__module__", module); + + return (PyObject *) type; +} + +NAMESPACE_END(detail) +NAMESPACE_END(pybind11) diff --git a/ptocr/postprocess/dbprocess/include/pybind11/common.h b/ptocr/postprocess/dbprocess/include/pybind11/common.h new file mode 100644 index 0000000..6c8a4f1 --- /dev/null +++ b/ptocr/postprocess/dbprocess/include/pybind11/common.h @@ -0,0 +1,2 @@ +#include "detail/common.h" +#warning "Including 'common.h' is deprecated. It will be removed in v3.0. Use 'pybind11.h'." diff --git a/ptocr/postprocess/dbprocess/include/pybind11/complex.h b/ptocr/postprocess/dbprocess/include/pybind11/complex.h new file mode 100644 index 0000000..3f89638 --- /dev/null +++ b/ptocr/postprocess/dbprocess/include/pybind11/complex.h @@ -0,0 +1,65 @@ +/* + pybind11/complex.h: Complex number support + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "pybind11.h" +#include + +/// glibc defines I as a macro which breaks things, e.g., boost template names +#ifdef I +# undef I +#endif + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) + +template struct format_descriptor, detail::enable_if_t::value>> { + static constexpr const char c = format_descriptor::c; + static constexpr const char value[3] = { 'Z', c, '\0' }; + static std::string format() { return std::string(value); } +}; + +#ifndef PYBIND11_CPP17 + +template constexpr const char format_descriptor< + std::complex, detail::enable_if_t::value>>::value[3]; + +#endif + +NAMESPACE_BEGIN(detail) + +template struct is_fmt_numeric, detail::enable_if_t::value>> { + static constexpr bool value = true; + static constexpr int index = is_fmt_numeric::index + 3; +}; + +template class type_caster> { +public: + bool load(handle src, bool convert) { + if (!src) + return false; + if (!convert && !PyComplex_Check(src.ptr())) + return false; + Py_complex result = PyComplex_AsCComplex(src.ptr()); + if (result.real == -1.0 && PyErr_Occurred()) { + PyErr_Clear(); + return false; + } + value = std::complex((T) result.real, (T) result.imag); + return true; + } + + static handle cast(const std::complex &src, return_value_policy /* policy */, handle /* parent */) { + return PyComplex_FromDoubles((double) src.real(), (double) src.imag()); + } + + PYBIND11_TYPE_CASTER(std::complex, _("complex")); +}; +NAMESPACE_END(detail) +NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/ptocr/postprocess/dbprocess/include/pybind11/descr.h b/ptocr/postprocess/dbprocess/include/pybind11/descr.h new file mode 100644 index 0000000..23a099c --- /dev/null +++ b/ptocr/postprocess/dbprocess/include/pybind11/descr.h @@ -0,0 +1,185 @@ +/* + pybind11/descr.h: Helper type for concatenating type signatures + either at runtime (C++11) or compile time (C++14) + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "common.h" + +NAMESPACE_BEGIN(pybind11) +NAMESPACE_BEGIN(detail) + +/* Concatenate type signatures at compile time using C++14 */ +#if defined(PYBIND11_CPP14) && !defined(_MSC_VER) +#define PYBIND11_CONSTEXPR_DESCR + +template class descr { + template friend class descr; +public: + constexpr descr(char const (&text) [Size1+1], const std::type_info * const (&types)[Size2+1]) + : descr(text, types, + make_index_sequence(), + make_index_sequence()) { } + + constexpr const char *text() const { return m_text; } + constexpr const std::type_info * const * types() const { return m_types; } + + template + constexpr descr operator+(const descr &other) const { + return concat(other, + make_index_sequence(), + make_index_sequence(), + make_index_sequence(), + make_index_sequence()); + } + +protected: + template + constexpr descr( + char const (&text) [Size1+1], + const std::type_info * const (&types) [Size2+1], + index_sequence, index_sequence) + : m_text{text[Indices1]..., '\0'}, + m_types{types[Indices2]..., nullptr } {} + + template + constexpr descr + concat(const descr &other, + index_sequence, index_sequence, + index_sequence, index_sequence) const { + return descr( + { m_text[Indices1]..., other.m_text[OtherIndices1]..., '\0' }, + { m_types[Indices2]..., other.m_types[OtherIndices2]..., nullptr } + ); + } + +protected: + char m_text[Size1 + 1]; + const std::type_info * m_types[Size2 + 1]; +}; + +template constexpr descr _(char const(&text)[Size]) { + return descr(text, { nullptr }); +} + +template struct int_to_str : int_to_str { }; +template struct int_to_str<0, Digits...> { + static constexpr auto digits = descr({ ('0' + Digits)..., '\0' }, { nullptr }); +}; + +// Ternary description (like std::conditional) +template +constexpr enable_if_t> _(char const(&text1)[Size1], char const(&)[Size2]) { + return _(text1); +} +template +constexpr enable_if_t> _(char const(&)[Size1], char const(&text2)[Size2]) { + return _(text2); +} +template +constexpr enable_if_t> _(descr d, descr) { return d; } +template +constexpr enable_if_t> _(descr, descr d) { return d; } + +template auto constexpr _() -> decltype(int_to_str::digits) { + return int_to_str::digits; +} + +template constexpr descr<1, 1> _() { + return descr<1, 1>({ '%', '\0' }, { &typeid(Type), nullptr }); +} + +inline constexpr descr<0, 0> concat() { return _(""); } +template auto constexpr concat(descr descr) { return descr; } +template auto constexpr concat(descr descr, Args&&... args) { return descr + _(", ") + concat(args...); } +template auto constexpr type_descr(descr descr) { return _("{") + descr + _("}"); } + +#define PYBIND11_DESCR constexpr auto + +#else /* Simpler C++11 implementation based on run-time memory allocation and copying */ + +class descr { +public: + PYBIND11_NOINLINE descr(const char *text, const std::type_info * const * types) { + size_t nChars = len(text), nTypes = len(types); + m_text = new char[nChars]; + m_types = new const std::type_info *[nTypes]; + memcpy(m_text, text, nChars * sizeof(char)); + memcpy(m_types, types, nTypes * sizeof(const std::type_info *)); + } + + PYBIND11_NOINLINE descr operator+(descr &&d2) && { + descr r; + + size_t nChars1 = len(m_text), nTypes1 = len(m_types); + size_t nChars2 = len(d2.m_text), nTypes2 = len(d2.m_types); + + r.m_text = new char[nChars1 + nChars2 - 1]; + r.m_types = new const std::type_info *[nTypes1 + nTypes2 - 1]; + memcpy(r.m_text, m_text, (nChars1-1) * sizeof(char)); + memcpy(r.m_text + nChars1 - 1, d2.m_text, nChars2 * sizeof(char)); + memcpy(r.m_types, m_types, (nTypes1-1) * sizeof(std::type_info *)); + memcpy(r.m_types + nTypes1 - 1, d2.m_types, nTypes2 * sizeof(std::type_info *)); + + delete[] m_text; delete[] m_types; + delete[] d2.m_text; delete[] d2.m_types; + + return r; + } + + char *text() { return m_text; } + const std::type_info * * types() { return m_types; } + +protected: + PYBIND11_NOINLINE descr() { } + + template static size_t len(const T *ptr) { // return length including null termination + const T *it = ptr; + while (*it++ != (T) 0) + ; + return static_cast(it - ptr); + } + + const std::type_info **m_types = nullptr; + char *m_text = nullptr; +}; + +/* The 'PYBIND11_NOINLINE inline' combinations below are intentional to get the desired linkage while producing as little object code as possible */ + +PYBIND11_NOINLINE inline descr _(const char *text) { + const std::type_info *types[1] = { nullptr }; + return descr(text, types); +} + +template PYBIND11_NOINLINE enable_if_t _(const char *text1, const char *) { return _(text1); } +template PYBIND11_NOINLINE enable_if_t _(char const *, const char *text2) { return _(text2); } +template PYBIND11_NOINLINE enable_if_t _(descr d, descr) { return d; } +template PYBIND11_NOINLINE enable_if_t _(descr, descr d) { return d; } + +template PYBIND11_NOINLINE descr _() { + const std::type_info *types[2] = { &typeid(Type), nullptr }; + return descr("%", types); +} + +template PYBIND11_NOINLINE descr _() { + const std::type_info *types[1] = { nullptr }; + return descr(std::to_string(Size).c_str(), types); +} + +PYBIND11_NOINLINE inline descr concat() { return _(""); } +PYBIND11_NOINLINE inline descr concat(descr &&d) { return d; } +template PYBIND11_NOINLINE descr concat(descr &&d, Args&&... args) { return std::move(d) + _(", ") + concat(std::forward(args)...); } +PYBIND11_NOINLINE inline descr type_descr(descr&& d) { return _("{") + std::move(d) + _("}"); } + +#define PYBIND11_DESCR ::pybind11::detail::descr +#endif + +NAMESPACE_END(detail) +NAMESPACE_END(pybind11) diff --git a/ptocr/postprocess/dbprocess/include/pybind11/detail/class.h b/ptocr/postprocess/dbprocess/include/pybind11/detail/class.h new file mode 100644 index 0000000..7a5dd01 --- /dev/null +++ b/ptocr/postprocess/dbprocess/include/pybind11/detail/class.h @@ -0,0 +1,622 @@ +/* + pybind11/detail/class.h: Python C API implementation details for py::class_ + + Copyright (c) 2017 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "../attr.h" + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +NAMESPACE_BEGIN(detail) + +#if PY_VERSION_HEX >= 0x03030000 +# define PYBIND11_BUILTIN_QUALNAME +# define PYBIND11_SET_OLDPY_QUALNAME(obj, nameobj) +#else +// In pre-3.3 Python, we still set __qualname__ so that we can produce reliable function type +// signatures; in 3.3+ this macro expands to nothing: +# define PYBIND11_SET_OLDPY_QUALNAME(obj, nameobj) setattr((PyObject *) obj, "__qualname__", nameobj) +#endif + +inline PyTypeObject *type_incref(PyTypeObject *type) { + Py_INCREF(type); + return type; +} + +#if !defined(PYPY_VERSION) + +/// `pybind11_static_property.__get__()`: Always pass the class instead of the instance. +extern "C" inline PyObject *pybind11_static_get(PyObject *self, PyObject * /*ob*/, PyObject *cls) { + return PyProperty_Type.tp_descr_get(self, cls, cls); +} + +/// `pybind11_static_property.__set__()`: Just like the above `__get__()`. +extern "C" inline int pybind11_static_set(PyObject *self, PyObject *obj, PyObject *value) { + PyObject *cls = PyType_Check(obj) ? obj : (PyObject *) Py_TYPE(obj); + return PyProperty_Type.tp_descr_set(self, cls, value); +} + +/** A `static_property` is the same as a `property` but the `__get__()` and `__set__()` + methods are modified to always use the object type instead of a concrete instance. + Return value: New reference. */ +inline PyTypeObject *make_static_property_type() { + constexpr auto *name = "pybind11_static_property"; + auto name_obj = reinterpret_steal(PYBIND11_FROM_STRING(name)); + + /* Danger zone: from now (and until PyType_Ready), make sure to + issue no Python C API calls which could potentially invoke the + garbage collector (the GC will call type_traverse(), which will in + turn find the newly constructed type in an invalid state) */ + auto heap_type = (PyHeapTypeObject *) PyType_Type.tp_alloc(&PyType_Type, 0); + if (!heap_type) + pybind11_fail("make_static_property_type(): error allocating type!"); + + heap_type->ht_name = name_obj.inc_ref().ptr(); +#ifdef PYBIND11_BUILTIN_QUALNAME + heap_type->ht_qualname = name_obj.inc_ref().ptr(); +#endif + + auto type = &heap_type->ht_type; + type->tp_name = name; + type->tp_base = type_incref(&PyProperty_Type); + type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE; + type->tp_descr_get = pybind11_static_get; + type->tp_descr_set = pybind11_static_set; + + if (PyType_Ready(type) < 0) + pybind11_fail("make_static_property_type(): failure in PyType_Ready()!"); + + setattr((PyObject *) type, "__module__", str("pybind11_builtins")); + PYBIND11_SET_OLDPY_QUALNAME(type, name_obj); + + return type; +} + +#else // PYPY + +/** PyPy has some issues with the above C API, so we evaluate Python code instead. + This function will only be called once so performance isn't really a concern. + Return value: New reference. */ +inline PyTypeObject *make_static_property_type() { + auto d = dict(); + PyObject *result = PyRun_String(R"(\ + class pybind11_static_property(property): + def __get__(self, obj, cls): + return property.__get__(self, cls, cls) + + def __set__(self, obj, value): + cls = obj if isinstance(obj, type) else type(obj) + property.__set__(self, cls, value) + )", Py_file_input, d.ptr(), d.ptr() + ); + if (result == nullptr) + throw error_already_set(); + Py_DECREF(result); + return (PyTypeObject *) d["pybind11_static_property"].cast().release().ptr(); +} + +#endif // PYPY + +/** Types with static properties need to handle `Type.static_prop = x` in a specific way. + By default, Python replaces the `static_property` itself, but for wrapped C++ types + we need to call `static_property.__set__()` in order to propagate the new value to + the underlying C++ data structure. */ +extern "C" inline int pybind11_meta_setattro(PyObject* obj, PyObject* name, PyObject* value) { + // Use `_PyType_Lookup()` instead of `PyObject_GetAttr()` in order to get the raw + // descriptor (`property`) instead of calling `tp_descr_get` (`property.__get__()`). + PyObject *descr = _PyType_Lookup((PyTypeObject *) obj, name); + + // The following assignment combinations are possible: + // 1. `Type.static_prop = value` --> descr_set: `Type.static_prop.__set__(value)` + // 2. `Type.static_prop = other_static_prop` --> setattro: replace existing `static_prop` + // 3. `Type.regular_attribute = value` --> setattro: regular attribute assignment + const auto static_prop = (PyObject *) get_internals().static_property_type; + const auto call_descr_set = descr && PyObject_IsInstance(descr, static_prop) + && !PyObject_IsInstance(value, static_prop); + if (call_descr_set) { + // Call `static_property.__set__()` instead of replacing the `static_property`. +#if !defined(PYPY_VERSION) + return Py_TYPE(descr)->tp_descr_set(descr, obj, value); +#else + if (PyObject *result = PyObject_CallMethod(descr, "__set__", "OO", obj, value)) { + Py_DECREF(result); + return 0; + } else { + return -1; + } +#endif + } else { + // Replace existing attribute. + return PyType_Type.tp_setattro(obj, name, value); + } +} + +#if PY_MAJOR_VERSION >= 3 +/** + * Python 3's PyInstanceMethod_Type hides itself via its tp_descr_get, which prevents aliasing + * methods via cls.attr("m2") = cls.attr("m1"): instead the tp_descr_get returns a plain function, + * when called on a class, or a PyMethod, when called on an instance. Override that behaviour here + * to do a special case bypass for PyInstanceMethod_Types. + */ +extern "C" inline PyObject *pybind11_meta_getattro(PyObject *obj, PyObject *name) { + PyObject *descr = _PyType_Lookup((PyTypeObject *) obj, name); + if (descr && PyInstanceMethod_Check(descr)) { + Py_INCREF(descr); + return descr; + } + else { + return PyType_Type.tp_getattro(obj, name); + } +} +#endif + +/** This metaclass is assigned by default to all pybind11 types and is required in order + for static properties to function correctly. Users may override this using `py::metaclass`. + Return value: New reference. */ +inline PyTypeObject* make_default_metaclass() { + constexpr auto *name = "pybind11_type"; + auto name_obj = reinterpret_steal(PYBIND11_FROM_STRING(name)); + + /* Danger zone: from now (and until PyType_Ready), make sure to + issue no Python C API calls which could potentially invoke the + garbage collector (the GC will call type_traverse(), which will in + turn find the newly constructed type in an invalid state) */ + auto heap_type = (PyHeapTypeObject *) PyType_Type.tp_alloc(&PyType_Type, 0); + if (!heap_type) + pybind11_fail("make_default_metaclass(): error allocating metaclass!"); + + heap_type->ht_name = name_obj.inc_ref().ptr(); +#ifdef PYBIND11_BUILTIN_QUALNAME + heap_type->ht_qualname = name_obj.inc_ref().ptr(); +#endif + + auto type = &heap_type->ht_type; + type->tp_name = name; + type->tp_base = type_incref(&PyType_Type); + type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE; + + type->tp_setattro = pybind11_meta_setattro; +#if PY_MAJOR_VERSION >= 3 + type->tp_getattro = pybind11_meta_getattro; +#endif + + if (PyType_Ready(type) < 0) + pybind11_fail("make_default_metaclass(): failure in PyType_Ready()!"); + + setattr((PyObject *) type, "__module__", str("pybind11_builtins")); + PYBIND11_SET_OLDPY_QUALNAME(type, name_obj); + + return type; +} + +/// For multiple inheritance types we need to recursively register/deregister base pointers for any +/// base classes with pointers that are difference from the instance value pointer so that we can +/// correctly recognize an offset base class pointer. This calls a function with any offset base ptrs. +inline void traverse_offset_bases(void *valueptr, const detail::type_info *tinfo, instance *self, + bool (*f)(void * /*parentptr*/, instance * /*self*/)) { + for (handle h : reinterpret_borrow(tinfo->type->tp_bases)) { + if (auto parent_tinfo = get_type_info((PyTypeObject *) h.ptr())) { + for (auto &c : parent_tinfo->implicit_casts) { + if (c.first == tinfo->cpptype) { + auto *parentptr = c.second(valueptr); + if (parentptr != valueptr) + f(parentptr, self); + traverse_offset_bases(parentptr, parent_tinfo, self, f); + break; + } + } + } + } +} + +inline bool register_instance_impl(void *ptr, instance *self) { + get_internals().registered_instances.emplace(ptr, self); + return true; // unused, but gives the same signature as the deregister func +} +inline bool deregister_instance_impl(void *ptr, instance *self) { + auto ®istered_instances = get_internals().registered_instances; + auto range = registered_instances.equal_range(ptr); + for (auto it = range.first; it != range.second; ++it) { + if (Py_TYPE(self) == Py_TYPE(it->second)) { + registered_instances.erase(it); + return true; + } + } + return false; +} + +inline void register_instance(instance *self, void *valptr, const type_info *tinfo) { + register_instance_impl(valptr, self); + if (!tinfo->simple_ancestors) + traverse_offset_bases(valptr, tinfo, self, register_instance_impl); +} + +inline bool deregister_instance(instance *self, void *valptr, const type_info *tinfo) { + bool ret = deregister_instance_impl(valptr, self); + if (!tinfo->simple_ancestors) + traverse_offset_bases(valptr, tinfo, self, deregister_instance_impl); + return ret; +} + +/// Instance creation function for all pybind11 types. It allocates the internal instance layout for +/// holding C++ objects and holders. Allocation is done lazily (the first time the instance is cast +/// to a reference or pointer), and initialization is done by an `__init__` function. +inline PyObject *make_new_instance(PyTypeObject *type) { +#if defined(PYPY_VERSION) + // PyPy gets tp_basicsize wrong (issue 2482) under multiple inheritance when the first inherited + // object is a a plain Python type (i.e. not derived from an extension type). Fix it. + ssize_t instance_size = static_cast(sizeof(instance)); + if (type->tp_basicsize < instance_size) { + type->tp_basicsize = instance_size; + } +#endif + PyObject *self = type->tp_alloc(type, 0); + auto inst = reinterpret_cast(self); + // Allocate the value/holder internals: + inst->allocate_layout(); + + inst->owned = true; + + return self; +} + +/// Instance creation function for all pybind11 types. It only allocates space for the +/// C++ object, but doesn't call the constructor -- an `__init__` function must do that. +extern "C" inline PyObject *pybind11_object_new(PyTypeObject *type, PyObject *, PyObject *) { + return make_new_instance(type); +} + +/// An `__init__` function constructs the C++ object. Users should provide at least one +/// of these using `py::init` or directly with `.def(__init__, ...)`. Otherwise, the +/// following default function will be used which simply throws an exception. +extern "C" inline int pybind11_object_init(PyObject *self, PyObject *, PyObject *) { + PyTypeObject *type = Py_TYPE(self); + std::string msg; +#if defined(PYPY_VERSION) + msg += handle((PyObject *) type).attr("__module__").cast() + "."; +#endif + msg += type->tp_name; + msg += ": No constructor defined!"; + PyErr_SetString(PyExc_TypeError, msg.c_str()); + return -1; +} + +inline void add_patient(PyObject *nurse, PyObject *patient) { + auto &internals = get_internals(); + auto instance = reinterpret_cast(nurse); + instance->has_patients = true; + Py_INCREF(patient); + internals.patients[nurse].push_back(patient); +} + +inline void clear_patients(PyObject *self) { + auto instance = reinterpret_cast(self); + auto &internals = get_internals(); + auto pos = internals.patients.find(self); + assert(pos != internals.patients.end()); + // Clearing the patients can cause more Python code to run, which + // can invalidate the iterator. Extract the vector of patients + // from the unordered_map first. + auto patients = std::move(pos->second); + internals.patients.erase(pos); + instance->has_patients = false; + for (PyObject *&patient : patients) + Py_CLEAR(patient); +} + +/// Clears all internal data from the instance and removes it from registered instances in +/// preparation for deallocation. +inline void clear_instance(PyObject *self) { + auto instance = reinterpret_cast(self); + + // Deallocate any values/holders, if present: + for (auto &v_h : values_and_holders(instance)) { + if (v_h) { + + // We have to deregister before we call dealloc because, for virtual MI types, we still + // need to be able to get the parent pointers. + if (v_h.instance_registered() && !deregister_instance(instance, v_h.value_ptr(), v_h.type)) + pybind11_fail("pybind11_object_dealloc(): Tried to deallocate unregistered instance!"); + + if (instance->owned || v_h.holder_constructed()) + v_h.type->dealloc(v_h); + } + } + // Deallocate the value/holder layout internals: + instance->deallocate_layout(); + + if (instance->weakrefs) + PyObject_ClearWeakRefs(self); + + PyObject **dict_ptr = _PyObject_GetDictPtr(self); + if (dict_ptr) + Py_CLEAR(*dict_ptr); + + if (instance->has_patients) + clear_patients(self); +} + +/// Instance destructor function for all pybind11 types. It calls `type_info.dealloc` +/// to destroy the C++ object itself, while the rest is Python bookkeeping. +extern "C" inline void pybind11_object_dealloc(PyObject *self) { + clear_instance(self); + + auto type = Py_TYPE(self); + type->tp_free(self); + + // `type->tp_dealloc != pybind11_object_dealloc` means that we're being called + // as part of a derived type's dealloc, in which case we're not allowed to decref + // the type here. For cross-module compatibility, we shouldn't compare directly + // with `pybind11_object_dealloc`, but with the common one stashed in internals. + auto pybind11_object_type = (PyTypeObject *) get_internals().instance_base; + if (type->tp_dealloc == pybind11_object_type->tp_dealloc) + Py_DECREF(type); +} + +/** Create the type which can be used as a common base for all classes. This is + needed in order to satisfy Python's requirements for multiple inheritance. + Return value: New reference. */ +inline PyObject *make_object_base_type(PyTypeObject *metaclass) { + constexpr auto *name = "pybind11_object"; + auto name_obj = reinterpret_steal(PYBIND11_FROM_STRING(name)); + + /* Danger zone: from now (and until PyType_Ready), make sure to + issue no Python C API calls which could potentially invoke the + garbage collector (the GC will call type_traverse(), which will in + turn find the newly constructed type in an invalid state) */ + auto heap_type = (PyHeapTypeObject *) metaclass->tp_alloc(metaclass, 0); + if (!heap_type) + pybind11_fail("make_object_base_type(): error allocating type!"); + + heap_type->ht_name = name_obj.inc_ref().ptr(); +#ifdef PYBIND11_BUILTIN_QUALNAME + heap_type->ht_qualname = name_obj.inc_ref().ptr(); +#endif + + auto type = &heap_type->ht_type; + type->tp_name = name; + type->tp_base = type_incref(&PyBaseObject_Type); + type->tp_basicsize = static_cast(sizeof(instance)); + type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE; + + type->tp_new = pybind11_object_new; + type->tp_init = pybind11_object_init; + type->tp_dealloc = pybind11_object_dealloc; + + /* Support weak references (needed for the keep_alive feature) */ + type->tp_weaklistoffset = offsetof(instance, weakrefs); + + if (PyType_Ready(type) < 0) + pybind11_fail("PyType_Ready failed in make_object_base_type():" + error_string()); + + setattr((PyObject *) type, "__module__", str("pybind11_builtins")); + PYBIND11_SET_OLDPY_QUALNAME(type, name_obj); + + assert(!PyType_HasFeature(type, Py_TPFLAGS_HAVE_GC)); + return (PyObject *) heap_type; +} + +/// dynamic_attr: Support for `d = instance.__dict__`. +extern "C" inline PyObject *pybind11_get_dict(PyObject *self, void *) { + PyObject *&dict = *_PyObject_GetDictPtr(self); + if (!dict) + dict = PyDict_New(); + Py_XINCREF(dict); + return dict; +} + +/// dynamic_attr: Support for `instance.__dict__ = dict()`. +extern "C" inline int pybind11_set_dict(PyObject *self, PyObject *new_dict, void *) { + if (!PyDict_Check(new_dict)) { + PyErr_Format(PyExc_TypeError, "__dict__ must be set to a dictionary, not a '%.200s'", + Py_TYPE(new_dict)->tp_name); + return -1; + } + PyObject *&dict = *_PyObject_GetDictPtr(self); + Py_INCREF(new_dict); + Py_CLEAR(dict); + dict = new_dict; + return 0; +} + +/// dynamic_attr: Allow the garbage collector to traverse the internal instance `__dict__`. +extern "C" inline int pybind11_traverse(PyObject *self, visitproc visit, void *arg) { + PyObject *&dict = *_PyObject_GetDictPtr(self); + Py_VISIT(dict); + return 0; +} + +/// dynamic_attr: Allow the GC to clear the dictionary. +extern "C" inline int pybind11_clear(PyObject *self) { + PyObject *&dict = *_PyObject_GetDictPtr(self); + Py_CLEAR(dict); + return 0; +} + +/// Give instances of this type a `__dict__` and opt into garbage collection. +inline void enable_dynamic_attributes(PyHeapTypeObject *heap_type) { + auto type = &heap_type->ht_type; +#if defined(PYPY_VERSION) + pybind11_fail(std::string(type->tp_name) + ": dynamic attributes are " + "currently not supported in " + "conjunction with PyPy!"); +#endif + type->tp_flags |= Py_TPFLAGS_HAVE_GC; + type->tp_dictoffset = type->tp_basicsize; // place dict at the end + type->tp_basicsize += (ssize_t)sizeof(PyObject *); // and allocate enough space for it + type->tp_traverse = pybind11_traverse; + type->tp_clear = pybind11_clear; + + static PyGetSetDef getset[] = { + {const_cast("__dict__"), pybind11_get_dict, pybind11_set_dict, nullptr, nullptr}, + {nullptr, nullptr, nullptr, nullptr, nullptr} + }; + type->tp_getset = getset; +} + +/// buffer_protocol: Fill in the view as specified by flags. +extern "C" inline int pybind11_getbuffer(PyObject *obj, Py_buffer *view, int flags) { + // Look for a `get_buffer` implementation in this type's info or any bases (following MRO). + type_info *tinfo = nullptr; + for (auto type : reinterpret_borrow(Py_TYPE(obj)->tp_mro)) { + tinfo = get_type_info((PyTypeObject *) type.ptr()); + if (tinfo && tinfo->get_buffer) + break; + } + if (view == nullptr || obj == nullptr || !tinfo || !tinfo->get_buffer) { + if (view) + view->obj = nullptr; + PyErr_SetString(PyExc_BufferError, "pybind11_getbuffer(): Internal error"); + return -1; + } + std::memset(view, 0, sizeof(Py_buffer)); + buffer_info *info = tinfo->get_buffer(obj, tinfo->get_buffer_data); + view->obj = obj; + view->ndim = 1; + view->internal = info; + view->buf = info->ptr; + view->itemsize = info->itemsize; + view->len = view->itemsize; + for (auto s : info->shape) + view->len *= s; + if ((flags & PyBUF_FORMAT) == PyBUF_FORMAT) + view->format = const_cast(info->format.c_str()); + if ((flags & PyBUF_STRIDES) == PyBUF_STRIDES) { + view->ndim = (int) info->ndim; + view->strides = &info->strides[0]; + view->shape = &info->shape[0]; + } + Py_INCREF(view->obj); + return 0; +} + +/// buffer_protocol: Release the resources of the buffer. +extern "C" inline void pybind11_releasebuffer(PyObject *, Py_buffer *view) { + delete (buffer_info *) view->internal; +} + +/// Give this type a buffer interface. +inline void enable_buffer_protocol(PyHeapTypeObject *heap_type) { + heap_type->ht_type.tp_as_buffer = &heap_type->as_buffer; +#if PY_MAJOR_VERSION < 3 + heap_type->ht_type.tp_flags |= Py_TPFLAGS_HAVE_NEWBUFFER; +#endif + + heap_type->as_buffer.bf_getbuffer = pybind11_getbuffer; + heap_type->as_buffer.bf_releasebuffer = pybind11_releasebuffer; +} + +/** Create a brand new Python type according to the `type_record` specification. + Return value: New reference. */ +inline PyObject* make_new_python_type(const type_record &rec) { + auto name = reinterpret_steal(PYBIND11_FROM_STRING(rec.name)); + + auto qualname = name; + if (rec.scope && !PyModule_Check(rec.scope.ptr()) && hasattr(rec.scope, "__qualname__")) { +#if PY_MAJOR_VERSION >= 3 + qualname = reinterpret_steal( + PyUnicode_FromFormat("%U.%U", rec.scope.attr("__qualname__").ptr(), name.ptr())); +#else + qualname = str(rec.scope.attr("__qualname__").cast() + "." + rec.name); +#endif + } + + object module; + if (rec.scope) { + if (hasattr(rec.scope, "__module__")) + module = rec.scope.attr("__module__"); + else if (hasattr(rec.scope, "__name__")) + module = rec.scope.attr("__name__"); + } + + auto full_name = c_str( +#if !defined(PYPY_VERSION) + module ? str(module).cast() + "." + rec.name : +#endif + rec.name); + + char *tp_doc = nullptr; + if (rec.doc && options::show_user_defined_docstrings()) { + /* Allocate memory for docstring (using PyObject_MALLOC, since + Python will free this later on) */ + size_t size = strlen(rec.doc) + 1; + tp_doc = (char *) PyObject_MALLOC(size); + memcpy((void *) tp_doc, rec.doc, size); + } + + auto &internals = get_internals(); + auto bases = tuple(rec.bases); + auto base = (bases.size() == 0) ? internals.instance_base + : bases[0].ptr(); + + /* Danger zone: from now (and until PyType_Ready), make sure to + issue no Python C API calls which could potentially invoke the + garbage collector (the GC will call type_traverse(), which will in + turn find the newly constructed type in an invalid state) */ + auto metaclass = rec.metaclass.ptr() ? (PyTypeObject *) rec.metaclass.ptr() + : internals.default_metaclass; + + auto heap_type = (PyHeapTypeObject *) metaclass->tp_alloc(metaclass, 0); + if (!heap_type) + pybind11_fail(std::string(rec.name) + ": Unable to create type object!"); + + heap_type->ht_name = name.release().ptr(); +#ifdef PYBIND11_BUILTIN_QUALNAME + heap_type->ht_qualname = qualname.inc_ref().ptr(); +#endif + + auto type = &heap_type->ht_type; + type->tp_name = full_name; + type->tp_doc = tp_doc; + type->tp_base = type_incref((PyTypeObject *)base); + type->tp_basicsize = static_cast(sizeof(instance)); + if (bases.size() > 0) + type->tp_bases = bases.release().ptr(); + + /* Don't inherit base __init__ */ + type->tp_init = pybind11_object_init; + + /* Supported protocols */ + type->tp_as_number = &heap_type->as_number; + type->tp_as_sequence = &heap_type->as_sequence; + type->tp_as_mapping = &heap_type->as_mapping; + + /* Flags */ + type->tp_flags |= Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE; +#if PY_MAJOR_VERSION < 3 + type->tp_flags |= Py_TPFLAGS_CHECKTYPES; +#endif + + if (rec.dynamic_attr) + enable_dynamic_attributes(heap_type); + + if (rec.buffer_protocol) + enable_buffer_protocol(heap_type); + + if (PyType_Ready(type) < 0) + pybind11_fail(std::string(rec.name) + ": PyType_Ready failed (" + error_string() + ")!"); + + assert(rec.dynamic_attr ? PyType_HasFeature(type, Py_TPFLAGS_HAVE_GC) + : !PyType_HasFeature(type, Py_TPFLAGS_HAVE_GC)); + + /* Register type with the parent scope */ + if (rec.scope) + setattr(rec.scope, rec.name, (PyObject *) type); + else + Py_INCREF(type); // Keep it alive forever (reference leak) + + if (module) // Needed by pydoc + setattr((PyObject *) type, "__module__", module); + + PYBIND11_SET_OLDPY_QUALNAME(type, qualname); + + return (PyObject *) type; +} + +NAMESPACE_END(detail) +NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/ptocr/postprocess/dbprocess/include/pybind11/detail/common.h b/ptocr/postprocess/dbprocess/include/pybind11/detail/common.h new file mode 100644 index 0000000..5ff7485 --- /dev/null +++ b/ptocr/postprocess/dbprocess/include/pybind11/detail/common.h @@ -0,0 +1,807 @@ +/* + pybind11/detail/common.h -- Basic macros + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#if !defined(NAMESPACE_BEGIN) +# define NAMESPACE_BEGIN(name) namespace name { +#endif +#if !defined(NAMESPACE_END) +# define NAMESPACE_END(name) } +#endif + +// Robust support for some features and loading modules compiled against different pybind versions +// requires forcing hidden visibility on pybind code, so we enforce this by setting the attribute on +// the main `pybind11` namespace. +#if !defined(PYBIND11_NAMESPACE) +# ifdef __GNUG__ +# define PYBIND11_NAMESPACE pybind11 __attribute__((visibility("hidden"))) +# else +# define PYBIND11_NAMESPACE pybind11 +# endif +#endif + +#if !(defined(_MSC_VER) && __cplusplus == 199711L) && !defined(__INTEL_COMPILER) +# if __cplusplus >= 201402L +# define PYBIND11_CPP14 +# if __cplusplus >= 201703L +# define PYBIND11_CPP17 +# endif +# endif +#elif defined(_MSC_VER) && __cplusplus == 199711L +// MSVC sets _MSVC_LANG rather than __cplusplus (supposedly until the standard is fully implemented) +// Unless you use the /Zc:__cplusplus flag on Visual Studio 2017 15.7 Preview 3 or newer +# if _MSVC_LANG >= 201402L +# define PYBIND11_CPP14 +# if _MSVC_LANG > 201402L && _MSC_VER >= 1910 +# define PYBIND11_CPP17 +# endif +# endif +#endif + +// Compiler version assertions +#if defined(__INTEL_COMPILER) +# if __INTEL_COMPILER < 1700 +# error pybind11 requires Intel C++ compiler v17 or newer +# endif +#elif defined(__clang__) && !defined(__apple_build_version__) +# if __clang_major__ < 3 || (__clang_major__ == 3 && __clang_minor__ < 3) +# error pybind11 requires clang 3.3 or newer +# endif +#elif defined(__clang__) +// Apple changes clang version macros to its Xcode version; the first Xcode release based on +// (upstream) clang 3.3 was Xcode 5: +# if __clang_major__ < 5 +# error pybind11 requires Xcode/clang 5.0 or newer +# endif +#elif defined(__GNUG__) +# if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 8) +# error pybind11 requires gcc 4.8 or newer +# endif +#elif defined(_MSC_VER) +// Pybind hits various compiler bugs in 2015u2 and earlier, and also makes use of some stl features +// (e.g. std::negation) added in 2015u3: +# if _MSC_FULL_VER < 190024210 +# error pybind11 requires MSVC 2015 update 3 or newer +# endif +#endif + +#if !defined(PYBIND11_EXPORT) +# if defined(WIN32) || defined(_WIN32) +# define PYBIND11_EXPORT __declspec(dllexport) +# else +# define PYBIND11_EXPORT __attribute__ ((visibility("default"))) +# endif +#endif + +#if defined(_MSC_VER) +# define PYBIND11_NOINLINE __declspec(noinline) +#else +# define PYBIND11_NOINLINE __attribute__ ((noinline)) +#endif + +#if defined(PYBIND11_CPP14) +# define PYBIND11_DEPRECATED(reason) [[deprecated(reason)]] +#else +# define PYBIND11_DEPRECATED(reason) __attribute__((deprecated(reason))) +#endif + +#define PYBIND11_VERSION_MAJOR 2 +#define PYBIND11_VERSION_MINOR 3 +#define PYBIND11_VERSION_PATCH dev0 + +/// Include Python header, disable linking to pythonX_d.lib on Windows in debug mode +#if defined(_MSC_VER) +# if (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION < 4) +# define HAVE_ROUND 1 +# endif +# pragma warning(push) +# pragma warning(disable: 4510 4610 4512 4005) +# if defined(_DEBUG) +# define PYBIND11_DEBUG_MARKER +# undef _DEBUG +# endif +#endif + +#include +#include +#include + +#if defined(_WIN32) && (defined(min) || defined(max)) +# error Macro clash with min and max -- define NOMINMAX when compiling your program on Windows +#endif + +#if defined(isalnum) +# undef isalnum +# undef isalpha +# undef islower +# undef isspace +# undef isupper +# undef tolower +# undef toupper +#endif + +#if defined(_MSC_VER) +# if defined(PYBIND11_DEBUG_MARKER) +# define _DEBUG +# undef PYBIND11_DEBUG_MARKER +# endif +# pragma warning(pop) +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if PY_MAJOR_VERSION >= 3 /// Compatibility macros for various Python versions +#define PYBIND11_INSTANCE_METHOD_NEW(ptr, class_) PyInstanceMethod_New(ptr) +#define PYBIND11_INSTANCE_METHOD_CHECK PyInstanceMethod_Check +#define PYBIND11_INSTANCE_METHOD_GET_FUNCTION PyInstanceMethod_GET_FUNCTION +#define PYBIND11_BYTES_CHECK PyBytes_Check +#define PYBIND11_BYTES_FROM_STRING PyBytes_FromString +#define PYBIND11_BYTES_FROM_STRING_AND_SIZE PyBytes_FromStringAndSize +#define PYBIND11_BYTES_AS_STRING_AND_SIZE PyBytes_AsStringAndSize +#define PYBIND11_BYTES_AS_STRING PyBytes_AsString +#define PYBIND11_BYTES_SIZE PyBytes_Size +#define PYBIND11_LONG_CHECK(o) PyLong_Check(o) +#define PYBIND11_LONG_AS_LONGLONG(o) PyLong_AsLongLong(o) +#define PYBIND11_LONG_FROM_SIGNED(o) PyLong_FromSsize_t((ssize_t) o) +#define PYBIND11_LONG_FROM_UNSIGNED(o) PyLong_FromSize_t((size_t) o) +#define PYBIND11_BYTES_NAME "bytes" +#define PYBIND11_STRING_NAME "str" +#define PYBIND11_SLICE_OBJECT PyObject +#define PYBIND11_FROM_STRING PyUnicode_FromString +#define PYBIND11_STR_TYPE ::pybind11::str +#define PYBIND11_BOOL_ATTR "__bool__" +#define PYBIND11_NB_BOOL(ptr) ((ptr)->nb_bool) +#define PYBIND11_PLUGIN_IMPL(name) \ + extern "C" PYBIND11_EXPORT PyObject *PyInit_##name() + +#else +#define PYBIND11_INSTANCE_METHOD_NEW(ptr, class_) PyMethod_New(ptr, nullptr, class_) +#define PYBIND11_INSTANCE_METHOD_CHECK PyMethod_Check +#define PYBIND11_INSTANCE_METHOD_GET_FUNCTION PyMethod_GET_FUNCTION +#define PYBIND11_BYTES_CHECK PyString_Check +#define PYBIND11_BYTES_FROM_STRING PyString_FromString +#define PYBIND11_BYTES_FROM_STRING_AND_SIZE PyString_FromStringAndSize +#define PYBIND11_BYTES_AS_STRING_AND_SIZE PyString_AsStringAndSize +#define PYBIND11_BYTES_AS_STRING PyString_AsString +#define PYBIND11_BYTES_SIZE PyString_Size +#define PYBIND11_LONG_CHECK(o) (PyInt_Check(o) || PyLong_Check(o)) +#define PYBIND11_LONG_AS_LONGLONG(o) (PyInt_Check(o) ? (long long) PyLong_AsLong(o) : PyLong_AsLongLong(o)) +#define PYBIND11_LONG_FROM_SIGNED(o) PyInt_FromSsize_t((ssize_t) o) // Returns long if needed. +#define PYBIND11_LONG_FROM_UNSIGNED(o) PyInt_FromSize_t((size_t) o) // Returns long if needed. +#define PYBIND11_BYTES_NAME "str" +#define PYBIND11_STRING_NAME "unicode" +#define PYBIND11_SLICE_OBJECT PySliceObject +#define PYBIND11_FROM_STRING PyString_FromString +#define PYBIND11_STR_TYPE ::pybind11::bytes +#define PYBIND11_BOOL_ATTR "__nonzero__" +#define PYBIND11_NB_BOOL(ptr) ((ptr)->nb_nonzero) +#define PYBIND11_PLUGIN_IMPL(name) \ + static PyObject *pybind11_init_wrapper(); \ + extern "C" PYBIND11_EXPORT void init##name() { \ + (void)pybind11_init_wrapper(); \ + } \ + PyObject *pybind11_init_wrapper() +#endif + +#if PY_VERSION_HEX >= 0x03050000 && PY_VERSION_HEX < 0x03050200 +extern "C" { + struct _Py_atomic_address { void *value; }; + PyAPI_DATA(_Py_atomic_address) _PyThreadState_Current; +} +#endif + +#define PYBIND11_TRY_NEXT_OVERLOAD ((PyObject *) 1) // special failure return code +#define PYBIND11_STRINGIFY(x) #x +#define PYBIND11_TOSTRING(x) PYBIND11_STRINGIFY(x) +#define PYBIND11_CONCAT(first, second) first##second + +#define PYBIND11_CHECK_PYTHON_VERSION \ + { \ + const char *compiled_ver = PYBIND11_TOSTRING(PY_MAJOR_VERSION) \ + "." PYBIND11_TOSTRING(PY_MINOR_VERSION); \ + const char *runtime_ver = Py_GetVersion(); \ + size_t len = std::strlen(compiled_ver); \ + if (std::strncmp(runtime_ver, compiled_ver, len) != 0 \ + || (runtime_ver[len] >= '0' && runtime_ver[len] <= '9')) { \ + PyErr_Format(PyExc_ImportError, \ + "Python version mismatch: module was compiled for Python %s, " \ + "but the interpreter version is incompatible: %s.", \ + compiled_ver, runtime_ver); \ + return nullptr; \ + } \ + } + +#define PYBIND11_CATCH_INIT_EXCEPTIONS \ + catch (pybind11::error_already_set &e) { \ + PyErr_SetString(PyExc_ImportError, e.what()); \ + return nullptr; \ + } catch (const std::exception &e) { \ + PyErr_SetString(PyExc_ImportError, e.what()); \ + return nullptr; \ + } \ + +/** \rst + ***Deprecated in favor of PYBIND11_MODULE*** + + This macro creates the entry point that will be invoked when the Python interpreter + imports a plugin library. Please create a `module` in the function body and return + the pointer to its underlying Python object at the end. + + .. code-block:: cpp + + PYBIND11_PLUGIN(example) { + pybind11::module m("example", "pybind11 example plugin"); + /// Set up bindings here + return m.ptr(); + } +\endrst */ +#define PYBIND11_PLUGIN(name) \ + PYBIND11_DEPRECATED("PYBIND11_PLUGIN is deprecated, use PYBIND11_MODULE") \ + static PyObject *pybind11_init(); \ + PYBIND11_PLUGIN_IMPL(name) { \ + PYBIND11_CHECK_PYTHON_VERSION \ + try { \ + return pybind11_init(); \ + } PYBIND11_CATCH_INIT_EXCEPTIONS \ + } \ + PyObject *pybind11_init() + +/** \rst + This macro creates the entry point that will be invoked when the Python interpreter + imports an extension module. The module name is given as the fist argument and it + should not be in quotes. The second macro argument defines a variable of type + `py::module` which can be used to initialize the module. + + .. code-block:: cpp + + PYBIND11_MODULE(example, m) { + m.doc() = "pybind11 example module"; + + // Add bindings here + m.def("foo", []() { + return "Hello, World!"; + }); + } +\endrst */ +#define PYBIND11_MODULE(name, variable) \ + static void PYBIND11_CONCAT(pybind11_init_, name)(pybind11::module &); \ + PYBIND11_PLUGIN_IMPL(name) { \ + PYBIND11_CHECK_PYTHON_VERSION \ + auto m = pybind11::module(PYBIND11_TOSTRING(name)); \ + try { \ + PYBIND11_CONCAT(pybind11_init_, name)(m); \ + return m.ptr(); \ + } PYBIND11_CATCH_INIT_EXCEPTIONS \ + } \ + void PYBIND11_CONCAT(pybind11_init_, name)(pybind11::module &variable) + + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) + +using ssize_t = Py_ssize_t; +using size_t = std::size_t; + +/// Approach used to cast a previously unknown C++ instance into a Python object +enum class return_value_policy : uint8_t { + /** This is the default return value policy, which falls back to the policy + return_value_policy::take_ownership when the return value is a pointer. + Otherwise, it uses return_value::move or return_value::copy for rvalue + and lvalue references, respectively. See below for a description of what + all of these different policies do. */ + automatic = 0, + + /** As above, but use policy return_value_policy::reference when the return + value is a pointer. This is the default conversion policy for function + arguments when calling Python functions manually from C++ code (i.e. via + handle::operator()). You probably won't need to use this. */ + automatic_reference, + + /** Reference an existing object (i.e. do not create a new copy) and take + ownership. Python will call the destructor and delete operator when the + object’s reference count reaches zero. Undefined behavior ensues when + the C++ side does the same.. */ + take_ownership, + + /** Create a new copy of the returned object, which will be owned by + Python. This policy is comparably safe because the lifetimes of the two + instances are decoupled. */ + copy, + + /** Use std::move to move the return value contents into a new instance + that will be owned by Python. This policy is comparably safe because the + lifetimes of the two instances (move source and destination) are + decoupled. */ + move, + + /** Reference an existing object, but do not take ownership. The C++ side + is responsible for managing the object’s lifetime and deallocating it + when it is no longer used. Warning: undefined behavior will ensue when + the C++ side deletes an object that is still referenced and used by + Python. */ + reference, + + /** This policy only applies to methods and properties. It references the + object without taking ownership similar to the above + return_value_policy::reference policy. In contrast to that policy, the + function or property’s implicit this argument (called the parent) is + considered to be the the owner of the return value (the child). + pybind11 then couples the lifetime of the parent to the child via a + reference relationship that ensures that the parent cannot be garbage + collected while Python is still using the child. More advanced + variations of this scheme are also possible using combinations of + return_value_policy::reference and the keep_alive call policy */ + reference_internal +}; + +NAMESPACE_BEGIN(detail) + +inline static constexpr int log2(size_t n, int k = 0) { return (n <= 1) ? k : log2(n >> 1, k + 1); } + +// Returns the size as a multiple of sizeof(void *), rounded up. +inline static constexpr size_t size_in_ptrs(size_t s) { return 1 + ((s - 1) >> log2(sizeof(void *))); } + +/** + * The space to allocate for simple layout instance holders (see below) in multiple of the size of + * a pointer (e.g. 2 means 16 bytes on 64-bit architectures). The default is the minimum required + * to holder either a std::unique_ptr or std::shared_ptr (which is almost always + * sizeof(std::shared_ptr)). + */ +constexpr size_t instance_simple_holder_in_ptrs() { + static_assert(sizeof(std::shared_ptr) >= sizeof(std::unique_ptr), + "pybind assumes std::shared_ptrs are at least as big as std::unique_ptrs"); + return size_in_ptrs(sizeof(std::shared_ptr)); +} + +// Forward declarations +struct type_info; +struct value_and_holder; + +struct nonsimple_values_and_holders { + void **values_and_holders; + uint8_t *status; +}; + +/// The 'instance' type which needs to be standard layout (need to be able to use 'offsetof') +struct instance { + PyObject_HEAD + /// Storage for pointers and holder; see simple_layout, below, for a description + union { + void *simple_value_holder[1 + instance_simple_holder_in_ptrs()]; + nonsimple_values_and_holders nonsimple; + }; + /// Weak references + PyObject *weakrefs; + /// If true, the pointer is owned which means we're free to manage it with a holder. + bool owned : 1; + /** + * An instance has two possible value/holder layouts. + * + * Simple layout (when this flag is true), means the `simple_value_holder` is set with a pointer + * and the holder object governing that pointer, i.e. [val1*][holder]. This layout is applied + * whenever there is no python-side multiple inheritance of bound C++ types *and* the type's + * holder will fit in the default space (which is large enough to hold either a std::unique_ptr + * or std::shared_ptr). + * + * Non-simple layout applies when using custom holders that require more space than `shared_ptr` + * (which is typically the size of two pointers), or when multiple inheritance is used on the + * python side. Non-simple layout allocates the required amount of memory to have multiple + * bound C++ classes as parents. Under this layout, `nonsimple.values_and_holders` is set to a + * pointer to allocated space of the required space to hold a sequence of value pointers and + * holders followed `status`, a set of bit flags (1 byte each), i.e. + * [val1*][holder1][val2*][holder2]...[bb...] where each [block] is rounded up to a multiple of + * `sizeof(void *)`. `nonsimple.status` is, for convenience, a pointer to the + * beginning of the [bb...] block (but not independently allocated). + * + * Status bits indicate whether the associated holder is constructed (& + * status_holder_constructed) and whether the value pointer is registered (& + * status_instance_registered) in `registered_instances`. + */ + bool simple_layout : 1; + /// For simple layout, tracks whether the holder has been constructed + bool simple_holder_constructed : 1; + /// For simple layout, tracks whether the instance is registered in `registered_instances` + bool simple_instance_registered : 1; + /// If true, get_internals().patients has an entry for this object + bool has_patients : 1; + + /// Initializes all of the above type/values/holders data (but not the instance values themselves) + void allocate_layout(); + + /// Destroys/deallocates all of the above + void deallocate_layout(); + + /// Returns the value_and_holder wrapper for the given type (or the first, if `find_type` + /// omitted). Returns a default-constructed (with `.inst = nullptr`) object on failure if + /// `throw_if_missing` is false. + value_and_holder get_value_and_holder(const type_info *find_type = nullptr, bool throw_if_missing = true); + + /// Bit values for the non-simple status flags + static constexpr uint8_t status_holder_constructed = 1; + static constexpr uint8_t status_instance_registered = 2; +}; + +static_assert(std::is_standard_layout::value, "Internal error: `pybind11::detail::instance` is not standard layout!"); + +/// from __cpp_future__ import (convenient aliases from C++14/17) +#if defined(PYBIND11_CPP14) && (!defined(_MSC_VER) || _MSC_VER >= 1910) +using std::enable_if_t; +using std::conditional_t; +using std::remove_cv_t; +using std::remove_reference_t; +#else +template using enable_if_t = typename std::enable_if::type; +template using conditional_t = typename std::conditional::type; +template using remove_cv_t = typename std::remove_cv::type; +template using remove_reference_t = typename std::remove_reference::type; +#endif + +/// Index sequences +#if defined(PYBIND11_CPP14) +using std::index_sequence; +using std::make_index_sequence; +#else +template struct index_sequence { }; +template struct make_index_sequence_impl : make_index_sequence_impl { }; +template struct make_index_sequence_impl <0, S...> { typedef index_sequence type; }; +template using make_index_sequence = typename make_index_sequence_impl::type; +#endif + +/// Make an index sequence of the indices of true arguments +template struct select_indices_impl { using type = ISeq; }; +template struct select_indices_impl, I, B, Bs...> + : select_indices_impl, index_sequence>, I + 1, Bs...> {}; +template using select_indices = typename select_indices_impl, 0, Bs...>::type; + +/// Backports of std::bool_constant and std::negation to accommodate older compilers +template using bool_constant = std::integral_constant; +template struct negation : bool_constant { }; + +template struct void_t_impl { using type = void; }; +template using void_t = typename void_t_impl::type; + +/// Compile-time all/any/none of that check the boolean value of all template types +#if defined(__cpp_fold_expressions) && !(defined(_MSC_VER) && (_MSC_VER < 1916)) +template using all_of = bool_constant<(Ts::value && ...)>; +template using any_of = bool_constant<(Ts::value || ...)>; +#elif !defined(_MSC_VER) +template struct bools {}; +template using all_of = std::is_same< + bools, + bools>; +template using any_of = negation...>>; +#else +// MSVC has trouble with the above, but supports std::conjunction, which we can use instead (albeit +// at a slight loss of compilation efficiency). +template using all_of = std::conjunction; +template using any_of = std::disjunction; +#endif +template using none_of = negation>; + +template class... Predicates> using satisfies_all_of = all_of...>; +template class... Predicates> using satisfies_any_of = any_of...>; +template class... Predicates> using satisfies_none_of = none_of...>; + +/// Strip the class from a method type +template struct remove_class { }; +template struct remove_class { typedef R type(A...); }; +template struct remove_class { typedef R type(A...); }; + +/// Helper template to strip away type modifiers +template struct intrinsic_type { typedef T type; }; +template struct intrinsic_type { typedef typename intrinsic_type::type type; }; +template struct intrinsic_type { typedef typename intrinsic_type::type type; }; +template struct intrinsic_type { typedef typename intrinsic_type::type type; }; +template struct intrinsic_type { typedef typename intrinsic_type::type type; }; +template struct intrinsic_type { typedef typename intrinsic_type::type type; }; +template struct intrinsic_type { typedef typename intrinsic_type::type type; }; +template using intrinsic_t = typename intrinsic_type::type; + +/// Helper type to replace 'void' in some expressions +struct void_type { }; + +/// Helper template which holds a list of types +template struct type_list { }; + +/// Compile-time integer sum +#ifdef __cpp_fold_expressions +template constexpr size_t constexpr_sum(Ts... ns) { return (0 + ... + size_t{ns}); } +#else +constexpr size_t constexpr_sum() { return 0; } +template +constexpr size_t constexpr_sum(T n, Ts... ns) { return size_t{n} + constexpr_sum(ns...); } +#endif + +NAMESPACE_BEGIN(constexpr_impl) +/// Implementation details for constexpr functions +constexpr int first(int i) { return i; } +template +constexpr int first(int i, T v, Ts... vs) { return v ? i : first(i + 1, vs...); } + +constexpr int last(int /*i*/, int result) { return result; } +template +constexpr int last(int i, int result, T v, Ts... vs) { return last(i + 1, v ? i : result, vs...); } +NAMESPACE_END(constexpr_impl) + +/// Return the index of the first type in Ts which satisfies Predicate. Returns sizeof...(Ts) if +/// none match. +template class Predicate, typename... Ts> +constexpr int constexpr_first() { return constexpr_impl::first(0, Predicate::value...); } + +/// Return the index of the last type in Ts which satisfies Predicate, or -1 if none match. +template class Predicate, typename... Ts> +constexpr int constexpr_last() { return constexpr_impl::last(0, -1, Predicate::value...); } + +/// Return the Nth element from the parameter pack +template +struct pack_element { using type = typename pack_element::type; }; +template +struct pack_element<0, T, Ts...> { using type = T; }; + +/// Return the one and only type which matches the predicate, or Default if none match. +/// If more than one type matches the predicate, fail at compile-time. +template class Predicate, typename Default, typename... Ts> +struct exactly_one { + static constexpr auto found = constexpr_sum(Predicate::value...); + static_assert(found <= 1, "Found more than one type matching the predicate"); + + static constexpr auto index = found ? constexpr_first() : 0; + using type = conditional_t::type, Default>; +}; +template class P, typename Default> +struct exactly_one { using type = Default; }; + +template class Predicate, typename Default, typename... Ts> +using exactly_one_t = typename exactly_one::type; + +/// Defer the evaluation of type T until types Us are instantiated +template struct deferred_type { using type = T; }; +template using deferred_t = typename deferred_type::type; + +/// Like is_base_of, but requires a strict base (i.e. `is_strict_base_of::value == false`, +/// unlike `std::is_base_of`) +template using is_strict_base_of = bool_constant< + std::is_base_of::value && !std::is_same::value>; + +/// Like is_base_of, but also requires that the base type is accessible (i.e. that a Derived pointer +/// can be converted to a Base pointer) +template using is_accessible_base_of = bool_constant< + std::is_base_of::value && std::is_convertible::value>; + +template class Base> +struct is_template_base_of_impl { + template static std::true_type check(Base *); + static std::false_type check(...); +}; + +/// Check if a template is the base of a type. For example: +/// `is_template_base_of` is true if `struct T : Base {}` where U can be anything +template class Base, typename T> +#if !defined(_MSC_VER) +using is_template_base_of = decltype(is_template_base_of_impl::check((intrinsic_t*)nullptr)); +#else // MSVC2015 has trouble with decltype in template aliases +struct is_template_base_of : decltype(is_template_base_of_impl::check((intrinsic_t*)nullptr)) { }; +#endif + +/// Check if T is an instantiation of the template `Class`. For example: +/// `is_instantiation` is true if `T == shared_ptr` where U can be anything. +template class Class, typename T> +struct is_instantiation : std::false_type { }; +template class Class, typename... Us> +struct is_instantiation> : std::true_type { }; + +/// Check if T is std::shared_ptr where U can be anything +template using is_shared_ptr = is_instantiation; + +/// Check if T looks like an input iterator +template struct is_input_iterator : std::false_type {}; +template +struct is_input_iterator()), decltype(++std::declval())>> + : std::true_type {}; + +template using is_function_pointer = bool_constant< + std::is_pointer::value && std::is_function::type>::value>; + +template struct strip_function_object { + using type = typename remove_class::type; +}; + +// Extracts the function signature from a function, function pointer or lambda. +template > +using function_signature_t = conditional_t< + std::is_function::value, + F, + typename conditional_t< + std::is_pointer::value || std::is_member_pointer::value, + std::remove_pointer, + strip_function_object + >::type +>; + +/// Returns true if the type looks like a lambda: that is, isn't a function, pointer or member +/// pointer. Note that this can catch all sorts of other things, too; this is intended to be used +/// in a place where passing a lambda makes sense. +template using is_lambda = satisfies_none_of, + std::is_function, std::is_pointer, std::is_member_pointer>; + +/// Ignore that a variable is unused in compiler warnings +inline void ignore_unused(const int *) { } + +/// Apply a function over each element of a parameter pack +#ifdef __cpp_fold_expressions +#define PYBIND11_EXPAND_SIDE_EFFECTS(PATTERN) (((PATTERN), void()), ...) +#else +using expand_side_effects = bool[]; +#define PYBIND11_EXPAND_SIDE_EFFECTS(PATTERN) pybind11::detail::expand_side_effects{ ((PATTERN), void(), false)..., false } +#endif + +NAMESPACE_END(detail) + +/// C++ bindings of builtin Python exceptions +class builtin_exception : public std::runtime_error { +public: + using std::runtime_error::runtime_error; + /// Set the error using the Python C API + virtual void set_error() const = 0; +}; + +#define PYBIND11_RUNTIME_EXCEPTION(name, type) \ + class name : public builtin_exception { public: \ + using builtin_exception::builtin_exception; \ + name() : name("") { } \ + void set_error() const override { PyErr_SetString(type, what()); } \ + }; + +PYBIND11_RUNTIME_EXCEPTION(stop_iteration, PyExc_StopIteration) +PYBIND11_RUNTIME_EXCEPTION(index_error, PyExc_IndexError) +PYBIND11_RUNTIME_EXCEPTION(key_error, PyExc_KeyError) +PYBIND11_RUNTIME_EXCEPTION(value_error, PyExc_ValueError) +PYBIND11_RUNTIME_EXCEPTION(type_error, PyExc_TypeError) +PYBIND11_RUNTIME_EXCEPTION(cast_error, PyExc_RuntimeError) /// Thrown when pybind11::cast or handle::call fail due to a type casting error +PYBIND11_RUNTIME_EXCEPTION(reference_cast_error, PyExc_RuntimeError) /// Used internally + +[[noreturn]] PYBIND11_NOINLINE inline void pybind11_fail(const char *reason) { throw std::runtime_error(reason); } +[[noreturn]] PYBIND11_NOINLINE inline void pybind11_fail(const std::string &reason) { throw std::runtime_error(reason); } + +template struct format_descriptor { }; + +NAMESPACE_BEGIN(detail) +// Returns the index of the given type in the type char array below, and in the list in numpy.h +// The order here is: bool; 8 ints ((signed,unsigned)x(8,16,32,64)bits); float,double,long double; +// complex float,double,long double. Note that the long double types only participate when long +// double is actually longer than double (it isn't under MSVC). +// NB: not only the string below but also complex.h and numpy.h rely on this order. +template struct is_fmt_numeric { static constexpr bool value = false; }; +template struct is_fmt_numeric::value>> { + static constexpr bool value = true; + static constexpr int index = std::is_same::value ? 0 : 1 + ( + std::is_integral::value ? detail::log2(sizeof(T))*2 + std::is_unsigned::value : 8 + ( + std::is_same::value ? 1 : std::is_same::value ? 2 : 0)); +}; +NAMESPACE_END(detail) + +template struct format_descriptor::value>> { + static constexpr const char c = "?bBhHiIqQfdg"[detail::is_fmt_numeric::index]; + static constexpr const char value[2] = { c, '\0' }; + static std::string format() { return std::string(1, c); } +}; + +#if !defined(PYBIND11_CPP17) + +template constexpr const char format_descriptor< + T, detail::enable_if_t::value>>::value[2]; + +#endif + +/// RAII wrapper that temporarily clears any Python error state +struct error_scope { + PyObject *type, *value, *trace; + error_scope() { PyErr_Fetch(&type, &value, &trace); } + ~error_scope() { PyErr_Restore(type, value, trace); } +}; + +/// Dummy destructor wrapper that can be used to expose classes with a private destructor +struct nodelete { template void operator()(T*) { } }; + +// overload_cast requires variable templates: C++14 +#if defined(PYBIND11_CPP14) +#define PYBIND11_OVERLOAD_CAST 1 + +NAMESPACE_BEGIN(detail) +template +struct overload_cast_impl { + constexpr overload_cast_impl() {} // MSVC 2015 needs this + + template + constexpr auto operator()(Return (*pf)(Args...)) const noexcept + -> decltype(pf) { return pf; } + + template + constexpr auto operator()(Return (Class::*pmf)(Args...), std::false_type = {}) const noexcept + -> decltype(pmf) { return pmf; } + + template + constexpr auto operator()(Return (Class::*pmf)(Args...) const, std::true_type) const noexcept + -> decltype(pmf) { return pmf; } +}; +NAMESPACE_END(detail) + +/// Syntax sugar for resolving overloaded function pointers: +/// - regular: static_cast(&Class::func) +/// - sweet: overload_cast(&Class::func) +template +static constexpr detail::overload_cast_impl overload_cast = {}; +// MSVC 2015 only accepts this particular initialization syntax for this variable template. + +/// Const member function selector for overload_cast +/// - regular: static_cast(&Class::func) +/// - sweet: overload_cast(&Class::func, const_) +static constexpr auto const_ = std::true_type{}; + +#else // no overload_cast: providing something that static_assert-fails: +template struct overload_cast { + static_assert(detail::deferred_t::value, + "pybind11::overload_cast<...> requires compiling in C++14 mode"); +}; +#endif // overload_cast + +NAMESPACE_BEGIN(detail) + +// Adaptor for converting arbitrary container arguments into a vector; implicitly convertible from +// any standard container (or C-style array) supporting std::begin/std::end, any singleton +// arithmetic type (if T is arithmetic), or explicitly constructible from an iterator pair. +template +class any_container { + std::vector v; +public: + any_container() = default; + + // Can construct from a pair of iterators + template ::value>> + any_container(It first, It last) : v(first, last) { } + + // Implicit conversion constructor from any arbitrary container type with values convertible to T + template ())), T>::value>> + any_container(const Container &c) : any_container(std::begin(c), std::end(c)) { } + + // initializer_list's aren't deducible, so don't get matched by the above template; we need this + // to explicitly allow implicit conversion from one: + template ::value>> + any_container(const std::initializer_list &c) : any_container(c.begin(), c.end()) { } + + // Avoid copying if given an rvalue vector of the correct type. + any_container(std::vector &&v) : v(std::move(v)) { } + + // Moves the vector out of an rvalue any_container + operator std::vector &&() && { return std::move(v); } + + // Dereferencing obtains a reference to the underlying vector + std::vector &operator*() { return v; } + const std::vector &operator*() const { return v; } + + // -> lets you call methods on the underlying vector + std::vector *operator->() { return &v; } + const std::vector *operator->() const { return &v; } +}; + +NAMESPACE_END(detail) + + + +NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/ptocr/postprocess/dbprocess/include/pybind11/detail/descr.h b/ptocr/postprocess/dbprocess/include/pybind11/detail/descr.h new file mode 100644 index 0000000..8d404e5 --- /dev/null +++ b/ptocr/postprocess/dbprocess/include/pybind11/detail/descr.h @@ -0,0 +1,100 @@ +/* + pybind11/detail/descr.h: Helper type for concatenating type signatures at compile time + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "common.h" + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +NAMESPACE_BEGIN(detail) + +#if !defined(_MSC_VER) +# define PYBIND11_DESCR_CONSTEXPR static constexpr +#else +# define PYBIND11_DESCR_CONSTEXPR const +#endif + +/* Concatenate type signatures at compile time */ +template +struct descr { + char text[N + 1]; + + constexpr descr() : text{'\0'} { } + constexpr descr(char const (&s)[N+1]) : descr(s, make_index_sequence()) { } + + template + constexpr descr(char const (&s)[N+1], index_sequence) : text{s[Is]..., '\0'} { } + + template + constexpr descr(char c, Chars... cs) : text{c, static_cast(cs)..., '\0'} { } + + static constexpr std::array types() { + return {{&typeid(Ts)..., nullptr}}; + } +}; + +template +constexpr descr plus_impl(const descr &a, const descr &b, + index_sequence, index_sequence) { + return {a.text[Is1]..., b.text[Is2]...}; +} + +template +constexpr descr operator+(const descr &a, const descr &b) { + return plus_impl(a, b, make_index_sequence(), make_index_sequence()); +} + +template +constexpr descr _(char const(&text)[N]) { return descr(text); } +constexpr descr<0> _(char const(&)[1]) { return {}; } + +template struct int_to_str : int_to_str { }; +template struct int_to_str<0, Digits...> { + static constexpr auto digits = descr(('0' + Digits)...); +}; + +// Ternary description (like std::conditional) +template +constexpr enable_if_t> _(char const(&text1)[N1], char const(&)[N2]) { + return _(text1); +} +template +constexpr enable_if_t> _(char const(&)[N1], char const(&text2)[N2]) { + return _(text2); +} + +template +constexpr enable_if_t _(const T1 &d, const T2 &) { return d; } +template +constexpr enable_if_t _(const T1 &, const T2 &d) { return d; } + +template auto constexpr _() -> decltype(int_to_str::digits) { + return int_to_str::digits; +} + +template constexpr descr<1, Type> _() { return {'%'}; } + +constexpr descr<0> concat() { return {}; } + +template +constexpr descr concat(const descr &descr) { return descr; } + +template +constexpr auto concat(const descr &d, const Args &...args) + -> decltype(std::declval>() + concat(args...)) { + return d + _(", ") + concat(args...); +} + +template +constexpr descr type_descr(const descr &descr) { + return _("{") + descr + _("}"); +} + +NAMESPACE_END(detail) +NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/ptocr/postprocess/dbprocess/include/pybind11/detail/init.h b/ptocr/postprocess/dbprocess/include/pybind11/detail/init.h new file mode 100644 index 0000000..acfe00b --- /dev/null +++ b/ptocr/postprocess/dbprocess/include/pybind11/detail/init.h @@ -0,0 +1,335 @@ +/* + pybind11/detail/init.h: init factory function implementation and support code. + + Copyright (c) 2017 Jason Rhinelander + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "class.h" + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +NAMESPACE_BEGIN(detail) + +template <> +class type_caster { +public: + bool load(handle h, bool) { + value = reinterpret_cast(h.ptr()); + return true; + } + + template using cast_op_type = value_and_holder &; + operator value_and_holder &() { return *value; } + static constexpr auto name = _(); + +private: + value_and_holder *value = nullptr; +}; + +NAMESPACE_BEGIN(initimpl) + +inline void no_nullptr(void *ptr) { + if (!ptr) throw type_error("pybind11::init(): factory function returned nullptr"); +} + +// Implementing functions for all forms of py::init<...> and py::init(...) +template using Cpp = typename Class::type; +template using Alias = typename Class::type_alias; +template using Holder = typename Class::holder_type; + +template using is_alias_constructible = std::is_constructible, Cpp &&>; + +// Takes a Cpp pointer and returns true if it actually is a polymorphic Alias instance. +template = 0> +bool is_alias(Cpp *ptr) { + return dynamic_cast *>(ptr) != nullptr; +} +// Failing fallback version of the above for a no-alias class (always returns false) +template +constexpr bool is_alias(void *) { return false; } + +// Constructs and returns a new object; if the given arguments don't map to a constructor, we fall +// back to brace aggregate initiailization so that for aggregate initialization can be used with +// py::init, e.g. `py::init` to initialize a `struct T { int a; int b; }`. For +// non-aggregate types, we need to use an ordinary T(...) constructor (invoking as `T{...}` usually +// works, but will not do the expected thing when `T` has an `initializer_list` constructor). +template ::value, int> = 0> +inline Class *construct_or_initialize(Args &&...args) { return new Class(std::forward(args)...); } +template ::value, int> = 0> +inline Class *construct_or_initialize(Args &&...args) { return new Class{std::forward(args)...}; } + +// Attempts to constructs an alias using a `Alias(Cpp &&)` constructor. This allows types with +// an alias to provide only a single Cpp factory function as long as the Alias can be +// constructed from an rvalue reference of the base Cpp type. This means that Alias classes +// can, when appropriate, simply define a `Alias(Cpp &&)` constructor rather than needing to +// inherit all the base class constructors. +template +void construct_alias_from_cpp(std::true_type /*is_alias_constructible*/, + value_and_holder &v_h, Cpp &&base) { + v_h.value_ptr() = new Alias(std::move(base)); +} +template +[[noreturn]] void construct_alias_from_cpp(std::false_type /*!is_alias_constructible*/, + value_and_holder &, Cpp &&) { + throw type_error("pybind11::init(): unable to convert returned instance to required " + "alias class: no `Alias(Class &&)` constructor available"); +} + +// Error-generating fallback for factories that don't match one of the below construction +// mechanisms. +template +void construct(...) { + static_assert(!std::is_same::value /* always false */, + "pybind11::init(): init function must return a compatible pointer, " + "holder, or value"); +} + +// Pointer return v1: the factory function returns a class pointer for a registered class. +// If we don't need an alias (because this class doesn't have one, or because the final type is +// inherited on the Python side) we can simply take over ownership. Otherwise we need to try to +// construct an Alias from the returned base instance. +template +void construct(value_and_holder &v_h, Cpp *ptr, bool need_alias) { + no_nullptr(ptr); + if (Class::has_alias && need_alias && !is_alias(ptr)) { + // We're going to try to construct an alias by moving the cpp type. Whether or not + // that succeeds, we still need to destroy the original cpp pointer (either the + // moved away leftover, if the alias construction works, or the value itself if we + // throw an error), but we can't just call `delete ptr`: it might have a special + // deleter, or might be shared_from_this. So we construct a holder around it as if + // it was a normal instance, then steal the holder away into a local variable; thus + // the holder and destruction happens when we leave the C++ scope, and the holder + // class gets to handle the destruction however it likes. + v_h.value_ptr() = ptr; + v_h.set_instance_registered(true); // To prevent init_instance from registering it + v_h.type->init_instance(v_h.inst, nullptr); // Set up the holder + Holder temp_holder(std::move(v_h.holder>())); // Steal the holder + v_h.type->dealloc(v_h); // Destroys the moved-out holder remains, resets value ptr to null + v_h.set_instance_registered(false); + + construct_alias_from_cpp(is_alias_constructible{}, v_h, std::move(*ptr)); + } else { + // Otherwise the type isn't inherited, so we don't need an Alias + v_h.value_ptr() = ptr; + } +} + +// Pointer return v2: a factory that always returns an alias instance ptr. We simply take over +// ownership of the pointer. +template = 0> +void construct(value_and_holder &v_h, Alias *alias_ptr, bool) { + no_nullptr(alias_ptr); + v_h.value_ptr() = static_cast *>(alias_ptr); +} + +// Holder return: copy its pointer, and move or copy the returned holder into the new instance's +// holder. This also handles types like std::shared_ptr and std::unique_ptr where T is a +// derived type (through those holder's implicit conversion from derived class holder constructors). +template +void construct(value_and_holder &v_h, Holder holder, bool need_alias) { + auto *ptr = holder_helper>::get(holder); + // If we need an alias, check that the held pointer is actually an alias instance + if (Class::has_alias && need_alias && !is_alias(ptr)) + throw type_error("pybind11::init(): construction failed: returned holder-wrapped instance " + "is not an alias instance"); + + v_h.value_ptr() = ptr; + v_h.type->init_instance(v_h.inst, &holder); +} + +// return-by-value version 1: returning a cpp class by value. If the class has an alias and an +// alias is required the alias must have an `Alias(Cpp &&)` constructor so that we can construct +// the alias from the base when needed (i.e. because of Python-side inheritance). When we don't +// need it, we simply move-construct the cpp value into a new instance. +template +void construct(value_and_holder &v_h, Cpp &&result, bool need_alias) { + static_assert(std::is_move_constructible>::value, + "pybind11::init() return-by-value factory function requires a movable class"); + if (Class::has_alias && need_alias) + construct_alias_from_cpp(is_alias_constructible{}, v_h, std::move(result)); + else + v_h.value_ptr() = new Cpp(std::move(result)); +} + +// return-by-value version 2: returning a value of the alias type itself. We move-construct an +// Alias instance (even if no the python-side inheritance is involved). The is intended for +// cases where Alias initialization is always desired. +template +void construct(value_and_holder &v_h, Alias &&result, bool) { + static_assert(std::is_move_constructible>::value, + "pybind11::init() return-by-alias-value factory function requires a movable alias class"); + v_h.value_ptr() = new Alias(std::move(result)); +} + +// Implementing class for py::init<...>() +template +struct constructor { + template = 0> + static void execute(Class &cl, const Extra&... extra) { + cl.def("__init__", [](value_and_holder &v_h, Args... args) { + v_h.value_ptr() = construct_or_initialize>(std::forward(args)...); + }, is_new_style_constructor(), extra...); + } + + template , Args...>::value, int> = 0> + static void execute(Class &cl, const Extra&... extra) { + cl.def("__init__", [](value_and_holder &v_h, Args... args) { + if (Py_TYPE(v_h.inst) == v_h.type->type) + v_h.value_ptr() = construct_or_initialize>(std::forward(args)...); + else + v_h.value_ptr() = construct_or_initialize>(std::forward(args)...); + }, is_new_style_constructor(), extra...); + } + + template , Args...>::value, int> = 0> + static void execute(Class &cl, const Extra&... extra) { + cl.def("__init__", [](value_and_holder &v_h, Args... args) { + v_h.value_ptr() = construct_or_initialize>(std::forward(args)...); + }, is_new_style_constructor(), extra...); + } +}; + +// Implementing class for py::init_alias<...>() +template struct alias_constructor { + template , Args...>::value, int> = 0> + static void execute(Class &cl, const Extra&... extra) { + cl.def("__init__", [](value_and_holder &v_h, Args... args) { + v_h.value_ptr() = construct_or_initialize>(std::forward(args)...); + }, is_new_style_constructor(), extra...); + } +}; + +// Implementation class for py::init(Func) and py::init(Func, AliasFunc) +template , typename = function_signature_t> +struct factory; + +// Specialization for py::init(Func) +template +struct factory { + remove_reference_t class_factory; + + factory(Func &&f) : class_factory(std::forward(f)) { } + + // The given class either has no alias or has no separate alias factory; + // this always constructs the class itself. If the class is registered with an alias + // type and an alias instance is needed (i.e. because the final type is a Python class + // inheriting from the C++ type) the returned value needs to either already be an alias + // instance, or the alias needs to be constructible from a `Class &&` argument. + template + void execute(Class &cl, const Extra &...extra) && { + #if defined(PYBIND11_CPP14) + cl.def("__init__", [func = std::move(class_factory)] + #else + auto &func = class_factory; + cl.def("__init__", [func] + #endif + (value_and_holder &v_h, Args... args) { + construct(v_h, func(std::forward(args)...), + Py_TYPE(v_h.inst) != v_h.type->type); + }, is_new_style_constructor(), extra...); + } +}; + +// Specialization for py::init(Func, AliasFunc) +template +struct factory { + static_assert(sizeof...(CArgs) == sizeof...(AArgs), + "pybind11::init(class_factory, alias_factory): class and alias factories " + "must have identical argument signatures"); + static_assert(all_of...>::value, + "pybind11::init(class_factory, alias_factory): class and alias factories " + "must have identical argument signatures"); + + remove_reference_t class_factory; + remove_reference_t alias_factory; + + factory(CFunc &&c, AFunc &&a) + : class_factory(std::forward(c)), alias_factory(std::forward(a)) { } + + // The class factory is called when the `self` type passed to `__init__` is the direct + // class (i.e. not inherited), the alias factory when `self` is a Python-side subtype. + template + void execute(Class &cl, const Extra&... extra) && { + static_assert(Class::has_alias, "The two-argument version of `py::init()` can " + "only be used if the class has an alias"); + #if defined(PYBIND11_CPP14) + cl.def("__init__", [class_func = std::move(class_factory), alias_func = std::move(alias_factory)] + #else + auto &class_func = class_factory; + auto &alias_func = alias_factory; + cl.def("__init__", [class_func, alias_func] + #endif + (value_and_holder &v_h, CArgs... args) { + if (Py_TYPE(v_h.inst) == v_h.type->type) + // If the instance type equals the registered type we don't have inheritance, so + // don't need the alias and can construct using the class function: + construct(v_h, class_func(std::forward(args)...), false); + else + construct(v_h, alias_func(std::forward(args)...), true); + }, is_new_style_constructor(), extra...); + } +}; + +/// Set just the C++ state. Same as `__init__`. +template +void setstate(value_and_holder &v_h, T &&result, bool need_alias) { + construct(v_h, std::forward(result), need_alias); +} + +/// Set both the C++ and Python states +template ::value, int> = 0> +void setstate(value_and_holder &v_h, std::pair &&result, bool need_alias) { + construct(v_h, std::move(result.first), need_alias); + setattr((PyObject *) v_h.inst, "__dict__", result.second); +} + +/// Implementation for py::pickle(GetState, SetState) +template , typename = function_signature_t> +struct pickle_factory; + +template +struct pickle_factory { + static_assert(std::is_same, intrinsic_t>::value, + "The type returned by `__getstate__` must be the same " + "as the argument accepted by `__setstate__`"); + + remove_reference_t get; + remove_reference_t set; + + pickle_factory(Get get, Set set) + : get(std::forward(get)), set(std::forward(set)) { } + + template + void execute(Class &cl, const Extra &...extra) && { + cl.def("__getstate__", std::move(get)); + +#if defined(PYBIND11_CPP14) + cl.def("__setstate__", [func = std::move(set)] +#else + auto &func = set; + cl.def("__setstate__", [func] +#endif + (value_and_holder &v_h, ArgState state) { + setstate(v_h, func(std::forward(state)), + Py_TYPE(v_h.inst) != v_h.type->type); + }, is_new_style_constructor(), extra...); + } +}; + +NAMESPACE_END(initimpl) +NAMESPACE_END(detail) +NAMESPACE_END(pybind11) diff --git a/ptocr/postprocess/dbprocess/include/pybind11/detail/internals.h b/ptocr/postprocess/dbprocess/include/pybind11/detail/internals.h new file mode 100644 index 0000000..6d7dc5c --- /dev/null +++ b/ptocr/postprocess/dbprocess/include/pybind11/detail/internals.h @@ -0,0 +1,291 @@ +/* + pybind11/detail/internals.h: Internal data structure and related functions + + Copyright (c) 2017 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "../pytypes.h" + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +NAMESPACE_BEGIN(detail) +// Forward declarations +inline PyTypeObject *make_static_property_type(); +inline PyTypeObject *make_default_metaclass(); +inline PyObject *make_object_base_type(PyTypeObject *metaclass); + +// The old Python Thread Local Storage (TLS) API is deprecated in Python 3.7 in favor of the new +// Thread Specific Storage (TSS) API. +#if PY_VERSION_HEX >= 0x03070000 +# define PYBIND11_TLS_KEY_INIT(var) Py_tss_t *var = nullptr +# define PYBIND11_TLS_GET_VALUE(key) PyThread_tss_get((key)) +# define PYBIND11_TLS_REPLACE_VALUE(key, value) PyThread_tss_set((key), (tstate)) +# define PYBIND11_TLS_DELETE_VALUE(key) PyThread_tss_set((key), nullptr) +#else + // Usually an int but a long on Cygwin64 with Python 3.x +# define PYBIND11_TLS_KEY_INIT(var) decltype(PyThread_create_key()) var = 0 +# define PYBIND11_TLS_GET_VALUE(key) PyThread_get_key_value((key)) +# if PY_MAJOR_VERSION < 3 +# define PYBIND11_TLS_DELETE_VALUE(key) \ + PyThread_delete_key_value(key) +# define PYBIND11_TLS_REPLACE_VALUE(key, value) \ + do { \ + PyThread_delete_key_value((key)); \ + PyThread_set_key_value((key), (value)); \ + } while (false) +# else +# define PYBIND11_TLS_DELETE_VALUE(key) \ + PyThread_set_key_value((key), nullptr) +# define PYBIND11_TLS_REPLACE_VALUE(key, value) \ + PyThread_set_key_value((key), (value)) +# endif +#endif + +// Python loads modules by default with dlopen with the RTLD_LOCAL flag; under libc++ and possibly +// other STLs, this means `typeid(A)` from one module won't equal `typeid(A)` from another module +// even when `A` is the same, non-hidden-visibility type (e.g. from a common include). Under +// libstdc++, this doesn't happen: equality and the type_index hash are based on the type name, +// which works. If not under a known-good stl, provide our own name-based hash and equality +// functions that use the type name. +#if defined(__GLIBCXX__) +inline bool same_type(const std::type_info &lhs, const std::type_info &rhs) { return lhs == rhs; } +using type_hash = std::hash; +using type_equal_to = std::equal_to; +#else +inline bool same_type(const std::type_info &lhs, const std::type_info &rhs) { + return lhs.name() == rhs.name() || std::strcmp(lhs.name(), rhs.name()) == 0; +} + +struct type_hash { + size_t operator()(const std::type_index &t) const { + size_t hash = 5381; + const char *ptr = t.name(); + while (auto c = static_cast(*ptr++)) + hash = (hash * 33) ^ c; + return hash; + } +}; + +struct type_equal_to { + bool operator()(const std::type_index &lhs, const std::type_index &rhs) const { + return lhs.name() == rhs.name() || std::strcmp(lhs.name(), rhs.name()) == 0; + } +}; +#endif + +template +using type_map = std::unordered_map; + +struct overload_hash { + inline size_t operator()(const std::pair& v) const { + size_t value = std::hash()(v.first); + value ^= std::hash()(v.second) + 0x9e3779b9 + (value<<6) + (value>>2); + return value; + } +}; + +/// Internal data structure used to track registered instances and types. +/// Whenever binary incompatible changes are made to this structure, +/// `PYBIND11_INTERNALS_VERSION` must be incremented. +struct internals { + type_map registered_types_cpp; // std::type_index -> pybind11's type information + std::unordered_map> registered_types_py; // PyTypeObject* -> base type_info(s) + std::unordered_multimap registered_instances; // void * -> instance* + std::unordered_set, overload_hash> inactive_overload_cache; + type_map> direct_conversions; + std::unordered_map> patients; + std::forward_list registered_exception_translators; + std::unordered_map shared_data; // Custom data to be shared across extensions + std::vector loader_patient_stack; // Used by `loader_life_support` + std::forward_list static_strings; // Stores the std::strings backing detail::c_str() + PyTypeObject *static_property_type; + PyTypeObject *default_metaclass; + PyObject *instance_base; +#if defined(WITH_THREAD) + PYBIND11_TLS_KEY_INIT(tstate); + PyInterpreterState *istate = nullptr; +#endif +}; + +/// Additional type information which does not fit into the PyTypeObject. +/// Changes to this struct also require bumping `PYBIND11_INTERNALS_VERSION`. +struct type_info { + PyTypeObject *type; + const std::type_info *cpptype; + size_t type_size, type_align, holder_size_in_ptrs; + void *(*operator_new)(size_t); + void (*init_instance)(instance *, const void *); + void (*dealloc)(value_and_holder &v_h); + std::vector implicit_conversions; + std::vector> implicit_casts; + std::vector *direct_conversions; + buffer_info *(*get_buffer)(PyObject *, void *) = nullptr; + void *get_buffer_data = nullptr; + void *(*module_local_load)(PyObject *, const type_info *) = nullptr; + /* A simple type never occurs as a (direct or indirect) parent + * of a class that makes use of multiple inheritance */ + bool simple_type : 1; + /* True if there is no multiple inheritance in this type's inheritance tree */ + bool simple_ancestors : 1; + /* for base vs derived holder_type checks */ + bool default_holder : 1; + /* true if this is a type registered with py::module_local */ + bool module_local : 1; +}; + +/// Tracks the `internals` and `type_info` ABI version independent of the main library version +#define PYBIND11_INTERNALS_VERSION 3 + +#if defined(_DEBUG) +# define PYBIND11_BUILD_TYPE "_debug" +#else +# define PYBIND11_BUILD_TYPE "" +#endif + +#if defined(WITH_THREAD) +# define PYBIND11_INTERNALS_KIND "" +#else +# define PYBIND11_INTERNALS_KIND "_without_thread" +#endif + +#define PYBIND11_INTERNALS_ID "__pybind11_internals_v" \ + PYBIND11_TOSTRING(PYBIND11_INTERNALS_VERSION) PYBIND11_INTERNALS_KIND PYBIND11_BUILD_TYPE "__" + +#define PYBIND11_MODULE_LOCAL_ID "__pybind11_module_local_v" \ + PYBIND11_TOSTRING(PYBIND11_INTERNALS_VERSION) PYBIND11_INTERNALS_KIND PYBIND11_BUILD_TYPE "__" + +/// Each module locally stores a pointer to the `internals` data. The data +/// itself is shared among modules with the same `PYBIND11_INTERNALS_ID`. +inline internals **&get_internals_pp() { + static internals **internals_pp = nullptr; + return internals_pp; +} + +/// Return a reference to the current `internals` data +PYBIND11_NOINLINE inline internals &get_internals() { + auto **&internals_pp = get_internals_pp(); + if (internals_pp && *internals_pp) + return **internals_pp; + + constexpr auto *id = PYBIND11_INTERNALS_ID; + auto builtins = handle(PyEval_GetBuiltins()); + if (builtins.contains(id) && isinstance(builtins[id])) { + internals_pp = static_cast(capsule(builtins[id])); + + // We loaded builtins through python's builtins, which means that our `error_already_set` + // and `builtin_exception` may be different local classes than the ones set up in the + // initial exception translator, below, so add another for our local exception classes. + // + // libstdc++ doesn't require this (types there are identified only by name) +#if !defined(__GLIBCXX__) + (*internals_pp)->registered_exception_translators.push_front( + [](std::exception_ptr p) -> void { + try { + if (p) std::rethrow_exception(p); + } catch (error_already_set &e) { e.restore(); return; + } catch (const builtin_exception &e) { e.set_error(); return; + } + } + ); +#endif + } else { + if (!internals_pp) internals_pp = new internals*(); + auto *&internals_ptr = *internals_pp; + internals_ptr = new internals(); +#if defined(WITH_THREAD) + PyEval_InitThreads(); + PyThreadState *tstate = PyThreadState_Get(); + #if PY_VERSION_HEX >= 0x03070000 + internals_ptr->tstate = PyThread_tss_alloc(); + if (!internals_ptr->tstate || PyThread_tss_create(internals_ptr->tstate)) + pybind11_fail("get_internals: could not successfully initialize the TSS key!"); + PyThread_tss_set(internals_ptr->tstate, tstate); + #else + internals_ptr->tstate = PyThread_create_key(); + if (internals_ptr->tstate == -1) + pybind11_fail("get_internals: could not successfully initialize the TLS key!"); + PyThread_set_key_value(internals_ptr->tstate, tstate); + #endif + internals_ptr->istate = tstate->interp; +#endif + builtins[id] = capsule(internals_pp); + internals_ptr->registered_exception_translators.push_front( + [](std::exception_ptr p) -> void { + try { + if (p) std::rethrow_exception(p); + } catch (error_already_set &e) { e.restore(); return; + } catch (const builtin_exception &e) { e.set_error(); return; + } catch (const std::bad_alloc &e) { PyErr_SetString(PyExc_MemoryError, e.what()); return; + } catch (const std::domain_error &e) { PyErr_SetString(PyExc_ValueError, e.what()); return; + } catch (const std::invalid_argument &e) { PyErr_SetString(PyExc_ValueError, e.what()); return; + } catch (const std::length_error &e) { PyErr_SetString(PyExc_ValueError, e.what()); return; + } catch (const std::out_of_range &e) { PyErr_SetString(PyExc_IndexError, e.what()); return; + } catch (const std::range_error &e) { PyErr_SetString(PyExc_ValueError, e.what()); return; + } catch (const std::exception &e) { PyErr_SetString(PyExc_RuntimeError, e.what()); return; + } catch (...) { + PyErr_SetString(PyExc_RuntimeError, "Caught an unknown exception!"); + return; + } + } + ); + internals_ptr->static_property_type = make_static_property_type(); + internals_ptr->default_metaclass = make_default_metaclass(); + internals_ptr->instance_base = make_object_base_type(internals_ptr->default_metaclass); + } + return **internals_pp; +} + +/// Works like `internals.registered_types_cpp`, but for module-local registered types: +inline type_map ®istered_local_types_cpp() { + static type_map locals{}; + return locals; +} + +/// Constructs a std::string with the given arguments, stores it in `internals`, and returns its +/// `c_str()`. Such strings objects have a long storage duration -- the internal strings are only +/// cleared when the program exits or after interpreter shutdown (when embedding), and so are +/// suitable for c-style strings needed by Python internals (such as PyTypeObject's tp_name). +template +const char *c_str(Args &&...args) { + auto &strings = get_internals().static_strings; + strings.emplace_front(std::forward(args)...); + return strings.front().c_str(); +} + +NAMESPACE_END(detail) + +/// Returns a named pointer that is shared among all extension modules (using the same +/// pybind11 version) running in the current interpreter. Names starting with underscores +/// are reserved for internal usage. Returns `nullptr` if no matching entry was found. +inline PYBIND11_NOINLINE void *get_shared_data(const std::string &name) { + auto &internals = detail::get_internals(); + auto it = internals.shared_data.find(name); + return it != internals.shared_data.end() ? it->second : nullptr; +} + +/// Set the shared data that can be later recovered by `get_shared_data()`. +inline PYBIND11_NOINLINE void *set_shared_data(const std::string &name, void *data) { + detail::get_internals().shared_data[name] = data; + return data; +} + +/// Returns a typed reference to a shared data entry (by using `get_shared_data()`) if +/// such entry exists. Otherwise, a new object of default-constructible type `T` is +/// added to the shared data under the given name and a reference to it is returned. +template +T &get_or_create_shared_data(const std::string &name) { + auto &internals = detail::get_internals(); + auto it = internals.shared_data.find(name); + T *ptr = (T *) (it != internals.shared_data.end() ? it->second : nullptr); + if (!ptr) { + ptr = new T(); + internals.shared_data[name] = ptr; + } + return *ptr; +} + +NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/ptocr/postprocess/dbprocess/include/pybind11/detail/typeid.h b/ptocr/postprocess/dbprocess/include/pybind11/detail/typeid.h new file mode 100644 index 0000000..6f36aab --- /dev/null +++ b/ptocr/postprocess/dbprocess/include/pybind11/detail/typeid.h @@ -0,0 +1,53 @@ +/* + pybind11/detail/typeid.h: Compiler-independent access to type identifiers + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include +#include + +#if defined(__GNUG__) +#include +#endif + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +NAMESPACE_BEGIN(detail) +/// Erase all occurrences of a substring +inline void erase_all(std::string &string, const std::string &search) { + for (size_t pos = 0;;) { + pos = string.find(search, pos); + if (pos == std::string::npos) break; + string.erase(pos, search.length()); + } +} + +PYBIND11_NOINLINE inline void clean_type_id(std::string &name) { +#if defined(__GNUG__) + int status = 0; + std::unique_ptr res { + abi::__cxa_demangle(name.c_str(), nullptr, nullptr, &status), std::free }; + if (status == 0) + name = res.get(); +#else + detail::erase_all(name, "class "); + detail::erase_all(name, "struct "); + detail::erase_all(name, "enum "); +#endif + detail::erase_all(name, "pybind11::"); +} +NAMESPACE_END(detail) + +/// Return a string representation of a C++ type +template static std::string type_id() { + std::string name(typeid(T).name()); + detail::clean_type_id(name); + return name; +} + +NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/ptocr/postprocess/dbprocess/include/pybind11/eigen.h b/ptocr/postprocess/dbprocess/include/pybind11/eigen.h new file mode 100644 index 0000000..d963d96 --- /dev/null +++ b/ptocr/postprocess/dbprocess/include/pybind11/eigen.h @@ -0,0 +1,607 @@ +/* + pybind11/eigen.h: Transparent conversion for dense and sparse Eigen matrices + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "numpy.h" + +#if defined(__INTEL_COMPILER) +# pragma warning(disable: 1682) // implicit conversion of a 64-bit integral type to a smaller integral type (potential portability problem) +#elif defined(__GNUG__) || defined(__clang__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wconversion" +# pragma GCC diagnostic ignored "-Wdeprecated-declarations" +# ifdef __clang__ +// Eigen generates a bunch of implicit-copy-constructor-is-deprecated warnings with -Wdeprecated +// under Clang, so disable that warning here: +# pragma GCC diagnostic ignored "-Wdeprecated" +# endif +# if __GNUC__ >= 7 +# pragma GCC diagnostic ignored "-Wint-in-bool-context" +# endif +#endif + +#if defined(_MSC_VER) +# pragma warning(push) +# pragma warning(disable: 4127) // warning C4127: Conditional expression is constant +# pragma warning(disable: 4996) // warning C4996: std::unary_negate is deprecated in C++17 +#endif + +#include +#include + +// Eigen prior to 3.2.7 doesn't have proper move constructors--but worse, some classes get implicit +// move constructors that break things. We could detect this an explicitly copy, but an extra copy +// of matrices seems highly undesirable. +static_assert(EIGEN_VERSION_AT_LEAST(3,2,7), "Eigen support in pybind11 requires Eigen >= 3.2.7"); + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) + +// Provide a convenience alias for easier pass-by-ref usage with fully dynamic strides: +using EigenDStride = Eigen::Stride; +template using EigenDRef = Eigen::Ref; +template using EigenDMap = Eigen::Map; + +NAMESPACE_BEGIN(detail) + +#if EIGEN_VERSION_AT_LEAST(3,3,0) +using EigenIndex = Eigen::Index; +#else +using EigenIndex = EIGEN_DEFAULT_DENSE_INDEX_TYPE; +#endif + +// Matches Eigen::Map, Eigen::Ref, blocks, etc: +template using is_eigen_dense_map = all_of, std::is_base_of, T>>; +template using is_eigen_mutable_map = std::is_base_of, T>; +template using is_eigen_dense_plain = all_of>, is_template_base_of>; +template using is_eigen_sparse = is_template_base_of; +// Test for objects inheriting from EigenBase that aren't captured by the above. This +// basically covers anything that can be assigned to a dense matrix but that don't have a typical +// matrix data layout that can be copied from their .data(). For example, DiagonalMatrix and +// SelfAdjointView fall into this category. +template using is_eigen_other = all_of< + is_template_base_of, + negation, is_eigen_dense_plain, is_eigen_sparse>> +>; + +// Captures numpy/eigen conformability status (returned by EigenProps::conformable()): +template struct EigenConformable { + bool conformable = false; + EigenIndex rows = 0, cols = 0; + EigenDStride stride{0, 0}; // Only valid if negativestrides is false! + bool negativestrides = false; // If true, do not use stride! + + EigenConformable(bool fits = false) : conformable{fits} {} + // Matrix type: + EigenConformable(EigenIndex r, EigenIndex c, + EigenIndex rstride, EigenIndex cstride) : + conformable{true}, rows{r}, cols{c} { + // TODO: when Eigen bug #747 is fixed, remove the tests for non-negativity. http://eigen.tuxfamily.org/bz/show_bug.cgi?id=747 + if (rstride < 0 || cstride < 0) { + negativestrides = true; + } else { + stride = {EigenRowMajor ? rstride : cstride /* outer stride */, + EigenRowMajor ? cstride : rstride /* inner stride */ }; + } + } + // Vector type: + EigenConformable(EigenIndex r, EigenIndex c, EigenIndex stride) + : EigenConformable(r, c, r == 1 ? c*stride : stride, c == 1 ? r : r*stride) {} + + template bool stride_compatible() const { + // To have compatible strides, we need (on both dimensions) one of fully dynamic strides, + // matching strides, or a dimension size of 1 (in which case the stride value is irrelevant) + return + !negativestrides && + (props::inner_stride == Eigen::Dynamic || props::inner_stride == stride.inner() || + (EigenRowMajor ? cols : rows) == 1) && + (props::outer_stride == Eigen::Dynamic || props::outer_stride == stride.outer() || + (EigenRowMajor ? rows : cols) == 1); + } + operator bool() const { return conformable; } +}; + +template struct eigen_extract_stride { using type = Type; }; +template +struct eigen_extract_stride> { using type = StrideType; }; +template +struct eigen_extract_stride> { using type = StrideType; }; + +// Helper struct for extracting information from an Eigen type +template struct EigenProps { + using Type = Type_; + using Scalar = typename Type::Scalar; + using StrideType = typename eigen_extract_stride::type; + static constexpr EigenIndex + rows = Type::RowsAtCompileTime, + cols = Type::ColsAtCompileTime, + size = Type::SizeAtCompileTime; + static constexpr bool + row_major = Type::IsRowMajor, + vector = Type::IsVectorAtCompileTime, // At least one dimension has fixed size 1 + fixed_rows = rows != Eigen::Dynamic, + fixed_cols = cols != Eigen::Dynamic, + fixed = size != Eigen::Dynamic, // Fully-fixed size + dynamic = !fixed_rows && !fixed_cols; // Fully-dynamic size + + template using if_zero = std::integral_constant; + static constexpr EigenIndex inner_stride = if_zero::value, + outer_stride = if_zero::value; + static constexpr bool dynamic_stride = inner_stride == Eigen::Dynamic && outer_stride == Eigen::Dynamic; + static constexpr bool requires_row_major = !dynamic_stride && !vector && (row_major ? inner_stride : outer_stride) == 1; + static constexpr bool requires_col_major = !dynamic_stride && !vector && (row_major ? outer_stride : inner_stride) == 1; + + // Takes an input array and determines whether we can make it fit into the Eigen type. If + // the array is a vector, we attempt to fit it into either an Eigen 1xN or Nx1 vector + // (preferring the latter if it will fit in either, i.e. for a fully dynamic matrix type). + static EigenConformable conformable(const array &a) { + const auto dims = a.ndim(); + if (dims < 1 || dims > 2) + return false; + + if (dims == 2) { // Matrix type: require exact match (or dynamic) + + EigenIndex + np_rows = a.shape(0), + np_cols = a.shape(1), + np_rstride = a.strides(0) / static_cast(sizeof(Scalar)), + np_cstride = a.strides(1) / static_cast(sizeof(Scalar)); + if ((fixed_rows && np_rows != rows) || (fixed_cols && np_cols != cols)) + return false; + + return {np_rows, np_cols, np_rstride, np_cstride}; + } + + // Otherwise we're storing an n-vector. Only one of the strides will be used, but whichever + // is used, we want the (single) numpy stride value. + const EigenIndex n = a.shape(0), + stride = a.strides(0) / static_cast(sizeof(Scalar)); + + if (vector) { // Eigen type is a compile-time vector + if (fixed && size != n) + return false; // Vector size mismatch + return {rows == 1 ? 1 : n, cols == 1 ? 1 : n, stride}; + } + else if (fixed) { + // The type has a fixed size, but is not a vector: abort + return false; + } + else if (fixed_cols) { + // Since this isn't a vector, cols must be != 1. We allow this only if it exactly + // equals the number of elements (rows is Dynamic, and so 1 row is allowed). + if (cols != n) return false; + return {1, n, stride}; + } + else { + // Otherwise it's either fully dynamic, or column dynamic; both become a column vector + if (fixed_rows && rows != n) return false; + return {n, 1, stride}; + } + } + + static constexpr bool show_writeable = is_eigen_dense_map::value && is_eigen_mutable_map::value; + static constexpr bool show_order = is_eigen_dense_map::value; + static constexpr bool show_c_contiguous = show_order && requires_row_major; + static constexpr bool show_f_contiguous = !show_c_contiguous && show_order && requires_col_major; + + static constexpr auto descriptor = + _("numpy.ndarray[") + npy_format_descriptor::name + + _("[") + _(_<(size_t) rows>(), _("m")) + + _(", ") + _(_<(size_t) cols>(), _("n")) + + _("]") + + // For a reference type (e.g. Ref) we have other constraints that might need to be + // satisfied: writeable=True (for a mutable reference), and, depending on the map's stride + // options, possibly f_contiguous or c_contiguous. We include them in the descriptor output + // to provide some hint as to why a TypeError is occurring (otherwise it can be confusing to + // see that a function accepts a 'numpy.ndarray[float64[3,2]]' and an error message that you + // *gave* a numpy.ndarray of the right type and dimensions. + _(", flags.writeable", "") + + _(", flags.c_contiguous", "") + + _(", flags.f_contiguous", "") + + _("]"); +}; + +// Casts an Eigen type to numpy array. If given a base, the numpy array references the src data, +// otherwise it'll make a copy. writeable lets you turn off the writeable flag for the array. +template handle eigen_array_cast(typename props::Type const &src, handle base = handle(), bool writeable = true) { + constexpr ssize_t elem_size = sizeof(typename props::Scalar); + array a; + if (props::vector) + a = array({ src.size() }, { elem_size * src.innerStride() }, src.data(), base); + else + a = array({ src.rows(), src.cols() }, { elem_size * src.rowStride(), elem_size * src.colStride() }, + src.data(), base); + + if (!writeable) + array_proxy(a.ptr())->flags &= ~detail::npy_api::NPY_ARRAY_WRITEABLE_; + + return a.release(); +} + +// Takes an lvalue ref to some Eigen type and a (python) base object, creating a numpy array that +// reference the Eigen object's data with `base` as the python-registered base class (if omitted, +// the base will be set to None, and lifetime management is up to the caller). The numpy array is +// non-writeable if the given type is const. +template +handle eigen_ref_array(Type &src, handle parent = none()) { + // none here is to get past array's should-we-copy detection, which currently always + // copies when there is no base. Setting the base to None should be harmless. + return eigen_array_cast(src, parent, !std::is_const::value); +} + +// Takes a pointer to some dense, plain Eigen type, builds a capsule around it, then returns a numpy +// array that references the encapsulated data with a python-side reference to the capsule to tie +// its destruction to that of any dependent python objects. Const-ness is determined by whether or +// not the Type of the pointer given is const. +template ::value>> +handle eigen_encapsulate(Type *src) { + capsule base(src, [](void *o) { delete static_cast(o); }); + return eigen_ref_array(*src, base); +} + +// Type caster for regular, dense matrix types (e.g. MatrixXd), but not maps/refs/etc. of dense +// types. +template +struct type_caster::value>> { + using Scalar = typename Type::Scalar; + using props = EigenProps; + + bool load(handle src, bool convert) { + // If we're in no-convert mode, only load if given an array of the correct type + if (!convert && !isinstance>(src)) + return false; + + // Coerce into an array, but don't do type conversion yet; the copy below handles it. + auto buf = array::ensure(src); + + if (!buf) + return false; + + auto dims = buf.ndim(); + if (dims < 1 || dims > 2) + return false; + + auto fits = props::conformable(buf); + if (!fits) + return false; + + // Allocate the new type, then build a numpy reference into it + value = Type(fits.rows, fits.cols); + auto ref = reinterpret_steal(eigen_ref_array(value)); + if (dims == 1) ref = ref.squeeze(); + else if (ref.ndim() == 1) buf = buf.squeeze(); + + int result = detail::npy_api::get().PyArray_CopyInto_(ref.ptr(), buf.ptr()); + + if (result < 0) { // Copy failed! + PyErr_Clear(); + return false; + } + + return true; + } + +private: + + // Cast implementation + template + static handle cast_impl(CType *src, return_value_policy policy, handle parent) { + switch (policy) { + case return_value_policy::take_ownership: + case return_value_policy::automatic: + return eigen_encapsulate(src); + case return_value_policy::move: + return eigen_encapsulate(new CType(std::move(*src))); + case return_value_policy::copy: + return eigen_array_cast(*src); + case return_value_policy::reference: + case return_value_policy::automatic_reference: + return eigen_ref_array(*src); + case return_value_policy::reference_internal: + return eigen_ref_array(*src, parent); + default: + throw cast_error("unhandled return_value_policy: should not happen!"); + }; + } + +public: + + // Normal returned non-reference, non-const value: + static handle cast(Type &&src, return_value_policy /* policy */, handle parent) { + return cast_impl(&src, return_value_policy::move, parent); + } + // If you return a non-reference const, we mark the numpy array readonly: + static handle cast(const Type &&src, return_value_policy /* policy */, handle parent) { + return cast_impl(&src, return_value_policy::move, parent); + } + // lvalue reference return; default (automatic) becomes copy + static handle cast(Type &src, return_value_policy policy, handle parent) { + if (policy == return_value_policy::automatic || policy == return_value_policy::automatic_reference) + policy = return_value_policy::copy; + return cast_impl(&src, policy, parent); + } + // const lvalue reference return; default (automatic) becomes copy + static handle cast(const Type &src, return_value_policy policy, handle parent) { + if (policy == return_value_policy::automatic || policy == return_value_policy::automatic_reference) + policy = return_value_policy::copy; + return cast(&src, policy, parent); + } + // non-const pointer return + static handle cast(Type *src, return_value_policy policy, handle parent) { + return cast_impl(src, policy, parent); + } + // const pointer return + static handle cast(const Type *src, return_value_policy policy, handle parent) { + return cast_impl(src, policy, parent); + } + + static constexpr auto name = props::descriptor; + + operator Type*() { return &value; } + operator Type&() { return value; } + operator Type&&() && { return std::move(value); } + template using cast_op_type = movable_cast_op_type; + +private: + Type value; +}; + +// Base class for casting reference/map/block/etc. objects back to python. +template struct eigen_map_caster { +private: + using props = EigenProps; + +public: + + // Directly referencing a ref/map's data is a bit dangerous (whatever the map/ref points to has + // to stay around), but we'll allow it under the assumption that you know what you're doing (and + // have an appropriate keep_alive in place). We return a numpy array pointing directly at the + // ref's data (The numpy array ends up read-only if the ref was to a const matrix type.) Note + // that this means you need to ensure you don't destroy the object in some other way (e.g. with + // an appropriate keep_alive, or with a reference to a statically allocated matrix). + static handle cast(const MapType &src, return_value_policy policy, handle parent) { + switch (policy) { + case return_value_policy::copy: + return eigen_array_cast(src); + case return_value_policy::reference_internal: + return eigen_array_cast(src, parent, is_eigen_mutable_map::value); + case return_value_policy::reference: + case return_value_policy::automatic: + case return_value_policy::automatic_reference: + return eigen_array_cast(src, none(), is_eigen_mutable_map::value); + default: + // move, take_ownership don't make any sense for a ref/map: + pybind11_fail("Invalid return_value_policy for Eigen Map/Ref/Block type"); + } + } + + static constexpr auto name = props::descriptor; + + // Explicitly delete these: support python -> C++ conversion on these (i.e. these can be return + // types but not bound arguments). We still provide them (with an explicitly delete) so that + // you end up here if you try anyway. + bool load(handle, bool) = delete; + operator MapType() = delete; + template using cast_op_type = MapType; +}; + +// We can return any map-like object (but can only load Refs, specialized next): +template struct type_caster::value>> + : eigen_map_caster {}; + +// Loader for Ref<...> arguments. See the documentation for info on how to make this work without +// copying (it requires some extra effort in many cases). +template +struct type_caster< + Eigen::Ref, + enable_if_t>::value> +> : public eigen_map_caster> { +private: + using Type = Eigen::Ref; + using props = EigenProps; + using Scalar = typename props::Scalar; + using MapType = Eigen::Map; + using Array = array_t; + static constexpr bool need_writeable = is_eigen_mutable_map::value; + // Delay construction (these have no default constructor) + std::unique_ptr map; + std::unique_ptr ref; + // Our array. When possible, this is just a numpy array pointing to the source data, but + // sometimes we can't avoid copying (e.g. input is not a numpy array at all, has an incompatible + // layout, or is an array of a type that needs to be converted). Using a numpy temporary + // (rather than an Eigen temporary) saves an extra copy when we need both type conversion and + // storage order conversion. (Note that we refuse to use this temporary copy when loading an + // argument for a Ref with M non-const, i.e. a read-write reference). + Array copy_or_ref; +public: + bool load(handle src, bool convert) { + // First check whether what we have is already an array of the right type. If not, we can't + // avoid a copy (because the copy is also going to do type conversion). + bool need_copy = !isinstance(src); + + EigenConformable fits; + if (!need_copy) { + // We don't need a converting copy, but we also need to check whether the strides are + // compatible with the Ref's stride requirements + Array aref = reinterpret_borrow(src); + + if (aref && (!need_writeable || aref.writeable())) { + fits = props::conformable(aref); + if (!fits) return false; // Incompatible dimensions + if (!fits.template stride_compatible()) + need_copy = true; + else + copy_or_ref = std::move(aref); + } + else { + need_copy = true; + } + } + + if (need_copy) { + // We need to copy: If we need a mutable reference, or we're not supposed to convert + // (either because we're in the no-convert overload pass, or because we're explicitly + // instructed not to copy (via `py::arg().noconvert()`) we have to fail loading. + if (!convert || need_writeable) return false; + + Array copy = Array::ensure(src); + if (!copy) return false; + fits = props::conformable(copy); + if (!fits || !fits.template stride_compatible()) + return false; + copy_or_ref = std::move(copy); + loader_life_support::add_patient(copy_or_ref); + } + + ref.reset(); + map.reset(new MapType(data(copy_or_ref), fits.rows, fits.cols, make_stride(fits.stride.outer(), fits.stride.inner()))); + ref.reset(new Type(*map)); + + return true; + } + + operator Type*() { return ref.get(); } + operator Type&() { return *ref; } + template using cast_op_type = pybind11::detail::cast_op_type<_T>; + +private: + template ::value, int> = 0> + Scalar *data(Array &a) { return a.mutable_data(); } + + template ::value, int> = 0> + const Scalar *data(Array &a) { return a.data(); } + + // Attempt to figure out a constructor of `Stride` that will work. + // If both strides are fixed, use a default constructor: + template using stride_ctor_default = bool_constant< + S::InnerStrideAtCompileTime != Eigen::Dynamic && S::OuterStrideAtCompileTime != Eigen::Dynamic && + std::is_default_constructible::value>; + // Otherwise, if there is a two-index constructor, assume it is (outer,inner) like + // Eigen::Stride, and use it: + template using stride_ctor_dual = bool_constant< + !stride_ctor_default::value && std::is_constructible::value>; + // Otherwise, if there is a one-index constructor, and just one of the strides is dynamic, use + // it (passing whichever stride is dynamic). + template using stride_ctor_outer = bool_constant< + !any_of, stride_ctor_dual>::value && + S::OuterStrideAtCompileTime == Eigen::Dynamic && S::InnerStrideAtCompileTime != Eigen::Dynamic && + std::is_constructible::value>; + template using stride_ctor_inner = bool_constant< + !any_of, stride_ctor_dual>::value && + S::InnerStrideAtCompileTime == Eigen::Dynamic && S::OuterStrideAtCompileTime != Eigen::Dynamic && + std::is_constructible::value>; + + template ::value, int> = 0> + static S make_stride(EigenIndex, EigenIndex) { return S(); } + template ::value, int> = 0> + static S make_stride(EigenIndex outer, EigenIndex inner) { return S(outer, inner); } + template ::value, int> = 0> + static S make_stride(EigenIndex outer, EigenIndex) { return S(outer); } + template ::value, int> = 0> + static S make_stride(EigenIndex, EigenIndex inner) { return S(inner); } + +}; + +// type_caster for special matrix types (e.g. DiagonalMatrix), which are EigenBase, but not +// EigenDense (i.e. they don't have a data(), at least not with the usual matrix layout). +// load() is not supported, but we can cast them into the python domain by first copying to a +// regular Eigen::Matrix, then casting that. +template +struct type_caster::value>> { +protected: + using Matrix = Eigen::Matrix; + using props = EigenProps; +public: + static handle cast(const Type &src, return_value_policy /* policy */, handle /* parent */) { + handle h = eigen_encapsulate(new Matrix(src)); + return h; + } + static handle cast(const Type *src, return_value_policy policy, handle parent) { return cast(*src, policy, parent); } + + static constexpr auto name = props::descriptor; + + // Explicitly delete these: support python -> C++ conversion on these (i.e. these can be return + // types but not bound arguments). We still provide them (with an explicitly delete) so that + // you end up here if you try anyway. + bool load(handle, bool) = delete; + operator Type() = delete; + template using cast_op_type = Type; +}; + +template +struct type_caster::value>> { + typedef typename Type::Scalar Scalar; + typedef remove_reference_t().outerIndexPtr())> StorageIndex; + typedef typename Type::Index Index; + static constexpr bool rowMajor = Type::IsRowMajor; + + bool load(handle src, bool) { + if (!src) + return false; + + auto obj = reinterpret_borrow(src); + object sparse_module = module::import("scipy.sparse"); + object matrix_type = sparse_module.attr( + rowMajor ? "csr_matrix" : "csc_matrix"); + + if (!obj.get_type().is(matrix_type)) { + try { + obj = matrix_type(obj); + } catch (const error_already_set &) { + return false; + } + } + + auto values = array_t((object) obj.attr("data")); + auto innerIndices = array_t((object) obj.attr("indices")); + auto outerIndices = array_t((object) obj.attr("indptr")); + auto shape = pybind11::tuple((pybind11::object) obj.attr("shape")); + auto nnz = obj.attr("nnz").cast(); + + if (!values || !innerIndices || !outerIndices) + return false; + + value = Eigen::MappedSparseMatrix( + shape[0].cast(), shape[1].cast(), nnz, + outerIndices.mutable_data(), innerIndices.mutable_data(), values.mutable_data()); + + return true; + } + + static handle cast(const Type &src, return_value_policy /* policy */, handle /* parent */) { + const_cast(src).makeCompressed(); + + object matrix_type = module::import("scipy.sparse").attr( + rowMajor ? "csr_matrix" : "csc_matrix"); + + array data(src.nonZeros(), src.valuePtr()); + array outerIndices((rowMajor ? src.rows() : src.cols()) + 1, src.outerIndexPtr()); + array innerIndices(src.nonZeros(), src.innerIndexPtr()); + + return matrix_type( + std::make_tuple(data, innerIndices, outerIndices), + std::make_pair(src.rows(), src.cols()) + ).release(); + } + + PYBIND11_TYPE_CASTER(Type, _<(Type::IsRowMajor) != 0>("scipy.sparse.csr_matrix[", "scipy.sparse.csc_matrix[") + + npy_format_descriptor::name + _("]")); +}; + +NAMESPACE_END(detail) +NAMESPACE_END(PYBIND11_NAMESPACE) + +#if defined(__GNUG__) || defined(__clang__) +# pragma GCC diagnostic pop +#elif defined(_MSC_VER) +# pragma warning(pop) +#endif diff --git a/ptocr/postprocess/dbprocess/include/pybind11/embed.h b/ptocr/postprocess/dbprocess/include/pybind11/embed.h new file mode 100644 index 0000000..7265588 --- /dev/null +++ b/ptocr/postprocess/dbprocess/include/pybind11/embed.h @@ -0,0 +1,200 @@ +/* + pybind11/embed.h: Support for embedding the interpreter + + Copyright (c) 2017 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "pybind11.h" +#include "eval.h" + +#if defined(PYPY_VERSION) +# error Embedding the interpreter is not supported with PyPy +#endif + +#if PY_MAJOR_VERSION >= 3 +# define PYBIND11_EMBEDDED_MODULE_IMPL(name) \ + extern "C" PyObject *pybind11_init_impl_##name() { \ + return pybind11_init_wrapper_##name(); \ + } +#else +# define PYBIND11_EMBEDDED_MODULE_IMPL(name) \ + extern "C" void pybind11_init_impl_##name() { \ + pybind11_init_wrapper_##name(); \ + } +#endif + +/** \rst + Add a new module to the table of builtins for the interpreter. Must be + defined in global scope. The first macro parameter is the name of the + module (without quotes). The second parameter is the variable which will + be used as the interface to add functions and classes to the module. + + .. code-block:: cpp + + PYBIND11_EMBEDDED_MODULE(example, m) { + // ... initialize functions and classes here + m.def("foo", []() { + return "Hello, World!"; + }); + } + \endrst */ +#define PYBIND11_EMBEDDED_MODULE(name, variable) \ + static void PYBIND11_CONCAT(pybind11_init_, name)(pybind11::module &); \ + static PyObject PYBIND11_CONCAT(*pybind11_init_wrapper_, name)() { \ + auto m = pybind11::module(PYBIND11_TOSTRING(name)); \ + try { \ + PYBIND11_CONCAT(pybind11_init_, name)(m); \ + return m.ptr(); \ + } catch (pybind11::error_already_set &e) { \ + PyErr_SetString(PyExc_ImportError, e.what()); \ + return nullptr; \ + } catch (const std::exception &e) { \ + PyErr_SetString(PyExc_ImportError, e.what()); \ + return nullptr; \ + } \ + } \ + PYBIND11_EMBEDDED_MODULE_IMPL(name) \ + pybind11::detail::embedded_module name(PYBIND11_TOSTRING(name), \ + PYBIND11_CONCAT(pybind11_init_impl_, name)); \ + void PYBIND11_CONCAT(pybind11_init_, name)(pybind11::module &variable) + + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +NAMESPACE_BEGIN(detail) + +/// Python 2.7/3.x compatible version of `PyImport_AppendInittab` and error checks. +struct embedded_module { +#if PY_MAJOR_VERSION >= 3 + using init_t = PyObject *(*)(); +#else + using init_t = void (*)(); +#endif + embedded_module(const char *name, init_t init) { + if (Py_IsInitialized()) + pybind11_fail("Can't add new modules after the interpreter has been initialized"); + + auto result = PyImport_AppendInittab(name, init); + if (result == -1) + pybind11_fail("Insufficient memory to add a new module"); + } +}; + +NAMESPACE_END(detail) + +/** \rst + Initialize the Python interpreter. No other pybind11 or CPython API functions can be + called before this is done; with the exception of `PYBIND11_EMBEDDED_MODULE`. The + optional parameter can be used to skip the registration of signal handlers (see the + `Python documentation`_ for details). Calling this function again after the interpreter + has already been initialized is a fatal error. + + If initializing the Python interpreter fails, then the program is terminated. (This + is controlled by the CPython runtime and is an exception to pybind11's normal behavior + of throwing exceptions on errors.) + + .. _Python documentation: https://docs.python.org/3/c-api/init.html#c.Py_InitializeEx + \endrst */ +inline void initialize_interpreter(bool init_signal_handlers = true) { + if (Py_IsInitialized()) + pybind11_fail("The interpreter is already running"); + + Py_InitializeEx(init_signal_handlers ? 1 : 0); + + // Make .py files in the working directory available by default + module::import("sys").attr("path").cast().append("."); +} + +/** \rst + Shut down the Python interpreter. No pybind11 or CPython API functions can be called + after this. In addition, pybind11 objects must not outlive the interpreter: + + .. code-block:: cpp + + { // BAD + py::initialize_interpreter(); + auto hello = py::str("Hello, World!"); + py::finalize_interpreter(); + } // <-- BOOM, hello's destructor is called after interpreter shutdown + + { // GOOD + py::initialize_interpreter(); + { // scoped + auto hello = py::str("Hello, World!"); + } // <-- OK, hello is cleaned up properly + py::finalize_interpreter(); + } + + { // BETTER + py::scoped_interpreter guard{}; + auto hello = py::str("Hello, World!"); + } + + .. warning:: + + The interpreter can be restarted by calling `initialize_interpreter` again. + Modules created using pybind11 can be safely re-initialized. However, Python + itself cannot completely unload binary extension modules and there are several + caveats with regard to interpreter restarting. All the details can be found + in the CPython documentation. In short, not all interpreter memory may be + freed, either due to reference cycles or user-created global data. + + \endrst */ +inline void finalize_interpreter() { + handle builtins(PyEval_GetBuiltins()); + const char *id = PYBIND11_INTERNALS_ID; + + // Get the internals pointer (without creating it if it doesn't exist). It's possible for the + // internals to be created during Py_Finalize() (e.g. if a py::capsule calls `get_internals()` + // during destruction), so we get the pointer-pointer here and check it after Py_Finalize(). + detail::internals **internals_ptr_ptr = detail::get_internals_pp(); + // It could also be stashed in builtins, so look there too: + if (builtins.contains(id) && isinstance(builtins[id])) + internals_ptr_ptr = capsule(builtins[id]); + + Py_Finalize(); + + if (internals_ptr_ptr) { + delete *internals_ptr_ptr; + *internals_ptr_ptr = nullptr; + } +} + +/** \rst + Scope guard version of `initialize_interpreter` and `finalize_interpreter`. + This a move-only guard and only a single instance can exist. + + .. code-block:: cpp + + #include + + int main() { + py::scoped_interpreter guard{}; + py::print(Hello, World!); + } // <-- interpreter shutdown + \endrst */ +class scoped_interpreter { +public: + scoped_interpreter(bool init_signal_handlers = true) { + initialize_interpreter(init_signal_handlers); + } + + scoped_interpreter(const scoped_interpreter &) = delete; + scoped_interpreter(scoped_interpreter &&other) noexcept { other.is_valid = false; } + scoped_interpreter &operator=(const scoped_interpreter &) = delete; + scoped_interpreter &operator=(scoped_interpreter &&) = delete; + + ~scoped_interpreter() { + if (is_valid) + finalize_interpreter(); + } + +private: + bool is_valid = true; +}; + +NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/ptocr/postprocess/dbprocess/include/pybind11/eval.h b/ptocr/postprocess/dbprocess/include/pybind11/eval.h new file mode 100644 index 0000000..ea85ba1 --- /dev/null +++ b/ptocr/postprocess/dbprocess/include/pybind11/eval.h @@ -0,0 +1,117 @@ +/* + pybind11/exec.h: Support for evaluating Python expressions and statements + from strings and files + + Copyright (c) 2016 Klemens Morgenstern and + Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "pybind11.h" + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) + +enum eval_mode { + /// Evaluate a string containing an isolated expression + eval_expr, + + /// Evaluate a string containing a single statement. Returns \c none + eval_single_statement, + + /// Evaluate a string containing a sequence of statement. Returns \c none + eval_statements +}; + +template +object eval(str expr, object global = globals(), object local = object()) { + if (!local) + local = global; + + /* PyRun_String does not accept a PyObject / encoding specifier, + this seems to be the only alternative */ + std::string buffer = "# -*- coding: utf-8 -*-\n" + (std::string) expr; + + int start; + switch (mode) { + case eval_expr: start = Py_eval_input; break; + case eval_single_statement: start = Py_single_input; break; + case eval_statements: start = Py_file_input; break; + default: pybind11_fail("invalid evaluation mode"); + } + + PyObject *result = PyRun_String(buffer.c_str(), start, global.ptr(), local.ptr()); + if (!result) + throw error_already_set(); + return reinterpret_steal(result); +} + +template +object eval(const char (&s)[N], object global = globals(), object local = object()) { + /* Support raw string literals by removing common leading whitespace */ + auto expr = (s[0] == '\n') ? str(module::import("textwrap").attr("dedent")(s)) + : str(s); + return eval(expr, global, local); +} + +inline void exec(str expr, object global = globals(), object local = object()) { + eval(expr, global, local); +} + +template +void exec(const char (&s)[N], object global = globals(), object local = object()) { + eval(s, global, local); +} + +template +object eval_file(str fname, object global = globals(), object local = object()) { + if (!local) + local = global; + + int start; + switch (mode) { + case eval_expr: start = Py_eval_input; break; + case eval_single_statement: start = Py_single_input; break; + case eval_statements: start = Py_file_input; break; + default: pybind11_fail("invalid evaluation mode"); + } + + int closeFile = 1; + std::string fname_str = (std::string) fname; +#if PY_VERSION_HEX >= 0x03040000 + FILE *f = _Py_fopen_obj(fname.ptr(), "r"); +#elif PY_VERSION_HEX >= 0x03000000 + FILE *f = _Py_fopen(fname.ptr(), "r"); +#else + /* No unicode support in open() :( */ + auto fobj = reinterpret_steal(PyFile_FromString( + const_cast(fname_str.c_str()), + const_cast("r"))); + FILE *f = nullptr; + if (fobj) + f = PyFile_AsFile(fobj.ptr()); + closeFile = 0; +#endif + if (!f) { + PyErr_Clear(); + pybind11_fail("File \"" + fname_str + "\" could not be opened!"); + } + +#if PY_VERSION_HEX < 0x03000000 && defined(PYPY_VERSION) + PyObject *result = PyRun_File(f, fname_str.c_str(), start, global.ptr(), + local.ptr()); + (void) closeFile; +#else + PyObject *result = PyRun_FileEx(f, fname_str.c_str(), start, global.ptr(), + local.ptr(), closeFile); +#endif + + if (!result) + throw error_already_set(); + return reinterpret_steal(result); +} + +NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/ptocr/postprocess/dbprocess/include/pybind11/functional.h b/ptocr/postprocess/dbprocess/include/pybind11/functional.h new file mode 100644 index 0000000..9cdf21f --- /dev/null +++ b/ptocr/postprocess/dbprocess/include/pybind11/functional.h @@ -0,0 +1,83 @@ +/* + pybind11/functional.h: std::function<> support + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "pybind11.h" +#include + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +NAMESPACE_BEGIN(detail) + +template +struct type_caster> { + using type = std::function; + using retval_type = conditional_t::value, void_type, Return>; + using function_type = Return (*) (Args...); + +public: + bool load(handle src, bool convert) { + if (src.is_none()) { + // Defer accepting None to other overloads (if we aren't in convert mode): + if (!convert) return false; + return true; + } + + if (!isinstance(src)) + return false; + + auto func = reinterpret_borrow(src); + + /* + When passing a C++ function as an argument to another C++ + function via Python, every function call would normally involve + a full C++ -> Python -> C++ roundtrip, which can be prohibitive. + Here, we try to at least detect the case where the function is + stateless (i.e. function pointer or lambda function without + captured variables), in which case the roundtrip can be avoided. + */ + if (auto cfunc = func.cpp_function()) { + auto c = reinterpret_borrow(PyCFunction_GET_SELF(cfunc.ptr())); + auto rec = (function_record *) c; + + if (rec && rec->is_stateless && + same_type(typeid(function_type), *reinterpret_cast(rec->data[1]))) { + struct capture { function_type f; }; + value = ((capture *) &rec->data)->f; + return true; + } + } + + value = [func](Args... args) -> Return { + gil_scoped_acquire acq; + object retval(func(std::forward(args)...)); + /* Visual studio 2015 parser issue: need parentheses around this expression */ + return (retval.template cast()); + }; + return true; + } + + template + static handle cast(Func &&f_, return_value_policy policy, handle /* parent */) { + if (!f_) + return none().inc_ref(); + + auto result = f_.template target(); + if (result) + return cpp_function(*result, policy).release(); + else + return cpp_function(std::forward(f_), policy).release(); + } + + PYBIND11_TYPE_CASTER(type, _("Callable[[") + concat(make_caster::name...) + _("], ") + + make_caster::name + _("]")); +}; + +NAMESPACE_END(detail) +NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/ptocr/postprocess/dbprocess/include/pybind11/iostream.h b/ptocr/postprocess/dbprocess/include/pybind11/iostream.h new file mode 100644 index 0000000..182e8ee --- /dev/null +++ b/ptocr/postprocess/dbprocess/include/pybind11/iostream.h @@ -0,0 +1,200 @@ +/* + pybind11/iostream.h -- Tools to assist with redirecting cout and cerr to Python + + Copyright (c) 2017 Henry F. Schreiner + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "pybind11.h" + +#include +#include +#include +#include +#include + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +NAMESPACE_BEGIN(detail) + +// Buffer that writes to Python instead of C++ +class pythonbuf : public std::streambuf { +private: + using traits_type = std::streambuf::traits_type; + + char d_buffer[1024]; + object pywrite; + object pyflush; + + int overflow(int c) { + if (!traits_type::eq_int_type(c, traits_type::eof())) { + *pptr() = traits_type::to_char_type(c); + pbump(1); + } + return sync() == 0 ? traits_type::not_eof(c) : traits_type::eof(); + } + + int sync() { + if (pbase() != pptr()) { + // This subtraction cannot be negative, so dropping the sign + str line(pbase(), static_cast(pptr() - pbase())); + + pywrite(line); + pyflush(); + + setp(pbase(), epptr()); + } + return 0; + } + +public: + pythonbuf(object pyostream) + : pywrite(pyostream.attr("write")), + pyflush(pyostream.attr("flush")) { + setp(d_buffer, d_buffer + sizeof(d_buffer) - 1); + } + + /// Sync before destroy + ~pythonbuf() { + sync(); + } +}; + +NAMESPACE_END(detail) + + +/** \rst + This a move-only guard that redirects output. + + .. code-block:: cpp + + #include + + ... + + { + py::scoped_ostream_redirect output; + std::cout << "Hello, World!"; // Python stdout + } // <-- return std::cout to normal + + You can explicitly pass the c++ stream and the python object, + for example to guard stderr instead. + + .. code-block:: cpp + + { + py::scoped_ostream_redirect output{std::cerr, py::module::import("sys").attr("stderr")}; + std::cerr << "Hello, World!"; + } + \endrst */ +class scoped_ostream_redirect { +protected: + std::streambuf *old; + std::ostream &costream; + detail::pythonbuf buffer; + +public: + scoped_ostream_redirect( + std::ostream &costream = std::cout, + object pyostream = module::import("sys").attr("stdout")) + : costream(costream), buffer(pyostream) { + old = costream.rdbuf(&buffer); + } + + ~scoped_ostream_redirect() { + costream.rdbuf(old); + } + + scoped_ostream_redirect(const scoped_ostream_redirect &) = delete; + scoped_ostream_redirect(scoped_ostream_redirect &&other) = default; + scoped_ostream_redirect &operator=(const scoped_ostream_redirect &) = delete; + scoped_ostream_redirect &operator=(scoped_ostream_redirect &&) = delete; +}; + + +/** \rst + Like `scoped_ostream_redirect`, but redirects cerr by default. This class + is provided primary to make ``py::call_guard`` easier to make. + + .. code-block:: cpp + + m.def("noisy_func", &noisy_func, + py::call_guard()); + +\endrst */ +class scoped_estream_redirect : public scoped_ostream_redirect { +public: + scoped_estream_redirect( + std::ostream &costream = std::cerr, + object pyostream = module::import("sys").attr("stderr")) + : scoped_ostream_redirect(costream,pyostream) {} +}; + + +NAMESPACE_BEGIN(detail) + +// Class to redirect output as a context manager. C++ backend. +class OstreamRedirect { + bool do_stdout_; + bool do_stderr_; + std::unique_ptr redirect_stdout; + std::unique_ptr redirect_stderr; + +public: + OstreamRedirect(bool do_stdout = true, bool do_stderr = true) + : do_stdout_(do_stdout), do_stderr_(do_stderr) {} + + void enter() { + if (do_stdout_) + redirect_stdout.reset(new scoped_ostream_redirect()); + if (do_stderr_) + redirect_stderr.reset(new scoped_estream_redirect()); + } + + void exit() { + redirect_stdout.reset(); + redirect_stderr.reset(); + } +}; + +NAMESPACE_END(detail) + +/** \rst + This is a helper function to add a C++ redirect context manager to Python + instead of using a C++ guard. To use it, add the following to your binding code: + + .. code-block:: cpp + + #include + + ... + + py::add_ostream_redirect(m, "ostream_redirect"); + + You now have a Python context manager that redirects your output: + + .. code-block:: python + + with m.ostream_redirect(): + m.print_to_cout_function() + + This manager can optionally be told which streams to operate on: + + .. code-block:: python + + with m.ostream_redirect(stdout=true, stderr=true): + m.noisy_function_with_error_printing() + + \endrst */ +inline class_ add_ostream_redirect(module m, std::string name = "ostream_redirect") { + return class_(m, name.c_str(), module_local()) + .def(init(), arg("stdout")=true, arg("stderr")=true) + .def("__enter__", &detail::OstreamRedirect::enter) + .def("__exit__", [](detail::OstreamRedirect &self_, args) { self_.exit(); }); +} + +NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/ptocr/postprocess/dbprocess/include/pybind11/numpy.h b/ptocr/postprocess/dbprocess/include/pybind11/numpy.h new file mode 100644 index 0000000..37471d8 --- /dev/null +++ b/ptocr/postprocess/dbprocess/include/pybind11/numpy.h @@ -0,0 +1,1610 @@ +/* + pybind11/numpy.h: Basic NumPy support, vectorize() wrapper + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "pybind11.h" +#include "complex.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(_MSC_VER) +# pragma warning(push) +# pragma warning(disable: 4127) // warning C4127: Conditional expression is constant +#endif + +/* This will be true on all flat address space platforms and allows us to reduce the + whole npy_intp / ssize_t / Py_intptr_t business down to just ssize_t for all size + and dimension types (e.g. shape, strides, indexing), instead of inflicting this + upon the library user. */ +static_assert(sizeof(ssize_t) == sizeof(Py_intptr_t), "ssize_t != Py_intptr_t"); + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) + +class array; // Forward declaration + +NAMESPACE_BEGIN(detail) +template struct npy_format_descriptor; + +struct PyArrayDescr_Proxy { + PyObject_HEAD + PyObject *typeobj; + char kind; + char type; + char byteorder; + char flags; + int type_num; + int elsize; + int alignment; + char *subarray; + PyObject *fields; + PyObject *names; +}; + +struct PyArray_Proxy { + PyObject_HEAD + char *data; + int nd; + ssize_t *dimensions; + ssize_t *strides; + PyObject *base; + PyObject *descr; + int flags; +}; + +struct PyVoidScalarObject_Proxy { + PyObject_VAR_HEAD + char *obval; + PyArrayDescr_Proxy *descr; + int flags; + PyObject *base; +}; + +struct numpy_type_info { + PyObject* dtype_ptr; + std::string format_str; +}; + +struct numpy_internals { + std::unordered_map registered_dtypes; + + numpy_type_info *get_type_info(const std::type_info& tinfo, bool throw_if_missing = true) { + auto it = registered_dtypes.find(std::type_index(tinfo)); + if (it != registered_dtypes.end()) + return &(it->second); + if (throw_if_missing) + pybind11_fail(std::string("NumPy type info missing for ") + tinfo.name()); + return nullptr; + } + + template numpy_type_info *get_type_info(bool throw_if_missing = true) { + return get_type_info(typeid(typename std::remove_cv::type), throw_if_missing); + } +}; + +inline PYBIND11_NOINLINE void load_numpy_internals(numpy_internals* &ptr) { + ptr = &get_or_create_shared_data("_numpy_internals"); +} + +inline numpy_internals& get_numpy_internals() { + static numpy_internals* ptr = nullptr; + if (!ptr) + load_numpy_internals(ptr); + return *ptr; +} + +struct npy_api { + enum constants { + NPY_ARRAY_C_CONTIGUOUS_ = 0x0001, + NPY_ARRAY_F_CONTIGUOUS_ = 0x0002, + NPY_ARRAY_OWNDATA_ = 0x0004, + NPY_ARRAY_FORCECAST_ = 0x0010, + NPY_ARRAY_ENSUREARRAY_ = 0x0040, + NPY_ARRAY_ALIGNED_ = 0x0100, + NPY_ARRAY_WRITEABLE_ = 0x0400, + NPY_BOOL_ = 0, + NPY_BYTE_, NPY_UBYTE_, + NPY_SHORT_, NPY_USHORT_, + NPY_INT_, NPY_UINT_, + NPY_LONG_, NPY_ULONG_, + NPY_LONGLONG_, NPY_ULONGLONG_, + NPY_FLOAT_, NPY_DOUBLE_, NPY_LONGDOUBLE_, + NPY_CFLOAT_, NPY_CDOUBLE_, NPY_CLONGDOUBLE_, + NPY_OBJECT_ = 17, + NPY_STRING_, NPY_UNICODE_, NPY_VOID_ + }; + + typedef struct { + Py_intptr_t *ptr; + int len; + } PyArray_Dims; + + static npy_api& get() { + static npy_api api = lookup(); + return api; + } + + bool PyArray_Check_(PyObject *obj) const { + return (bool) PyObject_TypeCheck(obj, PyArray_Type_); + } + bool PyArrayDescr_Check_(PyObject *obj) const { + return (bool) PyObject_TypeCheck(obj, PyArrayDescr_Type_); + } + + unsigned int (*PyArray_GetNDArrayCFeatureVersion_)(); + PyObject *(*PyArray_DescrFromType_)(int); + PyObject *(*PyArray_NewFromDescr_) + (PyTypeObject *, PyObject *, int, Py_intptr_t *, + Py_intptr_t *, void *, int, PyObject *); + PyObject *(*PyArray_DescrNewFromType_)(int); + int (*PyArray_CopyInto_)(PyObject *, PyObject *); + PyObject *(*PyArray_NewCopy_)(PyObject *, int); + PyTypeObject *PyArray_Type_; + PyTypeObject *PyVoidArrType_Type_; + PyTypeObject *PyArrayDescr_Type_; + PyObject *(*PyArray_DescrFromScalar_)(PyObject *); + PyObject *(*PyArray_FromAny_) (PyObject *, PyObject *, int, int, int, PyObject *); + int (*PyArray_DescrConverter_) (PyObject *, PyObject **); + bool (*PyArray_EquivTypes_) (PyObject *, PyObject *); + int (*PyArray_GetArrayParamsFromObject_)(PyObject *, PyObject *, char, PyObject **, int *, + Py_ssize_t *, PyObject **, PyObject *); + PyObject *(*PyArray_Squeeze_)(PyObject *); + int (*PyArray_SetBaseObject_)(PyObject *, PyObject *); + PyObject* (*PyArray_Resize_)(PyObject*, PyArray_Dims*, int, int); +private: + enum functions { + API_PyArray_GetNDArrayCFeatureVersion = 211, + API_PyArray_Type = 2, + API_PyArrayDescr_Type = 3, + API_PyVoidArrType_Type = 39, + API_PyArray_DescrFromType = 45, + API_PyArray_DescrFromScalar = 57, + API_PyArray_FromAny = 69, + API_PyArray_Resize = 80, + API_PyArray_CopyInto = 82, + API_PyArray_NewCopy = 85, + API_PyArray_NewFromDescr = 94, + API_PyArray_DescrNewFromType = 9, + API_PyArray_DescrConverter = 174, + API_PyArray_EquivTypes = 182, + API_PyArray_GetArrayParamsFromObject = 278, + API_PyArray_Squeeze = 136, + API_PyArray_SetBaseObject = 282 + }; + + static npy_api lookup() { + module m = module::import("numpy.core.multiarray"); + auto c = m.attr("_ARRAY_API"); +#if PY_MAJOR_VERSION >= 3 + void **api_ptr = (void **) PyCapsule_GetPointer(c.ptr(), NULL); +#else + void **api_ptr = (void **) PyCObject_AsVoidPtr(c.ptr()); +#endif + npy_api api; +#define DECL_NPY_API(Func) api.Func##_ = (decltype(api.Func##_)) api_ptr[API_##Func]; + DECL_NPY_API(PyArray_GetNDArrayCFeatureVersion); + if (api.PyArray_GetNDArrayCFeatureVersion_() < 0x7) + pybind11_fail("pybind11 numpy support requires numpy >= 1.7.0"); + DECL_NPY_API(PyArray_Type); + DECL_NPY_API(PyVoidArrType_Type); + DECL_NPY_API(PyArrayDescr_Type); + DECL_NPY_API(PyArray_DescrFromType); + DECL_NPY_API(PyArray_DescrFromScalar); + DECL_NPY_API(PyArray_FromAny); + DECL_NPY_API(PyArray_Resize); + DECL_NPY_API(PyArray_CopyInto); + DECL_NPY_API(PyArray_NewCopy); + DECL_NPY_API(PyArray_NewFromDescr); + DECL_NPY_API(PyArray_DescrNewFromType); + DECL_NPY_API(PyArray_DescrConverter); + DECL_NPY_API(PyArray_EquivTypes); + DECL_NPY_API(PyArray_GetArrayParamsFromObject); + DECL_NPY_API(PyArray_Squeeze); + DECL_NPY_API(PyArray_SetBaseObject); +#undef DECL_NPY_API + return api; + } +}; + +inline PyArray_Proxy* array_proxy(void* ptr) { + return reinterpret_cast(ptr); +} + +inline const PyArray_Proxy* array_proxy(const void* ptr) { + return reinterpret_cast(ptr); +} + +inline PyArrayDescr_Proxy* array_descriptor_proxy(PyObject* ptr) { + return reinterpret_cast(ptr); +} + +inline const PyArrayDescr_Proxy* array_descriptor_proxy(const PyObject* ptr) { + return reinterpret_cast(ptr); +} + +inline bool check_flags(const void* ptr, int flag) { + return (flag == (array_proxy(ptr)->flags & flag)); +} + +template struct is_std_array : std::false_type { }; +template struct is_std_array> : std::true_type { }; +template struct is_complex : std::false_type { }; +template struct is_complex> : std::true_type { }; + +template struct array_info_scalar { + typedef T type; + static constexpr bool is_array = false; + static constexpr bool is_empty = false; + static constexpr auto extents = _(""); + static void append_extents(list& /* shape */) { } +}; +// Computes underlying type and a comma-separated list of extents for array +// types (any mix of std::array and built-in arrays). An array of char is +// treated as scalar because it gets special handling. +template struct array_info : array_info_scalar { }; +template struct array_info> { + using type = typename array_info::type; + static constexpr bool is_array = true; + static constexpr bool is_empty = (N == 0) || array_info::is_empty; + static constexpr size_t extent = N; + + // appends the extents to shape + static void append_extents(list& shape) { + shape.append(N); + array_info::append_extents(shape); + } + + static constexpr auto extents = _::is_array>( + concat(_(), array_info::extents), _() + ); +}; +// For numpy we have special handling for arrays of characters, so we don't include +// the size in the array extents. +template struct array_info : array_info_scalar { }; +template struct array_info> : array_info_scalar> { }; +template struct array_info : array_info> { }; +template using remove_all_extents_t = typename array_info::type; + +template using is_pod_struct = all_of< + std::is_standard_layout, // since we're accessing directly in memory we need a standard layout type +#if !defined(__GNUG__) || defined(_LIBCPP_VERSION) || defined(_GLIBCXX_USE_CXX11_ABI) + // _GLIBCXX_USE_CXX11_ABI indicates that we're using libstdc++ from GCC 5 or newer, independent + // of the actual compiler (Clang can also use libstdc++, but it always defines __GNUC__ == 4). + std::is_trivially_copyable, +#else + // GCC 4 doesn't implement is_trivially_copyable, so approximate it + std::is_trivially_destructible, + satisfies_any_of, +#endif + satisfies_none_of +>; + +template ssize_t byte_offset_unsafe(const Strides &) { return 0; } +template +ssize_t byte_offset_unsafe(const Strides &strides, ssize_t i, Ix... index) { + return i * strides[Dim] + byte_offset_unsafe(strides, index...); +} + +/** + * Proxy class providing unsafe, unchecked const access to array data. This is constructed through + * the `unchecked()` method of `array` or the `unchecked()` method of `array_t`. `Dims` + * will be -1 for dimensions determined at runtime. + */ +template +class unchecked_reference { +protected: + static constexpr bool Dynamic = Dims < 0; + const unsigned char *data_; + // Storing the shape & strides in local variables (i.e. these arrays) allows the compiler to + // make large performance gains on big, nested loops, but requires compile-time dimensions + conditional_t> + shape_, strides_; + const ssize_t dims_; + + friend class pybind11::array; + // Constructor for compile-time dimensions: + template + unchecked_reference(const void *data, const ssize_t *shape, const ssize_t *strides, enable_if_t) + : data_{reinterpret_cast(data)}, dims_{Dims} { + for (size_t i = 0; i < (size_t) dims_; i++) { + shape_[i] = shape[i]; + strides_[i] = strides[i]; + } + } + // Constructor for runtime dimensions: + template + unchecked_reference(const void *data, const ssize_t *shape, const ssize_t *strides, enable_if_t dims) + : data_{reinterpret_cast(data)}, shape_{shape}, strides_{strides}, dims_{dims} {} + +public: + /** + * Unchecked const reference access to data at the given indices. For a compile-time known + * number of dimensions, this requires the correct number of arguments; for run-time + * dimensionality, this is not checked (and so is up to the caller to use safely). + */ + template const T &operator()(Ix... index) const { + static_assert(ssize_t{sizeof...(Ix)} == Dims || Dynamic, + "Invalid number of indices for unchecked array reference"); + return *reinterpret_cast(data_ + byte_offset_unsafe(strides_, ssize_t(index)...)); + } + /** + * Unchecked const reference access to data; this operator only participates if the reference + * is to a 1-dimensional array. When present, this is exactly equivalent to `obj(index)`. + */ + template > + const T &operator[](ssize_t index) const { return operator()(index); } + + /// Pointer access to the data at the given indices. + template const T *data(Ix... ix) const { return &operator()(ssize_t(ix)...); } + + /// Returns the item size, i.e. sizeof(T) + constexpr static ssize_t itemsize() { return sizeof(T); } + + /// Returns the shape (i.e. size) of dimension `dim` + ssize_t shape(ssize_t dim) const { return shape_[(size_t) dim]; } + + /// Returns the number of dimensions of the array + ssize_t ndim() const { return dims_; } + + /// Returns the total number of elements in the referenced array, i.e. the product of the shapes + template + enable_if_t size() const { + return std::accumulate(shape_.begin(), shape_.end(), (ssize_t) 1, std::multiplies()); + } + template + enable_if_t size() const { + return std::accumulate(shape_, shape_ + ndim(), (ssize_t) 1, std::multiplies()); + } + + /// Returns the total number of bytes used by the referenced data. Note that the actual span in + /// memory may be larger if the referenced array has non-contiguous strides (e.g. for a slice). + ssize_t nbytes() const { + return size() * itemsize(); + } +}; + +template +class unchecked_mutable_reference : public unchecked_reference { + friend class pybind11::array; + using ConstBase = unchecked_reference; + using ConstBase::ConstBase; + using ConstBase::Dynamic; +public: + /// Mutable, unchecked access to data at the given indices. + template T& operator()(Ix... index) { + static_assert(ssize_t{sizeof...(Ix)} == Dims || Dynamic, + "Invalid number of indices for unchecked array reference"); + return const_cast(ConstBase::operator()(index...)); + } + /** + * Mutable, unchecked access data at the given index; this operator only participates if the + * reference is to a 1-dimensional array (or has runtime dimensions). When present, this is + * exactly equivalent to `obj(index)`. + */ + template > + T &operator[](ssize_t index) { return operator()(index); } + + /// Mutable pointer access to the data at the given indices. + template T *mutable_data(Ix... ix) { return &operator()(ssize_t(ix)...); } +}; + +template +struct type_caster> { + static_assert(Dim == 0 && Dim > 0 /* always fail */, "unchecked array proxy object is not castable"); +}; +template +struct type_caster> : type_caster> {}; + +NAMESPACE_END(detail) + +class dtype : public object { +public: + PYBIND11_OBJECT_DEFAULT(dtype, object, detail::npy_api::get().PyArrayDescr_Check_); + + explicit dtype(const buffer_info &info) { + dtype descr(_dtype_from_pep3118()(PYBIND11_STR_TYPE(info.format))); + // If info.itemsize == 0, use the value calculated from the format string + m_ptr = descr.strip_padding(info.itemsize ? info.itemsize : descr.itemsize()).release().ptr(); + } + + explicit dtype(const std::string &format) { + m_ptr = from_args(pybind11::str(format)).release().ptr(); + } + + dtype(const char *format) : dtype(std::string(format)) { } + + dtype(list names, list formats, list offsets, ssize_t itemsize) { + dict args; + args["names"] = names; + args["formats"] = formats; + args["offsets"] = offsets; + args["itemsize"] = pybind11::int_(itemsize); + m_ptr = from_args(args).release().ptr(); + } + + /// This is essentially the same as calling numpy.dtype(args) in Python. + static dtype from_args(object args) { + PyObject *ptr = nullptr; + if (!detail::npy_api::get().PyArray_DescrConverter_(args.ptr(), &ptr) || !ptr) + throw error_already_set(); + return reinterpret_steal(ptr); + } + + /// Return dtype associated with a C++ type. + template static dtype of() { + return detail::npy_format_descriptor::type>::dtype(); + } + + /// Size of the data type in bytes. + ssize_t itemsize() const { + return detail::array_descriptor_proxy(m_ptr)->elsize; + } + + /// Returns true for structured data types. + bool has_fields() const { + return detail::array_descriptor_proxy(m_ptr)->names != nullptr; + } + + /// Single-character type code. + char kind() const { + return detail::array_descriptor_proxy(m_ptr)->kind; + } + +private: + static object _dtype_from_pep3118() { + static PyObject *obj = module::import("numpy.core._internal") + .attr("_dtype_from_pep3118").cast().release().ptr(); + return reinterpret_borrow(obj); + } + + dtype strip_padding(ssize_t itemsize) { + // Recursively strip all void fields with empty names that are generated for + // padding fields (as of NumPy v1.11). + if (!has_fields()) + return *this; + + struct field_descr { PYBIND11_STR_TYPE name; object format; pybind11::int_ offset; }; + std::vector field_descriptors; + + for (auto field : attr("fields").attr("items")()) { + auto spec = field.cast(); + auto name = spec[0].cast(); + auto format = spec[1].cast()[0].cast(); + auto offset = spec[1].cast()[1].cast(); + if (!len(name) && format.kind() == 'V') + continue; + field_descriptors.push_back({(PYBIND11_STR_TYPE) name, format.strip_padding(format.itemsize()), offset}); + } + + std::sort(field_descriptors.begin(), field_descriptors.end(), + [](const field_descr& a, const field_descr& b) { + return a.offset.cast() < b.offset.cast(); + }); + + list names, formats, offsets; + for (auto& descr : field_descriptors) { + names.append(descr.name); + formats.append(descr.format); + offsets.append(descr.offset); + } + return dtype(names, formats, offsets, itemsize); + } +}; + +class array : public buffer { +public: + PYBIND11_OBJECT_CVT(array, buffer, detail::npy_api::get().PyArray_Check_, raw_array) + + enum { + c_style = detail::npy_api::NPY_ARRAY_C_CONTIGUOUS_, + f_style = detail::npy_api::NPY_ARRAY_F_CONTIGUOUS_, + forcecast = detail::npy_api::NPY_ARRAY_FORCECAST_ + }; + + array() : array({{0}}, static_cast(nullptr)) {} + + using ShapeContainer = detail::any_container; + using StridesContainer = detail::any_container; + + // Constructs an array taking shape/strides from arbitrary container types + array(const pybind11::dtype &dt, ShapeContainer shape, StridesContainer strides, + const void *ptr = nullptr, handle base = handle()) { + + if (strides->empty()) + *strides = c_strides(*shape, dt.itemsize()); + + auto ndim = shape->size(); + if (ndim != strides->size()) + pybind11_fail("NumPy: shape ndim doesn't match strides ndim"); + auto descr = dt; + + int flags = 0; + if (base && ptr) { + if (isinstance(base)) + /* Copy flags from base (except ownership bit) */ + flags = reinterpret_borrow(base).flags() & ~detail::npy_api::NPY_ARRAY_OWNDATA_; + else + /* Writable by default, easy to downgrade later on if needed */ + flags = detail::npy_api::NPY_ARRAY_WRITEABLE_; + } + + auto &api = detail::npy_api::get(); + auto tmp = reinterpret_steal(api.PyArray_NewFromDescr_( + api.PyArray_Type_, descr.release().ptr(), (int) ndim, shape->data(), strides->data(), + const_cast(ptr), flags, nullptr)); + if (!tmp) + throw error_already_set(); + if (ptr) { + if (base) { + api.PyArray_SetBaseObject_(tmp.ptr(), base.inc_ref().ptr()); + } else { + tmp = reinterpret_steal(api.PyArray_NewCopy_(tmp.ptr(), -1 /* any order */)); + } + } + m_ptr = tmp.release().ptr(); + } + + array(const pybind11::dtype &dt, ShapeContainer shape, const void *ptr = nullptr, handle base = handle()) + : array(dt, std::move(shape), {}, ptr, base) { } + + template ::value && !std::is_same::value>> + array(const pybind11::dtype &dt, T count, const void *ptr = nullptr, handle base = handle()) + : array(dt, {{count}}, ptr, base) { } + + template + array(ShapeContainer shape, StridesContainer strides, const T *ptr, handle base = handle()) + : array(pybind11::dtype::of(), std::move(shape), std::move(strides), ptr, base) { } + + template + array(ShapeContainer shape, const T *ptr, handle base = handle()) + : array(std::move(shape), {}, ptr, base) { } + + template + explicit array(ssize_t count, const T *ptr, handle base = handle()) : array({count}, {}, ptr, base) { } + + explicit array(const buffer_info &info) + : array(pybind11::dtype(info), info.shape, info.strides, info.ptr) { } + + /// Array descriptor (dtype) + pybind11::dtype dtype() const { + return reinterpret_borrow(detail::array_proxy(m_ptr)->descr); + } + + /// Total number of elements + ssize_t size() const { + return std::accumulate(shape(), shape() + ndim(), (ssize_t) 1, std::multiplies()); + } + + /// Byte size of a single element + ssize_t itemsize() const { + return detail::array_descriptor_proxy(detail::array_proxy(m_ptr)->descr)->elsize; + } + + /// Total number of bytes + ssize_t nbytes() const { + return size() * itemsize(); + } + + /// Number of dimensions + ssize_t ndim() const { + return detail::array_proxy(m_ptr)->nd; + } + + /// Base object + object base() const { + return reinterpret_borrow(detail::array_proxy(m_ptr)->base); + } + + /// Dimensions of the array + const ssize_t* shape() const { + return detail::array_proxy(m_ptr)->dimensions; + } + + /// Dimension along a given axis + ssize_t shape(ssize_t dim) const { + if (dim >= ndim()) + fail_dim_check(dim, "invalid axis"); + return shape()[dim]; + } + + /// Strides of the array + const ssize_t* strides() const { + return detail::array_proxy(m_ptr)->strides; + } + + /// Stride along a given axis + ssize_t strides(ssize_t dim) const { + if (dim >= ndim()) + fail_dim_check(dim, "invalid axis"); + return strides()[dim]; + } + + /// Return the NumPy array flags + int flags() const { + return detail::array_proxy(m_ptr)->flags; + } + + /// If set, the array is writeable (otherwise the buffer is read-only) + bool writeable() const { + return detail::check_flags(m_ptr, detail::npy_api::NPY_ARRAY_WRITEABLE_); + } + + /// If set, the array owns the data (will be freed when the array is deleted) + bool owndata() const { + return detail::check_flags(m_ptr, detail::npy_api::NPY_ARRAY_OWNDATA_); + } + + /// Pointer to the contained data. If index is not provided, points to the + /// beginning of the buffer. May throw if the index would lead to out of bounds access. + template const void* data(Ix... index) const { + return static_cast(detail::array_proxy(m_ptr)->data + offset_at(index...)); + } + + /// Mutable pointer to the contained data. If index is not provided, points to the + /// beginning of the buffer. May throw if the index would lead to out of bounds access. + /// May throw if the array is not writeable. + template void* mutable_data(Ix... index) { + check_writeable(); + return static_cast(detail::array_proxy(m_ptr)->data + offset_at(index...)); + } + + /// Byte offset from beginning of the array to a given index (full or partial). + /// May throw if the index would lead to out of bounds access. + template ssize_t offset_at(Ix... index) const { + if ((ssize_t) sizeof...(index) > ndim()) + fail_dim_check(sizeof...(index), "too many indices for an array"); + return byte_offset(ssize_t(index)...); + } + + ssize_t offset_at() const { return 0; } + + /// Item count from beginning of the array to a given index (full or partial). + /// May throw if the index would lead to out of bounds access. + template ssize_t index_at(Ix... index) const { + return offset_at(index...) / itemsize(); + } + + /** + * Returns a proxy object that provides access to the array's data without bounds or + * dimensionality checking. Will throw if the array is missing the `writeable` flag. Use with + * care: the array must not be destroyed or reshaped for the duration of the returned object, + * and the caller must take care not to access invalid dimensions or dimension indices. + */ + template detail::unchecked_mutable_reference mutable_unchecked() & { + if (Dims >= 0 && ndim() != Dims) + throw std::domain_error("array has incorrect number of dimensions: " + std::to_string(ndim()) + + "; expected " + std::to_string(Dims)); + return detail::unchecked_mutable_reference(mutable_data(), shape(), strides(), ndim()); + } + + /** + * Returns a proxy object that provides const access to the array's data without bounds or + * dimensionality checking. Unlike `mutable_unchecked()`, this does not require that the + * underlying array have the `writable` flag. Use with care: the array must not be destroyed or + * reshaped for the duration of the returned object, and the caller must take care not to access + * invalid dimensions or dimension indices. + */ + template detail::unchecked_reference unchecked() const & { + if (Dims >= 0 && ndim() != Dims) + throw std::domain_error("array has incorrect number of dimensions: " + std::to_string(ndim()) + + "; expected " + std::to_string(Dims)); + return detail::unchecked_reference(data(), shape(), strides(), ndim()); + } + + /// Return a new view with all of the dimensions of length 1 removed + array squeeze() { + auto& api = detail::npy_api::get(); + return reinterpret_steal(api.PyArray_Squeeze_(m_ptr)); + } + + /// Resize array to given shape + /// If refcheck is true and more that one reference exist to this array + /// then resize will succeed only if it makes a reshape, i.e. original size doesn't change + void resize(ShapeContainer new_shape, bool refcheck = true) { + detail::npy_api::PyArray_Dims d = { + new_shape->data(), int(new_shape->size()) + }; + // try to resize, set ordering param to -1 cause it's not used anyway + object new_array = reinterpret_steal( + detail::npy_api::get().PyArray_Resize_(m_ptr, &d, int(refcheck), -1) + ); + if (!new_array) throw error_already_set(); + if (isinstance(new_array)) { *this = std::move(new_array); } + } + + /// Ensure that the argument is a NumPy array + /// In case of an error, nullptr is returned and the Python error is cleared. + static array ensure(handle h, int ExtraFlags = 0) { + auto result = reinterpret_steal(raw_array(h.ptr(), ExtraFlags)); + if (!result) + PyErr_Clear(); + return result; + } + +protected: + template friend struct detail::npy_format_descriptor; + + void fail_dim_check(ssize_t dim, const std::string& msg) const { + throw index_error(msg + ": " + std::to_string(dim) + + " (ndim = " + std::to_string(ndim()) + ")"); + } + + template ssize_t byte_offset(Ix... index) const { + check_dimensions(index...); + return detail::byte_offset_unsafe(strides(), ssize_t(index)...); + } + + void check_writeable() const { + if (!writeable()) + throw std::domain_error("array is not writeable"); + } + + // Default, C-style strides + static std::vector c_strides(const std::vector &shape, ssize_t itemsize) { + auto ndim = shape.size(); + std::vector strides(ndim, itemsize); + if (ndim > 0) + for (size_t i = ndim - 1; i > 0; --i) + strides[i - 1] = strides[i] * shape[i]; + return strides; + } + + // F-style strides; default when constructing an array_t with `ExtraFlags & f_style` + static std::vector f_strides(const std::vector &shape, ssize_t itemsize) { + auto ndim = shape.size(); + std::vector strides(ndim, itemsize); + for (size_t i = 1; i < ndim; ++i) + strides[i] = strides[i - 1] * shape[i - 1]; + return strides; + } + + template void check_dimensions(Ix... index) const { + check_dimensions_impl(ssize_t(0), shape(), ssize_t(index)...); + } + + void check_dimensions_impl(ssize_t, const ssize_t*) const { } + + template void check_dimensions_impl(ssize_t axis, const ssize_t* shape, ssize_t i, Ix... index) const { + if (i >= *shape) { + throw index_error(std::string("index ") + std::to_string(i) + + " is out of bounds for axis " + std::to_string(axis) + + " with size " + std::to_string(*shape)); + } + check_dimensions_impl(axis + 1, shape + 1, index...); + } + + /// Create array from any object -- always returns a new reference + static PyObject *raw_array(PyObject *ptr, int ExtraFlags = 0) { + if (ptr == nullptr) { + PyErr_SetString(PyExc_ValueError, "cannot create a pybind11::array from a nullptr"); + return nullptr; + } + return detail::npy_api::get().PyArray_FromAny_( + ptr, nullptr, 0, 0, detail::npy_api::NPY_ARRAY_ENSUREARRAY_ | ExtraFlags, nullptr); + } +}; + +template class array_t : public array { +private: + struct private_ctor {}; + // Delegating constructor needed when both moving and accessing in the same constructor + array_t(private_ctor, ShapeContainer &&shape, StridesContainer &&strides, const T *ptr, handle base) + : array(std::move(shape), std::move(strides), ptr, base) {} +public: + static_assert(!detail::array_info::is_array, "Array types cannot be used with array_t"); + + using value_type = T; + + array_t() : array(0, static_cast(nullptr)) {} + array_t(handle h, borrowed_t) : array(h, borrowed_t{}) { } + array_t(handle h, stolen_t) : array(h, stolen_t{}) { } + + PYBIND11_DEPRECATED("Use array_t::ensure() instead") + array_t(handle h, bool is_borrowed) : array(raw_array_t(h.ptr()), stolen_t{}) { + if (!m_ptr) PyErr_Clear(); + if (!is_borrowed) Py_XDECREF(h.ptr()); + } + + array_t(const object &o) : array(raw_array_t(o.ptr()), stolen_t{}) { + if (!m_ptr) throw error_already_set(); + } + + explicit array_t(const buffer_info& info) : array(info) { } + + array_t(ShapeContainer shape, StridesContainer strides, const T *ptr = nullptr, handle base = handle()) + : array(std::move(shape), std::move(strides), ptr, base) { } + + explicit array_t(ShapeContainer shape, const T *ptr = nullptr, handle base = handle()) + : array_t(private_ctor{}, std::move(shape), + ExtraFlags & f_style ? f_strides(*shape, itemsize()) : c_strides(*shape, itemsize()), + ptr, base) { } + + explicit array_t(size_t count, const T *ptr = nullptr, handle base = handle()) + : array({count}, {}, ptr, base) { } + + constexpr ssize_t itemsize() const { + return sizeof(T); + } + + template ssize_t index_at(Ix... index) const { + return offset_at(index...) / itemsize(); + } + + template const T* data(Ix... index) const { + return static_cast(array::data(index...)); + } + + template T* mutable_data(Ix... index) { + return static_cast(array::mutable_data(index...)); + } + + // Reference to element at a given index + template const T& at(Ix... index) const { + if (sizeof...(index) != ndim()) + fail_dim_check(sizeof...(index), "index dimension mismatch"); + return *(static_cast(array::data()) + byte_offset(ssize_t(index)...) / itemsize()); + } + + // Mutable reference to element at a given index + template T& mutable_at(Ix... index) { + if (sizeof...(index) != ndim()) + fail_dim_check(sizeof...(index), "index dimension mismatch"); + return *(static_cast(array::mutable_data()) + byte_offset(ssize_t(index)...) / itemsize()); + } + + /** + * Returns a proxy object that provides access to the array's data without bounds or + * dimensionality checking. Will throw if the array is missing the `writeable` flag. Use with + * care: the array must not be destroyed or reshaped for the duration of the returned object, + * and the caller must take care not to access invalid dimensions or dimension indices. + */ + template detail::unchecked_mutable_reference mutable_unchecked() & { + return array::mutable_unchecked(); + } + + /** + * Returns a proxy object that provides const access to the array's data without bounds or + * dimensionality checking. Unlike `unchecked()`, this does not require that the underlying + * array have the `writable` flag. Use with care: the array must not be destroyed or reshaped + * for the duration of the returned object, and the caller must take care not to access invalid + * dimensions or dimension indices. + */ + template detail::unchecked_reference unchecked() const & { + return array::unchecked(); + } + + /// Ensure that the argument is a NumPy array of the correct dtype (and if not, try to convert + /// it). In case of an error, nullptr is returned and the Python error is cleared. + static array_t ensure(handle h) { + auto result = reinterpret_steal(raw_array_t(h.ptr())); + if (!result) + PyErr_Clear(); + return result; + } + + static bool check_(handle h) { + const auto &api = detail::npy_api::get(); + return api.PyArray_Check_(h.ptr()) + && api.PyArray_EquivTypes_(detail::array_proxy(h.ptr())->descr, dtype::of().ptr()); + } + +protected: + /// Create array from any object -- always returns a new reference + static PyObject *raw_array_t(PyObject *ptr) { + if (ptr == nullptr) { + PyErr_SetString(PyExc_ValueError, "cannot create a pybind11::array_t from a nullptr"); + return nullptr; + } + return detail::npy_api::get().PyArray_FromAny_( + ptr, dtype::of().release().ptr(), 0, 0, + detail::npy_api::NPY_ARRAY_ENSUREARRAY_ | ExtraFlags, nullptr); + } +}; + +template +struct format_descriptor::value>> { + static std::string format() { + return detail::npy_format_descriptor::type>::format(); + } +}; + +template struct format_descriptor { + static std::string format() { return std::to_string(N) + "s"; } +}; +template struct format_descriptor> { + static std::string format() { return std::to_string(N) + "s"; } +}; + +template +struct format_descriptor::value>> { + static std::string format() { + return format_descriptor< + typename std::remove_cv::type>::type>::format(); + } +}; + +template +struct format_descriptor::is_array>> { + static std::string format() { + using namespace detail; + static constexpr auto extents = _("(") + array_info::extents + _(")"); + return extents.text + format_descriptor>::format(); + } +}; + +NAMESPACE_BEGIN(detail) +template +struct pyobject_caster> { + using type = array_t; + + bool load(handle src, bool convert) { + if (!convert && !type::check_(src)) + return false; + value = type::ensure(src); + return static_cast(value); + } + + static handle cast(const handle &src, return_value_policy /* policy */, handle /* parent */) { + return src.inc_ref(); + } + PYBIND11_TYPE_CASTER(type, handle_type_name::name); +}; + +template +struct compare_buffer_info::value>> { + static bool compare(const buffer_info& b) { + return npy_api::get().PyArray_EquivTypes_(dtype::of().ptr(), dtype(b).ptr()); + } +}; + +template +struct npy_format_descriptor_name; + +template +struct npy_format_descriptor_name::value>> { + static constexpr auto name = _::value>( + _("bool"), _::value>("int", "uint") + _() + ); +}; + +template +struct npy_format_descriptor_name::value>> { + static constexpr auto name = _::value || std::is_same::value>( + _("float") + _(), _("longdouble") + ); +}; + +template +struct npy_format_descriptor_name::value>> { + static constexpr auto name = _::value + || std::is_same::value>( + _("complex") + _(), _("longcomplex") + ); +}; + +template +struct npy_format_descriptor::value>> + : npy_format_descriptor_name { +private: + // NB: the order here must match the one in common.h + constexpr static const int values[15] = { + npy_api::NPY_BOOL_, + npy_api::NPY_BYTE_, npy_api::NPY_UBYTE_, npy_api::NPY_SHORT_, npy_api::NPY_USHORT_, + npy_api::NPY_INT_, npy_api::NPY_UINT_, npy_api::NPY_LONGLONG_, npy_api::NPY_ULONGLONG_, + npy_api::NPY_FLOAT_, npy_api::NPY_DOUBLE_, npy_api::NPY_LONGDOUBLE_, + npy_api::NPY_CFLOAT_, npy_api::NPY_CDOUBLE_, npy_api::NPY_CLONGDOUBLE_ + }; + +public: + static constexpr int value = values[detail::is_fmt_numeric::index]; + + static pybind11::dtype dtype() { + if (auto ptr = npy_api::get().PyArray_DescrFromType_(value)) + return reinterpret_borrow(ptr); + pybind11_fail("Unsupported buffer format!"); + } +}; + +#define PYBIND11_DECL_CHAR_FMT \ + static constexpr auto name = _("S") + _(); \ + static pybind11::dtype dtype() { return pybind11::dtype(std::string("S") + std::to_string(N)); } +template struct npy_format_descriptor { PYBIND11_DECL_CHAR_FMT }; +template struct npy_format_descriptor> { PYBIND11_DECL_CHAR_FMT }; +#undef PYBIND11_DECL_CHAR_FMT + +template struct npy_format_descriptor::is_array>> { +private: + using base_descr = npy_format_descriptor::type>; +public: + static_assert(!array_info::is_empty, "Zero-sized arrays are not supported"); + + static constexpr auto name = _("(") + array_info::extents + _(")") + base_descr::name; + static pybind11::dtype dtype() { + list shape; + array_info::append_extents(shape); + return pybind11::dtype::from_args(pybind11::make_tuple(base_descr::dtype(), shape)); + } +}; + +template struct npy_format_descriptor::value>> { +private: + using base_descr = npy_format_descriptor::type>; +public: + static constexpr auto name = base_descr::name; + static pybind11::dtype dtype() { return base_descr::dtype(); } +}; + +struct field_descriptor { + const char *name; + ssize_t offset; + ssize_t size; + std::string format; + dtype descr; +}; + +inline PYBIND11_NOINLINE void register_structured_dtype( + any_container fields, + const std::type_info& tinfo, ssize_t itemsize, + bool (*direct_converter)(PyObject *, void *&)) { + + auto& numpy_internals = get_numpy_internals(); + if (numpy_internals.get_type_info(tinfo, false)) + pybind11_fail("NumPy: dtype is already registered"); + + list names, formats, offsets; + for (auto field : *fields) { + if (!field.descr) + pybind11_fail(std::string("NumPy: unsupported field dtype: `") + + field.name + "` @ " + tinfo.name()); + names.append(PYBIND11_STR_TYPE(field.name)); + formats.append(field.descr); + offsets.append(pybind11::int_(field.offset)); + } + auto dtype_ptr = pybind11::dtype(names, formats, offsets, itemsize).release().ptr(); + + // There is an existing bug in NumPy (as of v1.11): trailing bytes are + // not encoded explicitly into the format string. This will supposedly + // get fixed in v1.12; for further details, see these: + // - https://github.com/numpy/numpy/issues/7797 + // - https://github.com/numpy/numpy/pull/7798 + // Because of this, we won't use numpy's logic to generate buffer format + // strings and will just do it ourselves. + std::vector ordered_fields(std::move(fields)); + std::sort(ordered_fields.begin(), ordered_fields.end(), + [](const field_descriptor &a, const field_descriptor &b) { return a.offset < b.offset; }); + ssize_t offset = 0; + std::ostringstream oss; + // mark the structure as unaligned with '^', because numpy and C++ don't + // always agree about alignment (particularly for complex), and we're + // explicitly listing all our padding. This depends on none of the fields + // overriding the endianness. Putting the ^ in front of individual fields + // isn't guaranteed to work due to https://github.com/numpy/numpy/issues/9049 + oss << "^T{"; + for (auto& field : ordered_fields) { + if (field.offset > offset) + oss << (field.offset - offset) << 'x'; + oss << field.format << ':' << field.name << ':'; + offset = field.offset + field.size; + } + if (itemsize > offset) + oss << (itemsize - offset) << 'x'; + oss << '}'; + auto format_str = oss.str(); + + // Sanity check: verify that NumPy properly parses our buffer format string + auto& api = npy_api::get(); + auto arr = array(buffer_info(nullptr, itemsize, format_str, 1)); + if (!api.PyArray_EquivTypes_(dtype_ptr, arr.dtype().ptr())) + pybind11_fail("NumPy: invalid buffer descriptor!"); + + auto tindex = std::type_index(tinfo); + numpy_internals.registered_dtypes[tindex] = { dtype_ptr, format_str }; + get_internals().direct_conversions[tindex].push_back(direct_converter); +} + +template struct npy_format_descriptor { + static_assert(is_pod_struct::value, "Attempt to use a non-POD or unimplemented POD type as a numpy dtype"); + + static constexpr auto name = make_caster::name; + + static pybind11::dtype dtype() { + return reinterpret_borrow(dtype_ptr()); + } + + static std::string format() { + static auto format_str = get_numpy_internals().get_type_info(true)->format_str; + return format_str; + } + + static void register_dtype(any_container fields) { + register_structured_dtype(std::move(fields), typeid(typename std::remove_cv::type), + sizeof(T), &direct_converter); + } + +private: + static PyObject* dtype_ptr() { + static PyObject* ptr = get_numpy_internals().get_type_info(true)->dtype_ptr; + return ptr; + } + + static bool direct_converter(PyObject *obj, void*& value) { + auto& api = npy_api::get(); + if (!PyObject_TypeCheck(obj, api.PyVoidArrType_Type_)) + return false; + if (auto descr = reinterpret_steal(api.PyArray_DescrFromScalar_(obj))) { + if (api.PyArray_EquivTypes_(dtype_ptr(), descr.ptr())) { + value = ((PyVoidScalarObject_Proxy *) obj)->obval; + return true; + } + } + return false; + } +}; + +#ifdef __CLION_IDE__ // replace heavy macro with dummy code for the IDE (doesn't affect code) +# define PYBIND11_NUMPY_DTYPE(Type, ...) ((void)0) +# define PYBIND11_NUMPY_DTYPE_EX(Type, ...) ((void)0) +#else + +#define PYBIND11_FIELD_DESCRIPTOR_EX(T, Field, Name) \ + ::pybind11::detail::field_descriptor { \ + Name, offsetof(T, Field), sizeof(decltype(std::declval().Field)), \ + ::pybind11::format_descriptor().Field)>::format(), \ + ::pybind11::detail::npy_format_descriptor().Field)>::dtype() \ + } + +// Extract name, offset and format descriptor for a struct field +#define PYBIND11_FIELD_DESCRIPTOR(T, Field) PYBIND11_FIELD_DESCRIPTOR_EX(T, Field, #Field) + +// The main idea of this macro is borrowed from https://github.com/swansontec/map-macro +// (C) William Swanson, Paul Fultz +#define PYBIND11_EVAL0(...) __VA_ARGS__ +#define PYBIND11_EVAL1(...) PYBIND11_EVAL0 (PYBIND11_EVAL0 (PYBIND11_EVAL0 (__VA_ARGS__))) +#define PYBIND11_EVAL2(...) PYBIND11_EVAL1 (PYBIND11_EVAL1 (PYBIND11_EVAL1 (__VA_ARGS__))) +#define PYBIND11_EVAL3(...) PYBIND11_EVAL2 (PYBIND11_EVAL2 (PYBIND11_EVAL2 (__VA_ARGS__))) +#define PYBIND11_EVAL4(...) PYBIND11_EVAL3 (PYBIND11_EVAL3 (PYBIND11_EVAL3 (__VA_ARGS__))) +#define PYBIND11_EVAL(...) PYBIND11_EVAL4 (PYBIND11_EVAL4 (PYBIND11_EVAL4 (__VA_ARGS__))) +#define PYBIND11_MAP_END(...) +#define PYBIND11_MAP_OUT +#define PYBIND11_MAP_COMMA , +#define PYBIND11_MAP_GET_END() 0, PYBIND11_MAP_END +#define PYBIND11_MAP_NEXT0(test, next, ...) next PYBIND11_MAP_OUT +#define PYBIND11_MAP_NEXT1(test, next) PYBIND11_MAP_NEXT0 (test, next, 0) +#define PYBIND11_MAP_NEXT(test, next) PYBIND11_MAP_NEXT1 (PYBIND11_MAP_GET_END test, next) +#ifdef _MSC_VER // MSVC is not as eager to expand macros, hence this workaround +#define PYBIND11_MAP_LIST_NEXT1(test, next) \ + PYBIND11_EVAL0 (PYBIND11_MAP_NEXT0 (test, PYBIND11_MAP_COMMA next, 0)) +#else +#define PYBIND11_MAP_LIST_NEXT1(test, next) \ + PYBIND11_MAP_NEXT0 (test, PYBIND11_MAP_COMMA next, 0) +#endif +#define PYBIND11_MAP_LIST_NEXT(test, next) \ + PYBIND11_MAP_LIST_NEXT1 (PYBIND11_MAP_GET_END test, next) +#define PYBIND11_MAP_LIST0(f, t, x, peek, ...) \ + f(t, x) PYBIND11_MAP_LIST_NEXT (peek, PYBIND11_MAP_LIST1) (f, t, peek, __VA_ARGS__) +#define PYBIND11_MAP_LIST1(f, t, x, peek, ...) \ + f(t, x) PYBIND11_MAP_LIST_NEXT (peek, PYBIND11_MAP_LIST0) (f, t, peek, __VA_ARGS__) +// PYBIND11_MAP_LIST(f, t, a1, a2, ...) expands to f(t, a1), f(t, a2), ... +#define PYBIND11_MAP_LIST(f, t, ...) \ + PYBIND11_EVAL (PYBIND11_MAP_LIST1 (f, t, __VA_ARGS__, (), 0)) + +#define PYBIND11_NUMPY_DTYPE(Type, ...) \ + ::pybind11::detail::npy_format_descriptor::register_dtype \ + (::std::vector<::pybind11::detail::field_descriptor> \ + {PYBIND11_MAP_LIST (PYBIND11_FIELD_DESCRIPTOR, Type, __VA_ARGS__)}) + +#ifdef _MSC_VER +#define PYBIND11_MAP2_LIST_NEXT1(test, next) \ + PYBIND11_EVAL0 (PYBIND11_MAP_NEXT0 (test, PYBIND11_MAP_COMMA next, 0)) +#else +#define PYBIND11_MAP2_LIST_NEXT1(test, next) \ + PYBIND11_MAP_NEXT0 (test, PYBIND11_MAP_COMMA next, 0) +#endif +#define PYBIND11_MAP2_LIST_NEXT(test, next) \ + PYBIND11_MAP2_LIST_NEXT1 (PYBIND11_MAP_GET_END test, next) +#define PYBIND11_MAP2_LIST0(f, t, x1, x2, peek, ...) \ + f(t, x1, x2) PYBIND11_MAP2_LIST_NEXT (peek, PYBIND11_MAP2_LIST1) (f, t, peek, __VA_ARGS__) +#define PYBIND11_MAP2_LIST1(f, t, x1, x2, peek, ...) \ + f(t, x1, x2) PYBIND11_MAP2_LIST_NEXT (peek, PYBIND11_MAP2_LIST0) (f, t, peek, __VA_ARGS__) +// PYBIND11_MAP2_LIST(f, t, a1, a2, ...) expands to f(t, a1, a2), f(t, a3, a4), ... +#define PYBIND11_MAP2_LIST(f, t, ...) \ + PYBIND11_EVAL (PYBIND11_MAP2_LIST1 (f, t, __VA_ARGS__, (), 0)) + +#define PYBIND11_NUMPY_DTYPE_EX(Type, ...) \ + ::pybind11::detail::npy_format_descriptor::register_dtype \ + (::std::vector<::pybind11::detail::field_descriptor> \ + {PYBIND11_MAP2_LIST (PYBIND11_FIELD_DESCRIPTOR_EX, Type, __VA_ARGS__)}) + +#endif // __CLION_IDE__ + +template +using array_iterator = typename std::add_pointer::type; + +template +array_iterator array_begin(const buffer_info& buffer) { + return array_iterator(reinterpret_cast(buffer.ptr)); +} + +template +array_iterator array_end(const buffer_info& buffer) { + return array_iterator(reinterpret_cast(buffer.ptr) + buffer.size); +} + +class common_iterator { +public: + using container_type = std::vector; + using value_type = container_type::value_type; + using size_type = container_type::size_type; + + common_iterator() : p_ptr(0), m_strides() {} + + common_iterator(void* ptr, const container_type& strides, const container_type& shape) + : p_ptr(reinterpret_cast(ptr)), m_strides(strides.size()) { + m_strides.back() = static_cast(strides.back()); + for (size_type i = m_strides.size() - 1; i != 0; --i) { + size_type j = i - 1; + value_type s = static_cast(shape[i]); + m_strides[j] = strides[j] + m_strides[i] - strides[i] * s; + } + } + + void increment(size_type dim) { + p_ptr += m_strides[dim]; + } + + void* data() const { + return p_ptr; + } + +private: + char* p_ptr; + container_type m_strides; +}; + +template class multi_array_iterator { +public: + using container_type = std::vector; + + multi_array_iterator(const std::array &buffers, + const container_type &shape) + : m_shape(shape.size()), m_index(shape.size(), 0), + m_common_iterator() { + + // Manual copy to avoid conversion warning if using std::copy + for (size_t i = 0; i < shape.size(); ++i) + m_shape[i] = shape[i]; + + container_type strides(shape.size()); + for (size_t i = 0; i < N; ++i) + init_common_iterator(buffers[i], shape, m_common_iterator[i], strides); + } + + multi_array_iterator& operator++() { + for (size_t j = m_index.size(); j != 0; --j) { + size_t i = j - 1; + if (++m_index[i] != m_shape[i]) { + increment_common_iterator(i); + break; + } else { + m_index[i] = 0; + } + } + return *this; + } + + template T* data() const { + return reinterpret_cast(m_common_iterator[K].data()); + } + +private: + + using common_iter = common_iterator; + + void init_common_iterator(const buffer_info &buffer, + const container_type &shape, + common_iter &iterator, + container_type &strides) { + auto buffer_shape_iter = buffer.shape.rbegin(); + auto buffer_strides_iter = buffer.strides.rbegin(); + auto shape_iter = shape.rbegin(); + auto strides_iter = strides.rbegin(); + + while (buffer_shape_iter != buffer.shape.rend()) { + if (*shape_iter == *buffer_shape_iter) + *strides_iter = *buffer_strides_iter; + else + *strides_iter = 0; + + ++buffer_shape_iter; + ++buffer_strides_iter; + ++shape_iter; + ++strides_iter; + } + + std::fill(strides_iter, strides.rend(), 0); + iterator = common_iter(buffer.ptr, strides, shape); + } + + void increment_common_iterator(size_t dim) { + for (auto &iter : m_common_iterator) + iter.increment(dim); + } + + container_type m_shape; + container_type m_index; + std::array m_common_iterator; +}; + +enum class broadcast_trivial { non_trivial, c_trivial, f_trivial }; + +// Populates the shape and number of dimensions for the set of buffers. Returns a broadcast_trivial +// enum value indicating whether the broadcast is "trivial"--that is, has each buffer being either a +// singleton or a full-size, C-contiguous (`c_trivial`) or Fortran-contiguous (`f_trivial`) storage +// buffer; returns `non_trivial` otherwise. +template +broadcast_trivial broadcast(const std::array &buffers, ssize_t &ndim, std::vector &shape) { + ndim = std::accumulate(buffers.begin(), buffers.end(), ssize_t(0), [](ssize_t res, const buffer_info &buf) { + return std::max(res, buf.ndim); + }); + + shape.clear(); + shape.resize((size_t) ndim, 1); + + // Figure out the output size, and make sure all input arrays conform (i.e. are either size 1 or + // the full size). + for (size_t i = 0; i < N; ++i) { + auto res_iter = shape.rbegin(); + auto end = buffers[i].shape.rend(); + for (auto shape_iter = buffers[i].shape.rbegin(); shape_iter != end; ++shape_iter, ++res_iter) { + const auto &dim_size_in = *shape_iter; + auto &dim_size_out = *res_iter; + + // Each input dimension can either be 1 or `n`, but `n` values must match across buffers + if (dim_size_out == 1) + dim_size_out = dim_size_in; + else if (dim_size_in != 1 && dim_size_in != dim_size_out) + pybind11_fail("pybind11::vectorize: incompatible size/dimension of inputs!"); + } + } + + bool trivial_broadcast_c = true; + bool trivial_broadcast_f = true; + for (size_t i = 0; i < N && (trivial_broadcast_c || trivial_broadcast_f); ++i) { + if (buffers[i].size == 1) + continue; + + // Require the same number of dimensions: + if (buffers[i].ndim != ndim) + return broadcast_trivial::non_trivial; + + // Require all dimensions be full-size: + if (!std::equal(buffers[i].shape.cbegin(), buffers[i].shape.cend(), shape.cbegin())) + return broadcast_trivial::non_trivial; + + // Check for C contiguity (but only if previous inputs were also C contiguous) + if (trivial_broadcast_c) { + ssize_t expect_stride = buffers[i].itemsize; + auto end = buffers[i].shape.crend(); + for (auto shape_iter = buffers[i].shape.crbegin(), stride_iter = buffers[i].strides.crbegin(); + trivial_broadcast_c && shape_iter != end; ++shape_iter, ++stride_iter) { + if (expect_stride == *stride_iter) + expect_stride *= *shape_iter; + else + trivial_broadcast_c = false; + } + } + + // Check for Fortran contiguity (if previous inputs were also F contiguous) + if (trivial_broadcast_f) { + ssize_t expect_stride = buffers[i].itemsize; + auto end = buffers[i].shape.cend(); + for (auto shape_iter = buffers[i].shape.cbegin(), stride_iter = buffers[i].strides.cbegin(); + trivial_broadcast_f && shape_iter != end; ++shape_iter, ++stride_iter) { + if (expect_stride == *stride_iter) + expect_stride *= *shape_iter; + else + trivial_broadcast_f = false; + } + } + } + + return + trivial_broadcast_c ? broadcast_trivial::c_trivial : + trivial_broadcast_f ? broadcast_trivial::f_trivial : + broadcast_trivial::non_trivial; +} + +template +struct vectorize_arg { + static_assert(!std::is_rvalue_reference::value, "Functions with rvalue reference arguments cannot be vectorized"); + // The wrapped function gets called with this type: + using call_type = remove_reference_t; + // Is this a vectorized argument? + static constexpr bool vectorize = + satisfies_any_of::value && + satisfies_none_of::value && + (!std::is_reference::value || + (std::is_lvalue_reference::value && std::is_const::value)); + // Accept this type: an array for vectorized types, otherwise the type as-is: + using type = conditional_t, array::forcecast>, T>; +}; + +template +struct vectorize_helper { +private: + static constexpr size_t N = sizeof...(Args); + static constexpr size_t NVectorized = constexpr_sum(vectorize_arg::vectorize...); + static_assert(NVectorized >= 1, + "pybind11::vectorize(...) requires a function with at least one vectorizable argument"); + +public: + template + explicit vectorize_helper(T &&f) : f(std::forward(f)) { } + + object operator()(typename vectorize_arg::type... args) { + return run(args..., + make_index_sequence(), + select_indices::vectorize...>(), + make_index_sequence()); + } + +private: + remove_reference_t f; + + // Internal compiler error in MSVC 19.16.27025.1 (Visual Studio 2017 15.9.4), when compiling with "/permissive-" flag + // when arg_call_types is manually inlined. + using arg_call_types = std::tuple::call_type...>; + template using param_n_t = typename std::tuple_element::type; + + // Runs a vectorized function given arguments tuple and three index sequences: + // - Index is the full set of 0 ... (N-1) argument indices; + // - VIndex is the subset of argument indices with vectorized parameters, letting us access + // vectorized arguments (anything not in this sequence is passed through) + // - BIndex is a incremental sequence (beginning at 0) of the same size as VIndex, so that + // we can store vectorized buffer_infos in an array (argument VIndex has its buffer at + // index BIndex in the array). + template object run( + typename vectorize_arg::type &...args, + index_sequence i_seq, index_sequence vi_seq, index_sequence bi_seq) { + + // Pointers to values the function was called with; the vectorized ones set here will start + // out as array_t pointers, but they will be changed them to T pointers before we make + // call the wrapped function. Non-vectorized pointers are left as-is. + std::array params{{ &args... }}; + + // The array of `buffer_info`s of vectorized arguments: + std::array buffers{{ reinterpret_cast(params[VIndex])->request()... }}; + + /* Determine dimensions parameters of output array */ + ssize_t nd = 0; + std::vector shape(0); + auto trivial = broadcast(buffers, nd, shape); + size_t ndim = (size_t) nd; + + size_t size = std::accumulate(shape.begin(), shape.end(), (size_t) 1, std::multiplies()); + + // If all arguments are 0-dimension arrays (i.e. single values) return a plain value (i.e. + // not wrapped in an array). + if (size == 1 && ndim == 0) { + PYBIND11_EXPAND_SIDE_EFFECTS(params[VIndex] = buffers[BIndex].ptr); + return cast(f(*reinterpret_cast *>(params[Index])...)); + } + + array_t result; + if (trivial == broadcast_trivial::f_trivial) result = array_t(shape); + else result = array_t(shape); + + if (size == 0) return result; + + /* Call the function */ + if (trivial == broadcast_trivial::non_trivial) + apply_broadcast(buffers, params, result, i_seq, vi_seq, bi_seq); + else + apply_trivial(buffers, params, result.mutable_data(), size, i_seq, vi_seq, bi_seq); + + return result; + } + + template + void apply_trivial(std::array &buffers, + std::array ¶ms, + Return *out, + size_t size, + index_sequence, index_sequence, index_sequence) { + + // Initialize an array of mutable byte references and sizes with references set to the + // appropriate pointer in `params`; as we iterate, we'll increment each pointer by its size + // (except for singletons, which get an increment of 0). + std::array, NVectorized> vecparams{{ + std::pair( + reinterpret_cast(params[VIndex] = buffers[BIndex].ptr), + buffers[BIndex].size == 1 ? 0 : sizeof(param_n_t) + )... + }}; + + for (size_t i = 0; i < size; ++i) { + out[i] = f(*reinterpret_cast *>(params[Index])...); + for (auto &x : vecparams) x.first += x.second; + } + } + + template + void apply_broadcast(std::array &buffers, + std::array ¶ms, + array_t &output_array, + index_sequence, index_sequence, index_sequence) { + + buffer_info output = output_array.request(); + multi_array_iterator input_iter(buffers, output.shape); + + for (array_iterator iter = array_begin(output), end = array_end(output); + iter != end; + ++iter, ++input_iter) { + PYBIND11_EXPAND_SIDE_EFFECTS(( + params[VIndex] = input_iter.template data() + )); + *iter = f(*reinterpret_cast *>(std::get(params))...); + } + } +}; + +template +vectorize_helper +vectorize_extractor(const Func &f, Return (*) (Args ...)) { + return detail::vectorize_helper(f); +} + +template struct handle_type_name> { + static constexpr auto name = _("numpy.ndarray[") + npy_format_descriptor::name + _("]"); +}; + +NAMESPACE_END(detail) + +// Vanilla pointer vectorizer: +template +detail::vectorize_helper +vectorize(Return (*f) (Args ...)) { + return detail::vectorize_helper(f); +} + +// lambda vectorizer: +template ::value, int> = 0> +auto vectorize(Func &&f) -> decltype( + detail::vectorize_extractor(std::forward(f), (detail::function_signature_t *) nullptr)) { + return detail::vectorize_extractor(std::forward(f), (detail::function_signature_t *) nullptr); +} + +// Vectorize a class method (non-const): +template ())), Return, Class *, Args...>> +Helper vectorize(Return (Class::*f)(Args...)) { + return Helper(std::mem_fn(f)); +} + +// Vectorize a class method (const): +template ())), Return, const Class *, Args...>> +Helper vectorize(Return (Class::*f)(Args...) const) { + return Helper(std::mem_fn(f)); +} + +NAMESPACE_END(PYBIND11_NAMESPACE) + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif diff --git a/ptocr/postprocess/dbprocess/include/pybind11/operators.h b/ptocr/postprocess/dbprocess/include/pybind11/operators.h new file mode 100644 index 0000000..b3dd62c --- /dev/null +++ b/ptocr/postprocess/dbprocess/include/pybind11/operators.h @@ -0,0 +1,168 @@ +/* + pybind11/operator.h: Metatemplates for operator overloading + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "pybind11.h" + +#if defined(__clang__) && !defined(__INTEL_COMPILER) +# pragma clang diagnostic ignored "-Wunsequenced" // multiple unsequenced modifications to 'self' (when using def(py::self OP Type())) +#elif defined(_MSC_VER) +# pragma warning(push) +# pragma warning(disable: 4127) // warning C4127: Conditional expression is constant +#endif + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +NAMESPACE_BEGIN(detail) + +/// Enumeration with all supported operator types +enum op_id : int { + op_add, op_sub, op_mul, op_div, op_mod, op_divmod, op_pow, op_lshift, + op_rshift, op_and, op_xor, op_or, op_neg, op_pos, op_abs, op_invert, + op_int, op_long, op_float, op_str, op_cmp, op_gt, op_ge, op_lt, op_le, + op_eq, op_ne, op_iadd, op_isub, op_imul, op_idiv, op_imod, op_ilshift, + op_irshift, op_iand, op_ixor, op_ior, op_complex, op_bool, op_nonzero, + op_repr, op_truediv, op_itruediv, op_hash +}; + +enum op_type : int { + op_l, /* base type on left */ + op_r, /* base type on right */ + op_u /* unary operator */ +}; + +struct self_t { }; +static const self_t self = self_t(); + +/// Type for an unused type slot +struct undefined_t { }; + +/// Don't warn about an unused variable +inline self_t __self() { return self; } + +/// base template of operator implementations +template struct op_impl { }; + +/// Operator implementation generator +template struct op_ { + template void execute(Class &cl, const Extra&... extra) const { + using Base = typename Class::type; + using L_type = conditional_t::value, Base, L>; + using R_type = conditional_t::value, Base, R>; + using op = op_impl; + cl.def(op::name(), &op::execute, is_operator(), extra...); + #if PY_MAJOR_VERSION < 3 + if (id == op_truediv || id == op_itruediv) + cl.def(id == op_itruediv ? "__idiv__" : ot == op_l ? "__div__" : "__rdiv__", + &op::execute, is_operator(), extra...); + #endif + } + template void execute_cast(Class &cl, const Extra&... extra) const { + using Base = typename Class::type; + using L_type = conditional_t::value, Base, L>; + using R_type = conditional_t::value, Base, R>; + using op = op_impl; + cl.def(op::name(), &op::execute_cast, is_operator(), extra...); + #if PY_MAJOR_VERSION < 3 + if (id == op_truediv || id == op_itruediv) + cl.def(id == op_itruediv ? "__idiv__" : ot == op_l ? "__div__" : "__rdiv__", + &op::execute, is_operator(), extra...); + #endif + } +}; + +#define PYBIND11_BINARY_OPERATOR(id, rid, op, expr) \ +template struct op_impl { \ + static char const* name() { return "__" #id "__"; } \ + static auto execute(const L &l, const R &r) -> decltype(expr) { return (expr); } \ + static B execute_cast(const L &l, const R &r) { return B(expr); } \ +}; \ +template struct op_impl { \ + static char const* name() { return "__" #rid "__"; } \ + static auto execute(const R &r, const L &l) -> decltype(expr) { return (expr); } \ + static B execute_cast(const R &r, const L &l) { return B(expr); } \ +}; \ +inline op_ op(const self_t &, const self_t &) { \ + return op_(); \ +} \ +template op_ op(const self_t &, const T &) { \ + return op_(); \ +} \ +template op_ op(const T &, const self_t &) { \ + return op_(); \ +} + +#define PYBIND11_INPLACE_OPERATOR(id, op, expr) \ +template struct op_impl { \ + static char const* name() { return "__" #id "__"; } \ + static auto execute(L &l, const R &r) -> decltype(expr) { return expr; } \ + static B execute_cast(L &l, const R &r) { return B(expr); } \ +}; \ +template op_ op(const self_t &, const T &) { \ + return op_(); \ +} + +#define PYBIND11_UNARY_OPERATOR(id, op, expr) \ +template struct op_impl { \ + static char const* name() { return "__" #id "__"; } \ + static auto execute(const L &l) -> decltype(expr) { return expr; } \ + static B execute_cast(const L &l) { return B(expr); } \ +}; \ +inline op_ op(const self_t &) { \ + return op_(); \ +} + +PYBIND11_BINARY_OPERATOR(sub, rsub, operator-, l - r) +PYBIND11_BINARY_OPERATOR(add, radd, operator+, l + r) +PYBIND11_BINARY_OPERATOR(mul, rmul, operator*, l * r) +PYBIND11_BINARY_OPERATOR(truediv, rtruediv, operator/, l / r) +PYBIND11_BINARY_OPERATOR(mod, rmod, operator%, l % r) +PYBIND11_BINARY_OPERATOR(lshift, rlshift, operator<<, l << r) +PYBIND11_BINARY_OPERATOR(rshift, rrshift, operator>>, l >> r) +PYBIND11_BINARY_OPERATOR(and, rand, operator&, l & r) +PYBIND11_BINARY_OPERATOR(xor, rxor, operator^, l ^ r) +PYBIND11_BINARY_OPERATOR(eq, eq, operator==, l == r) +PYBIND11_BINARY_OPERATOR(ne, ne, operator!=, l != r) +PYBIND11_BINARY_OPERATOR(or, ror, operator|, l | r) +PYBIND11_BINARY_OPERATOR(gt, lt, operator>, l > r) +PYBIND11_BINARY_OPERATOR(ge, le, operator>=, l >= r) +PYBIND11_BINARY_OPERATOR(lt, gt, operator<, l < r) +PYBIND11_BINARY_OPERATOR(le, ge, operator<=, l <= r) +//PYBIND11_BINARY_OPERATOR(pow, rpow, pow, std::pow(l, r)) +PYBIND11_INPLACE_OPERATOR(iadd, operator+=, l += r) +PYBIND11_INPLACE_OPERATOR(isub, operator-=, l -= r) +PYBIND11_INPLACE_OPERATOR(imul, operator*=, l *= r) +PYBIND11_INPLACE_OPERATOR(itruediv, operator/=, l /= r) +PYBIND11_INPLACE_OPERATOR(imod, operator%=, l %= r) +PYBIND11_INPLACE_OPERATOR(ilshift, operator<<=, l <<= r) +PYBIND11_INPLACE_OPERATOR(irshift, operator>>=, l >>= r) +PYBIND11_INPLACE_OPERATOR(iand, operator&=, l &= r) +PYBIND11_INPLACE_OPERATOR(ixor, operator^=, l ^= r) +PYBIND11_INPLACE_OPERATOR(ior, operator|=, l |= r) +PYBIND11_UNARY_OPERATOR(neg, operator-, -l) +PYBIND11_UNARY_OPERATOR(pos, operator+, +l) +PYBIND11_UNARY_OPERATOR(abs, abs, std::abs(l)) +PYBIND11_UNARY_OPERATOR(hash, hash, std::hash()(l)) +PYBIND11_UNARY_OPERATOR(invert, operator~, (~l)) +PYBIND11_UNARY_OPERATOR(bool, operator!, !!l) +PYBIND11_UNARY_OPERATOR(int, int_, (int) l) +PYBIND11_UNARY_OPERATOR(float, float_, (double) l) + +#undef PYBIND11_BINARY_OPERATOR +#undef PYBIND11_INPLACE_OPERATOR +#undef PYBIND11_UNARY_OPERATOR +NAMESPACE_END(detail) + +using detail::self; + +NAMESPACE_END(PYBIND11_NAMESPACE) + +#if defined(_MSC_VER) +# pragma warning(pop) +#endif diff --git a/ptocr/postprocess/dbprocess/include/pybind11/options.h b/ptocr/postprocess/dbprocess/include/pybind11/options.h new file mode 100644 index 0000000..cc1e1f6 --- /dev/null +++ b/ptocr/postprocess/dbprocess/include/pybind11/options.h @@ -0,0 +1,65 @@ +/* + pybind11/options.h: global settings that are configurable at runtime. + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "detail/common.h" + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) + +class options { +public: + + // Default RAII constructor, which leaves settings as they currently are. + options() : previous_state(global_state()) {} + + // Class is non-copyable. + options(const options&) = delete; + options& operator=(const options&) = delete; + + // Destructor, which restores settings that were in effect before. + ~options() { + global_state() = previous_state; + } + + // Setter methods (affect the global state): + + options& disable_user_defined_docstrings() & { global_state().show_user_defined_docstrings = false; return *this; } + + options& enable_user_defined_docstrings() & { global_state().show_user_defined_docstrings = true; return *this; } + + options& disable_function_signatures() & { global_state().show_function_signatures = false; return *this; } + + options& enable_function_signatures() & { global_state().show_function_signatures = true; return *this; } + + // Getter methods (return the global state): + + static bool show_user_defined_docstrings() { return global_state().show_user_defined_docstrings; } + + static bool show_function_signatures() { return global_state().show_function_signatures; } + + // This type is not meant to be allocated on the heap. + void* operator new(size_t) = delete; + +private: + + struct state { + bool show_user_defined_docstrings = true; //< Include user-supplied texts in docstrings. + bool show_function_signatures = true; //< Include auto-generated function signatures in docstrings. + }; + + static state &global_state() { + static state instance; + return instance; + } + + state previous_state; +}; + +NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/ptocr/postprocess/dbprocess/include/pybind11/pybind11.h b/ptocr/postprocess/dbprocess/include/pybind11/pybind11.h new file mode 100644 index 0000000..7fa0f0e --- /dev/null +++ b/ptocr/postprocess/dbprocess/include/pybind11/pybind11.h @@ -0,0 +1,2094 @@ +/* + pybind11/pybind11.h: Main header file of the C++11 python + binding generator library + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#if defined(__INTEL_COMPILER) +# pragma warning push +# pragma warning disable 68 // integer conversion resulted in a change of sign +# pragma warning disable 186 // pointless comparison of unsigned integer with zero +# pragma warning disable 878 // incompatible exception specifications +# pragma warning disable 1334 // the "template" keyword used for syntactic disambiguation may only be used within a template +# pragma warning disable 1682 // implicit conversion of a 64-bit integral type to a smaller integral type (potential portability problem) +# pragma warning disable 1786 // function "strdup" was declared deprecated +# pragma warning disable 1875 // offsetof applied to non-POD (Plain Old Data) types is nonstandard +# pragma warning disable 2196 // warning #2196: routine is both "inline" and "noinline" +#elif defined(_MSC_VER) +# pragma warning(push) +# pragma warning(disable: 4100) // warning C4100: Unreferenced formal parameter +# pragma warning(disable: 4127) // warning C4127: Conditional expression is constant +# pragma warning(disable: 4512) // warning C4512: Assignment operator was implicitly defined as deleted +# pragma warning(disable: 4800) // warning C4800: 'int': forcing value to bool 'true' or 'false' (performance warning) +# pragma warning(disable: 4996) // warning C4996: The POSIX name for this item is deprecated. Instead, use the ISO C and C++ conformant name +# pragma warning(disable: 4702) // warning C4702: unreachable code +# pragma warning(disable: 4522) // warning C4522: multiple assignment operators specified +#elif defined(__GNUG__) && !defined(__clang__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wunused-but-set-parameter" +# pragma GCC diagnostic ignored "-Wunused-but-set-variable" +# pragma GCC diagnostic ignored "-Wmissing-field-initializers" +# pragma GCC diagnostic ignored "-Wstrict-aliasing" +# pragma GCC diagnostic ignored "-Wattributes" +# if __GNUC__ >= 7 +# pragma GCC diagnostic ignored "-Wnoexcept-type" +# endif +#endif + +#include "attr.h" +#include "options.h" +#include "detail/class.h" +#include "detail/init.h" + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) + +/// Wraps an arbitrary C++ function/method/lambda function/.. into a callable Python object +class cpp_function : public function { +public: + cpp_function() { } + cpp_function(std::nullptr_t) { } + + /// Construct a cpp_function from a vanilla function pointer + template + cpp_function(Return (*f)(Args...), const Extra&... extra) { + initialize(f, f, extra...); + } + + /// Construct a cpp_function from a lambda function (possibly with internal state) + template ::value>> + cpp_function(Func &&f, const Extra&... extra) { + initialize(std::forward(f), + (detail::function_signature_t *) nullptr, extra...); + } + + /// Construct a cpp_function from a class method (non-const) + template + cpp_function(Return (Class::*f)(Arg...), const Extra&... extra) { + initialize([f](Class *c, Arg... args) -> Return { return (c->*f)(args...); }, + (Return (*) (Class *, Arg...)) nullptr, extra...); + } + + /// Construct a cpp_function from a class method (const) + template + cpp_function(Return (Class::*f)(Arg...) const, const Extra&... extra) { + initialize([f](const Class *c, Arg... args) -> Return { return (c->*f)(args...); }, + (Return (*)(const Class *, Arg ...)) nullptr, extra...); + } + + /// Return the function name + object name() const { return attr("__name__"); } + +protected: + /// Space optimization: don't inline this frequently instantiated fragment + PYBIND11_NOINLINE detail::function_record *make_function_record() { + return new detail::function_record(); + } + + /// Special internal constructor for functors, lambda functions, etc. + template + void initialize(Func &&f, Return (*)(Args...), const Extra&... extra) { + using namespace detail; + struct capture { remove_reference_t f; }; + + /* Store the function including any extra state it might have (e.g. a lambda capture object) */ + auto rec = make_function_record(); + + /* Store the capture object directly in the function record if there is enough space */ + if (sizeof(capture) <= sizeof(rec->data)) { + /* Without these pragmas, GCC warns that there might not be + enough space to use the placement new operator. However, the + 'if' statement above ensures that this is the case. */ +#if defined(__GNUG__) && !defined(__clang__) && __GNUC__ >= 6 +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wplacement-new" +#endif + new ((capture *) &rec->data) capture { std::forward(f) }; +#if defined(__GNUG__) && !defined(__clang__) && __GNUC__ >= 6 +# pragma GCC diagnostic pop +#endif + if (!std::is_trivially_destructible::value) + rec->free_data = [](function_record *r) { ((capture *) &r->data)->~capture(); }; + } else { + rec->data[0] = new capture { std::forward(f) }; + rec->free_data = [](function_record *r) { delete ((capture *) r->data[0]); }; + } + + /* Type casters for the function arguments and return value */ + using cast_in = argument_loader; + using cast_out = make_caster< + conditional_t::value, void_type, Return> + >; + + static_assert(expected_num_args(sizeof...(Args), cast_in::has_args, cast_in::has_kwargs), + "The number of argument annotations does not match the number of function arguments"); + + /* Dispatch code which converts function arguments and performs the actual function call */ + rec->impl = [](function_call &call) -> handle { + cast_in args_converter; + + /* Try to cast the function arguments into the C++ domain */ + if (!args_converter.load_args(call)) + return PYBIND11_TRY_NEXT_OVERLOAD; + + /* Invoke call policy pre-call hook */ + process_attributes::precall(call); + + /* Get a pointer to the capture object */ + auto data = (sizeof(capture) <= sizeof(call.func.data) + ? &call.func.data : call.func.data[0]); + capture *cap = const_cast(reinterpret_cast(data)); + + /* Override policy for rvalues -- usually to enforce rvp::move on an rvalue */ + return_value_policy policy = return_value_policy_override::policy(call.func.policy); + + /* Function scope guard -- defaults to the compile-to-nothing `void_type` */ + using Guard = extract_guard_t; + + /* Perform the function call */ + handle result = cast_out::cast( + std::move(args_converter).template call(cap->f), policy, call.parent); + + /* Invoke call policy post-call hook */ + process_attributes::postcall(call, result); + + return result; + }; + + /* Process any user-provided function attributes */ + process_attributes::init(extra..., rec); + + /* Generate a readable signature describing the function's arguments and return value types */ + static constexpr auto signature = _("(") + cast_in::arg_names + _(") -> ") + cast_out::name; + PYBIND11_DESCR_CONSTEXPR auto types = decltype(signature)::types(); + + /* Register the function with Python from generic (non-templated) code */ + initialize_generic(rec, signature.text, types.data(), sizeof...(Args)); + + if (cast_in::has_args) rec->has_args = true; + if (cast_in::has_kwargs) rec->has_kwargs = true; + + /* Stash some additional information used by an important optimization in 'functional.h' */ + using FunctionType = Return (*)(Args...); + constexpr bool is_function_ptr = + std::is_convertible::value && + sizeof(capture) == sizeof(void *); + if (is_function_ptr) { + rec->is_stateless = true; + rec->data[1] = const_cast(reinterpret_cast(&typeid(FunctionType))); + } + } + + /// Register a function call with Python (generic non-templated code goes here) + void initialize_generic(detail::function_record *rec, const char *text, + const std::type_info *const *types, size_t args) { + + /* Create copies of all referenced C-style strings */ + rec->name = strdup(rec->name ? rec->name : ""); + if (rec->doc) rec->doc = strdup(rec->doc); + for (auto &a: rec->args) { + if (a.name) + a.name = strdup(a.name); + if (a.descr) + a.descr = strdup(a.descr); + else if (a.value) + a.descr = strdup(a.value.attr("__repr__")().cast().c_str()); + } + + rec->is_constructor = !strcmp(rec->name, "__init__") || !strcmp(rec->name, "__setstate__"); + +#if !defined(NDEBUG) && !defined(PYBIND11_DISABLE_NEW_STYLE_INIT_WARNING) + if (rec->is_constructor && !rec->is_new_style_constructor) { + const auto class_name = std::string(((PyTypeObject *) rec->scope.ptr())->tp_name); + const auto func_name = std::string(rec->name); + PyErr_WarnEx( + PyExc_FutureWarning, + ("pybind11-bound class '" + class_name + "' is using an old-style " + "placement-new '" + func_name + "' which has been deprecated. See " + "the upgrade guide in pybind11's docs. This message is only visible " + "when compiled in debug mode.").c_str(), 0 + ); + } +#endif + + /* Generate a proper function signature */ + std::string signature; + size_t type_index = 0, arg_index = 0; + for (auto *pc = text; *pc != '\0'; ++pc) { + const auto c = *pc; + + if (c == '{') { + // Write arg name for everything except *args and **kwargs. + if (*(pc + 1) == '*') + continue; + + if (arg_index < rec->args.size() && rec->args[arg_index].name) { + signature += rec->args[arg_index].name; + } else if (arg_index == 0 && rec->is_method) { + signature += "self"; + } else { + signature += "arg" + std::to_string(arg_index - (rec->is_method ? 1 : 0)); + } + signature += ": "; + } else if (c == '}') { + // Write default value if available. + if (arg_index < rec->args.size() && rec->args[arg_index].descr) { + signature += " = "; + signature += rec->args[arg_index].descr; + } + arg_index++; + } else if (c == '%') { + const std::type_info *t = types[type_index++]; + if (!t) + pybind11_fail("Internal error while parsing type signature (1)"); + if (auto tinfo = detail::get_type_info(*t)) { + handle th((PyObject *) tinfo->type); + signature += + th.attr("__module__").cast() + "." + + th.attr("__qualname__").cast(); // Python 3.3+, but we backport it to earlier versions + } else if (rec->is_new_style_constructor && arg_index == 0) { + // A new-style `__init__` takes `self` as `value_and_holder`. + // Rewrite it to the proper class type. + signature += + rec->scope.attr("__module__").cast() + "." + + rec->scope.attr("__qualname__").cast(); + } else { + std::string tname(t->name()); + detail::clean_type_id(tname); + signature += tname; + } + } else { + signature += c; + } + } + if (arg_index != args || types[type_index] != nullptr) + pybind11_fail("Internal error while parsing type signature (2)"); + +#if PY_MAJOR_VERSION < 3 + if (strcmp(rec->name, "__next__") == 0) { + std::free(rec->name); + rec->name = strdup("next"); + } else if (strcmp(rec->name, "__bool__") == 0) { + std::free(rec->name); + rec->name = strdup("__nonzero__"); + } +#endif + rec->signature = strdup(signature.c_str()); + rec->args.shrink_to_fit(); + rec->nargs = (std::uint16_t) args; + + if (rec->sibling && PYBIND11_INSTANCE_METHOD_CHECK(rec->sibling.ptr())) + rec->sibling = PYBIND11_INSTANCE_METHOD_GET_FUNCTION(rec->sibling.ptr()); + + detail::function_record *chain = nullptr, *chain_start = rec; + if (rec->sibling) { + if (PyCFunction_Check(rec->sibling.ptr())) { + auto rec_capsule = reinterpret_borrow(PyCFunction_GET_SELF(rec->sibling.ptr())); + chain = (detail::function_record *) rec_capsule; + /* Never append a method to an overload chain of a parent class; + instead, hide the parent's overloads in this case */ + if (!chain->scope.is(rec->scope)) + chain = nullptr; + } + // Don't trigger for things like the default __init__, which are wrapper_descriptors that we are intentionally replacing + else if (!rec->sibling.is_none() && rec->name[0] != '_') + pybind11_fail("Cannot overload existing non-function object \"" + std::string(rec->name) + + "\" with a function of the same name"); + } + + if (!chain) { + /* No existing overload was found, create a new function object */ + rec->def = new PyMethodDef(); + std::memset(rec->def, 0, sizeof(PyMethodDef)); + rec->def->ml_name = rec->name; + rec->def->ml_meth = reinterpret_cast(reinterpret_cast(*dispatcher)); + rec->def->ml_flags = METH_VARARGS | METH_KEYWORDS; + + capsule rec_capsule(rec, [](void *ptr) { + destruct((detail::function_record *) ptr); + }); + + object scope_module; + if (rec->scope) { + if (hasattr(rec->scope, "__module__")) { + scope_module = rec->scope.attr("__module__"); + } else if (hasattr(rec->scope, "__name__")) { + scope_module = rec->scope.attr("__name__"); + } + } + + m_ptr = PyCFunction_NewEx(rec->def, rec_capsule.ptr(), scope_module.ptr()); + if (!m_ptr) + pybind11_fail("cpp_function::cpp_function(): Could not allocate function object"); + } else { + /* Append at the end of the overload chain */ + m_ptr = rec->sibling.ptr(); + inc_ref(); + chain_start = chain; + if (chain->is_method != rec->is_method) + pybind11_fail("overloading a method with both static and instance methods is not supported; " + #if defined(NDEBUG) + "compile in debug mode for more details" + #else + "error while attempting to bind " + std::string(rec->is_method ? "instance" : "static") + " method " + + std::string(pybind11::str(rec->scope.attr("__name__"))) + "." + std::string(rec->name) + signature + #endif + ); + while (chain->next) + chain = chain->next; + chain->next = rec; + } + + std::string signatures; + int index = 0; + /* Create a nice pydoc rec including all signatures and + docstrings of the functions in the overload chain */ + if (chain && options::show_function_signatures()) { + // First a generic signature + signatures += rec->name; + signatures += "(*args, **kwargs)\n"; + signatures += "Overloaded function.\n\n"; + } + // Then specific overload signatures + bool first_user_def = true; + for (auto it = chain_start; it != nullptr; it = it->next) { + if (options::show_function_signatures()) { + if (index > 0) signatures += "\n"; + if (chain) + signatures += std::to_string(++index) + ". "; + signatures += rec->name; + signatures += it->signature; + signatures += "\n"; + } + if (it->doc && strlen(it->doc) > 0 && options::show_user_defined_docstrings()) { + // If we're appending another docstring, and aren't printing function signatures, we + // need to append a newline first: + if (!options::show_function_signatures()) { + if (first_user_def) first_user_def = false; + else signatures += "\n"; + } + if (options::show_function_signatures()) signatures += "\n"; + signatures += it->doc; + if (options::show_function_signatures()) signatures += "\n"; + } + } + + /* Install docstring */ + PyCFunctionObject *func = (PyCFunctionObject *) m_ptr; + if (func->m_ml->ml_doc) + std::free(const_cast(func->m_ml->ml_doc)); + func->m_ml->ml_doc = strdup(signatures.c_str()); + + if (rec->is_method) { + m_ptr = PYBIND11_INSTANCE_METHOD_NEW(m_ptr, rec->scope.ptr()); + if (!m_ptr) + pybind11_fail("cpp_function::cpp_function(): Could not allocate instance method object"); + Py_DECREF(func); + } + } + + /// When a cpp_function is GCed, release any memory allocated by pybind11 + static void destruct(detail::function_record *rec) { + while (rec) { + detail::function_record *next = rec->next; + if (rec->free_data) + rec->free_data(rec); + std::free((char *) rec->name); + std::free((char *) rec->doc); + std::free((char *) rec->signature); + for (auto &arg: rec->args) { + std::free(const_cast(arg.name)); + std::free(const_cast(arg.descr)); + arg.value.dec_ref(); + } + if (rec->def) { + std::free(const_cast(rec->def->ml_doc)); + delete rec->def; + } + delete rec; + rec = next; + } + } + + /// Main dispatch logic for calls to functions bound using pybind11 + static PyObject *dispatcher(PyObject *self, PyObject *args_in, PyObject *kwargs_in) { + using namespace detail; + + /* Iterator over the list of potentially admissible overloads */ + const function_record *overloads = (function_record *) PyCapsule_GetPointer(self, nullptr), + *it = overloads; + + /* Need to know how many arguments + keyword arguments there are to pick the right overload */ + const size_t n_args_in = (size_t) PyTuple_GET_SIZE(args_in); + + handle parent = n_args_in > 0 ? PyTuple_GET_ITEM(args_in, 0) : nullptr, + result = PYBIND11_TRY_NEXT_OVERLOAD; + + auto self_value_and_holder = value_and_holder(); + if (overloads->is_constructor) { + const auto tinfo = get_type_info((PyTypeObject *) overloads->scope.ptr()); + const auto pi = reinterpret_cast(parent.ptr()); + self_value_and_holder = pi->get_value_and_holder(tinfo, false); + + if (!self_value_and_holder.type || !self_value_and_holder.inst) { + PyErr_SetString(PyExc_TypeError, "__init__(self, ...) called with invalid `self` argument"); + return nullptr; + } + + // If this value is already registered it must mean __init__ is invoked multiple times; + // we really can't support that in C++, so just ignore the second __init__. + if (self_value_and_holder.instance_registered()) + return none().release().ptr(); + } + + try { + // We do this in two passes: in the first pass, we load arguments with `convert=false`; + // in the second, we allow conversion (except for arguments with an explicit + // py::arg().noconvert()). This lets us prefer calls without conversion, with + // conversion as a fallback. + std::vector second_pass; + + // However, if there are no overloads, we can just skip the no-convert pass entirely + const bool overloaded = it != nullptr && it->next != nullptr; + + for (; it != nullptr; it = it->next) { + + /* For each overload: + 1. Copy all positional arguments we were given, also checking to make sure that + named positional arguments weren't *also* specified via kwarg. + 2. If we weren't given enough, try to make up the omitted ones by checking + whether they were provided by a kwarg matching the `py::arg("name")` name. If + so, use it (and remove it from kwargs; if not, see if the function binding + provided a default that we can use. + 3. Ensure that either all keyword arguments were "consumed", or that the function + takes a kwargs argument to accept unconsumed kwargs. + 4. Any positional arguments still left get put into a tuple (for args), and any + leftover kwargs get put into a dict. + 5. Pack everything into a vector; if we have py::args or py::kwargs, they are an + extra tuple or dict at the end of the positional arguments. + 6. Call the function call dispatcher (function_record::impl) + + If one of these fail, move on to the next overload and keep trying until we get a + result other than PYBIND11_TRY_NEXT_OVERLOAD. + */ + + const function_record &func = *it; + size_t pos_args = func.nargs; // Number of positional arguments that we need + if (func.has_args) --pos_args; // (but don't count py::args + if (func.has_kwargs) --pos_args; // or py::kwargs) + + if (!func.has_args && n_args_in > pos_args) + continue; // Too many arguments for this overload + + if (n_args_in < pos_args && func.args.size() < pos_args) + continue; // Not enough arguments given, and not enough defaults to fill in the blanks + + function_call call(func, parent); + + size_t args_to_copy = std::min(pos_args, n_args_in); + size_t args_copied = 0; + + // 0. Inject new-style `self` argument + if (func.is_new_style_constructor) { + // The `value` may have been preallocated by an old-style `__init__` + // if it was a preceding candidate for overload resolution. + if (self_value_and_holder) + self_value_and_holder.type->dealloc(self_value_and_holder); + + call.init_self = PyTuple_GET_ITEM(args_in, 0); + call.args.push_back(reinterpret_cast(&self_value_and_holder)); + call.args_convert.push_back(false); + ++args_copied; + } + + // 1. Copy any position arguments given. + bool bad_arg = false; + for (; args_copied < args_to_copy; ++args_copied) { + const argument_record *arg_rec = args_copied < func.args.size() ? &func.args[args_copied] : nullptr; + if (kwargs_in && arg_rec && arg_rec->name && PyDict_GetItemString(kwargs_in, arg_rec->name)) { + bad_arg = true; + break; + } + + handle arg(PyTuple_GET_ITEM(args_in, args_copied)); + if (arg_rec && !arg_rec->none && arg.is_none()) { + bad_arg = true; + break; + } + call.args.push_back(arg); + call.args_convert.push_back(arg_rec ? arg_rec->convert : true); + } + if (bad_arg) + continue; // Maybe it was meant for another overload (issue #688) + + // We'll need to copy this if we steal some kwargs for defaults + dict kwargs = reinterpret_borrow(kwargs_in); + + // 2. Check kwargs and, failing that, defaults that may help complete the list + if (args_copied < pos_args) { + bool copied_kwargs = false; + + for (; args_copied < pos_args; ++args_copied) { + const auto &arg = func.args[args_copied]; + + handle value; + if (kwargs_in && arg.name) + value = PyDict_GetItemString(kwargs.ptr(), arg.name); + + if (value) { + // Consume a kwargs value + if (!copied_kwargs) { + kwargs = reinterpret_steal(PyDict_Copy(kwargs.ptr())); + copied_kwargs = true; + } + PyDict_DelItemString(kwargs.ptr(), arg.name); + } else if (arg.value) { + value = arg.value; + } + + if (value) { + call.args.push_back(value); + call.args_convert.push_back(arg.convert); + } + else + break; + } + + if (args_copied < pos_args) + continue; // Not enough arguments, defaults, or kwargs to fill the positional arguments + } + + // 3. Check everything was consumed (unless we have a kwargs arg) + if (kwargs && kwargs.size() > 0 && !func.has_kwargs) + continue; // Unconsumed kwargs, but no py::kwargs argument to accept them + + // 4a. If we have a py::args argument, create a new tuple with leftovers + if (func.has_args) { + tuple extra_args; + if (args_to_copy == 0) { + // We didn't copy out any position arguments from the args_in tuple, so we + // can reuse it directly without copying: + extra_args = reinterpret_borrow(args_in); + } else if (args_copied >= n_args_in) { + extra_args = tuple(0); + } else { + size_t args_size = n_args_in - args_copied; + extra_args = tuple(args_size); + for (size_t i = 0; i < args_size; ++i) { + extra_args[i] = PyTuple_GET_ITEM(args_in, args_copied + i); + } + } + call.args.push_back(extra_args); + call.args_convert.push_back(false); + call.args_ref = std::move(extra_args); + } + + // 4b. If we have a py::kwargs, pass on any remaining kwargs + if (func.has_kwargs) { + if (!kwargs.ptr()) + kwargs = dict(); // If we didn't get one, send an empty one + call.args.push_back(kwargs); + call.args_convert.push_back(false); + call.kwargs_ref = std::move(kwargs); + } + + // 5. Put everything in a vector. Not technically step 5, we've been building it + // in `call.args` all along. + #if !defined(NDEBUG) + if (call.args.size() != func.nargs || call.args_convert.size() != func.nargs) + pybind11_fail("Internal error: function call dispatcher inserted wrong number of arguments!"); + #endif + + std::vector second_pass_convert; + if (overloaded) { + // We're in the first no-convert pass, so swap out the conversion flags for a + // set of all-false flags. If the call fails, we'll swap the flags back in for + // the conversion-allowed call below. + second_pass_convert.resize(func.nargs, false); + call.args_convert.swap(second_pass_convert); + } + + // 6. Call the function. + try { + loader_life_support guard{}; + result = func.impl(call); + } catch (reference_cast_error &) { + result = PYBIND11_TRY_NEXT_OVERLOAD; + } + + if (result.ptr() != PYBIND11_TRY_NEXT_OVERLOAD) + break; + + if (overloaded) { + // The (overloaded) call failed; if the call has at least one argument that + // permits conversion (i.e. it hasn't been explicitly specified `.noconvert()`) + // then add this call to the list of second pass overloads to try. + for (size_t i = func.is_method ? 1 : 0; i < pos_args; i++) { + if (second_pass_convert[i]) { + // Found one: swap the converting flags back in and store the call for + // the second pass. + call.args_convert.swap(second_pass_convert); + second_pass.push_back(std::move(call)); + break; + } + } + } + } + + if (overloaded && !second_pass.empty() && result.ptr() == PYBIND11_TRY_NEXT_OVERLOAD) { + // The no-conversion pass finished without success, try again with conversion allowed + for (auto &call : second_pass) { + try { + loader_life_support guard{}; + result = call.func.impl(call); + } catch (reference_cast_error &) { + result = PYBIND11_TRY_NEXT_OVERLOAD; + } + + if (result.ptr() != PYBIND11_TRY_NEXT_OVERLOAD) { + // The error reporting logic below expects 'it' to be valid, as it would be + // if we'd encountered this failure in the first-pass loop. + if (!result) + it = &call.func; + break; + } + } + } + } catch (error_already_set &e) { + e.restore(); + return nullptr; + } catch (...) { + /* When an exception is caught, give each registered exception + translator a chance to translate it to a Python exception + in reverse order of registration. + + A translator may choose to do one of the following: + + - catch the exception and call PyErr_SetString or PyErr_SetObject + to set a standard (or custom) Python exception, or + - do nothing and let the exception fall through to the next translator, or + - delegate translation to the next translator by throwing a new type of exception. */ + + auto last_exception = std::current_exception(); + auto ®istered_exception_translators = get_internals().registered_exception_translators; + for (auto& translator : registered_exception_translators) { + try { + translator(last_exception); + } catch (...) { + last_exception = std::current_exception(); + continue; + } + return nullptr; + } + PyErr_SetString(PyExc_SystemError, "Exception escaped from default exception translator!"); + return nullptr; + } + + auto append_note_if_missing_header_is_suspected = [](std::string &msg) { + if (msg.find("std::") != std::string::npos) { + msg += "\n\n" + "Did you forget to `#include `? Or ,\n" + ", , etc. Some automatic\n" + "conversions are optional and require extra headers to be included\n" + "when compiling your pybind11 module."; + } + }; + + if (result.ptr() == PYBIND11_TRY_NEXT_OVERLOAD) { + if (overloads->is_operator) + return handle(Py_NotImplemented).inc_ref().ptr(); + + std::string msg = std::string(overloads->name) + "(): incompatible " + + std::string(overloads->is_constructor ? "constructor" : "function") + + " arguments. The following argument types are supported:\n"; + + int ctr = 0; + for (const function_record *it2 = overloads; it2 != nullptr; it2 = it2->next) { + msg += " "+ std::to_string(++ctr) + ". "; + + bool wrote_sig = false; + if (overloads->is_constructor) { + // For a constructor, rewrite `(self: Object, arg0, ...) -> NoneType` as `Object(arg0, ...)` + std::string sig = it2->signature; + size_t start = sig.find('(') + 7; // skip "(self: " + if (start < sig.size()) { + // End at the , for the next argument + size_t end = sig.find(", "), next = end + 2; + size_t ret = sig.rfind(" -> "); + // Or the ), if there is no comma: + if (end >= sig.size()) next = end = sig.find(')'); + if (start < end && next < sig.size()) { + msg.append(sig, start, end - start); + msg += '('; + msg.append(sig, next, ret - next); + wrote_sig = true; + } + } + } + if (!wrote_sig) msg += it2->signature; + + msg += "\n"; + } + msg += "\nInvoked with: "; + auto args_ = reinterpret_borrow(args_in); + bool some_args = false; + for (size_t ti = overloads->is_constructor ? 1 : 0; ti < args_.size(); ++ti) { + if (!some_args) some_args = true; + else msg += ", "; + msg += pybind11::repr(args_[ti]); + } + if (kwargs_in) { + auto kwargs = reinterpret_borrow(kwargs_in); + if (kwargs.size() > 0) { + if (some_args) msg += "; "; + msg += "kwargs: "; + bool first = true; + for (auto kwarg : kwargs) { + if (first) first = false; + else msg += ", "; + msg += pybind11::str("{}={!r}").format(kwarg.first, kwarg.second); + } + } + } + + append_note_if_missing_header_is_suspected(msg); + PyErr_SetString(PyExc_TypeError, msg.c_str()); + return nullptr; + } else if (!result) { + std::string msg = "Unable to convert function return value to a " + "Python type! The signature was\n\t"; + msg += it->signature; + append_note_if_missing_header_is_suspected(msg); + PyErr_SetString(PyExc_TypeError, msg.c_str()); + return nullptr; + } else { + if (overloads->is_constructor && !self_value_and_holder.holder_constructed()) { + auto *pi = reinterpret_cast(parent.ptr()); + self_value_and_holder.type->init_instance(pi, nullptr); + } + return result.ptr(); + } + } +}; + +/// Wrapper for Python extension modules +class module : public object { +public: + PYBIND11_OBJECT_DEFAULT(module, object, PyModule_Check) + + /// Create a new top-level Python module with the given name and docstring + explicit module(const char *name, const char *doc = nullptr) { + if (!options::show_user_defined_docstrings()) doc = nullptr; +#if PY_MAJOR_VERSION >= 3 + PyModuleDef *def = new PyModuleDef(); + std::memset(def, 0, sizeof(PyModuleDef)); + def->m_name = name; + def->m_doc = doc; + def->m_size = -1; + Py_INCREF(def); + m_ptr = PyModule_Create(def); +#else + m_ptr = Py_InitModule3(name, nullptr, doc); +#endif + if (m_ptr == nullptr) + pybind11_fail("Internal error in module::module()"); + inc_ref(); + } + + /** \rst + Create Python binding for a new function within the module scope. ``Func`` + can be a plain C++ function, a function pointer, or a lambda function. For + details on the ``Extra&& ... extra`` argument, see section :ref:`extras`. + \endrst */ + template + module &def(const char *name_, Func &&f, const Extra& ... extra) { + cpp_function func(std::forward(f), name(name_), scope(*this), + sibling(getattr(*this, name_, none())), extra...); + // NB: allow overwriting here because cpp_function sets up a chain with the intention of + // overwriting (and has already checked internally that it isn't overwriting non-functions). + add_object(name_, func, true /* overwrite */); + return *this; + } + + /** \rst + Create and return a new Python submodule with the given name and docstring. + This also works recursively, i.e. + + .. code-block:: cpp + + py::module m("example", "pybind11 example plugin"); + py::module m2 = m.def_submodule("sub", "A submodule of 'example'"); + py::module m3 = m2.def_submodule("subsub", "A submodule of 'example.sub'"); + \endrst */ + module def_submodule(const char *name, const char *doc = nullptr) { + std::string full_name = std::string(PyModule_GetName(m_ptr)) + + std::string(".") + std::string(name); + auto result = reinterpret_borrow(PyImport_AddModule(full_name.c_str())); + if (doc && options::show_user_defined_docstrings()) + result.attr("__doc__") = pybind11::str(doc); + attr(name) = result; + return result; + } + + /// Import and return a module or throws `error_already_set`. + static module import(const char *name) { + PyObject *obj = PyImport_ImportModule(name); + if (!obj) + throw error_already_set(); + return reinterpret_steal(obj); + } + + /// Reload the module or throws `error_already_set`. + void reload() { + PyObject *obj = PyImport_ReloadModule(ptr()); + if (!obj) + throw error_already_set(); + *this = reinterpret_steal(obj); + } + + // Adds an object to the module using the given name. Throws if an object with the given name + // already exists. + // + // overwrite should almost always be false: attempting to overwrite objects that pybind11 has + // established will, in most cases, break things. + PYBIND11_NOINLINE void add_object(const char *name, handle obj, bool overwrite = false) { + if (!overwrite && hasattr(*this, name)) + pybind11_fail("Error during initialization: multiple incompatible definitions with name \"" + + std::string(name) + "\""); + + PyModule_AddObject(ptr(), name, obj.inc_ref().ptr() /* steals a reference */); + } +}; + +/// \ingroup python_builtins +/// Return a dictionary representing the global variables in the current execution frame, +/// or ``__main__.__dict__`` if there is no frame (usually when the interpreter is embedded). +inline dict globals() { + PyObject *p = PyEval_GetGlobals(); + return reinterpret_borrow(p ? p : module::import("__main__").attr("__dict__").ptr()); +} + +NAMESPACE_BEGIN(detail) +/// Generic support for creating new Python heap types +class generic_type : public object { + template friend class class_; +public: + PYBIND11_OBJECT_DEFAULT(generic_type, object, PyType_Check) +protected: + void initialize(const type_record &rec) { + if (rec.scope && hasattr(rec.scope, rec.name)) + pybind11_fail("generic_type: cannot initialize type \"" + std::string(rec.name) + + "\": an object with that name is already defined"); + + if (rec.module_local ? get_local_type_info(*rec.type) : get_global_type_info(*rec.type)) + pybind11_fail("generic_type: type \"" + std::string(rec.name) + + "\" is already registered!"); + + m_ptr = make_new_python_type(rec); + + /* Register supplemental type information in C++ dict */ + auto *tinfo = new detail::type_info(); + tinfo->type = (PyTypeObject *) m_ptr; + tinfo->cpptype = rec.type; + tinfo->type_size = rec.type_size; + tinfo->type_align = rec.type_align; + tinfo->operator_new = rec.operator_new; + tinfo->holder_size_in_ptrs = size_in_ptrs(rec.holder_size); + tinfo->init_instance = rec.init_instance; + tinfo->dealloc = rec.dealloc; + tinfo->simple_type = true; + tinfo->simple_ancestors = true; + tinfo->default_holder = rec.default_holder; + tinfo->module_local = rec.module_local; + + auto &internals = get_internals(); + auto tindex = std::type_index(*rec.type); + tinfo->direct_conversions = &internals.direct_conversions[tindex]; + if (rec.module_local) + registered_local_types_cpp()[tindex] = tinfo; + else + internals.registered_types_cpp[tindex] = tinfo; + internals.registered_types_py[(PyTypeObject *) m_ptr] = { tinfo }; + + if (rec.bases.size() > 1 || rec.multiple_inheritance) { + mark_parents_nonsimple(tinfo->type); + tinfo->simple_ancestors = false; + } + else if (rec.bases.size() == 1) { + auto parent_tinfo = get_type_info((PyTypeObject *) rec.bases[0].ptr()); + tinfo->simple_ancestors = parent_tinfo->simple_ancestors; + } + + if (rec.module_local) { + // Stash the local typeinfo and loader so that external modules can access it. + tinfo->module_local_load = &type_caster_generic::local_load; + setattr(m_ptr, PYBIND11_MODULE_LOCAL_ID, capsule(tinfo)); + } + } + + /// Helper function which tags all parents of a type using mult. inheritance + void mark_parents_nonsimple(PyTypeObject *value) { + auto t = reinterpret_borrow(value->tp_bases); + for (handle h : t) { + auto tinfo2 = get_type_info((PyTypeObject *) h.ptr()); + if (tinfo2) + tinfo2->simple_type = false; + mark_parents_nonsimple((PyTypeObject *) h.ptr()); + } + } + + void install_buffer_funcs( + buffer_info *(*get_buffer)(PyObject *, void *), + void *get_buffer_data) { + PyHeapTypeObject *type = (PyHeapTypeObject*) m_ptr; + auto tinfo = detail::get_type_info(&type->ht_type); + + if (!type->ht_type.tp_as_buffer) + pybind11_fail( + "To be able to register buffer protocol support for the type '" + + std::string(tinfo->type->tp_name) + + "' the associated class<>(..) invocation must " + "include the pybind11::buffer_protocol() annotation!"); + + tinfo->get_buffer = get_buffer; + tinfo->get_buffer_data = get_buffer_data; + } + + // rec_func must be set for either fget or fset. + void def_property_static_impl(const char *name, + handle fget, handle fset, + detail::function_record *rec_func) { + const auto is_static = rec_func && !(rec_func->is_method && rec_func->scope); + const auto has_doc = rec_func && rec_func->doc && pybind11::options::show_user_defined_docstrings(); + auto property = handle((PyObject *) (is_static ? get_internals().static_property_type + : &PyProperty_Type)); + attr(name) = property(fget.ptr() ? fget : none(), + fset.ptr() ? fset : none(), + /*deleter*/none(), + pybind11::str(has_doc ? rec_func->doc : "")); + } +}; + +/// Set the pointer to operator new if it exists. The cast is needed because it can be overloaded. +template (T::operator new))>> +void set_operator_new(type_record *r) { r->operator_new = &T::operator new; } + +template void set_operator_new(...) { } + +template struct has_operator_delete : std::false_type { }; +template struct has_operator_delete(T::operator delete))>> + : std::true_type { }; +template struct has_operator_delete_size : std::false_type { }; +template struct has_operator_delete_size(T::operator delete))>> + : std::true_type { }; +/// Call class-specific delete if it exists or global otherwise. Can also be an overload set. +template ::value, int> = 0> +void call_operator_delete(T *p, size_t, size_t) { T::operator delete(p); } +template ::value && has_operator_delete_size::value, int> = 0> +void call_operator_delete(T *p, size_t s, size_t) { T::operator delete(p, s); } + +inline void call_operator_delete(void *p, size_t s, size_t a) { + (void)s; (void)a; +#if defined(PYBIND11_CPP17) + if (a > __STDCPP_DEFAULT_NEW_ALIGNMENT__) + ::operator delete(p, s, std::align_val_t(a)); + else + ::operator delete(p, s); +#else + ::operator delete(p); +#endif +} + +NAMESPACE_END(detail) + +/// Given a pointer to a member function, cast it to its `Derived` version. +/// Forward everything else unchanged. +template +auto method_adaptor(F &&f) -> decltype(std::forward(f)) { return std::forward(f); } + +template +auto method_adaptor(Return (Class::*pmf)(Args...)) -> Return (Derived::*)(Args...) { + static_assert(detail::is_accessible_base_of::value, + "Cannot bind an inaccessible base class method; use a lambda definition instead"); + return pmf; +} + +template +auto method_adaptor(Return (Class::*pmf)(Args...) const) -> Return (Derived::*)(Args...) const { + static_assert(detail::is_accessible_base_of::value, + "Cannot bind an inaccessible base class method; use a lambda definition instead"); + return pmf; +} + +template +class class_ : public detail::generic_type { + template using is_holder = detail::is_holder_type; + template using is_subtype = detail::is_strict_base_of; + template using is_base = detail::is_strict_base_of; + // struct instead of using here to help MSVC: + template struct is_valid_class_option : + detail::any_of, is_subtype, is_base> {}; + +public: + using type = type_; + using type_alias = detail::exactly_one_t; + constexpr static bool has_alias = !std::is_void::value; + using holder_type = detail::exactly_one_t, options...>; + + static_assert(detail::all_of...>::value, + "Unknown/invalid class_ template parameters provided"); + + static_assert(!has_alias || std::is_polymorphic::value, + "Cannot use an alias class with a non-polymorphic type"); + + PYBIND11_OBJECT(class_, generic_type, PyType_Check) + + template + class_(handle scope, const char *name, const Extra &... extra) { + using namespace detail; + + // MI can only be specified via class_ template options, not constructor parameters + static_assert( + none_of...>::value || // no base class arguments, or: + ( constexpr_sum(is_pyobject::value...) == 1 && // Exactly one base + constexpr_sum(is_base::value...) == 0 && // no template option bases + none_of...>::value), // no multiple_inheritance attr + "Error: multiple inheritance bases must be specified via class_ template options"); + + type_record record; + record.scope = scope; + record.name = name; + record.type = &typeid(type); + record.type_size = sizeof(conditional_t); + record.type_align = alignof(conditional_t&); + record.holder_size = sizeof(holder_type); + record.init_instance = init_instance; + record.dealloc = dealloc; + record.default_holder = detail::is_instantiation::value; + + set_operator_new(&record); + + /* Register base classes specified via template arguments to class_, if any */ + PYBIND11_EXPAND_SIDE_EFFECTS(add_base(record)); + + /* Process optional arguments, if any */ + process_attributes::init(extra..., &record); + + generic_type::initialize(record); + + if (has_alias) { + auto &instances = record.module_local ? registered_local_types_cpp() : get_internals().registered_types_cpp; + instances[std::type_index(typeid(type_alias))] = instances[std::type_index(typeid(type))]; + } + } + + template ::value, int> = 0> + static void add_base(detail::type_record &rec) { + rec.add_base(typeid(Base), [](void *src) -> void * { + return static_cast(reinterpret_cast(src)); + }); + } + + template ::value, int> = 0> + static void add_base(detail::type_record &) { } + + template + class_ &def(const char *name_, Func&& f, const Extra&... extra) { + cpp_function cf(method_adaptor(std::forward(f)), name(name_), is_method(*this), + sibling(getattr(*this, name_, none())), extra...); + attr(cf.name()) = cf; + return *this; + } + + template class_ & + def_static(const char *name_, Func &&f, const Extra&... extra) { + static_assert(!std::is_member_function_pointer::value, + "def_static(...) called with a non-static member function pointer"); + cpp_function cf(std::forward(f), name(name_), scope(*this), + sibling(getattr(*this, name_, none())), extra...); + attr(cf.name()) = cf; + return *this; + } + + template + class_ &def(const detail::op_ &op, const Extra&... extra) { + op.execute(*this, extra...); + return *this; + } + + template + class_ & def_cast(const detail::op_ &op, const Extra&... extra) { + op.execute_cast(*this, extra...); + return *this; + } + + template + class_ &def(const detail::initimpl::constructor &init, const Extra&... extra) { + init.execute(*this, extra...); + return *this; + } + + template + class_ &def(const detail::initimpl::alias_constructor &init, const Extra&... extra) { + init.execute(*this, extra...); + return *this; + } + + template + class_ &def(detail::initimpl::factory &&init, const Extra&... extra) { + std::move(init).execute(*this, extra...); + return *this; + } + + template + class_ &def(detail::initimpl::pickle_factory &&pf, const Extra &...extra) { + std::move(pf).execute(*this, extra...); + return *this; + } + + template class_& def_buffer(Func &&func) { + struct capture { Func func; }; + capture *ptr = new capture { std::forward(func) }; + install_buffer_funcs([](PyObject *obj, void *ptr) -> buffer_info* { + detail::make_caster caster; + if (!caster.load(obj, false)) + return nullptr; + return new buffer_info(((capture *) ptr)->func(caster)); + }, ptr); + return *this; + } + + template + class_ &def_buffer(Return (Class::*func)(Args...)) { + return def_buffer([func] (type &obj) { return (obj.*func)(); }); + } + + template + class_ &def_buffer(Return (Class::*func)(Args...) const) { + return def_buffer([func] (const type &obj) { return (obj.*func)(); }); + } + + template + class_ &def_readwrite(const char *name, D C::*pm, const Extra&... extra) { + static_assert(std::is_base_of::value, "def_readwrite() requires a class member (or base class member)"); + cpp_function fget([pm](const type &c) -> const D &{ return c.*pm; }, is_method(*this)), + fset([pm](type &c, const D &value) { c.*pm = value; }, is_method(*this)); + def_property(name, fget, fset, return_value_policy::reference_internal, extra...); + return *this; + } + + template + class_ &def_readonly(const char *name, const D C::*pm, const Extra& ...extra) { + static_assert(std::is_base_of::value, "def_readonly() requires a class member (or base class member)"); + cpp_function fget([pm](const type &c) -> const D &{ return c.*pm; }, is_method(*this)); + def_property_readonly(name, fget, return_value_policy::reference_internal, extra...); + return *this; + } + + template + class_ &def_readwrite_static(const char *name, D *pm, const Extra& ...extra) { + cpp_function fget([pm](object) -> const D &{ return *pm; }, scope(*this)), + fset([pm](object, const D &value) { *pm = value; }, scope(*this)); + def_property_static(name, fget, fset, return_value_policy::reference, extra...); + return *this; + } + + template + class_ &def_readonly_static(const char *name, const D *pm, const Extra& ...extra) { + cpp_function fget([pm](object) -> const D &{ return *pm; }, scope(*this)); + def_property_readonly_static(name, fget, return_value_policy::reference, extra...); + return *this; + } + + /// Uses return_value_policy::reference_internal by default + template + class_ &def_property_readonly(const char *name, const Getter &fget, const Extra& ...extra) { + return def_property_readonly(name, cpp_function(method_adaptor(fget)), + return_value_policy::reference_internal, extra...); + } + + /// Uses cpp_function's return_value_policy by default + template + class_ &def_property_readonly(const char *name, const cpp_function &fget, const Extra& ...extra) { + return def_property(name, fget, nullptr, extra...); + } + + /// Uses return_value_policy::reference by default + template + class_ &def_property_readonly_static(const char *name, const Getter &fget, const Extra& ...extra) { + return def_property_readonly_static(name, cpp_function(fget), return_value_policy::reference, extra...); + } + + /// Uses cpp_function's return_value_policy by default + template + class_ &def_property_readonly_static(const char *name, const cpp_function &fget, const Extra& ...extra) { + return def_property_static(name, fget, nullptr, extra...); + } + + /// Uses return_value_policy::reference_internal by default + template + class_ &def_property(const char *name, const Getter &fget, const Setter &fset, const Extra& ...extra) { + return def_property(name, fget, cpp_function(method_adaptor(fset)), extra...); + } + template + class_ &def_property(const char *name, const Getter &fget, const cpp_function &fset, const Extra& ...extra) { + return def_property(name, cpp_function(method_adaptor(fget)), fset, + return_value_policy::reference_internal, extra...); + } + + /// Uses cpp_function's return_value_policy by default + template + class_ &def_property(const char *name, const cpp_function &fget, const cpp_function &fset, const Extra& ...extra) { + return def_property_static(name, fget, fset, is_method(*this), extra...); + } + + /// Uses return_value_policy::reference by default + template + class_ &def_property_static(const char *name, const Getter &fget, const cpp_function &fset, const Extra& ...extra) { + return def_property_static(name, cpp_function(fget), fset, return_value_policy::reference, extra...); + } + + /// Uses cpp_function's return_value_policy by default + template + class_ &def_property_static(const char *name, const cpp_function &fget, const cpp_function &fset, const Extra& ...extra) { + auto rec_fget = get_function_record(fget), rec_fset = get_function_record(fset); + auto *rec_active = rec_fget; + if (rec_fget) { + char *doc_prev = rec_fget->doc; /* 'extra' field may include a property-specific documentation string */ + detail::process_attributes::init(extra..., rec_fget); + if (rec_fget->doc && rec_fget->doc != doc_prev) { + free(doc_prev); + rec_fget->doc = strdup(rec_fget->doc); + } + } + if (rec_fset) { + char *doc_prev = rec_fset->doc; + detail::process_attributes::init(extra..., rec_fset); + if (rec_fset->doc && rec_fset->doc != doc_prev) { + free(doc_prev); + rec_fset->doc = strdup(rec_fset->doc); + } + if (! rec_active) rec_active = rec_fset; + } + def_property_static_impl(name, fget, fset, rec_active); + return *this; + } + +private: + /// Initialize holder object, variant 1: object derives from enable_shared_from_this + template + static void init_holder(detail::instance *inst, detail::value_and_holder &v_h, + const holder_type * /* unused */, const std::enable_shared_from_this * /* dummy */) { + try { + auto sh = std::dynamic_pointer_cast( + v_h.value_ptr()->shared_from_this()); + if (sh) { + new (std::addressof(v_h.holder())) holder_type(std::move(sh)); + v_h.set_holder_constructed(); + } + } catch (const std::bad_weak_ptr &) {} + + if (!v_h.holder_constructed() && inst->owned) { + new (std::addressof(v_h.holder())) holder_type(v_h.value_ptr()); + v_h.set_holder_constructed(); + } + } + + static void init_holder_from_existing(const detail::value_and_holder &v_h, + const holder_type *holder_ptr, std::true_type /*is_copy_constructible*/) { + new (std::addressof(v_h.holder())) holder_type(*reinterpret_cast(holder_ptr)); + } + + static void init_holder_from_existing(const detail::value_and_holder &v_h, + const holder_type *holder_ptr, std::false_type /*is_copy_constructible*/) { + new (std::addressof(v_h.holder())) holder_type(std::move(*const_cast(holder_ptr))); + } + + /// Initialize holder object, variant 2: try to construct from existing holder object, if possible + static void init_holder(detail::instance *inst, detail::value_and_holder &v_h, + const holder_type *holder_ptr, const void * /* dummy -- not enable_shared_from_this) */) { + if (holder_ptr) { + init_holder_from_existing(v_h, holder_ptr, std::is_copy_constructible()); + v_h.set_holder_constructed(); + } else if (inst->owned || detail::always_construct_holder::value) { + new (std::addressof(v_h.holder())) holder_type(v_h.value_ptr()); + v_h.set_holder_constructed(); + } + } + + /// Performs instance initialization including constructing a holder and registering the known + /// instance. Should be called as soon as the `type` value_ptr is set for an instance. Takes an + /// optional pointer to an existing holder to use; if not specified and the instance is + /// `.owned`, a new holder will be constructed to manage the value pointer. + static void init_instance(detail::instance *inst, const void *holder_ptr) { + auto v_h = inst->get_value_and_holder(detail::get_type_info(typeid(type))); + if (!v_h.instance_registered()) { + register_instance(inst, v_h.value_ptr(), v_h.type); + v_h.set_instance_registered(); + } + init_holder(inst, v_h, (const holder_type *) holder_ptr, v_h.value_ptr()); + } + + /// Deallocates an instance; via holder, if constructed; otherwise via operator delete. + static void dealloc(detail::value_and_holder &v_h) { + if (v_h.holder_constructed()) { + v_h.holder().~holder_type(); + v_h.set_holder_constructed(false); + } + else { + detail::call_operator_delete(v_h.value_ptr(), + v_h.type->type_size, + v_h.type->type_align + ); + } + v_h.value_ptr() = nullptr; + } + + static detail::function_record *get_function_record(handle h) { + h = detail::get_function(h); + return h ? (detail::function_record *) reinterpret_borrow(PyCFunction_GET_SELF(h.ptr())) + : nullptr; + } +}; + +/// Binds an existing constructor taking arguments Args... +template detail::initimpl::constructor init() { return {}; } +/// Like `init()`, but the instance is always constructed through the alias class (even +/// when not inheriting on the Python side). +template detail::initimpl::alias_constructor init_alias() { return {}; } + +/// Binds a factory function as a constructor +template > +Ret init(Func &&f) { return {std::forward(f)}; } + +/// Dual-argument factory function: the first function is called when no alias is needed, the second +/// when an alias is needed (i.e. due to python-side inheritance). Arguments must be identical. +template > +Ret init(CFunc &&c, AFunc &&a) { + return {std::forward(c), std::forward(a)}; +} + +/// Binds pickling functions `__getstate__` and `__setstate__` and ensures that the type +/// returned by `__getstate__` is the same as the argument accepted by `__setstate__`. +template +detail::initimpl::pickle_factory pickle(GetState &&g, SetState &&s) { + return {std::forward(g), std::forward(s)}; +} + +NAMESPACE_BEGIN(detail) +struct enum_base { + enum_base(handle base, handle parent) : m_base(base), m_parent(parent) { } + + PYBIND11_NOINLINE void init(bool is_arithmetic, bool is_convertible) { + m_base.attr("__entries") = dict(); + auto property = handle((PyObject *) &PyProperty_Type); + auto static_property = handle((PyObject *) get_internals().static_property_type); + + m_base.attr("__repr__") = cpp_function( + [](handle arg) -> str { + handle type = arg.get_type(); + object type_name = type.attr("__name__"); + dict entries = type.attr("__entries"); + for (const auto &kv : entries) { + object other = kv.second[int_(0)]; + if (other.equal(arg)) + return pybind11::str("{}.{}").format(type_name, kv.first); + } + return pybind11::str("{}.???").format(type_name); + }, is_method(m_base) + ); + + m_base.attr("name") = property(cpp_function( + [](handle arg) -> str { + dict entries = arg.get_type().attr("__entries"); + for (const auto &kv : entries) { + if (handle(kv.second[int_(0)]).equal(arg)) + return pybind11::str(kv.first); + } + return "???"; + }, is_method(m_base) + )); + + m_base.attr("__doc__") = static_property(cpp_function( + [](handle arg) -> std::string { + std::string docstring; + dict entries = arg.attr("__entries"); + if (((PyTypeObject *) arg.ptr())->tp_doc) + docstring += std::string(((PyTypeObject *) arg.ptr())->tp_doc) + "\n\n"; + docstring += "Members:"; + for (const auto &kv : entries) { + auto key = std::string(pybind11::str(kv.first)); + auto comment = kv.second[int_(1)]; + docstring += "\n\n " + key; + if (!comment.is_none()) + docstring += " : " + (std::string) pybind11::str(comment); + } + return docstring; + } + ), none(), none(), ""); + + m_base.attr("__members__") = static_property(cpp_function( + [](handle arg) -> dict { + dict entries = arg.attr("__entries"), m; + for (const auto &kv : entries) + m[kv.first] = kv.second[int_(0)]; + return m; + }), none(), none(), "" + ); + + #define PYBIND11_ENUM_OP_STRICT(op, expr, strict_behavior) \ + m_base.attr(op) = cpp_function( \ + [](object a, object b) { \ + if (!a.get_type().is(b.get_type())) \ + strict_behavior; \ + return expr; \ + }, \ + is_method(m_base)) + + #define PYBIND11_ENUM_OP_CONV(op, expr) \ + m_base.attr(op) = cpp_function( \ + [](object a_, object b_) { \ + int_ a(a_), b(b_); \ + return expr; \ + }, \ + is_method(m_base)) + + if (is_convertible) { + PYBIND11_ENUM_OP_CONV("__eq__", !b.is_none() && a.equal(b)); + PYBIND11_ENUM_OP_CONV("__ne__", b.is_none() || !a.equal(b)); + + if (is_arithmetic) { + PYBIND11_ENUM_OP_CONV("__lt__", a < b); + PYBIND11_ENUM_OP_CONV("__gt__", a > b); + PYBIND11_ENUM_OP_CONV("__le__", a <= b); + PYBIND11_ENUM_OP_CONV("__ge__", a >= b); + PYBIND11_ENUM_OP_CONV("__and__", a & b); + PYBIND11_ENUM_OP_CONV("__rand__", a & b); + PYBIND11_ENUM_OP_CONV("__or__", a | b); + PYBIND11_ENUM_OP_CONV("__ror__", a | b); + PYBIND11_ENUM_OP_CONV("__xor__", a ^ b); + PYBIND11_ENUM_OP_CONV("__rxor__", a ^ b); + } + } else { + PYBIND11_ENUM_OP_STRICT("__eq__", int_(a).equal(int_(b)), return false); + PYBIND11_ENUM_OP_STRICT("__ne__", !int_(a).equal(int_(b)), return true); + + if (is_arithmetic) { + #define PYBIND11_THROW throw type_error("Expected an enumeration of matching type!"); + PYBIND11_ENUM_OP_STRICT("__lt__", int_(a) < int_(b), PYBIND11_THROW); + PYBIND11_ENUM_OP_STRICT("__gt__", int_(a) > int_(b), PYBIND11_THROW); + PYBIND11_ENUM_OP_STRICT("__le__", int_(a) <= int_(b), PYBIND11_THROW); + PYBIND11_ENUM_OP_STRICT("__ge__", int_(a) >= int_(b), PYBIND11_THROW); + #undef PYBIND11_THROW + } + } + + #undef PYBIND11_ENUM_OP_CONV + #undef PYBIND11_ENUM_OP_STRICT + + object getstate = cpp_function( + [](object arg) { return int_(arg); }, is_method(m_base)); + + m_base.attr("__getstate__") = getstate; + m_base.attr("__hash__") = getstate; + } + + PYBIND11_NOINLINE void value(char const* name_, object value, const char *doc = nullptr) { + dict entries = m_base.attr("__entries"); + str name(name_); + if (entries.contains(name)) { + std::string type_name = (std::string) str(m_base.attr("__name__")); + throw value_error(type_name + ": element \"" + std::string(name_) + "\" already exists!"); + } + + entries[name] = std::make_pair(value, doc); + m_base.attr(name) = value; + } + + PYBIND11_NOINLINE void export_values() { + dict entries = m_base.attr("__entries"); + for (const auto &kv : entries) + m_parent.attr(kv.first) = kv.second[int_(0)]; + } + + handle m_base; + handle m_parent; +}; + +NAMESPACE_END(detail) + +/// Binds C++ enumerations and enumeration classes to Python +template class enum_ : public class_ { +public: + using Base = class_; + using Base::def; + using Base::attr; + using Base::def_property_readonly; + using Base::def_property_readonly_static; + using Scalar = typename std::underlying_type::type; + + template + enum_(const handle &scope, const char *name, const Extra&... extra) + : class_(scope, name, extra...), m_base(*this, scope) { + constexpr bool is_arithmetic = detail::any_of...>::value; + constexpr bool is_convertible = std::is_convertible::value; + m_base.init(is_arithmetic, is_convertible); + + def(init([](Scalar i) { return static_cast(i); })); + def("__int__", [](Type value) { return (Scalar) value; }); + #if PY_MAJOR_VERSION < 3 + def("__long__", [](Type value) { return (Scalar) value; }); + #endif + cpp_function setstate( + [](Type &value, Scalar arg) { value = static_cast(arg); }, + is_method(*this)); + attr("__setstate__") = setstate; + } + + /// Export enumeration entries into the parent scope + enum_& export_values() { + m_base.export_values(); + return *this; + } + + /// Add an enumeration entry + enum_& value(char const* name, Type value, const char *doc = nullptr) { + m_base.value(name, pybind11::cast(value, return_value_policy::copy), doc); + return *this; + } + +private: + detail::enum_base m_base; +}; + +NAMESPACE_BEGIN(detail) + + +inline void keep_alive_impl(handle nurse, handle patient) { + if (!nurse || !patient) + pybind11_fail("Could not activate keep_alive!"); + + if (patient.is_none() || nurse.is_none()) + return; /* Nothing to keep alive or nothing to be kept alive by */ + + auto tinfo = all_type_info(Py_TYPE(nurse.ptr())); + if (!tinfo.empty()) { + /* It's a pybind-registered type, so we can store the patient in the + * internal list. */ + add_patient(nurse.ptr(), patient.ptr()); + } + else { + /* Fall back to clever approach based on weak references taken from + * Boost.Python. This is not used for pybind-registered types because + * the objects can be destroyed out-of-order in a GC pass. */ + cpp_function disable_lifesupport( + [patient](handle weakref) { patient.dec_ref(); weakref.dec_ref(); }); + + weakref wr(nurse, disable_lifesupport); + + patient.inc_ref(); /* reference patient and leak the weak reference */ + (void) wr.release(); + } +} + +PYBIND11_NOINLINE inline void keep_alive_impl(size_t Nurse, size_t Patient, function_call &call, handle ret) { + auto get_arg = [&](size_t n) { + if (n == 0) + return ret; + else if (n == 1 && call.init_self) + return call.init_self; + else if (n <= call.args.size()) + return call.args[n - 1]; + return handle(); + }; + + keep_alive_impl(get_arg(Nurse), get_arg(Patient)); +} + +inline std::pair all_type_info_get_cache(PyTypeObject *type) { + auto res = get_internals().registered_types_py +#ifdef __cpp_lib_unordered_map_try_emplace + .try_emplace(type); +#else + .emplace(type, std::vector()); +#endif + if (res.second) { + // New cache entry created; set up a weak reference to automatically remove it if the type + // gets destroyed: + weakref((PyObject *) type, cpp_function([type](handle wr) { + get_internals().registered_types_py.erase(type); + wr.dec_ref(); + })).release(); + } + + return res; +} + +template +struct iterator_state { + Iterator it; + Sentinel end; + bool first_or_done; +}; + +NAMESPACE_END(detail) + +/// Makes a python iterator from a first and past-the-end C++ InputIterator. +template ()), + typename... Extra> +iterator make_iterator(Iterator first, Sentinel last, Extra &&... extra) { + typedef detail::iterator_state state; + + if (!detail::get_type_info(typeid(state), false)) { + class_(handle(), "iterator", pybind11::module_local()) + .def("__iter__", [](state &s) -> state& { return s; }) + .def("__next__", [](state &s) -> ValueType { + if (!s.first_or_done) + ++s.it; + else + s.first_or_done = false; + if (s.it == s.end) { + s.first_or_done = true; + throw stop_iteration(); + } + return *s.it; + }, std::forward(extra)..., Policy); + } + + return cast(state{first, last, true}); +} + +/// Makes an python iterator over the keys (`.first`) of a iterator over pairs from a +/// first and past-the-end InputIterator. +template ()).first), + typename... Extra> +iterator make_key_iterator(Iterator first, Sentinel last, Extra &&... extra) { + typedef detail::iterator_state state; + + if (!detail::get_type_info(typeid(state), false)) { + class_(handle(), "iterator", pybind11::module_local()) + .def("__iter__", [](state &s) -> state& { return s; }) + .def("__next__", [](state &s) -> KeyType { + if (!s.first_or_done) + ++s.it; + else + s.first_or_done = false; + if (s.it == s.end) { + s.first_or_done = true; + throw stop_iteration(); + } + return (*s.it).first; + }, std::forward(extra)..., Policy); + } + + return cast(state{first, last, true}); +} + +/// Makes an iterator over values of an stl container or other container supporting +/// `std::begin()`/`std::end()` +template iterator make_iterator(Type &value, Extra&&... extra) { + return make_iterator(std::begin(value), std::end(value), extra...); +} + +/// Makes an iterator over the keys (`.first`) of a stl map-like container supporting +/// `std::begin()`/`std::end()` +template iterator make_key_iterator(Type &value, Extra&&... extra) { + return make_key_iterator(std::begin(value), std::end(value), extra...); +} + +template void implicitly_convertible() { + struct set_flag { + bool &flag; + set_flag(bool &flag) : flag(flag) { flag = true; } + ~set_flag() { flag = false; } + }; + auto implicit_caster = [](PyObject *obj, PyTypeObject *type) -> PyObject * { + static bool currently_used = false; + if (currently_used) // implicit conversions are non-reentrant + return nullptr; + set_flag flag_helper(currently_used); + if (!detail::make_caster().load(obj, false)) + return nullptr; + tuple args(1); + args[0] = obj; + PyObject *result = PyObject_Call((PyObject *) type, args.ptr(), nullptr); + if (result == nullptr) + PyErr_Clear(); + return result; + }; + + if (auto tinfo = detail::get_type_info(typeid(OutputType))) + tinfo->implicit_conversions.push_back(implicit_caster); + else + pybind11_fail("implicitly_convertible: Unable to find type " + type_id()); +} + +template +void register_exception_translator(ExceptionTranslator&& translator) { + detail::get_internals().registered_exception_translators.push_front( + std::forward(translator)); +} + +/** + * Wrapper to generate a new Python exception type. + * + * This should only be used with PyErr_SetString for now. + * It is not (yet) possible to use as a py::base. + * Template type argument is reserved for future use. + */ +template +class exception : public object { +public: + exception() = default; + exception(handle scope, const char *name, PyObject *base = PyExc_Exception) { + std::string full_name = scope.attr("__name__").cast() + + std::string(".") + name; + m_ptr = PyErr_NewException(const_cast(full_name.c_str()), base, NULL); + if (hasattr(scope, name)) + pybind11_fail("Error during initialization: multiple incompatible " + "definitions with name \"" + std::string(name) + "\""); + scope.attr(name) = *this; + } + + // Sets the current python exception to this exception object with the given message + void operator()(const char *message) { + PyErr_SetString(m_ptr, message); + } +}; + +NAMESPACE_BEGIN(detail) +// Returns a reference to a function-local static exception object used in the simple +// register_exception approach below. (It would be simpler to have the static local variable +// directly in register_exception, but that makes clang <3.5 segfault - issue #1349). +template +exception &get_exception_object() { static exception ex; return ex; } +NAMESPACE_END(detail) + +/** + * Registers a Python exception in `m` of the given `name` and installs an exception translator to + * translate the C++ exception to the created Python exception using the exceptions what() method. + * This is intended for simple exception translations; for more complex translation, register the + * exception object and translator directly. + */ +template +exception ®ister_exception(handle scope, + const char *name, + PyObject *base = PyExc_Exception) { + auto &ex = detail::get_exception_object(); + if (!ex) ex = exception(scope, name, base); + + register_exception_translator([](std::exception_ptr p) { + if (!p) return; + try { + std::rethrow_exception(p); + } catch (const CppException &e) { + detail::get_exception_object()(e.what()); + } + }); + return ex; +} + +NAMESPACE_BEGIN(detail) +PYBIND11_NOINLINE inline void print(tuple args, dict kwargs) { + auto strings = tuple(args.size()); + for (size_t i = 0; i < args.size(); ++i) { + strings[i] = str(args[i]); + } + auto sep = kwargs.contains("sep") ? kwargs["sep"] : cast(" "); + auto line = sep.attr("join")(strings); + + object file; + if (kwargs.contains("file")) { + file = kwargs["file"].cast(); + } else { + try { + file = module::import("sys").attr("stdout"); + } catch (const error_already_set &) { + /* If print() is called from code that is executed as + part of garbage collection during interpreter shutdown, + importing 'sys' can fail. Give up rather than crashing the + interpreter in this case. */ + return; + } + } + + auto write = file.attr("write"); + write(line); + write(kwargs.contains("end") ? kwargs["end"] : cast("\n")); + + if (kwargs.contains("flush") && kwargs["flush"].cast()) + file.attr("flush")(); +} +NAMESPACE_END(detail) + +template +void print(Args &&...args) { + auto c = detail::collect_arguments(std::forward(args)...); + detail::print(c.args(), c.kwargs()); +} + +#if defined(WITH_THREAD) && !defined(PYPY_VERSION) + +/* The functions below essentially reproduce the PyGILState_* API using a RAII + * pattern, but there are a few important differences: + * + * 1. When acquiring the GIL from an non-main thread during the finalization + * phase, the GILState API blindly terminates the calling thread, which + * is often not what is wanted. This API does not do this. + * + * 2. The gil_scoped_release function can optionally cut the relationship + * of a PyThreadState and its associated thread, which allows moving it to + * another thread (this is a fairly rare/advanced use case). + * + * 3. The reference count of an acquired thread state can be controlled. This + * can be handy to prevent cases where callbacks issued from an external + * thread would otherwise constantly construct and destroy thread state data + * structures. + * + * See the Python bindings of NanoGUI (http://github.com/wjakob/nanogui) for an + * example which uses features 2 and 3 to migrate the Python thread of + * execution to another thread (to run the event loop on the original thread, + * in this case). + */ + +class gil_scoped_acquire { +public: + PYBIND11_NOINLINE gil_scoped_acquire() { + auto const &internals = detail::get_internals(); + tstate = (PyThreadState *) PYBIND11_TLS_GET_VALUE(internals.tstate); + + if (!tstate) { + /* Check if the GIL was acquired using the PyGILState_* API instead (e.g. if + calling from a Python thread). Since we use a different key, this ensures + we don't create a new thread state and deadlock in PyEval_AcquireThread + below. Note we don't save this state with internals.tstate, since we don't + create it we would fail to clear it (its reference count should be > 0). */ + tstate = PyGILState_GetThisThreadState(); + } + + if (!tstate) { + tstate = PyThreadState_New(internals.istate); + #if !defined(NDEBUG) + if (!tstate) + pybind11_fail("scoped_acquire: could not create thread state!"); + #endif + tstate->gilstate_counter = 0; + PYBIND11_TLS_REPLACE_VALUE(internals.tstate, tstate); + } else { + release = detail::get_thread_state_unchecked() != tstate; + } + + if (release) { + /* Work around an annoying assertion in PyThreadState_Swap */ + #if defined(Py_DEBUG) + PyInterpreterState *interp = tstate->interp; + tstate->interp = nullptr; + #endif + PyEval_AcquireThread(tstate); + #if defined(Py_DEBUG) + tstate->interp = interp; + #endif + } + + inc_ref(); + } + + void inc_ref() { + ++tstate->gilstate_counter; + } + + PYBIND11_NOINLINE void dec_ref() { + --tstate->gilstate_counter; + #if !defined(NDEBUG) + if (detail::get_thread_state_unchecked() != tstate) + pybind11_fail("scoped_acquire::dec_ref(): thread state must be current!"); + if (tstate->gilstate_counter < 0) + pybind11_fail("scoped_acquire::dec_ref(): reference count underflow!"); + #endif + if (tstate->gilstate_counter == 0) { + #if !defined(NDEBUG) + if (!release) + pybind11_fail("scoped_acquire::dec_ref(): internal error!"); + #endif + PyThreadState_Clear(tstate); + PyThreadState_DeleteCurrent(); + PYBIND11_TLS_DELETE_VALUE(detail::get_internals().tstate); + release = false; + } + } + + PYBIND11_NOINLINE ~gil_scoped_acquire() { + dec_ref(); + if (release) + PyEval_SaveThread(); + } +private: + PyThreadState *tstate = nullptr; + bool release = true; +}; + +class gil_scoped_release { +public: + explicit gil_scoped_release(bool disassoc = false) : disassoc(disassoc) { + // `get_internals()` must be called here unconditionally in order to initialize + // `internals.tstate` for subsequent `gil_scoped_acquire` calls. Otherwise, an + // initialization race could occur as multiple threads try `gil_scoped_acquire`. + const auto &internals = detail::get_internals(); + tstate = PyEval_SaveThread(); + if (disassoc) { + auto key = internals.tstate; + PYBIND11_TLS_DELETE_VALUE(key); + } + } + ~gil_scoped_release() { + if (!tstate) + return; + PyEval_RestoreThread(tstate); + if (disassoc) { + auto key = detail::get_internals().tstate; + PYBIND11_TLS_REPLACE_VALUE(key, tstate); + } + } +private: + PyThreadState *tstate; + bool disassoc; +}; +#elif defined(PYPY_VERSION) +class gil_scoped_acquire { + PyGILState_STATE state; +public: + gil_scoped_acquire() { state = PyGILState_Ensure(); } + ~gil_scoped_acquire() { PyGILState_Release(state); } +}; + +class gil_scoped_release { + PyThreadState *state; +public: + gil_scoped_release() { state = PyEval_SaveThread(); } + ~gil_scoped_release() { PyEval_RestoreThread(state); } +}; +#else +class gil_scoped_acquire { }; +class gil_scoped_release { }; +#endif + +error_already_set::~error_already_set() { + if (type) { + error_scope scope; + gil_scoped_acquire gil; + type.release().dec_ref(); + value.release().dec_ref(); + trace.release().dec_ref(); + } +} + +inline function get_type_overload(const void *this_ptr, const detail::type_info *this_type, const char *name) { + handle self = detail::get_object_handle(this_ptr, this_type); + if (!self) + return function(); + handle type = self.get_type(); + auto key = std::make_pair(type.ptr(), name); + + /* Cache functions that aren't overloaded in Python to avoid + many costly Python dictionary lookups below */ + auto &cache = detail::get_internals().inactive_overload_cache; + if (cache.find(key) != cache.end()) + return function(); + + function overload = getattr(self, name, function()); + if (overload.is_cpp_function()) { + cache.insert(key); + return function(); + } + + /* Don't call dispatch code if invoked from overridden function. + Unfortunately this doesn't work on PyPy. */ +#if !defined(PYPY_VERSION) + PyFrameObject *frame = PyThreadState_Get()->frame; + if (frame && (std::string) str(frame->f_code->co_name) == name && + frame->f_code->co_argcount > 0) { + PyFrame_FastToLocals(frame); + PyObject *self_caller = PyDict_GetItem( + frame->f_locals, PyTuple_GET_ITEM(frame->f_code->co_varnames, 0)); + if (self_caller == self.ptr()) + return function(); + } +#else + /* PyPy currently doesn't provide a detailed cpyext emulation of + frame objects, so we have to emulate this using Python. This + is going to be slow..*/ + dict d; d["self"] = self; d["name"] = pybind11::str(name); + PyObject *result = PyRun_String( + "import inspect\n" + "frame = inspect.currentframe()\n" + "if frame is not None:\n" + " frame = frame.f_back\n" + " if frame is not None and str(frame.f_code.co_name) == name and " + "frame.f_code.co_argcount > 0:\n" + " self_caller = frame.f_locals[frame.f_code.co_varnames[0]]\n" + " if self_caller == self:\n" + " self = None\n", + Py_file_input, d.ptr(), d.ptr()); + if (result == nullptr) + throw error_already_set(); + if (d["self"].is_none()) + return function(); + Py_DECREF(result); +#endif + + return overload; +} + +template function get_overload(const T *this_ptr, const char *name) { + auto tinfo = detail::get_type_info(typeid(T)); + return tinfo ? get_type_overload(this_ptr, tinfo, name) : function(); +} + +#define PYBIND11_OVERLOAD_INT(ret_type, cname, name, ...) { \ + pybind11::gil_scoped_acquire gil; \ + pybind11::function overload = pybind11::get_overload(static_cast(this), name); \ + if (overload) { \ + auto o = overload(__VA_ARGS__); \ + if (pybind11::detail::cast_is_temporary_value_reference::value) { \ + static pybind11::detail::overload_caster_t caster; \ + return pybind11::detail::cast_ref(std::move(o), caster); \ + } \ + else return pybind11::detail::cast_safe(std::move(o)); \ + } \ + } + +#define PYBIND11_OVERLOAD_NAME(ret_type, cname, name, fn, ...) \ + PYBIND11_OVERLOAD_INT(PYBIND11_TYPE(ret_type), PYBIND11_TYPE(cname), name, __VA_ARGS__) \ + return cname::fn(__VA_ARGS__) + +#define PYBIND11_OVERLOAD_PURE_NAME(ret_type, cname, name, fn, ...) \ + PYBIND11_OVERLOAD_INT(PYBIND11_TYPE(ret_type), PYBIND11_TYPE(cname), name, __VA_ARGS__) \ + pybind11::pybind11_fail("Tried to call pure virtual function \"" PYBIND11_STRINGIFY(cname) "::" name "\""); + +#define PYBIND11_OVERLOAD(ret_type, cname, fn, ...) \ + PYBIND11_OVERLOAD_NAME(PYBIND11_TYPE(ret_type), PYBIND11_TYPE(cname), #fn, fn, __VA_ARGS__) + +#define PYBIND11_OVERLOAD_PURE(ret_type, cname, fn, ...) \ + PYBIND11_OVERLOAD_PURE_NAME(PYBIND11_TYPE(ret_type), PYBIND11_TYPE(cname), #fn, fn, __VA_ARGS__) + +NAMESPACE_END(PYBIND11_NAMESPACE) + +#if defined(_MSC_VER) && !defined(__INTEL_COMPILER) +# pragma warning(pop) +#elif defined(__GNUG__) && !defined(__clang__) +# pragma GCC diagnostic pop +#endif diff --git a/ptocr/postprocess/dbprocess/include/pybind11/pytypes.h b/ptocr/postprocess/dbprocess/include/pybind11/pytypes.h new file mode 100644 index 0000000..3329fda --- /dev/null +++ b/ptocr/postprocess/dbprocess/include/pybind11/pytypes.h @@ -0,0 +1,1438 @@ +/* + pybind11/pytypes.h: Convenience wrapper classes for basic Python types + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "detail/common.h" +#include "buffer_info.h" +#include +#include + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) + +/* A few forward declarations */ +class handle; class object; +class str; class iterator; +struct arg; struct arg_v; + +NAMESPACE_BEGIN(detail) +class args_proxy; +inline bool isinstance_generic(handle obj, const std::type_info &tp); + +// Accessor forward declarations +template class accessor; +namespace accessor_policies { + struct obj_attr; + struct str_attr; + struct generic_item; + struct sequence_item; + struct list_item; + struct tuple_item; +} +using obj_attr_accessor = accessor; +using str_attr_accessor = accessor; +using item_accessor = accessor; +using sequence_accessor = accessor; +using list_accessor = accessor; +using tuple_accessor = accessor; + +/// Tag and check to identify a class which implements the Python object API +class pyobject_tag { }; +template using is_pyobject = std::is_base_of>; + +/** \rst + A mixin class which adds common functions to `handle`, `object` and various accessors. + The only requirement for `Derived` is to implement ``PyObject *Derived::ptr() const``. +\endrst */ +template +class object_api : public pyobject_tag { + const Derived &derived() const { return static_cast(*this); } + +public: + /** \rst + Return an iterator equivalent to calling ``iter()`` in Python. The object + must be a collection which supports the iteration protocol. + \endrst */ + iterator begin() const; + /// Return a sentinel which ends iteration. + iterator end() const; + + /** \rst + Return an internal functor to invoke the object's sequence protocol. Casting + the returned ``detail::item_accessor`` instance to a `handle` or `object` + subclass causes a corresponding call to ``__getitem__``. Assigning a `handle` + or `object` subclass causes a call to ``__setitem__``. + \endrst */ + item_accessor operator[](handle key) const; + /// See above (the only difference is that they key is provided as a string literal) + item_accessor operator[](const char *key) const; + + /** \rst + Return an internal functor to access the object's attributes. Casting the + returned ``detail::obj_attr_accessor`` instance to a `handle` or `object` + subclass causes a corresponding call to ``getattr``. Assigning a `handle` + or `object` subclass causes a call to ``setattr``. + \endrst */ + obj_attr_accessor attr(handle key) const; + /// See above (the only difference is that they key is provided as a string literal) + str_attr_accessor attr(const char *key) const; + + /** \rst + Matches * unpacking in Python, e.g. to unpack arguments out of a ``tuple`` + or ``list`` for a function call. Applying another * to the result yields + ** unpacking, e.g. to unpack a dict as function keyword arguments. + See :ref:`calling_python_functions`. + \endrst */ + args_proxy operator*() const; + + /// Check if the given item is contained within this object, i.e. ``item in obj``. + template bool contains(T &&item) const; + + /** \rst + Assuming the Python object is a function or implements the ``__call__`` + protocol, ``operator()`` invokes the underlying function, passing an + arbitrary set of parameters. The result is returned as a `object` and + may need to be converted back into a Python object using `handle::cast()`. + + When some of the arguments cannot be converted to Python objects, the + function will throw a `cast_error` exception. When the Python function + call fails, a `error_already_set` exception is thrown. + \endrst */ + template + object operator()(Args &&...args) const; + template + PYBIND11_DEPRECATED("call(...) was deprecated in favor of operator()(...)") + object call(Args&&... args) const; + + /// Equivalent to ``obj is other`` in Python. + bool is(object_api const& other) const { return derived().ptr() == other.derived().ptr(); } + /// Equivalent to ``obj is None`` in Python. + bool is_none() const { return derived().ptr() == Py_None; } + /// Equivalent to obj == other in Python + bool equal(object_api const &other) const { return rich_compare(other, Py_EQ); } + bool not_equal(object_api const &other) const { return rich_compare(other, Py_NE); } + bool operator<(object_api const &other) const { return rich_compare(other, Py_LT); } + bool operator<=(object_api const &other) const { return rich_compare(other, Py_LE); } + bool operator>(object_api const &other) const { return rich_compare(other, Py_GT); } + bool operator>=(object_api const &other) const { return rich_compare(other, Py_GE); } + + object operator-() const; + object operator~() const; + object operator+(object_api const &other) const; + object operator+=(object_api const &other) const; + object operator-(object_api const &other) const; + object operator-=(object_api const &other) const; + object operator*(object_api const &other) const; + object operator*=(object_api const &other) const; + object operator/(object_api const &other) const; + object operator/=(object_api const &other) const; + object operator|(object_api const &other) const; + object operator|=(object_api const &other) const; + object operator&(object_api const &other) const; + object operator&=(object_api const &other) const; + object operator^(object_api const &other) const; + object operator^=(object_api const &other) const; + object operator<<(object_api const &other) const; + object operator<<=(object_api const &other) const; + object operator>>(object_api const &other) const; + object operator>>=(object_api const &other) const; + + PYBIND11_DEPRECATED("Use py::str(obj) instead") + pybind11::str str() const; + + /// Get or set the object's docstring, i.e. ``obj.__doc__``. + str_attr_accessor doc() const; + + /// Return the object's current reference count + int ref_count() const { return static_cast(Py_REFCNT(derived().ptr())); } + /// Return a handle to the Python type object underlying the instance + handle get_type() const; + +private: + bool rich_compare(object_api const &other, int value) const; +}; + +NAMESPACE_END(detail) + +/** \rst + Holds a reference to a Python object (no reference counting) + + The `handle` class is a thin wrapper around an arbitrary Python object (i.e. a + ``PyObject *`` in Python's C API). It does not perform any automatic reference + counting and merely provides a basic C++ interface to various Python API functions. + + .. seealso:: + The `object` class inherits from `handle` and adds automatic reference + counting features. +\endrst */ +class handle : public detail::object_api { +public: + /// The default constructor creates a handle with a ``nullptr``-valued pointer + handle() = default; + /// Creates a ``handle`` from the given raw Python object pointer + handle(PyObject *ptr) : m_ptr(ptr) { } // Allow implicit conversion from PyObject* + + /// Return the underlying ``PyObject *`` pointer + PyObject *ptr() const { return m_ptr; } + PyObject *&ptr() { return m_ptr; } + + /** \rst + Manually increase the reference count of the Python object. Usually, it is + preferable to use the `object` class which derives from `handle` and calls + this function automatically. Returns a reference to itself. + \endrst */ + const handle& inc_ref() const & { Py_XINCREF(m_ptr); return *this; } + + /** \rst + Manually decrease the reference count of the Python object. Usually, it is + preferable to use the `object` class which derives from `handle` and calls + this function automatically. Returns a reference to itself. + \endrst */ + const handle& dec_ref() const & { Py_XDECREF(m_ptr); return *this; } + + /** \rst + Attempt to cast the Python object into the given C++ type. A `cast_error` + will be throw upon failure. + \endrst */ + template T cast() const; + /// Return ``true`` when the `handle` wraps a valid Python object + explicit operator bool() const { return m_ptr != nullptr; } + /** \rst + Deprecated: Check that the underlying pointers are the same. + Equivalent to ``obj1 is obj2`` in Python. + \endrst */ + PYBIND11_DEPRECATED("Use obj1.is(obj2) instead") + bool operator==(const handle &h) const { return m_ptr == h.m_ptr; } + PYBIND11_DEPRECATED("Use !obj1.is(obj2) instead") + bool operator!=(const handle &h) const { return m_ptr != h.m_ptr; } + PYBIND11_DEPRECATED("Use handle::operator bool() instead") + bool check() const { return m_ptr != nullptr; } +protected: + PyObject *m_ptr = nullptr; +}; + +/** \rst + Holds a reference to a Python object (with reference counting) + + Like `handle`, the `object` class is a thin wrapper around an arbitrary Python + object (i.e. a ``PyObject *`` in Python's C API). In contrast to `handle`, it + optionally increases the object's reference count upon construction, and it + *always* decreases the reference count when the `object` instance goes out of + scope and is destructed. When using `object` instances consistently, it is much + easier to get reference counting right at the first attempt. +\endrst */ +class object : public handle { +public: + object() = default; + PYBIND11_DEPRECATED("Use reinterpret_borrow() or reinterpret_steal()") + object(handle h, bool is_borrowed) : handle(h) { if (is_borrowed) inc_ref(); } + /// Copy constructor; always increases the reference count + object(const object &o) : handle(o) { inc_ref(); } + /// Move constructor; steals the object from ``other`` and preserves its reference count + object(object &&other) noexcept { m_ptr = other.m_ptr; other.m_ptr = nullptr; } + /// Destructor; automatically calls `handle::dec_ref()` + ~object() { dec_ref(); } + + /** \rst + Resets the internal pointer to ``nullptr`` without without decreasing the + object's reference count. The function returns a raw handle to the original + Python object. + \endrst */ + handle release() { + PyObject *tmp = m_ptr; + m_ptr = nullptr; + return handle(tmp); + } + + object& operator=(const object &other) { + other.inc_ref(); + dec_ref(); + m_ptr = other.m_ptr; + return *this; + } + + object& operator=(object &&other) noexcept { + if (this != &other) { + handle temp(m_ptr); + m_ptr = other.m_ptr; + other.m_ptr = nullptr; + temp.dec_ref(); + } + return *this; + } + + // Calling cast() on an object lvalue just copies (via handle::cast) + template T cast() const &; + // Calling on an object rvalue does a move, if needed and/or possible + template T cast() &&; + +protected: + // Tags for choosing constructors from raw PyObject * + struct borrowed_t { }; + struct stolen_t { }; + + template friend T reinterpret_borrow(handle); + template friend T reinterpret_steal(handle); + +public: + // Only accessible from derived classes and the reinterpret_* functions + object(handle h, borrowed_t) : handle(h) { inc_ref(); } + object(handle h, stolen_t) : handle(h) { } +}; + +/** \rst + Declare that a `handle` or ``PyObject *`` is a certain type and borrow the reference. + The target type ``T`` must be `object` or one of its derived classes. The function + doesn't do any conversions or checks. It's up to the user to make sure that the + target type is correct. + + .. code-block:: cpp + + PyObject *p = PyList_GetItem(obj, index); + py::object o = reinterpret_borrow(p); + // or + py::tuple t = reinterpret_borrow(p); // <-- `p` must be already be a `tuple` +\endrst */ +template T reinterpret_borrow(handle h) { return {h, object::borrowed_t{}}; } + +/** \rst + Like `reinterpret_borrow`, but steals the reference. + + .. code-block:: cpp + + PyObject *p = PyObject_Str(obj); + py::str s = reinterpret_steal(p); // <-- `p` must be already be a `str` +\endrst */ +template T reinterpret_steal(handle h) { return {h, object::stolen_t{}}; } + +NAMESPACE_BEGIN(detail) +inline std::string error_string(); +NAMESPACE_END(detail) + +/// Fetch and hold an error which was already set in Python. An instance of this is typically +/// thrown to propagate python-side errors back through C++ which can either be caught manually or +/// else falls back to the function dispatcher (which then raises the captured error back to +/// python). +class error_already_set : public std::runtime_error { +public: + /// Constructs a new exception from the current Python error indicator, if any. The current + /// Python error indicator will be cleared. + error_already_set() : std::runtime_error(detail::error_string()) { + PyErr_Fetch(&type.ptr(), &value.ptr(), &trace.ptr()); + } + + error_already_set(const error_already_set &) = default; + error_already_set(error_already_set &&) = default; + + inline ~error_already_set(); + + /// Give the currently-held error back to Python, if any. If there is currently a Python error + /// already set it is cleared first. After this call, the current object no longer stores the + /// error variables (but the `.what()` string is still available). + void restore() { PyErr_Restore(type.release().ptr(), value.release().ptr(), trace.release().ptr()); } + + // Does nothing; provided for backwards compatibility. + PYBIND11_DEPRECATED("Use of error_already_set.clear() is deprecated") + void clear() {} + + /// Check if the currently trapped error type matches the given Python exception class (or a + /// subclass thereof). May also be passed a tuple to search for any exception class matches in + /// the given tuple. + bool matches(handle ex) const { return PyErr_GivenExceptionMatches(ex.ptr(), type.ptr()); } + +private: + object type, value, trace; +}; + +/** \defgroup python_builtins _ + Unless stated otherwise, the following C++ functions behave the same + as their Python counterparts. + */ + +/** \ingroup python_builtins + \rst + Return true if ``obj`` is an instance of ``T``. Type ``T`` must be a subclass of + `object` or a class which was exposed to Python as ``py::class_``. +\endrst */ +template ::value, int> = 0> +bool isinstance(handle obj) { return T::check_(obj); } + +template ::value, int> = 0> +bool isinstance(handle obj) { return detail::isinstance_generic(obj, typeid(T)); } + +template <> inline bool isinstance(handle obj) = delete; +template <> inline bool isinstance(handle obj) { return obj.ptr() != nullptr; } + +/// \ingroup python_builtins +/// Return true if ``obj`` is an instance of the ``type``. +inline bool isinstance(handle obj, handle type) { + const auto result = PyObject_IsInstance(obj.ptr(), type.ptr()); + if (result == -1) + throw error_already_set(); + return result != 0; +} + +/// \addtogroup python_builtins +/// @{ +inline bool hasattr(handle obj, handle name) { + return PyObject_HasAttr(obj.ptr(), name.ptr()) == 1; +} + +inline bool hasattr(handle obj, const char *name) { + return PyObject_HasAttrString(obj.ptr(), name) == 1; +} + +inline void delattr(handle obj, handle name) { + if (PyObject_DelAttr(obj.ptr(), name.ptr()) != 0) { throw error_already_set(); } +} + +inline void delattr(handle obj, const char *name) { + if (PyObject_DelAttrString(obj.ptr(), name) != 0) { throw error_already_set(); } +} + +inline object getattr(handle obj, handle name) { + PyObject *result = PyObject_GetAttr(obj.ptr(), name.ptr()); + if (!result) { throw error_already_set(); } + return reinterpret_steal(result); +} + +inline object getattr(handle obj, const char *name) { + PyObject *result = PyObject_GetAttrString(obj.ptr(), name); + if (!result) { throw error_already_set(); } + return reinterpret_steal(result); +} + +inline object getattr(handle obj, handle name, handle default_) { + if (PyObject *result = PyObject_GetAttr(obj.ptr(), name.ptr())) { + return reinterpret_steal(result); + } else { + PyErr_Clear(); + return reinterpret_borrow(default_); + } +} + +inline object getattr(handle obj, const char *name, handle default_) { + if (PyObject *result = PyObject_GetAttrString(obj.ptr(), name)) { + return reinterpret_steal(result); + } else { + PyErr_Clear(); + return reinterpret_borrow(default_); + } +} + +inline void setattr(handle obj, handle name, handle value) { + if (PyObject_SetAttr(obj.ptr(), name.ptr(), value.ptr()) != 0) { throw error_already_set(); } +} + +inline void setattr(handle obj, const char *name, handle value) { + if (PyObject_SetAttrString(obj.ptr(), name, value.ptr()) != 0) { throw error_already_set(); } +} + +inline ssize_t hash(handle obj) { + auto h = PyObject_Hash(obj.ptr()); + if (h == -1) { throw error_already_set(); } + return h; +} + +/// @} python_builtins + +NAMESPACE_BEGIN(detail) +inline handle get_function(handle value) { + if (value) { +#if PY_MAJOR_VERSION >= 3 + if (PyInstanceMethod_Check(value.ptr())) + value = PyInstanceMethod_GET_FUNCTION(value.ptr()); + else +#endif + if (PyMethod_Check(value.ptr())) + value = PyMethod_GET_FUNCTION(value.ptr()); + } + return value; +} + +// Helper aliases/functions to support implicit casting of values given to python accessors/methods. +// When given a pyobject, this simply returns the pyobject as-is; for other C++ type, the value goes +// through pybind11::cast(obj) to convert it to an `object`. +template ::value, int> = 0> +auto object_or_cast(T &&o) -> decltype(std::forward(o)) { return std::forward(o); } +// The following casting version is implemented in cast.h: +template ::value, int> = 0> +object object_or_cast(T &&o); +// Match a PyObject*, which we want to convert directly to handle via its converting constructor +inline handle object_or_cast(PyObject *ptr) { return ptr; } + +template +class accessor : public object_api> { + using key_type = typename Policy::key_type; + +public: + accessor(handle obj, key_type key) : obj(obj), key(std::move(key)) { } + accessor(const accessor &) = default; + accessor(accessor &&) = default; + + // accessor overload required to override default assignment operator (templates are not allowed + // to replace default compiler-generated assignments). + void operator=(const accessor &a) && { std::move(*this).operator=(handle(a)); } + void operator=(const accessor &a) & { operator=(handle(a)); } + + template void operator=(T &&value) && { + Policy::set(obj, key, object_or_cast(std::forward(value))); + } + template void operator=(T &&value) & { + get_cache() = reinterpret_borrow(object_or_cast(std::forward(value))); + } + + template + PYBIND11_DEPRECATED("Use of obj.attr(...) as bool is deprecated in favor of pybind11::hasattr(obj, ...)") + explicit operator enable_if_t::value || + std::is_same::value, bool>() const { + return hasattr(obj, key); + } + template + PYBIND11_DEPRECATED("Use of obj[key] as bool is deprecated in favor of obj.contains(key)") + explicit operator enable_if_t::value, bool>() const { + return obj.contains(key); + } + + operator object() const { return get_cache(); } + PyObject *ptr() const { return get_cache().ptr(); } + template T cast() const { return get_cache().template cast(); } + +private: + object &get_cache() const { + if (!cache) { cache = Policy::get(obj, key); } + return cache; + } + +private: + handle obj; + key_type key; + mutable object cache; +}; + +NAMESPACE_BEGIN(accessor_policies) +struct obj_attr { + using key_type = object; + static object get(handle obj, handle key) { return getattr(obj, key); } + static void set(handle obj, handle key, handle val) { setattr(obj, key, val); } +}; + +struct str_attr { + using key_type = const char *; + static object get(handle obj, const char *key) { return getattr(obj, key); } + static void set(handle obj, const char *key, handle val) { setattr(obj, key, val); } +}; + +struct generic_item { + using key_type = object; + + static object get(handle obj, handle key) { + PyObject *result = PyObject_GetItem(obj.ptr(), key.ptr()); + if (!result) { throw error_already_set(); } + return reinterpret_steal(result); + } + + static void set(handle obj, handle key, handle val) { + if (PyObject_SetItem(obj.ptr(), key.ptr(), val.ptr()) != 0) { throw error_already_set(); } + } +}; + +struct sequence_item { + using key_type = size_t; + + static object get(handle obj, size_t index) { + PyObject *result = PySequence_GetItem(obj.ptr(), static_cast(index)); + if (!result) { throw error_already_set(); } + return reinterpret_steal(result); + } + + static void set(handle obj, size_t index, handle val) { + // PySequence_SetItem does not steal a reference to 'val' + if (PySequence_SetItem(obj.ptr(), static_cast(index), val.ptr()) != 0) { + throw error_already_set(); + } + } +}; + +struct list_item { + using key_type = size_t; + + static object get(handle obj, size_t index) { + PyObject *result = PyList_GetItem(obj.ptr(), static_cast(index)); + if (!result) { throw error_already_set(); } + return reinterpret_borrow(result); + } + + static void set(handle obj, size_t index, handle val) { + // PyList_SetItem steals a reference to 'val' + if (PyList_SetItem(obj.ptr(), static_cast(index), val.inc_ref().ptr()) != 0) { + throw error_already_set(); + } + } +}; + +struct tuple_item { + using key_type = size_t; + + static object get(handle obj, size_t index) { + PyObject *result = PyTuple_GetItem(obj.ptr(), static_cast(index)); + if (!result) { throw error_already_set(); } + return reinterpret_borrow(result); + } + + static void set(handle obj, size_t index, handle val) { + // PyTuple_SetItem steals a reference to 'val' + if (PyTuple_SetItem(obj.ptr(), static_cast(index), val.inc_ref().ptr()) != 0) { + throw error_already_set(); + } + } +}; +NAMESPACE_END(accessor_policies) + +/// STL iterator template used for tuple, list, sequence and dict +template +class generic_iterator : public Policy { + using It = generic_iterator; + +public: + using difference_type = ssize_t; + using iterator_category = typename Policy::iterator_category; + using value_type = typename Policy::value_type; + using reference = typename Policy::reference; + using pointer = typename Policy::pointer; + + generic_iterator() = default; + generic_iterator(handle seq, ssize_t index) : Policy(seq, index) { } + + reference operator*() const { return Policy::dereference(); } + reference operator[](difference_type n) const { return *(*this + n); } + pointer operator->() const { return **this; } + + It &operator++() { Policy::increment(); return *this; } + It operator++(int) { auto copy = *this; Policy::increment(); return copy; } + It &operator--() { Policy::decrement(); return *this; } + It operator--(int) { auto copy = *this; Policy::decrement(); return copy; } + It &operator+=(difference_type n) { Policy::advance(n); return *this; } + It &operator-=(difference_type n) { Policy::advance(-n); return *this; } + + friend It operator+(const It &a, difference_type n) { auto copy = a; return copy += n; } + friend It operator+(difference_type n, const It &b) { return b + n; } + friend It operator-(const It &a, difference_type n) { auto copy = a; return copy -= n; } + friend difference_type operator-(const It &a, const It &b) { return a.distance_to(b); } + + friend bool operator==(const It &a, const It &b) { return a.equal(b); } + friend bool operator!=(const It &a, const It &b) { return !(a == b); } + friend bool operator< (const It &a, const It &b) { return b - a > 0; } + friend bool operator> (const It &a, const It &b) { return b < a; } + friend bool operator>=(const It &a, const It &b) { return !(a < b); } + friend bool operator<=(const It &a, const It &b) { return !(a > b); } +}; + +NAMESPACE_BEGIN(iterator_policies) +/// Quick proxy class needed to implement ``operator->`` for iterators which can't return pointers +template +struct arrow_proxy { + T value; + + arrow_proxy(T &&value) : value(std::move(value)) { } + T *operator->() const { return &value; } +}; + +/// Lightweight iterator policy using just a simple pointer: see ``PySequence_Fast_ITEMS`` +class sequence_fast_readonly { +protected: + using iterator_category = std::random_access_iterator_tag; + using value_type = handle; + using reference = const handle; + using pointer = arrow_proxy; + + sequence_fast_readonly(handle obj, ssize_t n) : ptr(PySequence_Fast_ITEMS(obj.ptr()) + n) { } + + reference dereference() const { return *ptr; } + void increment() { ++ptr; } + void decrement() { --ptr; } + void advance(ssize_t n) { ptr += n; } + bool equal(const sequence_fast_readonly &b) const { return ptr == b.ptr; } + ssize_t distance_to(const sequence_fast_readonly &b) const { return ptr - b.ptr; } + +private: + PyObject **ptr; +}; + +/// Full read and write access using the sequence protocol: see ``detail::sequence_accessor`` +class sequence_slow_readwrite { +protected: + using iterator_category = std::random_access_iterator_tag; + using value_type = object; + using reference = sequence_accessor; + using pointer = arrow_proxy; + + sequence_slow_readwrite(handle obj, ssize_t index) : obj(obj), index(index) { } + + reference dereference() const { return {obj, static_cast(index)}; } + void increment() { ++index; } + void decrement() { --index; } + void advance(ssize_t n) { index += n; } + bool equal(const sequence_slow_readwrite &b) const { return index == b.index; } + ssize_t distance_to(const sequence_slow_readwrite &b) const { return index - b.index; } + +private: + handle obj; + ssize_t index; +}; + +/// Python's dictionary protocol permits this to be a forward iterator +class dict_readonly { +protected: + using iterator_category = std::forward_iterator_tag; + using value_type = std::pair; + using reference = const value_type; + using pointer = arrow_proxy; + + dict_readonly() = default; + dict_readonly(handle obj, ssize_t pos) : obj(obj), pos(pos) { increment(); } + + reference dereference() const { return {key, value}; } + void increment() { if (!PyDict_Next(obj.ptr(), &pos, &key, &value)) { pos = -1; } } + bool equal(const dict_readonly &b) const { return pos == b.pos; } + +private: + handle obj; + PyObject *key, *value; + ssize_t pos = -1; +}; +NAMESPACE_END(iterator_policies) + +#if !defined(PYPY_VERSION) +using tuple_iterator = generic_iterator; +using list_iterator = generic_iterator; +#else +using tuple_iterator = generic_iterator; +using list_iterator = generic_iterator; +#endif + +using sequence_iterator = generic_iterator; +using dict_iterator = generic_iterator; + +inline bool PyIterable_Check(PyObject *obj) { + PyObject *iter = PyObject_GetIter(obj); + if (iter) { + Py_DECREF(iter); + return true; + } else { + PyErr_Clear(); + return false; + } +} + +inline bool PyNone_Check(PyObject *o) { return o == Py_None; } +#if PY_MAJOR_VERSION >= 3 +inline bool PyEllipsis_Check(PyObject *o) { return o == Py_Ellipsis; } +#endif + +inline bool PyUnicode_Check_Permissive(PyObject *o) { return PyUnicode_Check(o) || PYBIND11_BYTES_CHECK(o); } + +class kwargs_proxy : public handle { +public: + explicit kwargs_proxy(handle h) : handle(h) { } +}; + +class args_proxy : public handle { +public: + explicit args_proxy(handle h) : handle(h) { } + kwargs_proxy operator*() const { return kwargs_proxy(*this); } +}; + +/// Python argument categories (using PEP 448 terms) +template using is_keyword = std::is_base_of; +template using is_s_unpacking = std::is_same; // * unpacking +template using is_ds_unpacking = std::is_same; // ** unpacking +template using is_positional = satisfies_none_of; +template using is_keyword_or_ds = satisfies_any_of; + +// Call argument collector forward declarations +template +class simple_collector; +template +class unpacking_collector; + +NAMESPACE_END(detail) + +// TODO: After the deprecated constructors are removed, this macro can be simplified by +// inheriting ctors: `using Parent::Parent`. It's not an option right now because +// the `using` statement triggers the parent deprecation warning even if the ctor +// isn't even used. +#define PYBIND11_OBJECT_COMMON(Name, Parent, CheckFun) \ + public: \ + PYBIND11_DEPRECATED("Use reinterpret_borrow<"#Name">() or reinterpret_steal<"#Name">()") \ + Name(handle h, bool is_borrowed) : Parent(is_borrowed ? Parent(h, borrowed_t{}) : Parent(h, stolen_t{})) { } \ + Name(handle h, borrowed_t) : Parent(h, borrowed_t{}) { } \ + Name(handle h, stolen_t) : Parent(h, stolen_t{}) { } \ + PYBIND11_DEPRECATED("Use py::isinstance(obj) instead") \ + bool check() const { return m_ptr != nullptr && (bool) CheckFun(m_ptr); } \ + static bool check_(handle h) { return h.ptr() != nullptr && CheckFun(h.ptr()); } + +#define PYBIND11_OBJECT_CVT(Name, Parent, CheckFun, ConvertFun) \ + PYBIND11_OBJECT_COMMON(Name, Parent, CheckFun) \ + /* This is deliberately not 'explicit' to allow implicit conversion from object: */ \ + Name(const object &o) \ + : Parent(check_(o) ? o.inc_ref().ptr() : ConvertFun(o.ptr()), stolen_t{}) \ + { if (!m_ptr) throw error_already_set(); } \ + Name(object &&o) \ + : Parent(check_(o) ? o.release().ptr() : ConvertFun(o.ptr()), stolen_t{}) \ + { if (!m_ptr) throw error_already_set(); } \ + template \ + Name(const ::pybind11::detail::accessor &a) : Name(object(a)) { } + +#define PYBIND11_OBJECT(Name, Parent, CheckFun) \ + PYBIND11_OBJECT_COMMON(Name, Parent, CheckFun) \ + /* This is deliberately not 'explicit' to allow implicit conversion from object: */ \ + Name(const object &o) : Parent(o) { } \ + Name(object &&o) : Parent(std::move(o)) { } + +#define PYBIND11_OBJECT_DEFAULT(Name, Parent, CheckFun) \ + PYBIND11_OBJECT(Name, Parent, CheckFun) \ + Name() : Parent() { } + +/// \addtogroup pytypes +/// @{ + +/** \rst + Wraps a Python iterator so that it can also be used as a C++ input iterator + + Caveat: copying an iterator does not (and cannot) clone the internal + state of the Python iterable. This also applies to the post-increment + operator. This iterator should only be used to retrieve the current + value using ``operator*()``. +\endrst */ +class iterator : public object { +public: + using iterator_category = std::input_iterator_tag; + using difference_type = ssize_t; + using value_type = handle; + using reference = const handle; + using pointer = const handle *; + + PYBIND11_OBJECT_DEFAULT(iterator, object, PyIter_Check) + + iterator& operator++() { + advance(); + return *this; + } + + iterator operator++(int) { + auto rv = *this; + advance(); + return rv; + } + + reference operator*() const { + if (m_ptr && !value.ptr()) { + auto& self = const_cast(*this); + self.advance(); + } + return value; + } + + pointer operator->() const { operator*(); return &value; } + + /** \rst + The value which marks the end of the iteration. ``it == iterator::sentinel()`` + is equivalent to catching ``StopIteration`` in Python. + + .. code-block:: cpp + + void foo(py::iterator it) { + while (it != py::iterator::sentinel()) { + // use `*it` + ++it; + } + } + \endrst */ + static iterator sentinel() { return {}; } + + friend bool operator==(const iterator &a, const iterator &b) { return a->ptr() == b->ptr(); } + friend bool operator!=(const iterator &a, const iterator &b) { return a->ptr() != b->ptr(); } + +private: + void advance() { + value = reinterpret_steal(PyIter_Next(m_ptr)); + if (PyErr_Occurred()) { throw error_already_set(); } + } + +private: + object value = {}; +}; + +class iterable : public object { +public: + PYBIND11_OBJECT_DEFAULT(iterable, object, detail::PyIterable_Check) +}; + +class bytes; + +class str : public object { +public: + PYBIND11_OBJECT_CVT(str, object, detail::PyUnicode_Check_Permissive, raw_str) + + str(const char *c, size_t n) + : object(PyUnicode_FromStringAndSize(c, (ssize_t) n), stolen_t{}) { + if (!m_ptr) pybind11_fail("Could not allocate string object!"); + } + + // 'explicit' is explicitly omitted from the following constructors to allow implicit conversion to py::str from C++ string-like objects + str(const char *c = "") + : object(PyUnicode_FromString(c), stolen_t{}) { + if (!m_ptr) pybind11_fail("Could not allocate string object!"); + } + + str(const std::string &s) : str(s.data(), s.size()) { } + + explicit str(const bytes &b); + + /** \rst + Return a string representation of the object. This is analogous to + the ``str()`` function in Python. + \endrst */ + explicit str(handle h) : object(raw_str(h.ptr()), stolen_t{}) { } + + operator std::string() const { + object temp = *this; + if (PyUnicode_Check(m_ptr)) { + temp = reinterpret_steal(PyUnicode_AsUTF8String(m_ptr)); + if (!temp) + pybind11_fail("Unable to extract string contents! (encoding issue)"); + } + char *buffer; + ssize_t length; + if (PYBIND11_BYTES_AS_STRING_AND_SIZE(temp.ptr(), &buffer, &length)) + pybind11_fail("Unable to extract string contents! (invalid type)"); + return std::string(buffer, (size_t) length); + } + + template + str format(Args &&...args) const { + return attr("format")(std::forward(args)...); + } + +private: + /// Return string representation -- always returns a new reference, even if already a str + static PyObject *raw_str(PyObject *op) { + PyObject *str_value = PyObject_Str(op); +#if PY_MAJOR_VERSION < 3 + if (!str_value) throw error_already_set(); + PyObject *unicode = PyUnicode_FromEncodedObject(str_value, "utf-8", nullptr); + Py_XDECREF(str_value); str_value = unicode; +#endif + return str_value; + } +}; +/// @} pytypes + +inline namespace literals { +/** \rst + String literal version of `str` + \endrst */ +inline str operator"" _s(const char *s, size_t size) { return {s, size}; } +} + +/// \addtogroup pytypes +/// @{ +class bytes : public object { +public: + PYBIND11_OBJECT(bytes, object, PYBIND11_BYTES_CHECK) + + // Allow implicit conversion: + bytes(const char *c = "") + : object(PYBIND11_BYTES_FROM_STRING(c), stolen_t{}) { + if (!m_ptr) pybind11_fail("Could not allocate bytes object!"); + } + + bytes(const char *c, size_t n) + : object(PYBIND11_BYTES_FROM_STRING_AND_SIZE(c, (ssize_t) n), stolen_t{}) { + if (!m_ptr) pybind11_fail("Could not allocate bytes object!"); + } + + // Allow implicit conversion: + bytes(const std::string &s) : bytes(s.data(), s.size()) { } + + explicit bytes(const pybind11::str &s); + + operator std::string() const { + char *buffer; + ssize_t length; + if (PYBIND11_BYTES_AS_STRING_AND_SIZE(m_ptr, &buffer, &length)) + pybind11_fail("Unable to extract bytes contents!"); + return std::string(buffer, (size_t) length); + } +}; + +inline bytes::bytes(const pybind11::str &s) { + object temp = s; + if (PyUnicode_Check(s.ptr())) { + temp = reinterpret_steal(PyUnicode_AsUTF8String(s.ptr())); + if (!temp) + pybind11_fail("Unable to extract string contents! (encoding issue)"); + } + char *buffer; + ssize_t length; + if (PYBIND11_BYTES_AS_STRING_AND_SIZE(temp.ptr(), &buffer, &length)) + pybind11_fail("Unable to extract string contents! (invalid type)"); + auto obj = reinterpret_steal(PYBIND11_BYTES_FROM_STRING_AND_SIZE(buffer, length)); + if (!obj) + pybind11_fail("Could not allocate bytes object!"); + m_ptr = obj.release().ptr(); +} + +inline str::str(const bytes& b) { + char *buffer; + ssize_t length; + if (PYBIND11_BYTES_AS_STRING_AND_SIZE(b.ptr(), &buffer, &length)) + pybind11_fail("Unable to extract bytes contents!"); + auto obj = reinterpret_steal(PyUnicode_FromStringAndSize(buffer, (ssize_t) length)); + if (!obj) + pybind11_fail("Could not allocate string object!"); + m_ptr = obj.release().ptr(); +} + +class none : public object { +public: + PYBIND11_OBJECT(none, object, detail::PyNone_Check) + none() : object(Py_None, borrowed_t{}) { } +}; + +#if PY_MAJOR_VERSION >= 3 +class ellipsis : public object { +public: + PYBIND11_OBJECT(ellipsis, object, detail::PyEllipsis_Check) + ellipsis() : object(Py_Ellipsis, borrowed_t{}) { } +}; +#endif + +class bool_ : public object { +public: + PYBIND11_OBJECT_CVT(bool_, object, PyBool_Check, raw_bool) + bool_() : object(Py_False, borrowed_t{}) { } + // Allow implicit conversion from and to `bool`: + bool_(bool value) : object(value ? Py_True : Py_False, borrowed_t{}) { } + operator bool() const { return m_ptr && PyLong_AsLong(m_ptr) != 0; } + +private: + /// Return the truth value of an object -- always returns a new reference + static PyObject *raw_bool(PyObject *op) { + const auto value = PyObject_IsTrue(op); + if (value == -1) return nullptr; + return handle(value ? Py_True : Py_False).inc_ref().ptr(); + } +}; + +NAMESPACE_BEGIN(detail) +// Converts a value to the given unsigned type. If an error occurs, you get back (Unsigned) -1; +// otherwise you get back the unsigned long or unsigned long long value cast to (Unsigned). +// (The distinction is critically important when casting a returned -1 error value to some other +// unsigned type: (A)-1 != (B)-1 when A and B are unsigned types of different sizes). +template +Unsigned as_unsigned(PyObject *o) { + if (sizeof(Unsigned) <= sizeof(unsigned long) +#if PY_VERSION_HEX < 0x03000000 + || PyInt_Check(o) +#endif + ) { + unsigned long v = PyLong_AsUnsignedLong(o); + return v == (unsigned long) -1 && PyErr_Occurred() ? (Unsigned) -1 : (Unsigned) v; + } + else { + unsigned long long v = PyLong_AsUnsignedLongLong(o); + return v == (unsigned long long) -1 && PyErr_Occurred() ? (Unsigned) -1 : (Unsigned) v; + } +} +NAMESPACE_END(detail) + +class int_ : public object { +public: + PYBIND11_OBJECT_CVT(int_, object, PYBIND11_LONG_CHECK, PyNumber_Long) + int_() : object(PyLong_FromLong(0), stolen_t{}) { } + // Allow implicit conversion from C++ integral types: + template ::value, int> = 0> + int_(T value) { + if (sizeof(T) <= sizeof(long)) { + if (std::is_signed::value) + m_ptr = PyLong_FromLong((long) value); + else + m_ptr = PyLong_FromUnsignedLong((unsigned long) value); + } else { + if (std::is_signed::value) + m_ptr = PyLong_FromLongLong((long long) value); + else + m_ptr = PyLong_FromUnsignedLongLong((unsigned long long) value); + } + if (!m_ptr) pybind11_fail("Could not allocate int object!"); + } + + template ::value, int> = 0> + operator T() const { + return std::is_unsigned::value + ? detail::as_unsigned(m_ptr) + : sizeof(T) <= sizeof(long) + ? (T) PyLong_AsLong(m_ptr) + : (T) PYBIND11_LONG_AS_LONGLONG(m_ptr); + } +}; + +class float_ : public object { +public: + PYBIND11_OBJECT_CVT(float_, object, PyFloat_Check, PyNumber_Float) + // Allow implicit conversion from float/double: + float_(float value) : object(PyFloat_FromDouble((double) value), stolen_t{}) { + if (!m_ptr) pybind11_fail("Could not allocate float object!"); + } + float_(double value = .0) : object(PyFloat_FromDouble((double) value), stolen_t{}) { + if (!m_ptr) pybind11_fail("Could not allocate float object!"); + } + operator float() const { return (float) PyFloat_AsDouble(m_ptr); } + operator double() const { return (double) PyFloat_AsDouble(m_ptr); } +}; + +class weakref : public object { +public: + PYBIND11_OBJECT_DEFAULT(weakref, object, PyWeakref_Check) + explicit weakref(handle obj, handle callback = {}) + : object(PyWeakref_NewRef(obj.ptr(), callback.ptr()), stolen_t{}) { + if (!m_ptr) pybind11_fail("Could not allocate weak reference!"); + } +}; + +class slice : public object { +public: + PYBIND11_OBJECT_DEFAULT(slice, object, PySlice_Check) + slice(ssize_t start_, ssize_t stop_, ssize_t step_) { + int_ start(start_), stop(stop_), step(step_); + m_ptr = PySlice_New(start.ptr(), stop.ptr(), step.ptr()); + if (!m_ptr) pybind11_fail("Could not allocate slice object!"); + } + bool compute(size_t length, size_t *start, size_t *stop, size_t *step, + size_t *slicelength) const { + return PySlice_GetIndicesEx((PYBIND11_SLICE_OBJECT *) m_ptr, + (ssize_t) length, (ssize_t *) start, + (ssize_t *) stop, (ssize_t *) step, + (ssize_t *) slicelength) == 0; + } +}; + +class capsule : public object { +public: + PYBIND11_OBJECT_DEFAULT(capsule, object, PyCapsule_CheckExact) + PYBIND11_DEPRECATED("Use reinterpret_borrow() or reinterpret_steal()") + capsule(PyObject *ptr, bool is_borrowed) : object(is_borrowed ? object(ptr, borrowed_t{}) : object(ptr, stolen_t{})) { } + + explicit capsule(const void *value, const char *name = nullptr, void (*destructor)(PyObject *) = nullptr) + : object(PyCapsule_New(const_cast(value), name, destructor), stolen_t{}) { + if (!m_ptr) + pybind11_fail("Could not allocate capsule object!"); + } + + PYBIND11_DEPRECATED("Please pass a destructor that takes a void pointer as input") + capsule(const void *value, void (*destruct)(PyObject *)) + : object(PyCapsule_New(const_cast(value), nullptr, destruct), stolen_t{}) { + if (!m_ptr) + pybind11_fail("Could not allocate capsule object!"); + } + + capsule(const void *value, void (*destructor)(void *)) { + m_ptr = PyCapsule_New(const_cast(value), nullptr, [](PyObject *o) { + auto destructor = reinterpret_cast(PyCapsule_GetContext(o)); + void *ptr = PyCapsule_GetPointer(o, nullptr); + destructor(ptr); + }); + + if (!m_ptr) + pybind11_fail("Could not allocate capsule object!"); + + if (PyCapsule_SetContext(m_ptr, (void *) destructor) != 0) + pybind11_fail("Could not set capsule context!"); + } + + capsule(void (*destructor)()) { + m_ptr = PyCapsule_New(reinterpret_cast(destructor), nullptr, [](PyObject *o) { + auto destructor = reinterpret_cast(PyCapsule_GetPointer(o, nullptr)); + destructor(); + }); + + if (!m_ptr) + pybind11_fail("Could not allocate capsule object!"); + } + + template operator T *() const { + auto name = this->name(); + T * result = static_cast(PyCapsule_GetPointer(m_ptr, name)); + if (!result) pybind11_fail("Unable to extract capsule contents!"); + return result; + } + + const char *name() const { return PyCapsule_GetName(m_ptr); } +}; + +class tuple : public object { +public: + PYBIND11_OBJECT_CVT(tuple, object, PyTuple_Check, PySequence_Tuple) + explicit tuple(size_t size = 0) : object(PyTuple_New((ssize_t) size), stolen_t{}) { + if (!m_ptr) pybind11_fail("Could not allocate tuple object!"); + } + size_t size() const { return (size_t) PyTuple_Size(m_ptr); } + detail::tuple_accessor operator[](size_t index) const { return {*this, index}; } + detail::item_accessor operator[](handle h) const { return object::operator[](h); } + detail::tuple_iterator begin() const { return {*this, 0}; } + detail::tuple_iterator end() const { return {*this, PyTuple_GET_SIZE(m_ptr)}; } +}; + +class dict : public object { +public: + PYBIND11_OBJECT_CVT(dict, object, PyDict_Check, raw_dict) + dict() : object(PyDict_New(), stolen_t{}) { + if (!m_ptr) pybind11_fail("Could not allocate dict object!"); + } + template ...>::value>, + // MSVC workaround: it can't compile an out-of-line definition, so defer the collector + typename collector = detail::deferred_t, Args...>> + explicit dict(Args &&...args) : dict(collector(std::forward(args)...).kwargs()) { } + + size_t size() const { return (size_t) PyDict_Size(m_ptr); } + detail::dict_iterator begin() const { return {*this, 0}; } + detail::dict_iterator end() const { return {}; } + void clear() const { PyDict_Clear(ptr()); } + bool contains(handle key) const { return PyDict_Contains(ptr(), key.ptr()) == 1; } + bool contains(const char *key) const { return PyDict_Contains(ptr(), pybind11::str(key).ptr()) == 1; } + +private: + /// Call the `dict` Python type -- always returns a new reference + static PyObject *raw_dict(PyObject *op) { + if (PyDict_Check(op)) + return handle(op).inc_ref().ptr(); + return PyObject_CallFunctionObjArgs((PyObject *) &PyDict_Type, op, nullptr); + } +}; + +class sequence : public object { +public: + PYBIND11_OBJECT_DEFAULT(sequence, object, PySequence_Check) + size_t size() const { return (size_t) PySequence_Size(m_ptr); } + detail::sequence_accessor operator[](size_t index) const { return {*this, index}; } + detail::item_accessor operator[](handle h) const { return object::operator[](h); } + detail::sequence_iterator begin() const { return {*this, 0}; } + detail::sequence_iterator end() const { return {*this, PySequence_Size(m_ptr)}; } +}; + +class list : public object { +public: + PYBIND11_OBJECT_CVT(list, object, PyList_Check, PySequence_List) + explicit list(size_t size = 0) : object(PyList_New((ssize_t) size), stolen_t{}) { + if (!m_ptr) pybind11_fail("Could not allocate list object!"); + } + size_t size() const { return (size_t) PyList_Size(m_ptr); } + detail::list_accessor operator[](size_t index) const { return {*this, index}; } + detail::item_accessor operator[](handle h) const { return object::operator[](h); } + detail::list_iterator begin() const { return {*this, 0}; } + detail::list_iterator end() const { return {*this, PyList_GET_SIZE(m_ptr)}; } + template void append(T &&val) const { + PyList_Append(m_ptr, detail::object_or_cast(std::forward(val)).ptr()); + } +}; + +class args : public tuple { PYBIND11_OBJECT_DEFAULT(args, tuple, PyTuple_Check) }; +class kwargs : public dict { PYBIND11_OBJECT_DEFAULT(kwargs, dict, PyDict_Check) }; + +class set : public object { +public: + PYBIND11_OBJECT_CVT(set, object, PySet_Check, PySet_New) + set() : object(PySet_New(nullptr), stolen_t{}) { + if (!m_ptr) pybind11_fail("Could not allocate set object!"); + } + size_t size() const { return (size_t) PySet_Size(m_ptr); } + template bool add(T &&val) const { + return PySet_Add(m_ptr, detail::object_or_cast(std::forward(val)).ptr()) == 0; + } + void clear() const { PySet_Clear(m_ptr); } +}; + +class function : public object { +public: + PYBIND11_OBJECT_DEFAULT(function, object, PyCallable_Check) + handle cpp_function() const { + handle fun = detail::get_function(m_ptr); + if (fun && PyCFunction_Check(fun.ptr())) + return fun; + return handle(); + } + bool is_cpp_function() const { return (bool) cpp_function(); } +}; + +class buffer : public object { +public: + PYBIND11_OBJECT_DEFAULT(buffer, object, PyObject_CheckBuffer) + + buffer_info request(bool writable = false) { + int flags = PyBUF_STRIDES | PyBUF_FORMAT; + if (writable) flags |= PyBUF_WRITABLE; + Py_buffer *view = new Py_buffer(); + if (PyObject_GetBuffer(m_ptr, view, flags) != 0) { + delete view; + throw error_already_set(); + } + return buffer_info(view); + } +}; + +class memoryview : public object { +public: + explicit memoryview(const buffer_info& info) { + static Py_buffer buf { }; + // Py_buffer uses signed sizes, strides and shape!.. + static std::vector py_strides { }; + static std::vector py_shape { }; + buf.buf = info.ptr; + buf.itemsize = info.itemsize; + buf.format = const_cast(info.format.c_str()); + buf.ndim = (int) info.ndim; + buf.len = info.size; + py_strides.clear(); + py_shape.clear(); + for (size_t i = 0; i < (size_t) info.ndim; ++i) { + py_strides.push_back(info.strides[i]); + py_shape.push_back(info.shape[i]); + } + buf.strides = py_strides.data(); + buf.shape = py_shape.data(); + buf.suboffsets = nullptr; + buf.readonly = false; + buf.internal = nullptr; + + m_ptr = PyMemoryView_FromBuffer(&buf); + if (!m_ptr) + pybind11_fail("Unable to create memoryview from buffer descriptor"); + } + + PYBIND11_OBJECT_CVT(memoryview, object, PyMemoryView_Check, PyMemoryView_FromObject) +}; +/// @} pytypes + +/// \addtogroup python_builtins +/// @{ +inline size_t len(handle h) { + ssize_t result = PyObject_Length(h.ptr()); + if (result < 0) + pybind11_fail("Unable to compute length of object"); + return (size_t) result; +} + +inline str repr(handle h) { + PyObject *str_value = PyObject_Repr(h.ptr()); + if (!str_value) throw error_already_set(); +#if PY_MAJOR_VERSION < 3 + PyObject *unicode = PyUnicode_FromEncodedObject(str_value, "utf-8", nullptr); + Py_XDECREF(str_value); str_value = unicode; + if (!str_value) throw error_already_set(); +#endif + return reinterpret_steal(str_value); +} + +inline iterator iter(handle obj) { + PyObject *result = PyObject_GetIter(obj.ptr()); + if (!result) { throw error_already_set(); } + return reinterpret_steal(result); +} +/// @} python_builtins + +NAMESPACE_BEGIN(detail) +template iterator object_api::begin() const { return iter(derived()); } +template iterator object_api::end() const { return iterator::sentinel(); } +template item_accessor object_api::operator[](handle key) const { + return {derived(), reinterpret_borrow(key)}; +} +template item_accessor object_api::operator[](const char *key) const { + return {derived(), pybind11::str(key)}; +} +template obj_attr_accessor object_api::attr(handle key) const { + return {derived(), reinterpret_borrow(key)}; +} +template str_attr_accessor object_api::attr(const char *key) const { + return {derived(), key}; +} +template args_proxy object_api::operator*() const { + return args_proxy(derived().ptr()); +} +template template bool object_api::contains(T &&item) const { + return attr("__contains__")(std::forward(item)).template cast(); +} + +template +pybind11::str object_api::str() const { return pybind11::str(derived()); } + +template +str_attr_accessor object_api::doc() const { return attr("__doc__"); } + +template +handle object_api::get_type() const { return (PyObject *) Py_TYPE(derived().ptr()); } + +template +bool object_api::rich_compare(object_api const &other, int value) const { + int rv = PyObject_RichCompareBool(derived().ptr(), other.derived().ptr(), value); + if (rv == -1) + throw error_already_set(); + return rv == 1; +} + +#define PYBIND11_MATH_OPERATOR_UNARY(op, fn) \ + template object object_api::op() const { \ + object result = reinterpret_steal(fn(derived().ptr())); \ + if (!result.ptr()) \ + throw error_already_set(); \ + return result; \ + } + +#define PYBIND11_MATH_OPERATOR_BINARY(op, fn) \ + template \ + object object_api::op(object_api const &other) const { \ + object result = reinterpret_steal( \ + fn(derived().ptr(), other.derived().ptr())); \ + if (!result.ptr()) \ + throw error_already_set(); \ + return result; \ + } + +PYBIND11_MATH_OPERATOR_UNARY (operator~, PyNumber_Invert) +PYBIND11_MATH_OPERATOR_UNARY (operator-, PyNumber_Negative) +PYBIND11_MATH_OPERATOR_BINARY(operator+, PyNumber_Add) +PYBIND11_MATH_OPERATOR_BINARY(operator+=, PyNumber_InPlaceAdd) +PYBIND11_MATH_OPERATOR_BINARY(operator-, PyNumber_Subtract) +PYBIND11_MATH_OPERATOR_BINARY(operator-=, PyNumber_InPlaceSubtract) +PYBIND11_MATH_OPERATOR_BINARY(operator*, PyNumber_Multiply) +PYBIND11_MATH_OPERATOR_BINARY(operator*=, PyNumber_InPlaceMultiply) +PYBIND11_MATH_OPERATOR_BINARY(operator/, PyNumber_TrueDivide) +PYBIND11_MATH_OPERATOR_BINARY(operator/=, PyNumber_InPlaceTrueDivide) +PYBIND11_MATH_OPERATOR_BINARY(operator|, PyNumber_Or) +PYBIND11_MATH_OPERATOR_BINARY(operator|=, PyNumber_InPlaceOr) +PYBIND11_MATH_OPERATOR_BINARY(operator&, PyNumber_And) +PYBIND11_MATH_OPERATOR_BINARY(operator&=, PyNumber_InPlaceAnd) +PYBIND11_MATH_OPERATOR_BINARY(operator^, PyNumber_Xor) +PYBIND11_MATH_OPERATOR_BINARY(operator^=, PyNumber_InPlaceXor) +PYBIND11_MATH_OPERATOR_BINARY(operator<<, PyNumber_Lshift) +PYBIND11_MATH_OPERATOR_BINARY(operator<<=, PyNumber_InPlaceLshift) +PYBIND11_MATH_OPERATOR_BINARY(operator>>, PyNumber_Rshift) +PYBIND11_MATH_OPERATOR_BINARY(operator>>=, PyNumber_InPlaceRshift) + +#undef PYBIND11_MATH_OPERATOR_UNARY +#undef PYBIND11_MATH_OPERATOR_BINARY + +NAMESPACE_END(detail) +NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/ptocr/postprocess/dbprocess/include/pybind11/stl.h b/ptocr/postprocess/dbprocess/include/pybind11/stl.h new file mode 100644 index 0000000..32f8d29 --- /dev/null +++ b/ptocr/postprocess/dbprocess/include/pybind11/stl.h @@ -0,0 +1,386 @@ +/* + pybind11/stl.h: Transparent conversion for STL data types + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "pybind11.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable: 4127) // warning C4127: Conditional expression is constant +#endif + +#ifdef __has_include +// std::optional (but including it in c++14 mode isn't allowed) +# if defined(PYBIND11_CPP17) && __has_include() +# include +# define PYBIND11_HAS_OPTIONAL 1 +# endif +// std::experimental::optional (but not allowed in c++11 mode) +# if defined(PYBIND11_CPP14) && (__has_include() && \ + !__has_include()) +# include +# define PYBIND11_HAS_EXP_OPTIONAL 1 +# endif +// std::variant +# if defined(PYBIND11_CPP17) && __has_include() +# include +# define PYBIND11_HAS_VARIANT 1 +# endif +#elif defined(_MSC_VER) && defined(PYBIND11_CPP17) +# include +# include +# define PYBIND11_HAS_OPTIONAL 1 +# define PYBIND11_HAS_VARIANT 1 +#endif + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +NAMESPACE_BEGIN(detail) + +/// Extracts an const lvalue reference or rvalue reference for U based on the type of T (e.g. for +/// forwarding a container element). Typically used indirect via forwarded_type(), below. +template +using forwarded_type = conditional_t< + std::is_lvalue_reference::value, remove_reference_t &, remove_reference_t &&>; + +/// Forwards a value U as rvalue or lvalue according to whether T is rvalue or lvalue; typically +/// used for forwarding a container's elements. +template +forwarded_type forward_like(U &&u) { + return std::forward>(std::forward(u)); +} + +template struct set_caster { + using type = Type; + using key_conv = make_caster; + + bool load(handle src, bool convert) { + if (!isinstance(src)) + return false; + auto s = reinterpret_borrow(src); + value.clear(); + for (auto entry : s) { + key_conv conv; + if (!conv.load(entry, convert)) + return false; + value.insert(cast_op(std::move(conv))); + } + return true; + } + + template + static handle cast(T &&src, return_value_policy policy, handle parent) { + if (!std::is_lvalue_reference::value) + policy = return_value_policy_override::policy(policy); + pybind11::set s; + for (auto &&value : src) { + auto value_ = reinterpret_steal(key_conv::cast(forward_like(value), policy, parent)); + if (!value_ || !s.add(value_)) + return handle(); + } + return s.release(); + } + + PYBIND11_TYPE_CASTER(type, _("Set[") + key_conv::name + _("]")); +}; + +template struct map_caster { + using key_conv = make_caster; + using value_conv = make_caster; + + bool load(handle src, bool convert) { + if (!isinstance(src)) + return false; + auto d = reinterpret_borrow(src); + value.clear(); + for (auto it : d) { + key_conv kconv; + value_conv vconv; + if (!kconv.load(it.first.ptr(), convert) || + !vconv.load(it.second.ptr(), convert)) + return false; + value.emplace(cast_op(std::move(kconv)), cast_op(std::move(vconv))); + } + return true; + } + + template + static handle cast(T &&src, return_value_policy policy, handle parent) { + dict d; + return_value_policy policy_key = policy; + return_value_policy policy_value = policy; + if (!std::is_lvalue_reference::value) { + policy_key = return_value_policy_override::policy(policy_key); + policy_value = return_value_policy_override::policy(policy_value); + } + for (auto &&kv : src) { + auto key = reinterpret_steal(key_conv::cast(forward_like(kv.first), policy_key, parent)); + auto value = reinterpret_steal(value_conv::cast(forward_like(kv.second), policy_value, parent)); + if (!key || !value) + return handle(); + d[key] = value; + } + return d.release(); + } + + PYBIND11_TYPE_CASTER(Type, _("Dict[") + key_conv::name + _(", ") + value_conv::name + _("]")); +}; + +template struct list_caster { + using value_conv = make_caster; + + bool load(handle src, bool convert) { + if (!isinstance(src) || isinstance(src)) + return false; + auto s = reinterpret_borrow(src); + value.clear(); + reserve_maybe(s, &value); + for (auto it : s) { + value_conv conv; + if (!conv.load(it, convert)) + return false; + value.push_back(cast_op(std::move(conv))); + } + return true; + } + +private: + template ().reserve(0)), void>::value, int> = 0> + void reserve_maybe(sequence s, Type *) { value.reserve(s.size()); } + void reserve_maybe(sequence, void *) { } + +public: + template + static handle cast(T &&src, return_value_policy policy, handle parent) { + if (!std::is_lvalue_reference::value) + policy = return_value_policy_override::policy(policy); + list l(src.size()); + size_t index = 0; + for (auto &&value : src) { + auto value_ = reinterpret_steal(value_conv::cast(forward_like(value), policy, parent)); + if (!value_) + return handle(); + PyList_SET_ITEM(l.ptr(), (ssize_t) index++, value_.release().ptr()); // steals a reference + } + return l.release(); + } + + PYBIND11_TYPE_CASTER(Type, _("List[") + value_conv::name + _("]")); +}; + +template struct type_caster> + : list_caster, Type> { }; + +template struct type_caster> + : list_caster, Type> { }; + +template struct type_caster> + : list_caster, Type> { }; + +template struct array_caster { + using value_conv = make_caster; + +private: + template + bool require_size(enable_if_t size) { + if (value.size() != size) + value.resize(size); + return true; + } + template + bool require_size(enable_if_t size) { + return size == Size; + } + +public: + bool load(handle src, bool convert) { + if (!isinstance(src)) + return false; + auto l = reinterpret_borrow(src); + if (!require_size(l.size())) + return false; + size_t ctr = 0; + for (auto it : l) { + value_conv conv; + if (!conv.load(it, convert)) + return false; + value[ctr++] = cast_op(std::move(conv)); + } + return true; + } + + template + static handle cast(T &&src, return_value_policy policy, handle parent) { + list l(src.size()); + size_t index = 0; + for (auto &&value : src) { + auto value_ = reinterpret_steal(value_conv::cast(forward_like(value), policy, parent)); + if (!value_) + return handle(); + PyList_SET_ITEM(l.ptr(), (ssize_t) index++, value_.release().ptr()); // steals a reference + } + return l.release(); + } + + PYBIND11_TYPE_CASTER(ArrayType, _("List[") + value_conv::name + _(_(""), _("[") + _() + _("]")) + _("]")); +}; + +template struct type_caster> + : array_caster, Type, false, Size> { }; + +template struct type_caster> + : array_caster, Type, true> { }; + +template struct type_caster> + : set_caster, Key> { }; + +template struct type_caster> + : set_caster, Key> { }; + +template struct type_caster> + : map_caster, Key, Value> { }; + +template struct type_caster> + : map_caster, Key, Value> { }; + +// This type caster is intended to be used for std::optional and std::experimental::optional +template struct optional_caster { + using value_conv = make_caster; + + template + static handle cast(T_ &&src, return_value_policy policy, handle parent) { + if (!src) + return none().inc_ref(); + policy = return_value_policy_override::policy(policy); + return value_conv::cast(*std::forward(src), policy, parent); + } + + bool load(handle src, bool convert) { + if (!src) { + return false; + } else if (src.is_none()) { + return true; // default-constructed value is already empty + } + value_conv inner_caster; + if (!inner_caster.load(src, convert)) + return false; + + value.emplace(cast_op(std::move(inner_caster))); + return true; + } + + PYBIND11_TYPE_CASTER(T, _("Optional[") + value_conv::name + _("]")); +}; + +#if PYBIND11_HAS_OPTIONAL +template struct type_caster> + : public optional_caster> {}; + +template<> struct type_caster + : public void_caster {}; +#endif + +#if PYBIND11_HAS_EXP_OPTIONAL +template struct type_caster> + : public optional_caster> {}; + +template<> struct type_caster + : public void_caster {}; +#endif + +/// Visit a variant and cast any found type to Python +struct variant_caster_visitor { + return_value_policy policy; + handle parent; + + using result_type = handle; // required by boost::variant in C++11 + + template + result_type operator()(T &&src) const { + return make_caster::cast(std::forward(src), policy, parent); + } +}; + +/// Helper class which abstracts away variant's `visit` function. `std::variant` and similar +/// `namespace::variant` types which provide a `namespace::visit()` function are handled here +/// automatically using argument-dependent lookup. Users can provide specializations for other +/// variant-like classes, e.g. `boost::variant` and `boost::apply_visitor`. +template class Variant> +struct visit_helper { + template + static auto call(Args &&...args) -> decltype(visit(std::forward(args)...)) { + return visit(std::forward(args)...); + } +}; + +/// Generic variant caster +template struct variant_caster; + +template class V, typename... Ts> +struct variant_caster> { + static_assert(sizeof...(Ts) > 0, "Variant must consist of at least one alternative."); + + template + bool load_alternative(handle src, bool convert, type_list) { + auto caster = make_caster(); + if (caster.load(src, convert)) { + value = cast_op(caster); + return true; + } + return load_alternative(src, convert, type_list{}); + } + + bool load_alternative(handle, bool, type_list<>) { return false; } + + bool load(handle src, bool convert) { + // Do a first pass without conversions to improve constructor resolution. + // E.g. `py::int_(1).cast>()` needs to fill the `int` + // slot of the variant. Without two-pass loading `double` would be filled + // because it appears first and a conversion is possible. + if (convert && load_alternative(src, false, type_list{})) + return true; + return load_alternative(src, convert, type_list{}); + } + + template + static handle cast(Variant &&src, return_value_policy policy, handle parent) { + return visit_helper::call(variant_caster_visitor{policy, parent}, + std::forward(src)); + } + + using Type = V; + PYBIND11_TYPE_CASTER(Type, _("Union[") + detail::concat(make_caster::name...) + _("]")); +}; + +#if PYBIND11_HAS_VARIANT +template +struct type_caster> : variant_caster> { }; +#endif + +NAMESPACE_END(detail) + +inline std::ostream &operator<<(std::ostream &os, const handle &obj) { + os << (std::string) str(obj); + return os; +} + +NAMESPACE_END(PYBIND11_NAMESPACE) + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif diff --git a/ptocr/postprocess/dbprocess/include/pybind11/stl_bind.h b/ptocr/postprocess/dbprocess/include/pybind11/stl_bind.h new file mode 100644 index 0000000..38dd68f --- /dev/null +++ b/ptocr/postprocess/dbprocess/include/pybind11/stl_bind.h @@ -0,0 +1,599 @@ +/* + pybind11/std_bind.h: Binding generators for STL data types + + Copyright (c) 2016 Sergey Lyskov and Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "detail/common.h" +#include "operators.h" + +#include +#include + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +NAMESPACE_BEGIN(detail) + +/* SFINAE helper class used by 'is_comparable */ +template struct container_traits { + template static std::true_type test_comparable(decltype(std::declval() == std::declval())*); + template static std::false_type test_comparable(...); + template static std::true_type test_value(typename T2::value_type *); + template static std::false_type test_value(...); + template static std::true_type test_pair(typename T2::first_type *, typename T2::second_type *); + template static std::false_type test_pair(...); + + static constexpr const bool is_comparable = std::is_same(nullptr))>::value; + static constexpr const bool is_pair = std::is_same(nullptr, nullptr))>::value; + static constexpr const bool is_vector = std::is_same(nullptr))>::value; + static constexpr const bool is_element = !is_pair && !is_vector; +}; + +/* Default: is_comparable -> std::false_type */ +template +struct is_comparable : std::false_type { }; + +/* For non-map data structures, check whether operator== can be instantiated */ +template +struct is_comparable< + T, enable_if_t::is_element && + container_traits::is_comparable>> + : std::true_type { }; + +/* For a vector/map data structure, recursively check the value type (which is std::pair for maps) */ +template +struct is_comparable::is_vector>> { + static constexpr const bool value = + is_comparable::value; +}; + +/* For pairs, recursively check the two data types */ +template +struct is_comparable::is_pair>> { + static constexpr const bool value = + is_comparable::value && + is_comparable::value; +}; + +/* Fallback functions */ +template void vector_if_copy_constructible(const Args &...) { } +template void vector_if_equal_operator(const Args &...) { } +template void vector_if_insertion_operator(const Args &...) { } +template void vector_modifiers(const Args &...) { } + +template +void vector_if_copy_constructible(enable_if_t::value, Class_> &cl) { + cl.def(init(), "Copy constructor"); +} + +template +void vector_if_equal_operator(enable_if_t::value, Class_> &cl) { + using T = typename Vector::value_type; + + cl.def(self == self); + cl.def(self != self); + + cl.def("count", + [](const Vector &v, const T &x) { + return std::count(v.begin(), v.end(), x); + }, + arg("x"), + "Return the number of times ``x`` appears in the list" + ); + + cl.def("remove", [](Vector &v, const T &x) { + auto p = std::find(v.begin(), v.end(), x); + if (p != v.end()) + v.erase(p); + else + throw value_error(); + }, + arg("x"), + "Remove the first item from the list whose value is x. " + "It is an error if there is no such item." + ); + + cl.def("__contains__", + [](const Vector &v, const T &x) { + return std::find(v.begin(), v.end(), x) != v.end(); + }, + arg("x"), + "Return true the container contains ``x``" + ); +} + +// Vector modifiers -- requires a copyable vector_type: +// (Technically, some of these (pop and __delitem__) don't actually require copyability, but it seems +// silly to allow deletion but not insertion, so include them here too.) +template +void vector_modifiers(enable_if_t::value, Class_> &cl) { + using T = typename Vector::value_type; + using SizeType = typename Vector::size_type; + using DiffType = typename Vector::difference_type; + + cl.def("append", + [](Vector &v, const T &value) { v.push_back(value); }, + arg("x"), + "Add an item to the end of the list"); + + cl.def(init([](iterable it) { + auto v = std::unique_ptr(new Vector()); + v->reserve(len(it)); + for (handle h : it) + v->push_back(h.cast()); + return v.release(); + })); + + cl.def("extend", + [](Vector &v, const Vector &src) { + v.insert(v.end(), src.begin(), src.end()); + }, + arg("L"), + "Extend the list by appending all the items in the given list" + ); + + cl.def("insert", + [](Vector &v, SizeType i, const T &x) { + if (i > v.size()) + throw index_error(); + v.insert(v.begin() + (DiffType) i, x); + }, + arg("i") , arg("x"), + "Insert an item at a given position." + ); + + cl.def("pop", + [](Vector &v) { + if (v.empty()) + throw index_error(); + T t = v.back(); + v.pop_back(); + return t; + }, + "Remove and return the last item" + ); + + cl.def("pop", + [](Vector &v, SizeType i) { + if (i >= v.size()) + throw index_error(); + T t = v[i]; + v.erase(v.begin() + (DiffType) i); + return t; + }, + arg("i"), + "Remove and return the item at index ``i``" + ); + + cl.def("__setitem__", + [](Vector &v, SizeType i, const T &t) { + if (i >= v.size()) + throw index_error(); + v[i] = t; + } + ); + + /// Slicing protocol + cl.def("__getitem__", + [](const Vector &v, slice slice) -> Vector * { + size_t start, stop, step, slicelength; + + if (!slice.compute(v.size(), &start, &stop, &step, &slicelength)) + throw error_already_set(); + + Vector *seq = new Vector(); + seq->reserve((size_t) slicelength); + + for (size_t i=0; ipush_back(v[start]); + start += step; + } + return seq; + }, + arg("s"), + "Retrieve list elements using a slice object" + ); + + cl.def("__setitem__", + [](Vector &v, slice slice, const Vector &value) { + size_t start, stop, step, slicelength; + if (!slice.compute(v.size(), &start, &stop, &step, &slicelength)) + throw error_already_set(); + + if (slicelength != value.size()) + throw std::runtime_error("Left and right hand size of slice assignment have different sizes!"); + + for (size_t i=0; i= v.size()) + throw index_error(); + v.erase(v.begin() + DiffType(i)); + }, + "Delete the list elements at index ``i``" + ); + + cl.def("__delitem__", + [](Vector &v, slice slice) { + size_t start, stop, step, slicelength; + + if (!slice.compute(v.size(), &start, &stop, &step, &slicelength)) + throw error_already_set(); + + if (step == 1 && false) { + v.erase(v.begin() + (DiffType) start, v.begin() + DiffType(start + slicelength)); + } else { + for (size_t i = 0; i < slicelength; ++i) { + v.erase(v.begin() + DiffType(start)); + start += step - 1; + } + } + }, + "Delete list elements using a slice object" + ); + +} + +// If the type has an operator[] that doesn't return a reference (most notably std::vector), +// we have to access by copying; otherwise we return by reference. +template using vector_needs_copy = negation< + std::is_same()[typename Vector::size_type()]), typename Vector::value_type &>>; + +// The usual case: access and iterate by reference +template +void vector_accessor(enable_if_t::value, Class_> &cl) { + using T = typename Vector::value_type; + using SizeType = typename Vector::size_type; + using ItType = typename Vector::iterator; + + cl.def("__getitem__", + [](Vector &v, SizeType i) -> T & { + if (i >= v.size()) + throw index_error(); + return v[i]; + }, + return_value_policy::reference_internal // ref + keepalive + ); + + cl.def("__iter__", + [](Vector &v) { + return make_iterator< + return_value_policy::reference_internal, ItType, ItType, T&>( + v.begin(), v.end()); + }, + keep_alive<0, 1>() /* Essential: keep list alive while iterator exists */ + ); +} + +// The case for special objects, like std::vector, that have to be returned-by-copy: +template +void vector_accessor(enable_if_t::value, Class_> &cl) { + using T = typename Vector::value_type; + using SizeType = typename Vector::size_type; + using ItType = typename Vector::iterator; + cl.def("__getitem__", + [](const Vector &v, SizeType i) -> T { + if (i >= v.size()) + throw index_error(); + return v[i]; + } + ); + + cl.def("__iter__", + [](Vector &v) { + return make_iterator< + return_value_policy::copy, ItType, ItType, T>( + v.begin(), v.end()); + }, + keep_alive<0, 1>() /* Essential: keep list alive while iterator exists */ + ); +} + +template auto vector_if_insertion_operator(Class_ &cl, std::string const &name) + -> decltype(std::declval() << std::declval(), void()) { + using size_type = typename Vector::size_type; + + cl.def("__repr__", + [name](Vector &v) { + std::ostringstream s; + s << name << '['; + for (size_type i=0; i < v.size(); ++i) { + s << v[i]; + if (i != v.size() - 1) + s << ", "; + } + s << ']'; + return s.str(); + }, + "Return the canonical string representation of this list." + ); +} + +// Provide the buffer interface for vectors if we have data() and we have a format for it +// GCC seems to have "void std::vector::data()" - doing SFINAE on the existence of data() is insufficient, we need to check it returns an appropriate pointer +template +struct vector_has_data_and_format : std::false_type {}; +template +struct vector_has_data_and_format::format(), std::declval().data()), typename Vector::value_type*>::value>> : std::true_type {}; + +// Add the buffer interface to a vector +template +enable_if_t...>::value> +vector_buffer(Class_& cl) { + using T = typename Vector::value_type; + + static_assert(vector_has_data_and_format::value, "There is not an appropriate format descriptor for this vector"); + + // numpy.h declares this for arbitrary types, but it may raise an exception and crash hard at runtime if PYBIND11_NUMPY_DTYPE hasn't been called, so check here + format_descriptor::format(); + + cl.def_buffer([](Vector& v) -> buffer_info { + return buffer_info(v.data(), static_cast(sizeof(T)), format_descriptor::format(), 1, {v.size()}, {sizeof(T)}); + }); + + cl.def(init([](buffer buf) { + auto info = buf.request(); + if (info.ndim != 1 || info.strides[0] % static_cast(sizeof(T))) + throw type_error("Only valid 1D buffers can be copied to a vector"); + if (!detail::compare_buffer_info::compare(info) || (ssize_t) sizeof(T) != info.itemsize) + throw type_error("Format mismatch (Python: " + info.format + " C++: " + format_descriptor::format() + ")"); + + auto vec = std::unique_ptr(new Vector()); + vec->reserve((size_t) info.shape[0]); + T *p = static_cast(info.ptr); + ssize_t step = info.strides[0] / static_cast(sizeof(T)); + T *end = p + info.shape[0] * step; + for (; p != end; p += step) + vec->push_back(*p); + return vec.release(); + })); + + return; +} + +template +enable_if_t...>::value> vector_buffer(Class_&) {} + +NAMESPACE_END(detail) + +// +// std::vector +// +template , typename... Args> +class_ bind_vector(handle scope, std::string const &name, Args&&... args) { + using Class_ = class_; + + // If the value_type is unregistered (e.g. a converting type) or is itself registered + // module-local then make the vector binding module-local as well: + using vtype = typename Vector::value_type; + auto vtype_info = detail::get_type_info(typeid(vtype)); + bool local = !vtype_info || vtype_info->module_local; + + Class_ cl(scope, name.c_str(), pybind11::module_local(local), std::forward(args)...); + + // Declare the buffer interface if a buffer_protocol() is passed in + detail::vector_buffer(cl); + + cl.def(init<>()); + + // Register copy constructor (if possible) + detail::vector_if_copy_constructible(cl); + + // Register comparison-related operators and functions (if possible) + detail::vector_if_equal_operator(cl); + + // Register stream insertion operator (if possible) + detail::vector_if_insertion_operator(cl, name); + + // Modifiers require copyable vector value type + detail::vector_modifiers(cl); + + // Accessor and iterator; return by value if copyable, otherwise we return by ref + keep-alive + detail::vector_accessor(cl); + + cl.def("__bool__", + [](const Vector &v) -> bool { + return !v.empty(); + }, + "Check whether the list is nonempty" + ); + + cl.def("__len__", &Vector::size); + + + + +#if 0 + // C++ style functions deprecated, leaving it here as an example + cl.def(init()); + + cl.def("resize", + (void (Vector::*) (size_type count)) & Vector::resize, + "changes the number of elements stored"); + + cl.def("erase", + [](Vector &v, SizeType i) { + if (i >= v.size()) + throw index_error(); + v.erase(v.begin() + i); + }, "erases element at index ``i``"); + + cl.def("empty", &Vector::empty, "checks whether the container is empty"); + cl.def("size", &Vector::size, "returns the number of elements"); + cl.def("push_back", (void (Vector::*)(const T&)) &Vector::push_back, "adds an element to the end"); + cl.def("pop_back", &Vector::pop_back, "removes the last element"); + + cl.def("max_size", &Vector::max_size, "returns the maximum possible number of elements"); + cl.def("reserve", &Vector::reserve, "reserves storage"); + cl.def("capacity", &Vector::capacity, "returns the number of elements that can be held in currently allocated storage"); + cl.def("shrink_to_fit", &Vector::shrink_to_fit, "reduces memory usage by freeing unused memory"); + + cl.def("clear", &Vector::clear, "clears the contents"); + cl.def("swap", &Vector::swap, "swaps the contents"); + + cl.def("front", [](Vector &v) { + if (v.size()) return v.front(); + else throw index_error(); + }, "access the first element"); + + cl.def("back", [](Vector &v) { + if (v.size()) return v.back(); + else throw index_error(); + }, "access the last element "); + +#endif + + return cl; +} + + + +// +// std::map, std::unordered_map +// + +NAMESPACE_BEGIN(detail) + +/* Fallback functions */ +template void map_if_insertion_operator(const Args &...) { } +template void map_assignment(const Args &...) { } + +// Map assignment when copy-assignable: just copy the value +template +void map_assignment(enable_if_t::value, Class_> &cl) { + using KeyType = typename Map::key_type; + using MappedType = typename Map::mapped_type; + + cl.def("__setitem__", + [](Map &m, const KeyType &k, const MappedType &v) { + auto it = m.find(k); + if (it != m.end()) it->second = v; + else m.emplace(k, v); + } + ); +} + +// Not copy-assignable, but still copy-constructible: we can update the value by erasing and reinserting +template +void map_assignment(enable_if_t< + !std::is_copy_assignable::value && + is_copy_constructible::value, + Class_> &cl) { + using KeyType = typename Map::key_type; + using MappedType = typename Map::mapped_type; + + cl.def("__setitem__", + [](Map &m, const KeyType &k, const MappedType &v) { + // We can't use m[k] = v; because value type might not be default constructable + auto r = m.emplace(k, v); + if (!r.second) { + // value type is not copy assignable so the only way to insert it is to erase it first... + m.erase(r.first); + m.emplace(k, v); + } + } + ); +} + + +template auto map_if_insertion_operator(Class_ &cl, std::string const &name) +-> decltype(std::declval() << std::declval() << std::declval(), void()) { + + cl.def("__repr__", + [name](Map &m) { + std::ostringstream s; + s << name << '{'; + bool f = false; + for (auto const &kv : m) { + if (f) + s << ", "; + s << kv.first << ": " << kv.second; + f = true; + } + s << '}'; + return s.str(); + }, + "Return the canonical string representation of this map." + ); +} + + +NAMESPACE_END(detail) + +template , typename... Args> +class_ bind_map(handle scope, const std::string &name, Args&&... args) { + using KeyType = typename Map::key_type; + using MappedType = typename Map::mapped_type; + using Class_ = class_; + + // If either type is a non-module-local bound type then make the map binding non-local as well; + // otherwise (e.g. both types are either module-local or converting) the map will be + // module-local. + auto tinfo = detail::get_type_info(typeid(MappedType)); + bool local = !tinfo || tinfo->module_local; + if (local) { + tinfo = detail::get_type_info(typeid(KeyType)); + local = !tinfo || tinfo->module_local; + } + + Class_ cl(scope, name.c_str(), pybind11::module_local(local), std::forward(args)...); + + cl.def(init<>()); + + // Register stream insertion operator (if possible) + detail::map_if_insertion_operator(cl, name); + + cl.def("__bool__", + [](const Map &m) -> bool { return !m.empty(); }, + "Check whether the map is nonempty" + ); + + cl.def("__iter__", + [](Map &m) { return make_key_iterator(m.begin(), m.end()); }, + keep_alive<0, 1>() /* Essential: keep list alive while iterator exists */ + ); + + cl.def("items", + [](Map &m) { return make_iterator(m.begin(), m.end()); }, + keep_alive<0, 1>() /* Essential: keep list alive while iterator exists */ + ); + + cl.def("__getitem__", + [](Map &m, const KeyType &k) -> MappedType & { + auto it = m.find(k); + if (it == m.end()) + throw key_error(); + return it->second; + }, + return_value_policy::reference_internal // ref + keepalive + ); + + // Assignment provided only if the type is copyable + detail::map_assignment(cl); + + cl.def("__delitem__", + [](Map &m, const KeyType &k) { + auto it = m.find(k); + if (it == m.end()) + throw key_error(); + m.erase(it); + } + ); + + cl.def("__len__", &Map::size); + + return cl; +} + +NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/ptocr/postprocess/dbprocess/include/pybind11/typeid.h b/ptocr/postprocess/dbprocess/include/pybind11/typeid.h new file mode 100644 index 0000000..c903fb1 --- /dev/null +++ b/ptocr/postprocess/dbprocess/include/pybind11/typeid.h @@ -0,0 +1,53 @@ +/* + pybind11/typeid.h: Compiler-independent access to type identifiers + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include +#include + +#if defined(__GNUG__) +#include +#endif + +NAMESPACE_BEGIN(pybind11) +NAMESPACE_BEGIN(detail) +/// Erase all occurrences of a substring +inline void erase_all(std::string &string, const std::string &search) { + for (size_t pos = 0;;) { + pos = string.find(search, pos); + if (pos == std::string::npos) break; + string.erase(pos, search.length()); + } +} + +PYBIND11_NOINLINE inline void clean_type_id(std::string &name) { +#if defined(__GNUG__) + int status = 0; + std::unique_ptr res { + abi::__cxa_demangle(name.c_str(), nullptr, nullptr, &status), std::free }; + if (status == 0) + name = res.get(); +#else + detail::erase_all(name, "class "); + detail::erase_all(name, "struct "); + detail::erase_all(name, "enum "); +#endif + detail::erase_all(name, "pybind11::"); +} +NAMESPACE_END(detail) + +/// Return a string representation of a C++ type +template static std::string type_id() { + std::string name(typeid(T).name()); + detail::clean_type_id(name); + return name; +} + +NAMESPACE_END(pybind11) diff --git a/ptocr/postprocess/lanms/.gitignore b/ptocr/postprocess/lanms/.gitignore new file mode 100644 index 0000000..6a57227 --- /dev/null +++ b/ptocr/postprocess/lanms/.gitignore @@ -0,0 +1 @@ +adaptor.so diff --git a/ptocr/postprocess/lanms/.ycm_extra_conf.py b/ptocr/postprocess/lanms/.ycm_extra_conf.py new file mode 100644 index 0000000..cd1a74e --- /dev/null +++ b/ptocr/postprocess/lanms/.ycm_extra_conf.py @@ -0,0 +1,140 @@ +#!/usr/bin/env python +# +# Copyright (C) 2014 Google Inc. +# +# This file is part of YouCompleteMe. +# +# YouCompleteMe is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# YouCompleteMe is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with YouCompleteMe. If not, see . + +import os +import sys +import glob +import ycm_core + +# These are the compilation flags that will be used in case there's no +# compilation database set (by default, one is not set). +# CHANGE THIS LIST OF FLAGS. YES, THIS IS THE DROID YOU HAVE BEEN LOOKING FOR. +sys.path.append(os.path.dirname(os.path.abspath(__file__))) + + +BASE_DIR = os.path.dirname(os.path.realpath(__file__)) + +from plumbum.cmd import python_config + + +flags = [ + '-Wall', + '-Wextra', + '-Wnon-virtual-dtor', + '-Winvalid-pch', + '-Wno-unused-local-typedefs', + '-std=c++11', + '-x', 'c++', + '-Iinclude', +] + python_config('--cflags').split() + + +# Set this to the absolute path to the folder (NOT the file!) containing the +# compile_commands.json file to use that instead of 'flags'. See here for +# more details: http://clang.llvm.org/docs/JSONCompilationDatabase.html +# +# Most projects will NOT need to set this to anything; you can just change the +# 'flags' list of compilation flags. +compilation_database_folder = '' + +if os.path.exists( compilation_database_folder ): + database = ycm_core.CompilationDatabase( compilation_database_folder ) +else: + database = None + +SOURCE_EXTENSIONS = [ '.cpp', '.cxx', '.cc', '.c', '.m', '.mm' ] + +def DirectoryOfThisScript(): + return os.path.dirname( os.path.abspath( __file__ ) ) + + +def MakeRelativePathsInFlagsAbsolute( flags, working_directory ): + if not working_directory: + return list( flags ) + new_flags = [] + make_next_absolute = False + path_flags = [ '-isystem', '-I', '-iquote', '--sysroot=' ] + for flag in flags: + new_flag = flag + + if make_next_absolute: + make_next_absolute = False + if not flag.startswith( '/' ): + new_flag = os.path.join( working_directory, flag ) + + for path_flag in path_flags: + if flag == path_flag: + make_next_absolute = True + break + + if flag.startswith( path_flag ): + path = flag[ len( path_flag ): ] + new_flag = path_flag + os.path.join( working_directory, path ) + break + + if new_flag: + new_flags.append( new_flag ) + return new_flags + + +def IsHeaderFile( filename ): + extension = os.path.splitext( filename )[ 1 ] + return extension in [ '.h', '.hxx', '.hpp', '.hh' ] + + +def GetCompilationInfoForFile( filename ): + # The compilation_commands.json file generated by CMake does not have entries + # for header files. So we do our best by asking the db for flags for a + # corresponding source file, if any. If one exists, the flags for that file + # should be good enough. + if IsHeaderFile( filename ): + basename = os.path.splitext( filename )[ 0 ] + for extension in SOURCE_EXTENSIONS: + replacement_file = basename + extension + if os.path.exists( replacement_file ): + compilation_info = database.GetCompilationInfoForFile( + replacement_file ) + if compilation_info.compiler_flags_: + return compilation_info + return None + return database.GetCompilationInfoForFile( filename ) + + +# This is the entry point; this function is called by ycmd to produce flags for +# a file. +def FlagsForFile( filename, **kwargs ): + if database: + # Bear in mind that compilation_info.compiler_flags_ does NOT return a + # python list, but a "list-like" StringVec object + compilation_info = GetCompilationInfoForFile( filename ) + if not compilation_info: + return None + + final_flags = MakeRelativePathsInFlagsAbsolute( + compilation_info.compiler_flags_, + compilation_info.compiler_working_dir_ ) + else: + relative_to = DirectoryOfThisScript() + final_flags = MakeRelativePathsInFlagsAbsolute( flags, relative_to ) + + return { + 'flags': final_flags, + 'do_cache': True + } + diff --git a/ptocr/postprocess/lanms/Makefile b/ptocr/postprocess/lanms/Makefile new file mode 100644 index 0000000..416871d --- /dev/null +++ b/ptocr/postprocess/lanms/Makefile @@ -0,0 +1,13 @@ +CXXFLAGS = -I include -std=c++11 -O3 $(shell python3-config --cflags) +LDFLAGS = $(shell python3-config --ldflags) + +DEPS = lanms.h $(shell find include -xtype f) +CXX_SOURCES = adaptor.cpp include/clipper/clipper.cpp + +LIB_SO = adaptor.so + +$(LIB_SO): $(CXX_SOURCES) $(DEPS) + $(CXX) -o $@ $(CXXFLAGS) $(LDFLAGS) $(CXX_SOURCES) --shared -fPIC + +clean: + rm -rf $(LIB_SO) diff --git a/ptocr/postprocess/lanms/__init__.py b/ptocr/postprocess/lanms/__init__.py new file mode 100644 index 0000000..649d646 --- /dev/null +++ b/ptocr/postprocess/lanms/__init__.py @@ -0,0 +1,20 @@ +import subprocess +import os +import numpy as np + +BASE_DIR = os.path.dirname(os.path.realpath(__file__)) + +if subprocess.call(['make', '-C', BASE_DIR]) != 0: # return value + raise RuntimeError('Cannot compile lanms: {}'.format(BASE_DIR)) + + +def merge_quadrangle_n9(polys, thres=0.3, precision=10000): + from .adaptor import merge_quadrangle_n9 as nms_impl + if len(polys) == 0: + return np.array([], dtype='float32') + p = polys.copy() + p[:,:8] *= precision + ret = np.array(nms_impl(p, thres), dtype='float32') + ret[:,:8] /= precision + return ret + diff --git a/ptocr/postprocess/lanms/__main__.py b/ptocr/postprocess/lanms/__main__.py new file mode 100644 index 0000000..72bba36 --- /dev/null +++ b/ptocr/postprocess/lanms/__main__.py @@ -0,0 +1,10 @@ +import numpy as np + + +from . import merge_quadrangle_n9 + +if __name__ == '__main__': + # unit square with confidence 1 + q = np.array([0, 0, 0, 1, 1, 1, 1, 0, 1], dtype='float32') + + print(merge_quadrangle_n9(np.array([q, q + 0.1, q + 2]))) diff --git a/ptocr/postprocess/lanms/__pycache__/__init__.cpython-36.pyc b/ptocr/postprocess/lanms/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..19a24766c30bba74ee59c935c2e8340a845fa7b8 GIT binary patch literal 734 zcmZuuyKWOf6umREYfl^}iWCK@qQzf+j`Mt&m28XY8yqyEDv=3x_oY zCM6{w!B_ARbR$twApZgtcQ+I%xT|~iG3P$c*@LaE-p{Y!#SsPg1-Bx>=NFjOTLcA) z7|3q!BqQxR&6p;$WR|wr(cKkUk&J7YZ78ak=2Nyp<|Xr|x~G!arsfUf6HyzgtN3S< z^%XgXVQ-BS;4~(Mywq3p@$uE2V_B9q?2T>Va-;W*EQ{*t-iPJygEoFVU++8u7_+7j zql4M(dyH9qL{LOg0|i+!0)f_md$3FD9|kr{rl1m)exc`3!z^9FGN}{P1J@L>b4@2S zg}YGGLttq$VqRQ@2d(^nd*OEx%4tQTKFBaEl>{yM^{sE#A+i^0J&7Ey_dR zoW<_MSQ!qUASqR0;q;m}C7rA*m2|;+->8br=rYpM#Z-DP7cm)Iw-|O}>a1B*F%1`9 zS8>C6Jl{AvdQNFT3l!pS`{z-&*4)HlZa#_{1+>{au3|Jp@raiwxzV4tQ(Oq>iCv8!@l z%0fr(wKVM~F2n@?tPmN0_452gynb_r(xfr5e>N{eUg#6=t&g3F^@R-W+*Zad7&H_3 XQDN2G=x2?oRUd)lwww;=fNZnBm590t literal 0 HcmV?d00001 diff --git a/ptocr/postprocess/lanms/adaptor.cpp b/ptocr/postprocess/lanms/adaptor.cpp new file mode 100644 index 0000000..7d38278 --- /dev/null +++ b/ptocr/postprocess/lanms/adaptor.cpp @@ -0,0 +1,61 @@ +#include "pybind11/pybind11.h" +#include "pybind11/numpy.h" +#include "pybind11/stl.h" +#include "pybind11/stl_bind.h" + +#include "lanms.h" + +namespace py = pybind11; + + +namespace lanms_adaptor { + + std::vector> polys2floats(const std::vector &polys) { + std::vector> ret; + for (size_t i = 0; i < polys.size(); i ++) { + auto &p = polys[i]; + auto &poly = p.poly; + ret.emplace_back(std::vector{ + float(poly[0].X), float(poly[0].Y), + float(poly[1].X), float(poly[1].Y), + float(poly[2].X), float(poly[2].Y), + float(poly[3].X), float(poly[3].Y), + float(p.score), + }); + } + + return ret; + } + + + /** + * + * \param quad_n9 an n-by-9 numpy array, where first 8 numbers denote the + * quadrangle, and the last one is the score + * \param iou_threshold two quadrangles with iou score above this threshold + * will be merged + * + * \return an n-by-9 numpy array, the merged quadrangles + */ + std::vector> merge_quadrangle_n9( + py::array_t quad_n9, + float iou_threshold) { + auto pbuf = quad_n9.request(); + if (pbuf.ndim != 2 || pbuf.shape[1] != 9) + throw std::runtime_error("quadrangles must have a shape of (n, 9)"); + auto n = pbuf.shape[0]; + auto ptr = static_cast(pbuf.ptr); + return polys2floats(lanms::merge_quadrangle_n9(ptr, n, iou_threshold)); + } + +} + +PYBIND11_PLUGIN(adaptor) { + py::module m("adaptor", "NMS"); + + m.def("merge_quadrangle_n9", &lanms_adaptor::merge_quadrangle_n9, + "merge quadrangels"); + + return m.ptr(); +} + diff --git a/ptocr/postprocess/lanms/include/clipper/clipper.cpp b/ptocr/postprocess/lanms/include/clipper/clipper.cpp new file mode 100644 index 0000000..4993ba9 --- /dev/null +++ b/ptocr/postprocess/lanms/include/clipper/clipper.cpp @@ -0,0 +1,4622 @@ +/******************************************************************************* +* * +* Author : Angus Johnson * +* Version : 6.4.0 * +* Date : 2 July 2015 * +* Website : http://www.angusj.com * +* Copyright : Angus Johnson 2010-2015 * +* * +* License: * +* Use, modification & distribution is subject to Boost Software License Ver 1. * +* http://www.boost.org/LICENSE_1_0.txt * +* * +* Attributions: * +* The code in this library is an extension of Bala Vatti's clipping algorithm: * +* "A generic solution to polygon clipping" * +* Communications of the ACM, Vol 35, Issue 7 (July 1992) pp 56-63. * +* http://portal.acm.org/citation.cfm?id=129906 * +* * +* Computer graphics and geometric modeling: implementation and algorithms * +* By Max K. Agoston * +* Springer; 1 edition (January 4, 2005) * +* http://books.google.com/books?q=vatti+clipping+agoston * +* * +* See also: * +* "Polygon Offsetting by Computing Winding Numbers" * +* Paper no. DETC2005-85513 pp. 565-575 * +* ASME 2005 International Design Engineering Technical Conferences * +* and Computers and Information in Engineering Conference (IDETC/CIE2005) * +* September 24-28, 2005 , Long Beach, California, USA * +* http://www.me.berkeley.edu/~mcmains/pubs/DAC05OffsetPolygon.pdf * +* * +*******************************************************************************/ + +/******************************************************************************* +* * +* This is a translation of the Delphi Clipper library and the naming style * +* used has retained a Delphi flavour. * +* * +*******************************************************************************/ + +#include "clipper.hpp" +#include +#include +#include +#include +#include +#include +#include +#include + +namespace ClipperLib { + +static double const pi = 3.141592653589793238; +static double const two_pi = pi *2; +static double const def_arc_tolerance = 0.25; + +enum Direction { dRightToLeft, dLeftToRight }; + +static int const Unassigned = -1; //edge not currently 'owning' a solution +static int const Skip = -2; //edge that would otherwise close a path + +#define HORIZONTAL (-1.0E+40) +#define TOLERANCE (1.0e-20) +#define NEAR_ZERO(val) (((val) > -TOLERANCE) && ((val) < TOLERANCE)) + +struct TEdge { + IntPoint Bot; + IntPoint Curr; //current (updated for every new scanbeam) + IntPoint Top; + double Dx; + PolyType PolyTyp; + EdgeSide Side; //side only refers to current side of solution poly + int WindDelta; //1 or -1 depending on winding direction + int WindCnt; + int WindCnt2; //winding count of the opposite polytype + int OutIdx; + TEdge *Next; + TEdge *Prev; + TEdge *NextInLML; + TEdge *NextInAEL; + TEdge *PrevInAEL; + TEdge *NextInSEL; + TEdge *PrevInSEL; +}; + +struct IntersectNode { + TEdge *Edge1; + TEdge *Edge2; + IntPoint Pt; +}; + +struct LocalMinimum { + cInt Y; + TEdge *LeftBound; + TEdge *RightBound; +}; + +struct OutPt; + +//OutRec: contains a path in the clipping solution. Edges in the AEL will +//carry a pointer to an OutRec when they are part of the clipping solution. +struct OutRec { + int Idx; + bool IsHole; + bool IsOpen; + OutRec *FirstLeft; //see comments in clipper.pas + PolyNode *PolyNd; + OutPt *Pts; + OutPt *BottomPt; +}; + +struct OutPt { + int Idx; + IntPoint Pt; + OutPt *Next; + OutPt *Prev; +}; + +struct Join { + OutPt *OutPt1; + OutPt *OutPt2; + IntPoint OffPt; +}; + +struct LocMinSorter +{ + inline bool operator()(const LocalMinimum& locMin1, const LocalMinimum& locMin2) + { + return locMin2.Y < locMin1.Y; + } +}; + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + +inline cInt Round(double val) +{ + if ((val < 0)) return static_cast(val - 0.5); + else return static_cast(val + 0.5); +} +//------------------------------------------------------------------------------ + +inline cInt Abs(cInt val) +{ + return val < 0 ? -val : val; +} + +//------------------------------------------------------------------------------ +// PolyTree methods ... +//------------------------------------------------------------------------------ + +void PolyTree::Clear() +{ + for (PolyNodes::size_type i = 0; i < AllNodes.size(); ++i) + delete AllNodes[i]; + AllNodes.resize(0); + Childs.resize(0); +} +//------------------------------------------------------------------------------ + +PolyNode* PolyTree::GetFirst() const +{ + if (!Childs.empty()) + return Childs[0]; + else + return 0; +} +//------------------------------------------------------------------------------ + +int PolyTree::Total() const +{ + int result = (int)AllNodes.size(); + //with negative offsets, ignore the hidden outer polygon ... + if (result > 0 && Childs[0] != AllNodes[0]) result--; + return result; +} + +//------------------------------------------------------------------------------ +// PolyNode methods ... +//------------------------------------------------------------------------------ + +PolyNode::PolyNode(): Childs(), Parent(0), Index(0), m_IsOpen(false) +{ +} +//------------------------------------------------------------------------------ + +int PolyNode::ChildCount() const +{ + return (int)Childs.size(); +} +//------------------------------------------------------------------------------ + +void PolyNode::AddChild(PolyNode& child) +{ + unsigned cnt = (unsigned)Childs.size(); + Childs.push_back(&child); + child.Parent = this; + child.Index = cnt; +} +//------------------------------------------------------------------------------ + +PolyNode* PolyNode::GetNext() const +{ + if (!Childs.empty()) + return Childs[0]; + else + return GetNextSiblingUp(); +} +//------------------------------------------------------------------------------ + +PolyNode* PolyNode::GetNextSiblingUp() const +{ + if (!Parent) //protects against PolyTree.GetNextSiblingUp() + return 0; + else if (Index == Parent->Childs.size() - 1) + return Parent->GetNextSiblingUp(); + else + return Parent->Childs[Index + 1]; +} +//------------------------------------------------------------------------------ + +bool PolyNode::IsHole() const +{ + bool result = true; + PolyNode* node = Parent; + while (node) + { + result = !result; + node = node->Parent; + } + return result; +} +//------------------------------------------------------------------------------ + +bool PolyNode::IsOpen() const +{ + return m_IsOpen; +} +//------------------------------------------------------------------------------ + +#ifndef use_int32 + +//------------------------------------------------------------------------------ +// Int128 class (enables safe math on signed 64bit integers) +// eg Int128 val1((long64)9223372036854775807); //ie 2^63 -1 +// Int128 val2((long64)9223372036854775807); +// Int128 val3 = val1 * val2; +// val3.AsString => "85070591730234615847396907784232501249" (8.5e+37) +//------------------------------------------------------------------------------ + +class Int128 +{ + public: + ulong64 lo; + long64 hi; + + Int128(long64 _lo = 0) + { + lo = (ulong64)_lo; + if (_lo < 0) hi = -1; else hi = 0; + } + + + Int128(const Int128 &val): lo(val.lo), hi(val.hi){} + + Int128(const long64& _hi, const ulong64& _lo): lo(_lo), hi(_hi){} + + Int128& operator = (const long64 &val) + { + lo = (ulong64)val; + if (val < 0) hi = -1; else hi = 0; + return *this; + } + + bool operator == (const Int128 &val) const + {return (hi == val.hi && lo == val.lo);} + + bool operator != (const Int128 &val) const + { return !(*this == val);} + + bool operator > (const Int128 &val) const + { + if (hi != val.hi) + return hi > val.hi; + else + return lo > val.lo; + } + + bool operator < (const Int128 &val) const + { + if (hi != val.hi) + return hi < val.hi; + else + return lo < val.lo; + } + + bool operator >= (const Int128 &val) const + { return !(*this < val);} + + bool operator <= (const Int128 &val) const + { return !(*this > val);} + + Int128& operator += (const Int128 &rhs) + { + hi += rhs.hi; + lo += rhs.lo; + if (lo < rhs.lo) hi++; + return *this; + } + + Int128 operator + (const Int128 &rhs) const + { + Int128 result(*this); + result+= rhs; + return result; + } + + Int128& operator -= (const Int128 &rhs) + { + *this += -rhs; + return *this; + } + + Int128 operator - (const Int128 &rhs) const + { + Int128 result(*this); + result -= rhs; + return result; + } + + Int128 operator-() const //unary negation + { + if (lo == 0) + return Int128(-hi, 0); + else + return Int128(~hi, ~lo + 1); + } + + operator double() const + { + const double shift64 = 18446744073709551616.0; //2^64 + if (hi < 0) + { + if (lo == 0) return (double)hi * shift64; + else return -(double)(~lo + ~hi * shift64); + } + else + return (double)(lo + hi * shift64); + } + +}; +//------------------------------------------------------------------------------ + +Int128 Int128Mul (long64 lhs, long64 rhs) +{ + bool negate = (lhs < 0) != (rhs < 0); + + if (lhs < 0) lhs = -lhs; + ulong64 int1Hi = ulong64(lhs) >> 32; + ulong64 int1Lo = ulong64(lhs & 0xFFFFFFFF); + + if (rhs < 0) rhs = -rhs; + ulong64 int2Hi = ulong64(rhs) >> 32; + ulong64 int2Lo = ulong64(rhs & 0xFFFFFFFF); + + //nb: see comments in clipper.pas + ulong64 a = int1Hi * int2Hi; + ulong64 b = int1Lo * int2Lo; + ulong64 c = int1Hi * int2Lo + int1Lo * int2Hi; + + Int128 tmp; + tmp.hi = long64(a + (c >> 32)); + tmp.lo = long64(c << 32); + tmp.lo += long64(b); + if (tmp.lo < b) tmp.hi++; + if (negate) tmp = -tmp; + return tmp; +}; +#endif + +//------------------------------------------------------------------------------ +// Miscellaneous global functions +//------------------------------------------------------------------------------ + +bool Orientation(const Path &poly) +{ + return Area(poly) >= 0; +} +//------------------------------------------------------------------------------ + +double Area(const Path &poly) +{ + int size = (int)poly.size(); + if (size < 3) return 0; + + double a = 0; + for (int i = 0, j = size -1; i < size; ++i) + { + a += ((double)poly[j].X + poly[i].X) * ((double)poly[j].Y - poly[i].Y); + j = i; + } + return -a * 0.5; +} +//------------------------------------------------------------------------------ + +double Area(const OutPt *op) +{ + const OutPt *startOp = op; + if (!op) return 0; + double a = 0; + do { + a += (double)(op->Prev->Pt.X + op->Pt.X) * (double)(op->Prev->Pt.Y - op->Pt.Y); + op = op->Next; + } while (op != startOp); + return a * 0.5; +} +//------------------------------------------------------------------------------ + +double Area(const OutRec &outRec) +{ + return Area(outRec.Pts); +} +//------------------------------------------------------------------------------ + +bool PointIsVertex(const IntPoint &Pt, OutPt *pp) +{ + OutPt *pp2 = pp; + do + { + if (pp2->Pt == Pt) return true; + pp2 = pp2->Next; + } + while (pp2 != pp); + return false; +} +//------------------------------------------------------------------------------ + +//See "The Point in Polygon Problem for Arbitrary Polygons" by Hormann & Agathos +//http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.88.5498&rep=rep1&type=pdf +int PointInPolygon(const IntPoint &pt, const Path &path) +{ + //returns 0 if false, +1 if true, -1 if pt ON polygon boundary + int result = 0; + size_t cnt = path.size(); + if (cnt < 3) return 0; + IntPoint ip = path[0]; + for(size_t i = 1; i <= cnt; ++i) + { + IntPoint ipNext = (i == cnt ? path[0] : path[i]); + if (ipNext.Y == pt.Y) + { + if ((ipNext.X == pt.X) || (ip.Y == pt.Y && + ((ipNext.X > pt.X) == (ip.X < pt.X)))) return -1; + } + if ((ip.Y < pt.Y) != (ipNext.Y < pt.Y)) + { + if (ip.X >= pt.X) + { + if (ipNext.X > pt.X) result = 1 - result; + else + { + double d = (double)(ip.X - pt.X) * (ipNext.Y - pt.Y) - + (double)(ipNext.X - pt.X) * (ip.Y - pt.Y); + if (!d) return -1; + if ((d > 0) == (ipNext.Y > ip.Y)) result = 1 - result; + } + } else + { + if (ipNext.X > pt.X) + { + double d = (double)(ip.X - pt.X) * (ipNext.Y - pt.Y) - + (double)(ipNext.X - pt.X) * (ip.Y - pt.Y); + if (!d) return -1; + if ((d > 0) == (ipNext.Y > ip.Y)) result = 1 - result; + } + } + } + ip = ipNext; + } + return result; +} +//------------------------------------------------------------------------------ + +int PointInPolygon (const IntPoint &pt, OutPt *op) +{ + //returns 0 if false, +1 if true, -1 if pt ON polygon boundary + int result = 0; + OutPt* startOp = op; + for(;;) + { + if (op->Next->Pt.Y == pt.Y) + { + if ((op->Next->Pt.X == pt.X) || (op->Pt.Y == pt.Y && + ((op->Next->Pt.X > pt.X) == (op->Pt.X < pt.X)))) return -1; + } + if ((op->Pt.Y < pt.Y) != (op->Next->Pt.Y < pt.Y)) + { + if (op->Pt.X >= pt.X) + { + if (op->Next->Pt.X > pt.X) result = 1 - result; + else + { + double d = (double)(op->Pt.X - pt.X) * (op->Next->Pt.Y - pt.Y) - + (double)(op->Next->Pt.X - pt.X) * (op->Pt.Y - pt.Y); + if (!d) return -1; + if ((d > 0) == (op->Next->Pt.Y > op->Pt.Y)) result = 1 - result; + } + } else + { + if (op->Next->Pt.X > pt.X) + { + double d = (double)(op->Pt.X - pt.X) * (op->Next->Pt.Y - pt.Y) - + (double)(op->Next->Pt.X - pt.X) * (op->Pt.Y - pt.Y); + if (!d) return -1; + if ((d > 0) == (op->Next->Pt.Y > op->Pt.Y)) result = 1 - result; + } + } + } + op = op->Next; + if (startOp == op) break; + } + return result; +} +//------------------------------------------------------------------------------ + +bool Poly2ContainsPoly1(OutPt *OutPt1, OutPt *OutPt2) +{ + OutPt* op = OutPt1; + do + { + //nb: PointInPolygon returns 0 if false, +1 if true, -1 if pt on polygon + int res = PointInPolygon(op->Pt, OutPt2); + if (res >= 0) return res > 0; + op = op->Next; + } + while (op != OutPt1); + return true; +} +//---------------------------------------------------------------------- + +bool SlopesEqual(const TEdge &e1, const TEdge &e2, bool UseFullInt64Range) +{ +#ifndef use_int32 + if (UseFullInt64Range) + return Int128Mul(e1.Top.Y - e1.Bot.Y, e2.Top.X - e2.Bot.X) == + Int128Mul(e1.Top.X - e1.Bot.X, e2.Top.Y - e2.Bot.Y); + else +#endif + return (e1.Top.Y - e1.Bot.Y) * (e2.Top.X - e2.Bot.X) == + (e1.Top.X - e1.Bot.X) * (e2.Top.Y - e2.Bot.Y); +} +//------------------------------------------------------------------------------ + +bool SlopesEqual(const IntPoint pt1, const IntPoint pt2, + const IntPoint pt3, bool UseFullInt64Range) +{ +#ifndef use_int32 + if (UseFullInt64Range) + return Int128Mul(pt1.Y-pt2.Y, pt2.X-pt3.X) == Int128Mul(pt1.X-pt2.X, pt2.Y-pt3.Y); + else +#endif + return (pt1.Y-pt2.Y)*(pt2.X-pt3.X) == (pt1.X-pt2.X)*(pt2.Y-pt3.Y); +} +//------------------------------------------------------------------------------ + +bool SlopesEqual(const IntPoint pt1, const IntPoint pt2, + const IntPoint pt3, const IntPoint pt4, bool UseFullInt64Range) +{ +#ifndef use_int32 + if (UseFullInt64Range) + return Int128Mul(pt1.Y-pt2.Y, pt3.X-pt4.X) == Int128Mul(pt1.X-pt2.X, pt3.Y-pt4.Y); + else +#endif + return (pt1.Y-pt2.Y)*(pt3.X-pt4.X) == (pt1.X-pt2.X)*(pt3.Y-pt4.Y); +} +//------------------------------------------------------------------------------ + +inline bool IsHorizontal(TEdge &e) +{ + return e.Dx == HORIZONTAL; +} +//------------------------------------------------------------------------------ + +inline double GetDx(const IntPoint pt1, const IntPoint pt2) +{ + return (pt1.Y == pt2.Y) ? + HORIZONTAL : (double)(pt2.X - pt1.X) / (pt2.Y - pt1.Y); +} +//--------------------------------------------------------------------------- + +inline void SetDx(TEdge &e) +{ + cInt dy = (e.Top.Y - e.Bot.Y); + if (dy == 0) e.Dx = HORIZONTAL; + else e.Dx = (double)(e.Top.X - e.Bot.X) / dy; +} +//--------------------------------------------------------------------------- + +inline void SwapSides(TEdge &Edge1, TEdge &Edge2) +{ + EdgeSide Side = Edge1.Side; + Edge1.Side = Edge2.Side; + Edge2.Side = Side; +} +//------------------------------------------------------------------------------ + +inline void SwapPolyIndexes(TEdge &Edge1, TEdge &Edge2) +{ + int OutIdx = Edge1.OutIdx; + Edge1.OutIdx = Edge2.OutIdx; + Edge2.OutIdx = OutIdx; +} +//------------------------------------------------------------------------------ + +inline cInt TopX(TEdge &edge, const cInt currentY) +{ + return ( currentY == edge.Top.Y ) ? + edge.Top.X : edge.Bot.X + Round(edge.Dx *(currentY - edge.Bot.Y)); +} +//------------------------------------------------------------------------------ + +void IntersectPoint(TEdge &Edge1, TEdge &Edge2, IntPoint &ip) +{ +#ifdef use_xyz + ip.Z = 0; +#endif + + double b1, b2; + if (Edge1.Dx == Edge2.Dx) + { + ip.Y = Edge1.Curr.Y; + ip.X = TopX(Edge1, ip.Y); + return; + } + else if (Edge1.Dx == 0) + { + ip.X = Edge1.Bot.X; + if (IsHorizontal(Edge2)) + ip.Y = Edge2.Bot.Y; + else + { + b2 = Edge2.Bot.Y - (Edge2.Bot.X / Edge2.Dx); + ip.Y = Round(ip.X / Edge2.Dx + b2); + } + } + else if (Edge2.Dx == 0) + { + ip.X = Edge2.Bot.X; + if (IsHorizontal(Edge1)) + ip.Y = Edge1.Bot.Y; + else + { + b1 = Edge1.Bot.Y - (Edge1.Bot.X / Edge1.Dx); + ip.Y = Round(ip.X / Edge1.Dx + b1); + } + } + else + { + b1 = Edge1.Bot.X - Edge1.Bot.Y * Edge1.Dx; + b2 = Edge2.Bot.X - Edge2.Bot.Y * Edge2.Dx; + double q = (b2-b1) / (Edge1.Dx - Edge2.Dx); + ip.Y = Round(q); + if (std::fabs(Edge1.Dx) < std::fabs(Edge2.Dx)) + ip.X = Round(Edge1.Dx * q + b1); + else + ip.X = Round(Edge2.Dx * q + b2); + } + + if (ip.Y < Edge1.Top.Y || ip.Y < Edge2.Top.Y) + { + if (Edge1.Top.Y > Edge2.Top.Y) + ip.Y = Edge1.Top.Y; + else + ip.Y = Edge2.Top.Y; + if (std::fabs(Edge1.Dx) < std::fabs(Edge2.Dx)) + ip.X = TopX(Edge1, ip.Y); + else + ip.X = TopX(Edge2, ip.Y); + } + //finally, don't allow 'ip' to be BELOW curr.Y (ie bottom of scanbeam) ... + if (ip.Y > Edge1.Curr.Y) + { + ip.Y = Edge1.Curr.Y; + //use the more vertical edge to derive X ... + if (std::fabs(Edge1.Dx) > std::fabs(Edge2.Dx)) + ip.X = TopX(Edge2, ip.Y); else + ip.X = TopX(Edge1, ip.Y); + } +} +//------------------------------------------------------------------------------ + +void ReversePolyPtLinks(OutPt *pp) +{ + if (!pp) return; + OutPt *pp1, *pp2; + pp1 = pp; + do { + pp2 = pp1->Next; + pp1->Next = pp1->Prev; + pp1->Prev = pp2; + pp1 = pp2; + } while( pp1 != pp ); +} +//------------------------------------------------------------------------------ + +void DisposeOutPts(OutPt*& pp) +{ + if (pp == 0) return; + pp->Prev->Next = 0; + while( pp ) + { + OutPt *tmpPp = pp; + pp = pp->Next; + delete tmpPp; + } +} +//------------------------------------------------------------------------------ + +inline void InitEdge(TEdge* e, TEdge* eNext, TEdge* ePrev, const IntPoint& Pt) +{ + std::memset(e, 0, sizeof(TEdge)); + e->Next = eNext; + e->Prev = ePrev; + e->Curr = Pt; + e->OutIdx = Unassigned; +} +//------------------------------------------------------------------------------ + +void InitEdge2(TEdge& e, PolyType Pt) +{ + if (e.Curr.Y >= e.Next->Curr.Y) + { + e.Bot = e.Curr; + e.Top = e.Next->Curr; + } else + { + e.Top = e.Curr; + e.Bot = e.Next->Curr; + } + SetDx(e); + e.PolyTyp = Pt; +} +//------------------------------------------------------------------------------ + +TEdge* RemoveEdge(TEdge* e) +{ + //removes e from double_linked_list (but without removing from memory) + e->Prev->Next = e->Next; + e->Next->Prev = e->Prev; + TEdge* result = e->Next; + e->Prev = 0; //flag as removed (see ClipperBase.Clear) + return result; +} +//------------------------------------------------------------------------------ + +inline void ReverseHorizontal(TEdge &e) +{ + //swap horizontal edges' Top and Bottom x's so they follow the natural + //progression of the bounds - ie so their xbots will align with the + //adjoining lower edge. [Helpful in the ProcessHorizontal() method.] + std::swap(e.Top.X, e.Bot.X); +#ifdef use_xyz + std::swap(e.Top.Z, e.Bot.Z); +#endif +} +//------------------------------------------------------------------------------ + +void SwapPoints(IntPoint &pt1, IntPoint &pt2) +{ + IntPoint tmp = pt1; + pt1 = pt2; + pt2 = tmp; +} +//------------------------------------------------------------------------------ + +bool GetOverlapSegment(IntPoint pt1a, IntPoint pt1b, IntPoint pt2a, + IntPoint pt2b, IntPoint &pt1, IntPoint &pt2) +{ + //precondition: segments are Collinear. + if (Abs(pt1a.X - pt1b.X) > Abs(pt1a.Y - pt1b.Y)) + { + if (pt1a.X > pt1b.X) SwapPoints(pt1a, pt1b); + if (pt2a.X > pt2b.X) SwapPoints(pt2a, pt2b); + if (pt1a.X > pt2a.X) pt1 = pt1a; else pt1 = pt2a; + if (pt1b.X < pt2b.X) pt2 = pt1b; else pt2 = pt2b; + return pt1.X < pt2.X; + } else + { + if (pt1a.Y < pt1b.Y) SwapPoints(pt1a, pt1b); + if (pt2a.Y < pt2b.Y) SwapPoints(pt2a, pt2b); + if (pt1a.Y < pt2a.Y) pt1 = pt1a; else pt1 = pt2a; + if (pt1b.Y > pt2b.Y) pt2 = pt1b; else pt2 = pt2b; + return pt1.Y > pt2.Y; + } +} +//------------------------------------------------------------------------------ + +bool FirstIsBottomPt(const OutPt* btmPt1, const OutPt* btmPt2) +{ + OutPt *p = btmPt1->Prev; + while ((p->Pt == btmPt1->Pt) && (p != btmPt1)) p = p->Prev; + double dx1p = std::fabs(GetDx(btmPt1->Pt, p->Pt)); + p = btmPt1->Next; + while ((p->Pt == btmPt1->Pt) && (p != btmPt1)) p = p->Next; + double dx1n = std::fabs(GetDx(btmPt1->Pt, p->Pt)); + + p = btmPt2->Prev; + while ((p->Pt == btmPt2->Pt) && (p != btmPt2)) p = p->Prev; + double dx2p = std::fabs(GetDx(btmPt2->Pt, p->Pt)); + p = btmPt2->Next; + while ((p->Pt == btmPt2->Pt) && (p != btmPt2)) p = p->Next; + double dx2n = std::fabs(GetDx(btmPt2->Pt, p->Pt)); + + if (std::max(dx1p, dx1n) == std::max(dx2p, dx2n) && + std::min(dx1p, dx1n) == std::min(dx2p, dx2n)) + return Area(btmPt1) > 0; //if otherwise identical use orientation + else + return (dx1p >= dx2p && dx1p >= dx2n) || (dx1n >= dx2p && dx1n >= dx2n); +} +//------------------------------------------------------------------------------ + +OutPt* GetBottomPt(OutPt *pp) +{ + OutPt* dups = 0; + OutPt* p = pp->Next; + while (p != pp) + { + if (p->Pt.Y > pp->Pt.Y) + { + pp = p; + dups = 0; + } + else if (p->Pt.Y == pp->Pt.Y && p->Pt.X <= pp->Pt.X) + { + if (p->Pt.X < pp->Pt.X) + { + dups = 0; + pp = p; + } else + { + if (p->Next != pp && p->Prev != pp) dups = p; + } + } + p = p->Next; + } + if (dups) + { + //there appears to be at least 2 vertices at BottomPt so ... + while (dups != p) + { + if (!FirstIsBottomPt(p, dups)) pp = dups; + dups = dups->Next; + while (dups->Pt != pp->Pt) dups = dups->Next; + } + } + return pp; +} +//------------------------------------------------------------------------------ + +bool Pt2IsBetweenPt1AndPt3(const IntPoint pt1, + const IntPoint pt2, const IntPoint pt3) +{ + if ((pt1 == pt3) || (pt1 == pt2) || (pt3 == pt2)) + return false; + else if (pt1.X != pt3.X) + return (pt2.X > pt1.X) == (pt2.X < pt3.X); + else + return (pt2.Y > pt1.Y) == (pt2.Y < pt3.Y); +} +//------------------------------------------------------------------------------ + +bool HorzSegmentsOverlap(cInt seg1a, cInt seg1b, cInt seg2a, cInt seg2b) +{ + if (seg1a > seg1b) std::swap(seg1a, seg1b); + if (seg2a > seg2b) std::swap(seg2a, seg2b); + return (seg1a < seg2b) && (seg2a < seg1b); +} + +//------------------------------------------------------------------------------ +// ClipperBase class methods ... +//------------------------------------------------------------------------------ + +ClipperBase::ClipperBase() //constructor +{ + m_CurrentLM = m_MinimaList.begin(); //begin() == end() here + m_UseFullRange = false; +} +//------------------------------------------------------------------------------ + +ClipperBase::~ClipperBase() //destructor +{ + Clear(); +} +//------------------------------------------------------------------------------ + +void RangeTest(const IntPoint& Pt, bool& useFullRange) +{ + if (useFullRange) + { + if (Pt.X > hiRange || Pt.Y > hiRange || -Pt.X > hiRange || -Pt.Y > hiRange) + throw clipperException("Coordinate outside allowed range"); + } + else if (Pt.X > loRange|| Pt.Y > loRange || -Pt.X > loRange || -Pt.Y > loRange) + { + useFullRange = true; + RangeTest(Pt, useFullRange); + } +} +//------------------------------------------------------------------------------ + +TEdge* FindNextLocMin(TEdge* E) +{ + for (;;) + { + while (E->Bot != E->Prev->Bot || E->Curr == E->Top) E = E->Next; + if (!IsHorizontal(*E) && !IsHorizontal(*E->Prev)) break; + while (IsHorizontal(*E->Prev)) E = E->Prev; + TEdge* E2 = E; + while (IsHorizontal(*E)) E = E->Next; + if (E->Top.Y == E->Prev->Bot.Y) continue; //ie just an intermediate horz. + if (E2->Prev->Bot.X < E->Bot.X) E = E2; + break; + } + return E; +} +//------------------------------------------------------------------------------ + +TEdge* ClipperBase::ProcessBound(TEdge* E, bool NextIsForward) +{ + TEdge *Result = E; + TEdge *Horz = 0; + + if (E->OutIdx == Skip) + { + //if edges still remain in the current bound beyond the skip edge then + //create another LocMin and call ProcessBound once more + if (NextIsForward) + { + while (E->Top.Y == E->Next->Bot.Y) E = E->Next; + //don't include top horizontals when parsing a bound a second time, + //they will be contained in the opposite bound ... + while (E != Result && IsHorizontal(*E)) E = E->Prev; + } + else + { + while (E->Top.Y == E->Prev->Bot.Y) E = E->Prev; + while (E != Result && IsHorizontal(*E)) E = E->Next; + } + + if (E == Result) + { + if (NextIsForward) Result = E->Next; + else Result = E->Prev; + } + else + { + //there are more edges in the bound beyond result starting with E + if (NextIsForward) + E = Result->Next; + else + E = Result->Prev; + MinimaList::value_type locMin; + locMin.Y = E->Bot.Y; + locMin.LeftBound = 0; + locMin.RightBound = E; + E->WindDelta = 0; + Result = ProcessBound(E, NextIsForward); + m_MinimaList.push_back(locMin); + } + return Result; + } + + TEdge *EStart; + + if (IsHorizontal(*E)) + { + //We need to be careful with open paths because this may not be a + //true local minima (ie E may be following a skip edge). + //Also, consecutive horz. edges may start heading left before going right. + if (NextIsForward) + EStart = E->Prev; + else + EStart = E->Next; + if (IsHorizontal(*EStart)) //ie an adjoining horizontal skip edge + { + if (EStart->Bot.X != E->Bot.X && EStart->Top.X != E->Bot.X) + ReverseHorizontal(*E); + } + else if (EStart->Bot.X != E->Bot.X) + ReverseHorizontal(*E); + } + + EStart = E; + if (NextIsForward) + { + while (Result->Top.Y == Result->Next->Bot.Y && Result->Next->OutIdx != Skip) + Result = Result->Next; + if (IsHorizontal(*Result) && Result->Next->OutIdx != Skip) + { + //nb: at the top of a bound, horizontals are added to the bound + //only when the preceding edge attaches to the horizontal's left vertex + //unless a Skip edge is encountered when that becomes the top divide + Horz = Result; + while (IsHorizontal(*Horz->Prev)) Horz = Horz->Prev; + if (Horz->Prev->Top.X > Result->Next->Top.X) Result = Horz->Prev; + } + while (E != Result) + { + E->NextInLML = E->Next; + if (IsHorizontal(*E) && E != EStart && + E->Bot.X != E->Prev->Top.X) ReverseHorizontal(*E); + E = E->Next; + } + if (IsHorizontal(*E) && E != EStart && E->Bot.X != E->Prev->Top.X) + ReverseHorizontal(*E); + Result = Result->Next; //move to the edge just beyond current bound + } else + { + while (Result->Top.Y == Result->Prev->Bot.Y && Result->Prev->OutIdx != Skip) + Result = Result->Prev; + if (IsHorizontal(*Result) && Result->Prev->OutIdx != Skip) + { + Horz = Result; + while (IsHorizontal(*Horz->Next)) Horz = Horz->Next; + if (Horz->Next->Top.X == Result->Prev->Top.X || + Horz->Next->Top.X > Result->Prev->Top.X) Result = Horz->Next; + } + + while (E != Result) + { + E->NextInLML = E->Prev; + if (IsHorizontal(*E) && E != EStart && E->Bot.X != E->Next->Top.X) + ReverseHorizontal(*E); + E = E->Prev; + } + if (IsHorizontal(*E) && E != EStart && E->Bot.X != E->Next->Top.X) + ReverseHorizontal(*E); + Result = Result->Prev; //move to the edge just beyond current bound + } + + return Result; +} +//------------------------------------------------------------------------------ + +bool ClipperBase::AddPath(const Path &pg, PolyType PolyTyp, bool Closed) +{ +#ifdef use_lines + if (!Closed && PolyTyp == ptClip) + throw clipperException("AddPath: Open paths must be subject."); +#else + if (!Closed) + throw clipperException("AddPath: Open paths have been disabled."); +#endif + + int highI = (int)pg.size() -1; + if (Closed) while (highI > 0 && (pg[highI] == pg[0])) --highI; + while (highI > 0 && (pg[highI] == pg[highI -1])) --highI; + if ((Closed && highI < 2) || (!Closed && highI < 1)) return false; + + //create a new edge array ... + TEdge *edges = new TEdge [highI +1]; + + bool IsFlat = true; + //1. Basic (first) edge initialization ... + try + { + edges[1].Curr = pg[1]; + RangeTest(pg[0], m_UseFullRange); + RangeTest(pg[highI], m_UseFullRange); + InitEdge(&edges[0], &edges[1], &edges[highI], pg[0]); + InitEdge(&edges[highI], &edges[0], &edges[highI-1], pg[highI]); + for (int i = highI - 1; i >= 1; --i) + { + RangeTest(pg[i], m_UseFullRange); + InitEdge(&edges[i], &edges[i+1], &edges[i-1], pg[i]); + } + } + catch(...) + { + delete [] edges; + throw; //range test fails + } + TEdge *eStart = &edges[0]; + + //2. Remove duplicate vertices, and (when closed) collinear edges ... + TEdge *E = eStart, *eLoopStop = eStart; + for (;;) + { + //nb: allows matching start and end points when not Closed ... + if (E->Curr == E->Next->Curr && (Closed || E->Next != eStart)) + { + if (E == E->Next) break; + if (E == eStart) eStart = E->Next; + E = RemoveEdge(E); + eLoopStop = E; + continue; + } + if (E->Prev == E->Next) + break; //only two vertices + else if (Closed && + SlopesEqual(E->Prev->Curr, E->Curr, E->Next->Curr, m_UseFullRange) && + (!m_PreserveCollinear || + !Pt2IsBetweenPt1AndPt3(E->Prev->Curr, E->Curr, E->Next->Curr))) + { + //Collinear edges are allowed for open paths but in closed paths + //the default is to merge adjacent collinear edges into a single edge. + //However, if the PreserveCollinear property is enabled, only overlapping + //collinear edges (ie spikes) will be removed from closed paths. + if (E == eStart) eStart = E->Next; + E = RemoveEdge(E); + E = E->Prev; + eLoopStop = E; + continue; + } + E = E->Next; + if ((E == eLoopStop) || (!Closed && E->Next == eStart)) break; + } + + if ((!Closed && (E == E->Next)) || (Closed && (E->Prev == E->Next))) + { + delete [] edges; + return false; + } + + if (!Closed) + { + m_HasOpenPaths = true; + eStart->Prev->OutIdx = Skip; + } + + //3. Do second stage of edge initialization ... + E = eStart; + do + { + InitEdge2(*E, PolyTyp); + E = E->Next; + if (IsFlat && E->Curr.Y != eStart->Curr.Y) IsFlat = false; + } + while (E != eStart); + + //4. Finally, add edge bounds to LocalMinima list ... + + //Totally flat paths must be handled differently when adding them + //to LocalMinima list to avoid endless loops etc ... + if (IsFlat) + { + if (Closed) + { + delete [] edges; + return false; + } + E->Prev->OutIdx = Skip; + MinimaList::value_type locMin; + locMin.Y = E->Bot.Y; + locMin.LeftBound = 0; + locMin.RightBound = E; + locMin.RightBound->Side = esRight; + locMin.RightBound->WindDelta = 0; + for (;;) + { + if (E->Bot.X != E->Prev->Top.X) ReverseHorizontal(*E); + if (E->Next->OutIdx == Skip) break; + E->NextInLML = E->Next; + E = E->Next; + } + m_MinimaList.push_back(locMin); + m_edges.push_back(edges); + return true; + } + + m_edges.push_back(edges); + bool leftBoundIsForward; + TEdge* EMin = 0; + + //workaround to avoid an endless loop in the while loop below when + //open paths have matching start and end points ... + if (E->Prev->Bot == E->Prev->Top) E = E->Next; + + for (;;) + { + E = FindNextLocMin(E); + if (E == EMin) break; + else if (!EMin) EMin = E; + + //E and E.Prev now share a local minima (left aligned if horizontal). + //Compare their slopes to find which starts which bound ... + MinimaList::value_type locMin; + locMin.Y = E->Bot.Y; + if (E->Dx < E->Prev->Dx) + { + locMin.LeftBound = E->Prev; + locMin.RightBound = E; + leftBoundIsForward = false; //Q.nextInLML = Q.prev + } else + { + locMin.LeftBound = E; + locMin.RightBound = E->Prev; + leftBoundIsForward = true; //Q.nextInLML = Q.next + } + + if (!Closed) locMin.LeftBound->WindDelta = 0; + else if (locMin.LeftBound->Next == locMin.RightBound) + locMin.LeftBound->WindDelta = -1; + else locMin.LeftBound->WindDelta = 1; + locMin.RightBound->WindDelta = -locMin.LeftBound->WindDelta; + + E = ProcessBound(locMin.LeftBound, leftBoundIsForward); + if (E->OutIdx == Skip) E = ProcessBound(E, leftBoundIsForward); + + TEdge* E2 = ProcessBound(locMin.RightBound, !leftBoundIsForward); + if (E2->OutIdx == Skip) E2 = ProcessBound(E2, !leftBoundIsForward); + + if (locMin.LeftBound->OutIdx == Skip) + locMin.LeftBound = 0; + else if (locMin.RightBound->OutIdx == Skip) + locMin.RightBound = 0; + m_MinimaList.push_back(locMin); + if (!leftBoundIsForward) E = E2; + } + return true; +} +//------------------------------------------------------------------------------ + +bool ClipperBase::AddPaths(const Paths &ppg, PolyType PolyTyp, bool Closed) +{ + bool result = false; + for (Paths::size_type i = 0; i < ppg.size(); ++i) + if (AddPath(ppg[i], PolyTyp, Closed)) result = true; + return result; +} +//------------------------------------------------------------------------------ + +void ClipperBase::Clear() +{ + DisposeLocalMinimaList(); + for (EdgeList::size_type i = 0; i < m_edges.size(); ++i) + { + TEdge* edges = m_edges[i]; + delete [] edges; + } + m_edges.clear(); + m_UseFullRange = false; + m_HasOpenPaths = false; +} +//------------------------------------------------------------------------------ + +void ClipperBase::Reset() +{ + m_CurrentLM = m_MinimaList.begin(); + if (m_CurrentLM == m_MinimaList.end()) return; //ie nothing to process + std::sort(m_MinimaList.begin(), m_MinimaList.end(), LocMinSorter()); + + m_Scanbeam = ScanbeamList(); //clears/resets priority_queue + //reset all edges ... + for (MinimaList::iterator lm = m_MinimaList.begin(); lm != m_MinimaList.end(); ++lm) + { + InsertScanbeam(lm->Y); + TEdge* e = lm->LeftBound; + if (e) + { + e->Curr = e->Bot; + e->Side = esLeft; + e->OutIdx = Unassigned; + } + + e = lm->RightBound; + if (e) + { + e->Curr = e->Bot; + e->Side = esRight; + e->OutIdx = Unassigned; + } + } + m_ActiveEdges = 0; + m_CurrentLM = m_MinimaList.begin(); +} +//------------------------------------------------------------------------------ + +void ClipperBase::DisposeLocalMinimaList() +{ + m_MinimaList.clear(); + m_CurrentLM = m_MinimaList.begin(); +} +//------------------------------------------------------------------------------ + +bool ClipperBase::PopLocalMinima(cInt Y, const LocalMinimum *&locMin) +{ + if (m_CurrentLM == m_MinimaList.end() || (*m_CurrentLM).Y != Y) return false; + locMin = &(*m_CurrentLM); + ++m_CurrentLM; + return true; +} +//------------------------------------------------------------------------------ + +IntRect ClipperBase::GetBounds() +{ + IntRect result; + MinimaList::iterator lm = m_MinimaList.begin(); + if (lm == m_MinimaList.end()) + { + result.left = result.top = result.right = result.bottom = 0; + return result; + } + result.left = lm->LeftBound->Bot.X; + result.top = lm->LeftBound->Bot.Y; + result.right = lm->LeftBound->Bot.X; + result.bottom = lm->LeftBound->Bot.Y; + while (lm != m_MinimaList.end()) + { + //todo - needs fixing for open paths + result.bottom = std::max(result.bottom, lm->LeftBound->Bot.Y); + TEdge* e = lm->LeftBound; + for (;;) { + TEdge* bottomE = e; + while (e->NextInLML) + { + if (e->Bot.X < result.left) result.left = e->Bot.X; + if (e->Bot.X > result.right) result.right = e->Bot.X; + e = e->NextInLML; + } + result.left = std::min(result.left, e->Bot.X); + result.right = std::max(result.right, e->Bot.X); + result.left = std::min(result.left, e->Top.X); + result.right = std::max(result.right, e->Top.X); + result.top = std::min(result.top, e->Top.Y); + if (bottomE == lm->LeftBound) e = lm->RightBound; + else break; + } + ++lm; + } + return result; +} +//------------------------------------------------------------------------------ + +void ClipperBase::InsertScanbeam(const cInt Y) +{ + m_Scanbeam.push(Y); +} +//------------------------------------------------------------------------------ + +bool ClipperBase::PopScanbeam(cInt &Y) +{ + if (m_Scanbeam.empty()) return false; + Y = m_Scanbeam.top(); + m_Scanbeam.pop(); + while (!m_Scanbeam.empty() && Y == m_Scanbeam.top()) { m_Scanbeam.pop(); } // Pop duplicates. + return true; +} +//------------------------------------------------------------------------------ + +void ClipperBase::DisposeAllOutRecs(){ + for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) + DisposeOutRec(i); + m_PolyOuts.clear(); +} +//------------------------------------------------------------------------------ + +void ClipperBase::DisposeOutRec(PolyOutList::size_type index) +{ + OutRec *outRec = m_PolyOuts[index]; + if (outRec->Pts) DisposeOutPts(outRec->Pts); + delete outRec; + m_PolyOuts[index] = 0; +} +//------------------------------------------------------------------------------ + +void ClipperBase::DeleteFromAEL(TEdge *e) +{ + TEdge* AelPrev = e->PrevInAEL; + TEdge* AelNext = e->NextInAEL; + if (!AelPrev && !AelNext && (e != m_ActiveEdges)) return; //already deleted + if (AelPrev) AelPrev->NextInAEL = AelNext; + else m_ActiveEdges = AelNext; + if (AelNext) AelNext->PrevInAEL = AelPrev; + e->NextInAEL = 0; + e->PrevInAEL = 0; +} +//------------------------------------------------------------------------------ + +OutRec* ClipperBase::CreateOutRec() +{ + OutRec* result = new OutRec; + result->IsHole = false; + result->IsOpen = false; + result->FirstLeft = 0; + result->Pts = 0; + result->BottomPt = 0; + result->PolyNd = 0; + m_PolyOuts.push_back(result); + result->Idx = (int)m_PolyOuts.size() - 1; + return result; +} +//------------------------------------------------------------------------------ + +void ClipperBase::SwapPositionsInAEL(TEdge *Edge1, TEdge *Edge2) +{ + //check that one or other edge hasn't already been removed from AEL ... + if (Edge1->NextInAEL == Edge1->PrevInAEL || + Edge2->NextInAEL == Edge2->PrevInAEL) return; + + if (Edge1->NextInAEL == Edge2) + { + TEdge* Next = Edge2->NextInAEL; + if (Next) Next->PrevInAEL = Edge1; + TEdge* Prev = Edge1->PrevInAEL; + if (Prev) Prev->NextInAEL = Edge2; + Edge2->PrevInAEL = Prev; + Edge2->NextInAEL = Edge1; + Edge1->PrevInAEL = Edge2; + Edge1->NextInAEL = Next; + } + else if (Edge2->NextInAEL == Edge1) + { + TEdge* Next = Edge1->NextInAEL; + if (Next) Next->PrevInAEL = Edge2; + TEdge* Prev = Edge2->PrevInAEL; + if (Prev) Prev->NextInAEL = Edge1; + Edge1->PrevInAEL = Prev; + Edge1->NextInAEL = Edge2; + Edge2->PrevInAEL = Edge1; + Edge2->NextInAEL = Next; + } + else + { + TEdge* Next = Edge1->NextInAEL; + TEdge* Prev = Edge1->PrevInAEL; + Edge1->NextInAEL = Edge2->NextInAEL; + if (Edge1->NextInAEL) Edge1->NextInAEL->PrevInAEL = Edge1; + Edge1->PrevInAEL = Edge2->PrevInAEL; + if (Edge1->PrevInAEL) Edge1->PrevInAEL->NextInAEL = Edge1; + Edge2->NextInAEL = Next; + if (Edge2->NextInAEL) Edge2->NextInAEL->PrevInAEL = Edge2; + Edge2->PrevInAEL = Prev; + if (Edge2->PrevInAEL) Edge2->PrevInAEL->NextInAEL = Edge2; + } + + if (!Edge1->PrevInAEL) m_ActiveEdges = Edge1; + else if (!Edge2->PrevInAEL) m_ActiveEdges = Edge2; +} +//------------------------------------------------------------------------------ + +void ClipperBase::UpdateEdgeIntoAEL(TEdge *&e) +{ + if (!e->NextInLML) + throw clipperException("UpdateEdgeIntoAEL: invalid call"); + + e->NextInLML->OutIdx = e->OutIdx; + TEdge* AelPrev = e->PrevInAEL; + TEdge* AelNext = e->NextInAEL; + if (AelPrev) AelPrev->NextInAEL = e->NextInLML; + else m_ActiveEdges = e->NextInLML; + if (AelNext) AelNext->PrevInAEL = e->NextInLML; + e->NextInLML->Side = e->Side; + e->NextInLML->WindDelta = e->WindDelta; + e->NextInLML->WindCnt = e->WindCnt; + e->NextInLML->WindCnt2 = e->WindCnt2; + e = e->NextInLML; + e->Curr = e->Bot; + e->PrevInAEL = AelPrev; + e->NextInAEL = AelNext; + if (!IsHorizontal(*e)) InsertScanbeam(e->Top.Y); +} +//------------------------------------------------------------------------------ + +bool ClipperBase::LocalMinimaPending() +{ + return (m_CurrentLM != m_MinimaList.end()); +} + +//------------------------------------------------------------------------------ +// TClipper methods ... +//------------------------------------------------------------------------------ + +Clipper::Clipper(int initOptions) : ClipperBase() //constructor +{ + m_ExecuteLocked = false; + m_UseFullRange = false; + m_ReverseOutput = ((initOptions & ioReverseSolution) != 0); + m_StrictSimple = ((initOptions & ioStrictlySimple) != 0); + m_PreserveCollinear = ((initOptions & ioPreserveCollinear) != 0); + m_HasOpenPaths = false; +#ifdef use_xyz + m_ZFill = 0; +#endif +} +//------------------------------------------------------------------------------ + +#ifdef use_xyz +void Clipper::ZFillFunction(ZFillCallback zFillFunc) +{ + m_ZFill = zFillFunc; +} +//------------------------------------------------------------------------------ +#endif + +bool Clipper::Execute(ClipType clipType, Paths &solution, PolyFillType fillType) +{ + return Execute(clipType, solution, fillType, fillType); +} +//------------------------------------------------------------------------------ + +bool Clipper::Execute(ClipType clipType, PolyTree &polytree, PolyFillType fillType) +{ + return Execute(clipType, polytree, fillType, fillType); +} +//------------------------------------------------------------------------------ + +bool Clipper::Execute(ClipType clipType, Paths &solution, + PolyFillType subjFillType, PolyFillType clipFillType) +{ + if( m_ExecuteLocked ) return false; + if (m_HasOpenPaths) + throw clipperException("Error: PolyTree struct is needed for open path clipping."); + m_ExecuteLocked = true; + solution.resize(0); + m_SubjFillType = subjFillType; + m_ClipFillType = clipFillType; + m_ClipType = clipType; + m_UsingPolyTree = false; + bool succeeded = ExecuteInternal(); + if (succeeded) BuildResult(solution); + DisposeAllOutRecs(); + m_ExecuteLocked = false; + return succeeded; +} +//------------------------------------------------------------------------------ + +bool Clipper::Execute(ClipType clipType, PolyTree& polytree, + PolyFillType subjFillType, PolyFillType clipFillType) +{ + if( m_ExecuteLocked ) return false; + m_ExecuteLocked = true; + m_SubjFillType = subjFillType; + m_ClipFillType = clipFillType; + m_ClipType = clipType; + m_UsingPolyTree = true; + bool succeeded = ExecuteInternal(); + if (succeeded) BuildResult2(polytree); + DisposeAllOutRecs(); + m_ExecuteLocked = false; + return succeeded; +} +//------------------------------------------------------------------------------ + +void Clipper::FixHoleLinkage(OutRec &outrec) +{ + //skip OutRecs that (a) contain outermost polygons or + //(b) already have the correct owner/child linkage ... + if (!outrec.FirstLeft || + (outrec.IsHole != outrec.FirstLeft->IsHole && + outrec.FirstLeft->Pts)) return; + + OutRec* orfl = outrec.FirstLeft; + while (orfl && ((orfl->IsHole == outrec.IsHole) || !orfl->Pts)) + orfl = orfl->FirstLeft; + outrec.FirstLeft = orfl; +} +//------------------------------------------------------------------------------ + +bool Clipper::ExecuteInternal() +{ + bool succeeded = true; + try { + Reset(); + m_Maxima = MaximaList(); + m_SortedEdges = 0; + + succeeded = true; + cInt botY, topY; + if (!PopScanbeam(botY)) return false; + InsertLocalMinimaIntoAEL(botY); + while (PopScanbeam(topY) || LocalMinimaPending()) + { + ProcessHorizontals(); + ClearGhostJoins(); + if (!ProcessIntersections(topY)) + { + succeeded = false; + break; + } + ProcessEdgesAtTopOfScanbeam(topY); + botY = topY; + InsertLocalMinimaIntoAEL(botY); + } + } + catch(...) + { + succeeded = false; + } + + if (succeeded) + { + //fix orientations ... + for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) + { + OutRec *outRec = m_PolyOuts[i]; + if (!outRec->Pts || outRec->IsOpen) continue; + if ((outRec->IsHole ^ m_ReverseOutput) == (Area(*outRec) > 0)) + ReversePolyPtLinks(outRec->Pts); + } + + if (!m_Joins.empty()) JoinCommonEdges(); + + //unfortunately FixupOutPolygon() must be done after JoinCommonEdges() + for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) + { + OutRec *outRec = m_PolyOuts[i]; + if (!outRec->Pts) continue; + if (outRec->IsOpen) + FixupOutPolyline(*outRec); + else + FixupOutPolygon(*outRec); + } + + if (m_StrictSimple) DoSimplePolygons(); + } + + ClearJoins(); + ClearGhostJoins(); + return succeeded; +} +//------------------------------------------------------------------------------ + +void Clipper::SetWindingCount(TEdge &edge) +{ + TEdge *e = edge.PrevInAEL; + //find the edge of the same polytype that immediately preceeds 'edge' in AEL + while (e && ((e->PolyTyp != edge.PolyTyp) || (e->WindDelta == 0))) e = e->PrevInAEL; + if (!e) + { + if (edge.WindDelta == 0) + { + PolyFillType pft = (edge.PolyTyp == ptSubject ? m_SubjFillType : m_ClipFillType); + edge.WindCnt = (pft == pftNegative ? -1 : 1); + } + else + edge.WindCnt = edge.WindDelta; + edge.WindCnt2 = 0; + e = m_ActiveEdges; //ie get ready to calc WindCnt2 + } + else if (edge.WindDelta == 0 && m_ClipType != ctUnion) + { + edge.WindCnt = 1; + edge.WindCnt2 = e->WindCnt2; + e = e->NextInAEL; //ie get ready to calc WindCnt2 + } + else if (IsEvenOddFillType(edge)) + { + //EvenOdd filling ... + if (edge.WindDelta == 0) + { + //are we inside a subj polygon ... + bool Inside = true; + TEdge *e2 = e->PrevInAEL; + while (e2) + { + if (e2->PolyTyp == e->PolyTyp && e2->WindDelta != 0) + Inside = !Inside; + e2 = e2->PrevInAEL; + } + edge.WindCnt = (Inside ? 0 : 1); + } + else + { + edge.WindCnt = edge.WindDelta; + } + edge.WindCnt2 = e->WindCnt2; + e = e->NextInAEL; //ie get ready to calc WindCnt2 + } + else + { + //nonZero, Positive or Negative filling ... + if (e->WindCnt * e->WindDelta < 0) + { + //prev edge is 'decreasing' WindCount (WC) toward zero + //so we're outside the previous polygon ... + if (Abs(e->WindCnt) > 1) + { + //outside prev poly but still inside another. + //when reversing direction of prev poly use the same WC + if (e->WindDelta * edge.WindDelta < 0) edge.WindCnt = e->WindCnt; + //otherwise continue to 'decrease' WC ... + else edge.WindCnt = e->WindCnt + edge.WindDelta; + } + else + //now outside all polys of same polytype so set own WC ... + edge.WindCnt = (edge.WindDelta == 0 ? 1 : edge.WindDelta); + } else + { + //prev edge is 'increasing' WindCount (WC) away from zero + //so we're inside the previous polygon ... + if (edge.WindDelta == 0) + edge.WindCnt = (e->WindCnt < 0 ? e->WindCnt - 1 : e->WindCnt + 1); + //if wind direction is reversing prev then use same WC + else if (e->WindDelta * edge.WindDelta < 0) edge.WindCnt = e->WindCnt; + //otherwise add to WC ... + else edge.WindCnt = e->WindCnt + edge.WindDelta; + } + edge.WindCnt2 = e->WindCnt2; + e = e->NextInAEL; //ie get ready to calc WindCnt2 + } + + //update WindCnt2 ... + if (IsEvenOddAltFillType(edge)) + { + //EvenOdd filling ... + while (e != &edge) + { + if (e->WindDelta != 0) + edge.WindCnt2 = (edge.WindCnt2 == 0 ? 1 : 0); + e = e->NextInAEL; + } + } else + { + //nonZero, Positive or Negative filling ... + while ( e != &edge ) + { + edge.WindCnt2 += e->WindDelta; + e = e->NextInAEL; + } + } +} +//------------------------------------------------------------------------------ + +bool Clipper::IsEvenOddFillType(const TEdge& edge) const +{ + if (edge.PolyTyp == ptSubject) + return m_SubjFillType == pftEvenOdd; else + return m_ClipFillType == pftEvenOdd; +} +//------------------------------------------------------------------------------ + +bool Clipper::IsEvenOddAltFillType(const TEdge& edge) const +{ + if (edge.PolyTyp == ptSubject) + return m_ClipFillType == pftEvenOdd; else + return m_SubjFillType == pftEvenOdd; +} +//------------------------------------------------------------------------------ + +bool Clipper::IsContributing(const TEdge& edge) const +{ + PolyFillType pft, pft2; + if (edge.PolyTyp == ptSubject) + { + pft = m_SubjFillType; + pft2 = m_ClipFillType; + } else + { + pft = m_ClipFillType; + pft2 = m_SubjFillType; + } + + switch(pft) + { + case pftEvenOdd: + //return false if a subj line has been flagged as inside a subj polygon + if (edge.WindDelta == 0 && edge.WindCnt != 1) return false; + break; + case pftNonZero: + if (Abs(edge.WindCnt) != 1) return false; + break; + case pftPositive: + if (edge.WindCnt != 1) return false; + break; + default: //pftNegative + if (edge.WindCnt != -1) return false; + } + + switch(m_ClipType) + { + case ctIntersection: + switch(pft2) + { + case pftEvenOdd: + case pftNonZero: + return (edge.WindCnt2 != 0); + case pftPositive: + return (edge.WindCnt2 > 0); + default: + return (edge.WindCnt2 < 0); + } + break; + case ctUnion: + switch(pft2) + { + case pftEvenOdd: + case pftNonZero: + return (edge.WindCnt2 == 0); + case pftPositive: + return (edge.WindCnt2 <= 0); + default: + return (edge.WindCnt2 >= 0); + } + break; + case ctDifference: + if (edge.PolyTyp == ptSubject) + switch(pft2) + { + case pftEvenOdd: + case pftNonZero: + return (edge.WindCnt2 == 0); + case pftPositive: + return (edge.WindCnt2 <= 0); + default: + return (edge.WindCnt2 >= 0); + } + else + switch(pft2) + { + case pftEvenOdd: + case pftNonZero: + return (edge.WindCnt2 != 0); + case pftPositive: + return (edge.WindCnt2 > 0); + default: + return (edge.WindCnt2 < 0); + } + break; + case ctXor: + if (edge.WindDelta == 0) //XOr always contributing unless open + switch(pft2) + { + case pftEvenOdd: + case pftNonZero: + return (edge.WindCnt2 == 0); + case pftPositive: + return (edge.WindCnt2 <= 0); + default: + return (edge.WindCnt2 >= 0); + } + else + return true; + break; + default: + return true; + } +} +//------------------------------------------------------------------------------ + +OutPt* Clipper::AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &Pt) +{ + OutPt* result; + TEdge *e, *prevE; + if (IsHorizontal(*e2) || ( e1->Dx > e2->Dx )) + { + result = AddOutPt(e1, Pt); + e2->OutIdx = e1->OutIdx; + e1->Side = esLeft; + e2->Side = esRight; + e = e1; + if (e->PrevInAEL == e2) + prevE = e2->PrevInAEL; + else + prevE = e->PrevInAEL; + } else + { + result = AddOutPt(e2, Pt); + e1->OutIdx = e2->OutIdx; + e1->Side = esRight; + e2->Side = esLeft; + e = e2; + if (e->PrevInAEL == e1) + prevE = e1->PrevInAEL; + else + prevE = e->PrevInAEL; + } + + if (prevE && prevE->OutIdx >= 0) + { + cInt xPrev = TopX(*prevE, Pt.Y); + cInt xE = TopX(*e, Pt.Y); + if (xPrev == xE && (e->WindDelta != 0) && (prevE->WindDelta != 0) && + SlopesEqual(IntPoint(xPrev, Pt.Y), prevE->Top, IntPoint(xE, Pt.Y), e->Top, m_UseFullRange)) + { + OutPt* outPt = AddOutPt(prevE, Pt); + AddJoin(result, outPt, e->Top); + } + } + return result; +} +//------------------------------------------------------------------------------ + +void Clipper::AddLocalMaxPoly(TEdge *e1, TEdge *e2, const IntPoint &Pt) +{ + AddOutPt( e1, Pt ); + if (e2->WindDelta == 0) AddOutPt(e2, Pt); + if( e1->OutIdx == e2->OutIdx ) + { + e1->OutIdx = Unassigned; + e2->OutIdx = Unassigned; + } + else if (e1->OutIdx < e2->OutIdx) + AppendPolygon(e1, e2); + else + AppendPolygon(e2, e1); +} +//------------------------------------------------------------------------------ + +void Clipper::AddEdgeToSEL(TEdge *edge) +{ + //SEL pointers in PEdge are reused to build a list of horizontal edges. + //However, we don't need to worry about order with horizontal edge processing. + if( !m_SortedEdges ) + { + m_SortedEdges = edge; + edge->PrevInSEL = 0; + edge->NextInSEL = 0; + } + else + { + edge->NextInSEL = m_SortedEdges; + edge->PrevInSEL = 0; + m_SortedEdges->PrevInSEL = edge; + m_SortedEdges = edge; + } +} +//------------------------------------------------------------------------------ + +bool Clipper::PopEdgeFromSEL(TEdge *&edge) +{ + if (!m_SortedEdges) return false; + edge = m_SortedEdges; + DeleteFromSEL(m_SortedEdges); + return true; +} +//------------------------------------------------------------------------------ + +void Clipper::CopyAELToSEL() +{ + TEdge* e = m_ActiveEdges; + m_SortedEdges = e; + while ( e ) + { + e->PrevInSEL = e->PrevInAEL; + e->NextInSEL = e->NextInAEL; + e = e->NextInAEL; + } +} +//------------------------------------------------------------------------------ + +void Clipper::AddJoin(OutPt *op1, OutPt *op2, const IntPoint OffPt) +{ + Join* j = new Join; + j->OutPt1 = op1; + j->OutPt2 = op2; + j->OffPt = OffPt; + m_Joins.push_back(j); +} +//------------------------------------------------------------------------------ + +void Clipper::ClearJoins() +{ + for (JoinList::size_type i = 0; i < m_Joins.size(); i++) + delete m_Joins[i]; + m_Joins.resize(0); +} +//------------------------------------------------------------------------------ + +void Clipper::ClearGhostJoins() +{ + for (JoinList::size_type i = 0; i < m_GhostJoins.size(); i++) + delete m_GhostJoins[i]; + m_GhostJoins.resize(0); +} +//------------------------------------------------------------------------------ + +void Clipper::AddGhostJoin(OutPt *op, const IntPoint OffPt) +{ + Join* j = new Join; + j->OutPt1 = op; + j->OutPt2 = 0; + j->OffPt = OffPt; + m_GhostJoins.push_back(j); +} +//------------------------------------------------------------------------------ + +void Clipper::InsertLocalMinimaIntoAEL(const cInt botY) +{ + const LocalMinimum *lm; + while (PopLocalMinima(botY, lm)) + { + TEdge* lb = lm->LeftBound; + TEdge* rb = lm->RightBound; + + OutPt *Op1 = 0; + if (!lb) + { + //nb: don't insert LB into either AEL or SEL + InsertEdgeIntoAEL(rb, 0); + SetWindingCount(*rb); + if (IsContributing(*rb)) + Op1 = AddOutPt(rb, rb->Bot); + } + else if (!rb) + { + InsertEdgeIntoAEL(lb, 0); + SetWindingCount(*lb); + if (IsContributing(*lb)) + Op1 = AddOutPt(lb, lb->Bot); + InsertScanbeam(lb->Top.Y); + } + else + { + InsertEdgeIntoAEL(lb, 0); + InsertEdgeIntoAEL(rb, lb); + SetWindingCount( *lb ); + rb->WindCnt = lb->WindCnt; + rb->WindCnt2 = lb->WindCnt2; + if (IsContributing(*lb)) + Op1 = AddLocalMinPoly(lb, rb, lb->Bot); + InsertScanbeam(lb->Top.Y); + } + + if (rb) + { + if (IsHorizontal(*rb)) + { + AddEdgeToSEL(rb); + if (rb->NextInLML) + InsertScanbeam(rb->NextInLML->Top.Y); + } + else InsertScanbeam( rb->Top.Y ); + } + + if (!lb || !rb) continue; + + //if any output polygons share an edge, they'll need joining later ... + if (Op1 && IsHorizontal(*rb) && + m_GhostJoins.size() > 0 && (rb->WindDelta != 0)) + { + for (JoinList::size_type i = 0; i < m_GhostJoins.size(); ++i) + { + Join* jr = m_GhostJoins[i]; + //if the horizontal Rb and a 'ghost' horizontal overlap, then convert + //the 'ghost' join to a real join ready for later ... + if (HorzSegmentsOverlap(jr->OutPt1->Pt.X, jr->OffPt.X, rb->Bot.X, rb->Top.X)) + AddJoin(jr->OutPt1, Op1, jr->OffPt); + } + } + + if (lb->OutIdx >= 0 && lb->PrevInAEL && + lb->PrevInAEL->Curr.X == lb->Bot.X && + lb->PrevInAEL->OutIdx >= 0 && + SlopesEqual(lb->PrevInAEL->Bot, lb->PrevInAEL->Top, lb->Curr, lb->Top, m_UseFullRange) && + (lb->WindDelta != 0) && (lb->PrevInAEL->WindDelta != 0)) + { + OutPt *Op2 = AddOutPt(lb->PrevInAEL, lb->Bot); + AddJoin(Op1, Op2, lb->Top); + } + + if(lb->NextInAEL != rb) + { + + if (rb->OutIdx >= 0 && rb->PrevInAEL->OutIdx >= 0 && + SlopesEqual(rb->PrevInAEL->Curr, rb->PrevInAEL->Top, rb->Curr, rb->Top, m_UseFullRange) && + (rb->WindDelta != 0) && (rb->PrevInAEL->WindDelta != 0)) + { + OutPt *Op2 = AddOutPt(rb->PrevInAEL, rb->Bot); + AddJoin(Op1, Op2, rb->Top); + } + + TEdge* e = lb->NextInAEL; + if (e) + { + while( e != rb ) + { + //nb: For calculating winding counts etc, IntersectEdges() assumes + //that param1 will be to the Right of param2 ABOVE the intersection ... + IntersectEdges(rb , e , lb->Curr); //order important here + e = e->NextInAEL; + } + } + } + + } +} +//------------------------------------------------------------------------------ + +void Clipper::DeleteFromSEL(TEdge *e) +{ + TEdge* SelPrev = e->PrevInSEL; + TEdge* SelNext = e->NextInSEL; + if( !SelPrev && !SelNext && (e != m_SortedEdges) ) return; //already deleted + if( SelPrev ) SelPrev->NextInSEL = SelNext; + else m_SortedEdges = SelNext; + if( SelNext ) SelNext->PrevInSEL = SelPrev; + e->NextInSEL = 0; + e->PrevInSEL = 0; +} +//------------------------------------------------------------------------------ + +#ifdef use_xyz +void Clipper::SetZ(IntPoint& pt, TEdge& e1, TEdge& e2) +{ + if (pt.Z != 0 || !m_ZFill) return; + else if (pt == e1.Bot) pt.Z = e1.Bot.Z; + else if (pt == e1.Top) pt.Z = e1.Top.Z; + else if (pt == e2.Bot) pt.Z = e2.Bot.Z; + else if (pt == e2.Top) pt.Z = e2.Top.Z; + else (*m_ZFill)(e1.Bot, e1.Top, e2.Bot, e2.Top, pt); +} +//------------------------------------------------------------------------------ +#endif + +void Clipper::IntersectEdges(TEdge *e1, TEdge *e2, IntPoint &Pt) +{ + bool e1Contributing = ( e1->OutIdx >= 0 ); + bool e2Contributing = ( e2->OutIdx >= 0 ); + +#ifdef use_xyz + SetZ(Pt, *e1, *e2); +#endif + +#ifdef use_lines + //if either edge is on an OPEN path ... + if (e1->WindDelta == 0 || e2->WindDelta == 0) + { + //ignore subject-subject open path intersections UNLESS they + //are both open paths, AND they are both 'contributing maximas' ... + if (e1->WindDelta == 0 && e2->WindDelta == 0) return; + + //if intersecting a subj line with a subj poly ... + else if (e1->PolyTyp == e2->PolyTyp && + e1->WindDelta != e2->WindDelta && m_ClipType == ctUnion) + { + if (e1->WindDelta == 0) + { + if (e2Contributing) + { + AddOutPt(e1, Pt); + if (e1Contributing) e1->OutIdx = Unassigned; + } + } + else + { + if (e1Contributing) + { + AddOutPt(e2, Pt); + if (e2Contributing) e2->OutIdx = Unassigned; + } + } + } + else if (e1->PolyTyp != e2->PolyTyp) + { + //toggle subj open path OutIdx on/off when Abs(clip.WndCnt) == 1 ... + if ((e1->WindDelta == 0) && abs(e2->WindCnt) == 1 && + (m_ClipType != ctUnion || e2->WindCnt2 == 0)) + { + AddOutPt(e1, Pt); + if (e1Contributing) e1->OutIdx = Unassigned; + } + else if ((e2->WindDelta == 0) && (abs(e1->WindCnt) == 1) && + (m_ClipType != ctUnion || e1->WindCnt2 == 0)) + { + AddOutPt(e2, Pt); + if (e2Contributing) e2->OutIdx = Unassigned; + } + } + return; + } +#endif + + //update winding counts... + //assumes that e1 will be to the Right of e2 ABOVE the intersection + if ( e1->PolyTyp == e2->PolyTyp ) + { + if ( IsEvenOddFillType( *e1) ) + { + int oldE1WindCnt = e1->WindCnt; + e1->WindCnt = e2->WindCnt; + e2->WindCnt = oldE1WindCnt; + } else + { + if (e1->WindCnt + e2->WindDelta == 0 ) e1->WindCnt = -e1->WindCnt; + else e1->WindCnt += e2->WindDelta; + if ( e2->WindCnt - e1->WindDelta == 0 ) e2->WindCnt = -e2->WindCnt; + else e2->WindCnt -= e1->WindDelta; + } + } else + { + if (!IsEvenOddFillType(*e2)) e1->WindCnt2 += e2->WindDelta; + else e1->WindCnt2 = ( e1->WindCnt2 == 0 ) ? 1 : 0; + if (!IsEvenOddFillType(*e1)) e2->WindCnt2 -= e1->WindDelta; + else e2->WindCnt2 = ( e2->WindCnt2 == 0 ) ? 1 : 0; + } + + PolyFillType e1FillType, e2FillType, e1FillType2, e2FillType2; + if (e1->PolyTyp == ptSubject) + { + e1FillType = m_SubjFillType; + e1FillType2 = m_ClipFillType; + } else + { + e1FillType = m_ClipFillType; + e1FillType2 = m_SubjFillType; + } + if (e2->PolyTyp == ptSubject) + { + e2FillType = m_SubjFillType; + e2FillType2 = m_ClipFillType; + } else + { + e2FillType = m_ClipFillType; + e2FillType2 = m_SubjFillType; + } + + cInt e1Wc, e2Wc; + switch (e1FillType) + { + case pftPositive: e1Wc = e1->WindCnt; break; + case pftNegative: e1Wc = -e1->WindCnt; break; + default: e1Wc = Abs(e1->WindCnt); + } + switch(e2FillType) + { + case pftPositive: e2Wc = e2->WindCnt; break; + case pftNegative: e2Wc = -e2->WindCnt; break; + default: e2Wc = Abs(e2->WindCnt); + } + + if ( e1Contributing && e2Contributing ) + { + if ((e1Wc != 0 && e1Wc != 1) || (e2Wc != 0 && e2Wc != 1) || + (e1->PolyTyp != e2->PolyTyp && m_ClipType != ctXor) ) + { + AddLocalMaxPoly(e1, e2, Pt); + } + else + { + AddOutPt(e1, Pt); + AddOutPt(e2, Pt); + SwapSides( *e1 , *e2 ); + SwapPolyIndexes( *e1 , *e2 ); + } + } + else if ( e1Contributing ) + { + if (e2Wc == 0 || e2Wc == 1) + { + AddOutPt(e1, Pt); + SwapSides(*e1, *e2); + SwapPolyIndexes(*e1, *e2); + } + } + else if ( e2Contributing ) + { + if (e1Wc == 0 || e1Wc == 1) + { + AddOutPt(e2, Pt); + SwapSides(*e1, *e2); + SwapPolyIndexes(*e1, *e2); + } + } + else if ( (e1Wc == 0 || e1Wc == 1) && (e2Wc == 0 || e2Wc == 1)) + { + //neither edge is currently contributing ... + + cInt e1Wc2, e2Wc2; + switch (e1FillType2) + { + case pftPositive: e1Wc2 = e1->WindCnt2; break; + case pftNegative : e1Wc2 = -e1->WindCnt2; break; + default: e1Wc2 = Abs(e1->WindCnt2); + } + switch (e2FillType2) + { + case pftPositive: e2Wc2 = e2->WindCnt2; break; + case pftNegative: e2Wc2 = -e2->WindCnt2; break; + default: e2Wc2 = Abs(e2->WindCnt2); + } + + if (e1->PolyTyp != e2->PolyTyp) + { + AddLocalMinPoly(e1, e2, Pt); + } + else if (e1Wc == 1 && e2Wc == 1) + switch( m_ClipType ) { + case ctIntersection: + if (e1Wc2 > 0 && e2Wc2 > 0) + AddLocalMinPoly(e1, e2, Pt); + break; + case ctUnion: + if ( e1Wc2 <= 0 && e2Wc2 <= 0 ) + AddLocalMinPoly(e1, e2, Pt); + break; + case ctDifference: + if (((e1->PolyTyp == ptClip) && (e1Wc2 > 0) && (e2Wc2 > 0)) || + ((e1->PolyTyp == ptSubject) && (e1Wc2 <= 0) && (e2Wc2 <= 0))) + AddLocalMinPoly(e1, e2, Pt); + break; + case ctXor: + AddLocalMinPoly(e1, e2, Pt); + } + else + SwapSides( *e1, *e2 ); + } +} +//------------------------------------------------------------------------------ + +void Clipper::SetHoleState(TEdge *e, OutRec *outrec) +{ + TEdge *e2 = e->PrevInAEL; + TEdge *eTmp = 0; + while (e2) + { + if (e2->OutIdx >= 0 && e2->WindDelta != 0) + { + if (!eTmp) eTmp = e2; + else if (eTmp->OutIdx == e2->OutIdx) eTmp = 0; + } + e2 = e2->PrevInAEL; + } + if (!eTmp) + { + outrec->FirstLeft = 0; + outrec->IsHole = false; + } + else + { + outrec->FirstLeft = m_PolyOuts[eTmp->OutIdx]; + outrec->IsHole = !outrec->FirstLeft->IsHole; + } +} +//------------------------------------------------------------------------------ + +OutRec* GetLowermostRec(OutRec *outRec1, OutRec *outRec2) +{ + //work out which polygon fragment has the correct hole state ... + if (!outRec1->BottomPt) + outRec1->BottomPt = GetBottomPt(outRec1->Pts); + if (!outRec2->BottomPt) + outRec2->BottomPt = GetBottomPt(outRec2->Pts); + OutPt *OutPt1 = outRec1->BottomPt; + OutPt *OutPt2 = outRec2->BottomPt; + if (OutPt1->Pt.Y > OutPt2->Pt.Y) return outRec1; + else if (OutPt1->Pt.Y < OutPt2->Pt.Y) return outRec2; + else if (OutPt1->Pt.X < OutPt2->Pt.X) return outRec1; + else if (OutPt1->Pt.X > OutPt2->Pt.X) return outRec2; + else if (OutPt1->Next == OutPt1) return outRec2; + else if (OutPt2->Next == OutPt2) return outRec1; + else if (FirstIsBottomPt(OutPt1, OutPt2)) return outRec1; + else return outRec2; +} +//------------------------------------------------------------------------------ + +bool OutRec1RightOfOutRec2(OutRec* outRec1, OutRec* outRec2) +{ + do + { + outRec1 = outRec1->FirstLeft; + if (outRec1 == outRec2) return true; + } while (outRec1); + return false; +} +//------------------------------------------------------------------------------ + +OutRec* Clipper::GetOutRec(int Idx) +{ + OutRec* outrec = m_PolyOuts[Idx]; + while (outrec != m_PolyOuts[outrec->Idx]) + outrec = m_PolyOuts[outrec->Idx]; + return outrec; +} +//------------------------------------------------------------------------------ + +void Clipper::AppendPolygon(TEdge *e1, TEdge *e2) +{ + //get the start and ends of both output polygons ... + OutRec *outRec1 = m_PolyOuts[e1->OutIdx]; + OutRec *outRec2 = m_PolyOuts[e2->OutIdx]; + + OutRec *holeStateRec; + if (OutRec1RightOfOutRec2(outRec1, outRec2)) + holeStateRec = outRec2; + else if (OutRec1RightOfOutRec2(outRec2, outRec1)) + holeStateRec = outRec1; + else + holeStateRec = GetLowermostRec(outRec1, outRec2); + + //get the start and ends of both output polygons and + //join e2 poly onto e1 poly and delete pointers to e2 ... + + OutPt* p1_lft = outRec1->Pts; + OutPt* p1_rt = p1_lft->Prev; + OutPt* p2_lft = outRec2->Pts; + OutPt* p2_rt = p2_lft->Prev; + + //join e2 poly onto e1 poly and delete pointers to e2 ... + if( e1->Side == esLeft ) + { + if( e2->Side == esLeft ) + { + //z y x a b c + ReversePolyPtLinks(p2_lft); + p2_lft->Next = p1_lft; + p1_lft->Prev = p2_lft; + p1_rt->Next = p2_rt; + p2_rt->Prev = p1_rt; + outRec1->Pts = p2_rt; + } else + { + //x y z a b c + p2_rt->Next = p1_lft; + p1_lft->Prev = p2_rt; + p2_lft->Prev = p1_rt; + p1_rt->Next = p2_lft; + outRec1->Pts = p2_lft; + } + } else + { + if( e2->Side == esRight ) + { + //a b c z y x + ReversePolyPtLinks(p2_lft); + p1_rt->Next = p2_rt; + p2_rt->Prev = p1_rt; + p2_lft->Next = p1_lft; + p1_lft->Prev = p2_lft; + } else + { + //a b c x y z + p1_rt->Next = p2_lft; + p2_lft->Prev = p1_rt; + p1_lft->Prev = p2_rt; + p2_rt->Next = p1_lft; + } + } + + outRec1->BottomPt = 0; + if (holeStateRec == outRec2) + { + if (outRec2->FirstLeft != outRec1) + outRec1->FirstLeft = outRec2->FirstLeft; + outRec1->IsHole = outRec2->IsHole; + } + outRec2->Pts = 0; + outRec2->BottomPt = 0; + outRec2->FirstLeft = outRec1; + + int OKIdx = e1->OutIdx; + int ObsoleteIdx = e2->OutIdx; + + e1->OutIdx = Unassigned; //nb: safe because we only get here via AddLocalMaxPoly + e2->OutIdx = Unassigned; + + TEdge* e = m_ActiveEdges; + while( e ) + { + if( e->OutIdx == ObsoleteIdx ) + { + e->OutIdx = OKIdx; + e->Side = e1->Side; + break; + } + e = e->NextInAEL; + } + + outRec2->Idx = outRec1->Idx; +} +//------------------------------------------------------------------------------ + +OutPt* Clipper::AddOutPt(TEdge *e, const IntPoint &pt) +{ + if( e->OutIdx < 0 ) + { + OutRec *outRec = CreateOutRec(); + outRec->IsOpen = (e->WindDelta == 0); + OutPt* newOp = new OutPt; + outRec->Pts = newOp; + newOp->Idx = outRec->Idx; + newOp->Pt = pt; + newOp->Next = newOp; + newOp->Prev = newOp; + if (!outRec->IsOpen) + SetHoleState(e, outRec); + e->OutIdx = outRec->Idx; + return newOp; + } else + { + OutRec *outRec = m_PolyOuts[e->OutIdx]; + //OutRec.Pts is the 'Left-most' point & OutRec.Pts.Prev is the 'Right-most' + OutPt* op = outRec->Pts; + + bool ToFront = (e->Side == esLeft); + if (ToFront && (pt == op->Pt)) return op; + else if (!ToFront && (pt == op->Prev->Pt)) return op->Prev; + + OutPt* newOp = new OutPt; + newOp->Idx = outRec->Idx; + newOp->Pt = pt; + newOp->Next = op; + newOp->Prev = op->Prev; + newOp->Prev->Next = newOp; + op->Prev = newOp; + if (ToFront) outRec->Pts = newOp; + return newOp; + } +} +//------------------------------------------------------------------------------ + +OutPt* Clipper::GetLastOutPt(TEdge *e) +{ + OutRec *outRec = m_PolyOuts[e->OutIdx]; + if (e->Side == esLeft) + return outRec->Pts; + else + return outRec->Pts->Prev; +} +//------------------------------------------------------------------------------ + +void Clipper::ProcessHorizontals() +{ + TEdge* horzEdge; + while (PopEdgeFromSEL(horzEdge)) + ProcessHorizontal(horzEdge); +} +//------------------------------------------------------------------------------ + +inline bool IsMinima(TEdge *e) +{ + return e && (e->Prev->NextInLML != e) && (e->Next->NextInLML != e); +} +//------------------------------------------------------------------------------ + +inline bool IsMaxima(TEdge *e, const cInt Y) +{ + return e && e->Top.Y == Y && !e->NextInLML; +} +//------------------------------------------------------------------------------ + +inline bool IsIntermediate(TEdge *e, const cInt Y) +{ + return e->Top.Y == Y && e->NextInLML; +} +//------------------------------------------------------------------------------ + +TEdge *GetMaximaPair(TEdge *e) +{ + if ((e->Next->Top == e->Top) && !e->Next->NextInLML) + return e->Next; + else if ((e->Prev->Top == e->Top) && !e->Prev->NextInLML) + return e->Prev; + else return 0; +} +//------------------------------------------------------------------------------ + +TEdge *GetMaximaPairEx(TEdge *e) +{ + //as GetMaximaPair() but returns 0 if MaxPair isn't in AEL (unless it's horizontal) + TEdge* result = GetMaximaPair(e); + if (result && (result->OutIdx == Skip || + (result->NextInAEL == result->PrevInAEL && !IsHorizontal(*result)))) return 0; + return result; +} +//------------------------------------------------------------------------------ + +void Clipper::SwapPositionsInSEL(TEdge *Edge1, TEdge *Edge2) +{ + if( !( Edge1->NextInSEL ) && !( Edge1->PrevInSEL ) ) return; + if( !( Edge2->NextInSEL ) && !( Edge2->PrevInSEL ) ) return; + + if( Edge1->NextInSEL == Edge2 ) + { + TEdge* Next = Edge2->NextInSEL; + if( Next ) Next->PrevInSEL = Edge1; + TEdge* Prev = Edge1->PrevInSEL; + if( Prev ) Prev->NextInSEL = Edge2; + Edge2->PrevInSEL = Prev; + Edge2->NextInSEL = Edge1; + Edge1->PrevInSEL = Edge2; + Edge1->NextInSEL = Next; + } + else if( Edge2->NextInSEL == Edge1 ) + { + TEdge* Next = Edge1->NextInSEL; + if( Next ) Next->PrevInSEL = Edge2; + TEdge* Prev = Edge2->PrevInSEL; + if( Prev ) Prev->NextInSEL = Edge1; + Edge1->PrevInSEL = Prev; + Edge1->NextInSEL = Edge2; + Edge2->PrevInSEL = Edge1; + Edge2->NextInSEL = Next; + } + else + { + TEdge* Next = Edge1->NextInSEL; + TEdge* Prev = Edge1->PrevInSEL; + Edge1->NextInSEL = Edge2->NextInSEL; + if( Edge1->NextInSEL ) Edge1->NextInSEL->PrevInSEL = Edge1; + Edge1->PrevInSEL = Edge2->PrevInSEL; + if( Edge1->PrevInSEL ) Edge1->PrevInSEL->NextInSEL = Edge1; + Edge2->NextInSEL = Next; + if( Edge2->NextInSEL ) Edge2->NextInSEL->PrevInSEL = Edge2; + Edge2->PrevInSEL = Prev; + if( Edge2->PrevInSEL ) Edge2->PrevInSEL->NextInSEL = Edge2; + } + + if( !Edge1->PrevInSEL ) m_SortedEdges = Edge1; + else if( !Edge2->PrevInSEL ) m_SortedEdges = Edge2; +} +//------------------------------------------------------------------------------ + +TEdge* GetNextInAEL(TEdge *e, Direction dir) +{ + return dir == dLeftToRight ? e->NextInAEL : e->PrevInAEL; +} +//------------------------------------------------------------------------------ + +void GetHorzDirection(TEdge& HorzEdge, Direction& Dir, cInt& Left, cInt& Right) +{ + if (HorzEdge.Bot.X < HorzEdge.Top.X) + { + Left = HorzEdge.Bot.X; + Right = HorzEdge.Top.X; + Dir = dLeftToRight; + } else + { + Left = HorzEdge.Top.X; + Right = HorzEdge.Bot.X; + Dir = dRightToLeft; + } +} +//------------------------------------------------------------------------ + +/******************************************************************************* +* Notes: Horizontal edges (HEs) at scanline intersections (ie at the Top or * +* Bottom of a scanbeam) are processed as if layered. The order in which HEs * +* are processed doesn't matter. HEs intersect with other HE Bot.Xs only [#] * +* (or they could intersect with Top.Xs only, ie EITHER Bot.Xs OR Top.Xs), * +* and with other non-horizontal edges [*]. Once these intersections are * +* processed, intermediate HEs then 'promote' the Edge above (NextInLML) into * +* the AEL. These 'promoted' edges may in turn intersect [%] with other HEs. * +*******************************************************************************/ + +void Clipper::ProcessHorizontal(TEdge *horzEdge) +{ + Direction dir; + cInt horzLeft, horzRight; + bool IsOpen = (horzEdge->WindDelta == 0); + + GetHorzDirection(*horzEdge, dir, horzLeft, horzRight); + + TEdge* eLastHorz = horzEdge, *eMaxPair = 0; + while (eLastHorz->NextInLML && IsHorizontal(*eLastHorz->NextInLML)) + eLastHorz = eLastHorz->NextInLML; + if (!eLastHorz->NextInLML) + eMaxPair = GetMaximaPair(eLastHorz); + + MaximaList::const_iterator maxIt; + MaximaList::const_reverse_iterator maxRit; + if (m_Maxima.size() > 0) + { + //get the first maxima in range (X) ... + if (dir == dLeftToRight) + { + maxIt = m_Maxima.begin(); + while (maxIt != m_Maxima.end() && *maxIt <= horzEdge->Bot.X) maxIt++; + if (maxIt != m_Maxima.end() && *maxIt >= eLastHorz->Top.X) + maxIt = m_Maxima.end(); + } + else + { + maxRit = m_Maxima.rbegin(); + while (maxRit != m_Maxima.rend() && *maxRit > horzEdge->Bot.X) maxRit++; + if (maxRit != m_Maxima.rend() && *maxRit <= eLastHorz->Top.X) + maxRit = m_Maxima.rend(); + } + } + + OutPt* op1 = 0; + + for (;;) //loop through consec. horizontal edges + { + + bool IsLastHorz = (horzEdge == eLastHorz); + TEdge* e = GetNextInAEL(horzEdge, dir); + while(e) + { + + //this code block inserts extra coords into horizontal edges (in output + //polygons) whereever maxima touch these horizontal edges. This helps + //'simplifying' polygons (ie if the Simplify property is set). + if (m_Maxima.size() > 0) + { + if (dir == dLeftToRight) + { + while (maxIt != m_Maxima.end() && *maxIt < e->Curr.X) + { + if (horzEdge->OutIdx >= 0 && !IsOpen) + AddOutPt(horzEdge, IntPoint(*maxIt, horzEdge->Bot.Y)); + maxIt++; + } + } + else + { + while (maxRit != m_Maxima.rend() && *maxRit > e->Curr.X) + { + if (horzEdge->OutIdx >= 0 && !IsOpen) + AddOutPt(horzEdge, IntPoint(*maxRit, horzEdge->Bot.Y)); + maxRit++; + } + } + }; + + if ((dir == dLeftToRight && e->Curr.X > horzRight) || + (dir == dRightToLeft && e->Curr.X < horzLeft)) break; + + //Also break if we've got to the end of an intermediate horizontal edge ... + //nb: Smaller Dx's are to the right of larger Dx's ABOVE the horizontal. + if (e->Curr.X == horzEdge->Top.X && horzEdge->NextInLML && + e->Dx < horzEdge->NextInLML->Dx) break; + + if (horzEdge->OutIdx >= 0 && !IsOpen) //note: may be done multiple times + { + op1 = AddOutPt(horzEdge, e->Curr); + TEdge* eNextHorz = m_SortedEdges; + while (eNextHorz) + { + if (eNextHorz->OutIdx >= 0 && + HorzSegmentsOverlap(horzEdge->Bot.X, + horzEdge->Top.X, eNextHorz->Bot.X, eNextHorz->Top.X)) + { + OutPt* op2 = GetLastOutPt(eNextHorz); + AddJoin(op2, op1, eNextHorz->Top); + } + eNextHorz = eNextHorz->NextInSEL; + } + AddGhostJoin(op1, horzEdge->Bot); + } + + //OK, so far we're still in range of the horizontal Edge but make sure + //we're at the last of consec. horizontals when matching with eMaxPair + if(e == eMaxPair && IsLastHorz) + { + if (horzEdge->OutIdx >= 0) + AddLocalMaxPoly(horzEdge, eMaxPair, horzEdge->Top); + DeleteFromAEL(horzEdge); + DeleteFromAEL(eMaxPair); + return; + } + + if(dir == dLeftToRight) + { + IntPoint Pt = IntPoint(e->Curr.X, horzEdge->Curr.Y); + IntersectEdges(horzEdge, e, Pt); + } + else + { + IntPoint Pt = IntPoint(e->Curr.X, horzEdge->Curr.Y); + IntersectEdges( e, horzEdge, Pt); + } + TEdge* eNext = GetNextInAEL(e, dir); + SwapPositionsInAEL( horzEdge, e ); + e = eNext; + } //end while(e) + + //Break out of loop if HorzEdge.NextInLML is not also horizontal ... + if (!horzEdge->NextInLML || !IsHorizontal(*horzEdge->NextInLML)) break; + + UpdateEdgeIntoAEL(horzEdge); + if (horzEdge->OutIdx >= 0) AddOutPt(horzEdge, horzEdge->Bot); + GetHorzDirection(*horzEdge, dir, horzLeft, horzRight); + + } //end for (;;) + + if (horzEdge->OutIdx >= 0 && !op1) + { + op1 = GetLastOutPt(horzEdge); + TEdge* eNextHorz = m_SortedEdges; + while (eNextHorz) + { + if (eNextHorz->OutIdx >= 0 && + HorzSegmentsOverlap(horzEdge->Bot.X, + horzEdge->Top.X, eNextHorz->Bot.X, eNextHorz->Top.X)) + { + OutPt* op2 = GetLastOutPt(eNextHorz); + AddJoin(op2, op1, eNextHorz->Top); + } + eNextHorz = eNextHorz->NextInSEL; + } + AddGhostJoin(op1, horzEdge->Top); + } + + if (horzEdge->NextInLML) + { + if(horzEdge->OutIdx >= 0) + { + op1 = AddOutPt( horzEdge, horzEdge->Top); + UpdateEdgeIntoAEL(horzEdge); + if (horzEdge->WindDelta == 0) return; + //nb: HorzEdge is no longer horizontal here + TEdge* ePrev = horzEdge->PrevInAEL; + TEdge* eNext = horzEdge->NextInAEL; + if (ePrev && ePrev->Curr.X == horzEdge->Bot.X && + ePrev->Curr.Y == horzEdge->Bot.Y && ePrev->WindDelta != 0 && + (ePrev->OutIdx >= 0 && ePrev->Curr.Y > ePrev->Top.Y && + SlopesEqual(*horzEdge, *ePrev, m_UseFullRange))) + { + OutPt* op2 = AddOutPt(ePrev, horzEdge->Bot); + AddJoin(op1, op2, horzEdge->Top); + } + else if (eNext && eNext->Curr.X == horzEdge->Bot.X && + eNext->Curr.Y == horzEdge->Bot.Y && eNext->WindDelta != 0 && + eNext->OutIdx >= 0 && eNext->Curr.Y > eNext->Top.Y && + SlopesEqual(*horzEdge, *eNext, m_UseFullRange)) + { + OutPt* op2 = AddOutPt(eNext, horzEdge->Bot); + AddJoin(op1, op2, horzEdge->Top); + } + } + else + UpdateEdgeIntoAEL(horzEdge); + } + else + { + if (horzEdge->OutIdx >= 0) AddOutPt(horzEdge, horzEdge->Top); + DeleteFromAEL(horzEdge); + } +} +//------------------------------------------------------------------------------ + +bool Clipper::ProcessIntersections(const cInt topY) +{ + if( !m_ActiveEdges ) return true; + try { + BuildIntersectList(topY); + size_t IlSize = m_IntersectList.size(); + if (IlSize == 0) return true; + if (IlSize == 1 || FixupIntersectionOrder()) ProcessIntersectList(); + else return false; + } + catch(...) + { + m_SortedEdges = 0; + DisposeIntersectNodes(); + throw clipperException("ProcessIntersections error"); + } + m_SortedEdges = 0; + return true; +} +//------------------------------------------------------------------------------ + +void Clipper::DisposeIntersectNodes() +{ + for (size_t i = 0; i < m_IntersectList.size(); ++i ) + delete m_IntersectList[i]; + m_IntersectList.clear(); +} +//------------------------------------------------------------------------------ + +void Clipper::BuildIntersectList(const cInt topY) +{ + if ( !m_ActiveEdges ) return; + + //prepare for sorting ... + TEdge* e = m_ActiveEdges; + m_SortedEdges = e; + while( e ) + { + e->PrevInSEL = e->PrevInAEL; + e->NextInSEL = e->NextInAEL; + e->Curr.X = TopX( *e, topY ); + e = e->NextInAEL; + } + + //bubblesort ... + bool isModified; + do + { + isModified = false; + e = m_SortedEdges; + while( e->NextInSEL ) + { + TEdge *eNext = e->NextInSEL; + IntPoint Pt; + if(e->Curr.X > eNext->Curr.X) + { + IntersectPoint(*e, *eNext, Pt); + if (Pt.Y < topY) Pt = IntPoint(TopX(*e, topY), topY); + IntersectNode * newNode = new IntersectNode; + newNode->Edge1 = e; + newNode->Edge2 = eNext; + newNode->Pt = Pt; + m_IntersectList.push_back(newNode); + + SwapPositionsInSEL(e, eNext); + isModified = true; + } + else + e = eNext; + } + if( e->PrevInSEL ) e->PrevInSEL->NextInSEL = 0; + else break; + } + while ( isModified ); + m_SortedEdges = 0; //important +} +//------------------------------------------------------------------------------ + + +void Clipper::ProcessIntersectList() +{ + for (size_t i = 0; i < m_IntersectList.size(); ++i) + { + IntersectNode* iNode = m_IntersectList[i]; + { + IntersectEdges( iNode->Edge1, iNode->Edge2, iNode->Pt); + SwapPositionsInAEL( iNode->Edge1 , iNode->Edge2 ); + } + delete iNode; + } + m_IntersectList.clear(); +} +//------------------------------------------------------------------------------ + +bool IntersectListSort(IntersectNode* node1, IntersectNode* node2) +{ + return node2->Pt.Y < node1->Pt.Y; +} +//------------------------------------------------------------------------------ + +inline bool EdgesAdjacent(const IntersectNode &inode) +{ + return (inode.Edge1->NextInSEL == inode.Edge2) || + (inode.Edge1->PrevInSEL == inode.Edge2); +} +//------------------------------------------------------------------------------ + +bool Clipper::FixupIntersectionOrder() +{ + //pre-condition: intersections are sorted Bottom-most first. + //Now it's crucial that intersections are made only between adjacent edges, + //so to ensure this the order of intersections may need adjusting ... + CopyAELToSEL(); + std::sort(m_IntersectList.begin(), m_IntersectList.end(), IntersectListSort); + size_t cnt = m_IntersectList.size(); + for (size_t i = 0; i < cnt; ++i) + { + if (!EdgesAdjacent(*m_IntersectList[i])) + { + size_t j = i + 1; + while (j < cnt && !EdgesAdjacent(*m_IntersectList[j])) j++; + if (j == cnt) return false; + std::swap(m_IntersectList[i], m_IntersectList[j]); + } + SwapPositionsInSEL(m_IntersectList[i]->Edge1, m_IntersectList[i]->Edge2); + } + return true; +} +//------------------------------------------------------------------------------ + +void Clipper::DoMaxima(TEdge *e) +{ + TEdge* eMaxPair = GetMaximaPairEx(e); + if (!eMaxPair) + { + if (e->OutIdx >= 0) + AddOutPt(e, e->Top); + DeleteFromAEL(e); + return; + } + + TEdge* eNext = e->NextInAEL; + while(eNext && eNext != eMaxPair) + { + IntersectEdges(e, eNext, e->Top); + SwapPositionsInAEL(e, eNext); + eNext = e->NextInAEL; + } + + if(e->OutIdx == Unassigned && eMaxPair->OutIdx == Unassigned) + { + DeleteFromAEL(e); + DeleteFromAEL(eMaxPair); + } + else if( e->OutIdx >= 0 && eMaxPair->OutIdx >= 0 ) + { + if (e->OutIdx >= 0) AddLocalMaxPoly(e, eMaxPair, e->Top); + DeleteFromAEL(e); + DeleteFromAEL(eMaxPair); + } +#ifdef use_lines + else if (e->WindDelta == 0) + { + if (e->OutIdx >= 0) + { + AddOutPt(e, e->Top); + e->OutIdx = Unassigned; + } + DeleteFromAEL(e); + + if (eMaxPair->OutIdx >= 0) + { + AddOutPt(eMaxPair, e->Top); + eMaxPair->OutIdx = Unassigned; + } + DeleteFromAEL(eMaxPair); + } +#endif + else throw clipperException("DoMaxima error"); +} +//------------------------------------------------------------------------------ + +void Clipper::ProcessEdgesAtTopOfScanbeam(const cInt topY) +{ + TEdge* e = m_ActiveEdges; + while( e ) + { + //1. process maxima, treating them as if they're 'bent' horizontal edges, + // but exclude maxima with horizontal edges. nb: e can't be a horizontal. + bool IsMaximaEdge = IsMaxima(e, topY); + + if(IsMaximaEdge) + { + TEdge* eMaxPair = GetMaximaPairEx(e); + IsMaximaEdge = (!eMaxPair || !IsHorizontal(*eMaxPair)); + } + + if(IsMaximaEdge) + { + if (m_StrictSimple) m_Maxima.push_back(e->Top.X); + TEdge* ePrev = e->PrevInAEL; + DoMaxima(e); + if( !ePrev ) e = m_ActiveEdges; + else e = ePrev->NextInAEL; + } + else + { + //2. promote horizontal edges, otherwise update Curr.X and Curr.Y ... + if (IsIntermediate(e, topY) && IsHorizontal(*e->NextInLML)) + { + UpdateEdgeIntoAEL(e); + if (e->OutIdx >= 0) + AddOutPt(e, e->Bot); + AddEdgeToSEL(e); + } + else + { + e->Curr.X = TopX( *e, topY ); + e->Curr.Y = topY; + } + + //When StrictlySimple and 'e' is being touched by another edge, then + //make sure both edges have a vertex here ... + if (m_StrictSimple) + { + TEdge* ePrev = e->PrevInAEL; + if ((e->OutIdx >= 0) && (e->WindDelta != 0) && ePrev && (ePrev->OutIdx >= 0) && + (ePrev->Curr.X == e->Curr.X) && (ePrev->WindDelta != 0)) + { + IntPoint pt = e->Curr; +#ifdef use_xyz + SetZ(pt, *ePrev, *e); +#endif + OutPt* op = AddOutPt(ePrev, pt); + OutPt* op2 = AddOutPt(e, pt); + AddJoin(op, op2, pt); //StrictlySimple (type-3) join + } + } + + e = e->NextInAEL; + } + } + + //3. Process horizontals at the Top of the scanbeam ... + m_Maxima.sort(); + ProcessHorizontals(); + m_Maxima.clear(); + + //4. Promote intermediate vertices ... + e = m_ActiveEdges; + while(e) + { + if(IsIntermediate(e, topY)) + { + OutPt* op = 0; + if( e->OutIdx >= 0 ) + op = AddOutPt(e, e->Top); + UpdateEdgeIntoAEL(e); + + //if output polygons share an edge, they'll need joining later ... + TEdge* ePrev = e->PrevInAEL; + TEdge* eNext = e->NextInAEL; + if (ePrev && ePrev->Curr.X == e->Bot.X && + ePrev->Curr.Y == e->Bot.Y && op && + ePrev->OutIdx >= 0 && ePrev->Curr.Y > ePrev->Top.Y && + SlopesEqual(e->Curr, e->Top, ePrev->Curr, ePrev->Top, m_UseFullRange) && + (e->WindDelta != 0) && (ePrev->WindDelta != 0)) + { + OutPt* op2 = AddOutPt(ePrev, e->Bot); + AddJoin(op, op2, e->Top); + } + else if (eNext && eNext->Curr.X == e->Bot.X && + eNext->Curr.Y == e->Bot.Y && op && + eNext->OutIdx >= 0 && eNext->Curr.Y > eNext->Top.Y && + SlopesEqual(e->Curr, e->Top, eNext->Curr, eNext->Top, m_UseFullRange) && + (e->WindDelta != 0) && (eNext->WindDelta != 0)) + { + OutPt* op2 = AddOutPt(eNext, e->Bot); + AddJoin(op, op2, e->Top); + } + } + e = e->NextInAEL; + } +} +//------------------------------------------------------------------------------ + +void Clipper::FixupOutPolyline(OutRec &outrec) +{ + OutPt *pp = outrec.Pts; + OutPt *lastPP = pp->Prev; + while (pp != lastPP) + { + pp = pp->Next; + if (pp->Pt == pp->Prev->Pt) + { + if (pp == lastPP) lastPP = pp->Prev; + OutPt *tmpPP = pp->Prev; + tmpPP->Next = pp->Next; + pp->Next->Prev = tmpPP; + delete pp; + pp = tmpPP; + } + } + + if (pp == pp->Prev) + { + DisposeOutPts(pp); + outrec.Pts = 0; + return; + } +} +//------------------------------------------------------------------------------ + +void Clipper::FixupOutPolygon(OutRec &outrec) +{ + //FixupOutPolygon() - removes duplicate points and simplifies consecutive + //parallel edges by removing the middle vertex. + OutPt *lastOK = 0; + outrec.BottomPt = 0; + OutPt *pp = outrec.Pts; + bool preserveCol = m_PreserveCollinear || m_StrictSimple; + + for (;;) + { + if (pp->Prev == pp || pp->Prev == pp->Next) + { + DisposeOutPts(pp); + outrec.Pts = 0; + return; + } + + //test for duplicate points and collinear edges ... + if ((pp->Pt == pp->Next->Pt) || (pp->Pt == pp->Prev->Pt) || + (SlopesEqual(pp->Prev->Pt, pp->Pt, pp->Next->Pt, m_UseFullRange) && + (!preserveCol || !Pt2IsBetweenPt1AndPt3(pp->Prev->Pt, pp->Pt, pp->Next->Pt)))) + { + lastOK = 0; + OutPt *tmp = pp; + pp->Prev->Next = pp->Next; + pp->Next->Prev = pp->Prev; + pp = pp->Prev; + delete tmp; + } + else if (pp == lastOK) break; + else + { + if (!lastOK) lastOK = pp; + pp = pp->Next; + } + } + outrec.Pts = pp; +} +//------------------------------------------------------------------------------ + +int PointCount(OutPt *Pts) +{ + if (!Pts) return 0; + int result = 0; + OutPt* p = Pts; + do + { + result++; + p = p->Next; + } + while (p != Pts); + return result; +} +//------------------------------------------------------------------------------ + +void Clipper::BuildResult(Paths &polys) +{ + polys.reserve(m_PolyOuts.size()); + for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) + { + if (!m_PolyOuts[i]->Pts) continue; + Path pg; + OutPt* p = m_PolyOuts[i]->Pts->Prev; + int cnt = PointCount(p); + if (cnt < 2) continue; + pg.reserve(cnt); + for (int i = 0; i < cnt; ++i) + { + pg.push_back(p->Pt); + p = p->Prev; + } + polys.push_back(pg); + } +} +//------------------------------------------------------------------------------ + +void Clipper::BuildResult2(PolyTree& polytree) +{ + polytree.Clear(); + polytree.AllNodes.reserve(m_PolyOuts.size()); + //add each output polygon/contour to polytree ... + for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); i++) + { + OutRec* outRec = m_PolyOuts[i]; + int cnt = PointCount(outRec->Pts); + if ((outRec->IsOpen && cnt < 2) || (!outRec->IsOpen && cnt < 3)) continue; + FixHoleLinkage(*outRec); + PolyNode* pn = new PolyNode(); + //nb: polytree takes ownership of all the PolyNodes + polytree.AllNodes.push_back(pn); + outRec->PolyNd = pn; + pn->Parent = 0; + pn->Index = 0; + pn->Contour.reserve(cnt); + OutPt *op = outRec->Pts->Prev; + for (int j = 0; j < cnt; j++) + { + pn->Contour.push_back(op->Pt); + op = op->Prev; + } + } + + //fixup PolyNode links etc ... + polytree.Childs.reserve(m_PolyOuts.size()); + for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); i++) + { + OutRec* outRec = m_PolyOuts[i]; + if (!outRec->PolyNd) continue; + if (outRec->IsOpen) + { + outRec->PolyNd->m_IsOpen = true; + polytree.AddChild(*outRec->PolyNd); + } + else if (outRec->FirstLeft && outRec->FirstLeft->PolyNd) + outRec->FirstLeft->PolyNd->AddChild(*outRec->PolyNd); + else + polytree.AddChild(*outRec->PolyNd); + } +} +//------------------------------------------------------------------------------ + +void SwapIntersectNodes(IntersectNode &int1, IntersectNode &int2) +{ + //just swap the contents (because fIntersectNodes is a single-linked-list) + IntersectNode inode = int1; //gets a copy of Int1 + int1.Edge1 = int2.Edge1; + int1.Edge2 = int2.Edge2; + int1.Pt = int2.Pt; + int2.Edge1 = inode.Edge1; + int2.Edge2 = inode.Edge2; + int2.Pt = inode.Pt; +} +//------------------------------------------------------------------------------ + +inline bool E2InsertsBeforeE1(TEdge &e1, TEdge &e2) +{ + if (e2.Curr.X == e1.Curr.X) + { + if (e2.Top.Y > e1.Top.Y) + return e2.Top.X < TopX(e1, e2.Top.Y); + else return e1.Top.X > TopX(e2, e1.Top.Y); + } + else return e2.Curr.X < e1.Curr.X; +} +//------------------------------------------------------------------------------ + +bool GetOverlap(const cInt a1, const cInt a2, const cInt b1, const cInt b2, + cInt& Left, cInt& Right) +{ + if (a1 < a2) + { + if (b1 < b2) {Left = std::max(a1,b1); Right = std::min(a2,b2);} + else {Left = std::max(a1,b2); Right = std::min(a2,b1);} + } + else + { + if (b1 < b2) {Left = std::max(a2,b1); Right = std::min(a1,b2);} + else {Left = std::max(a2,b2); Right = std::min(a1,b1);} + } + return Left < Right; +} +//------------------------------------------------------------------------------ + +inline void UpdateOutPtIdxs(OutRec& outrec) +{ + OutPt* op = outrec.Pts; + do + { + op->Idx = outrec.Idx; + op = op->Prev; + } + while(op != outrec.Pts); +} +//------------------------------------------------------------------------------ + +void Clipper::InsertEdgeIntoAEL(TEdge *edge, TEdge* startEdge) +{ + if(!m_ActiveEdges) + { + edge->PrevInAEL = 0; + edge->NextInAEL = 0; + m_ActiveEdges = edge; + } + else if(!startEdge && E2InsertsBeforeE1(*m_ActiveEdges, *edge)) + { + edge->PrevInAEL = 0; + edge->NextInAEL = m_ActiveEdges; + m_ActiveEdges->PrevInAEL = edge; + m_ActiveEdges = edge; + } + else + { + if(!startEdge) startEdge = m_ActiveEdges; + while(startEdge->NextInAEL && + !E2InsertsBeforeE1(*startEdge->NextInAEL , *edge)) + startEdge = startEdge->NextInAEL; + edge->NextInAEL = startEdge->NextInAEL; + if(startEdge->NextInAEL) startEdge->NextInAEL->PrevInAEL = edge; + edge->PrevInAEL = startEdge; + startEdge->NextInAEL = edge; + } +} +//---------------------------------------------------------------------- + +OutPt* DupOutPt(OutPt* outPt, bool InsertAfter) +{ + OutPt* result = new OutPt; + result->Pt = outPt->Pt; + result->Idx = outPt->Idx; + if (InsertAfter) + { + result->Next = outPt->Next; + result->Prev = outPt; + outPt->Next->Prev = result; + outPt->Next = result; + } + else + { + result->Prev = outPt->Prev; + result->Next = outPt; + outPt->Prev->Next = result; + outPt->Prev = result; + } + return result; +} +//------------------------------------------------------------------------------ + +bool JoinHorz(OutPt* op1, OutPt* op1b, OutPt* op2, OutPt* op2b, + const IntPoint Pt, bool DiscardLeft) +{ + Direction Dir1 = (op1->Pt.X > op1b->Pt.X ? dRightToLeft : dLeftToRight); + Direction Dir2 = (op2->Pt.X > op2b->Pt.X ? dRightToLeft : dLeftToRight); + if (Dir1 == Dir2) return false; + + //When DiscardLeft, we want Op1b to be on the Left of Op1, otherwise we + //want Op1b to be on the Right. (And likewise with Op2 and Op2b.) + //So, to facilitate this while inserting Op1b and Op2b ... + //when DiscardLeft, make sure we're AT or RIGHT of Pt before adding Op1b, + //otherwise make sure we're AT or LEFT of Pt. (Likewise with Op2b.) + if (Dir1 == dLeftToRight) + { + while (op1->Next->Pt.X <= Pt.X && + op1->Next->Pt.X >= op1->Pt.X && op1->Next->Pt.Y == Pt.Y) + op1 = op1->Next; + if (DiscardLeft && (op1->Pt.X != Pt.X)) op1 = op1->Next; + op1b = DupOutPt(op1, !DiscardLeft); + if (op1b->Pt != Pt) + { + op1 = op1b; + op1->Pt = Pt; + op1b = DupOutPt(op1, !DiscardLeft); + } + } + else + { + while (op1->Next->Pt.X >= Pt.X && + op1->Next->Pt.X <= op1->Pt.X && op1->Next->Pt.Y == Pt.Y) + op1 = op1->Next; + if (!DiscardLeft && (op1->Pt.X != Pt.X)) op1 = op1->Next; + op1b = DupOutPt(op1, DiscardLeft); + if (op1b->Pt != Pt) + { + op1 = op1b; + op1->Pt = Pt; + op1b = DupOutPt(op1, DiscardLeft); + } + } + + if (Dir2 == dLeftToRight) + { + while (op2->Next->Pt.X <= Pt.X && + op2->Next->Pt.X >= op2->Pt.X && op2->Next->Pt.Y == Pt.Y) + op2 = op2->Next; + if (DiscardLeft && (op2->Pt.X != Pt.X)) op2 = op2->Next; + op2b = DupOutPt(op2, !DiscardLeft); + if (op2b->Pt != Pt) + { + op2 = op2b; + op2->Pt = Pt; + op2b = DupOutPt(op2, !DiscardLeft); + }; + } else + { + while (op2->Next->Pt.X >= Pt.X && + op2->Next->Pt.X <= op2->Pt.X && op2->Next->Pt.Y == Pt.Y) + op2 = op2->Next; + if (!DiscardLeft && (op2->Pt.X != Pt.X)) op2 = op2->Next; + op2b = DupOutPt(op2, DiscardLeft); + if (op2b->Pt != Pt) + { + op2 = op2b; + op2->Pt = Pt; + op2b = DupOutPt(op2, DiscardLeft); + }; + }; + + if ((Dir1 == dLeftToRight) == DiscardLeft) + { + op1->Prev = op2; + op2->Next = op1; + op1b->Next = op2b; + op2b->Prev = op1b; + } + else + { + op1->Next = op2; + op2->Prev = op1; + op1b->Prev = op2b; + op2b->Next = op1b; + } + return true; +} +//------------------------------------------------------------------------------ + +bool Clipper::JoinPoints(Join *j, OutRec* outRec1, OutRec* outRec2) +{ + OutPt *op1 = j->OutPt1, *op1b; + OutPt *op2 = j->OutPt2, *op2b; + + //There are 3 kinds of joins for output polygons ... + //1. Horizontal joins where Join.OutPt1 & Join.OutPt2 are vertices anywhere + //along (horizontal) collinear edges (& Join.OffPt is on the same horizontal). + //2. Non-horizontal joins where Join.OutPt1 & Join.OutPt2 are at the same + //location at the Bottom of the overlapping segment (& Join.OffPt is above). + //3. StrictSimple joins where edges touch but are not collinear and where + //Join.OutPt1, Join.OutPt2 & Join.OffPt all share the same point. + bool isHorizontal = (j->OutPt1->Pt.Y == j->OffPt.Y); + + if (isHorizontal && (j->OffPt == j->OutPt1->Pt) && + (j->OffPt == j->OutPt2->Pt)) + { + //Strictly Simple join ... + if (outRec1 != outRec2) return false; + op1b = j->OutPt1->Next; + while (op1b != op1 && (op1b->Pt == j->OffPt)) + op1b = op1b->Next; + bool reverse1 = (op1b->Pt.Y > j->OffPt.Y); + op2b = j->OutPt2->Next; + while (op2b != op2 && (op2b->Pt == j->OffPt)) + op2b = op2b->Next; + bool reverse2 = (op2b->Pt.Y > j->OffPt.Y); + if (reverse1 == reverse2) return false; + if (reverse1) + { + op1b = DupOutPt(op1, false); + op2b = DupOutPt(op2, true); + op1->Prev = op2; + op2->Next = op1; + op1b->Next = op2b; + op2b->Prev = op1b; + j->OutPt1 = op1; + j->OutPt2 = op1b; + return true; + } else + { + op1b = DupOutPt(op1, true); + op2b = DupOutPt(op2, false); + op1->Next = op2; + op2->Prev = op1; + op1b->Prev = op2b; + op2b->Next = op1b; + j->OutPt1 = op1; + j->OutPt2 = op1b; + return true; + } + } + else if (isHorizontal) + { + //treat horizontal joins differently to non-horizontal joins since with + //them we're not yet sure where the overlapping is. OutPt1.Pt & OutPt2.Pt + //may be anywhere along the horizontal edge. + op1b = op1; + while (op1->Prev->Pt.Y == op1->Pt.Y && op1->Prev != op1b && op1->Prev != op2) + op1 = op1->Prev; + while (op1b->Next->Pt.Y == op1b->Pt.Y && op1b->Next != op1 && op1b->Next != op2) + op1b = op1b->Next; + if (op1b->Next == op1 || op1b->Next == op2) return false; //a flat 'polygon' + + op2b = op2; + while (op2->Prev->Pt.Y == op2->Pt.Y && op2->Prev != op2b && op2->Prev != op1b) + op2 = op2->Prev; + while (op2b->Next->Pt.Y == op2b->Pt.Y && op2b->Next != op2 && op2b->Next != op1) + op2b = op2b->Next; + if (op2b->Next == op2 || op2b->Next == op1) return false; //a flat 'polygon' + + cInt Left, Right; + //Op1 --> Op1b & Op2 --> Op2b are the extremites of the horizontal edges + if (!GetOverlap(op1->Pt.X, op1b->Pt.X, op2->Pt.X, op2b->Pt.X, Left, Right)) + return false; + + //DiscardLeftSide: when overlapping edges are joined, a spike will created + //which needs to be cleaned up. However, we don't want Op1 or Op2 caught up + //on the discard Side as either may still be needed for other joins ... + IntPoint Pt; + bool DiscardLeftSide; + if (op1->Pt.X >= Left && op1->Pt.X <= Right) + { + Pt = op1->Pt; DiscardLeftSide = (op1->Pt.X > op1b->Pt.X); + } + else if (op2->Pt.X >= Left&& op2->Pt.X <= Right) + { + Pt = op2->Pt; DiscardLeftSide = (op2->Pt.X > op2b->Pt.X); + } + else if (op1b->Pt.X >= Left && op1b->Pt.X <= Right) + { + Pt = op1b->Pt; DiscardLeftSide = op1b->Pt.X > op1->Pt.X; + } + else + { + Pt = op2b->Pt; DiscardLeftSide = (op2b->Pt.X > op2->Pt.X); + } + j->OutPt1 = op1; j->OutPt2 = op2; + return JoinHorz(op1, op1b, op2, op2b, Pt, DiscardLeftSide); + } else + { + //nb: For non-horizontal joins ... + // 1. Jr.OutPt1.Pt.Y == Jr.OutPt2.Pt.Y + // 2. Jr.OutPt1.Pt > Jr.OffPt.Y + + //make sure the polygons are correctly oriented ... + op1b = op1->Next; + while ((op1b->Pt == op1->Pt) && (op1b != op1)) op1b = op1b->Next; + bool Reverse1 = ((op1b->Pt.Y > op1->Pt.Y) || + !SlopesEqual(op1->Pt, op1b->Pt, j->OffPt, m_UseFullRange)); + if (Reverse1) + { + op1b = op1->Prev; + while ((op1b->Pt == op1->Pt) && (op1b != op1)) op1b = op1b->Prev; + if ((op1b->Pt.Y > op1->Pt.Y) || + !SlopesEqual(op1->Pt, op1b->Pt, j->OffPt, m_UseFullRange)) return false; + }; + op2b = op2->Next; + while ((op2b->Pt == op2->Pt) && (op2b != op2))op2b = op2b->Next; + bool Reverse2 = ((op2b->Pt.Y > op2->Pt.Y) || + !SlopesEqual(op2->Pt, op2b->Pt, j->OffPt, m_UseFullRange)); + if (Reverse2) + { + op2b = op2->Prev; + while ((op2b->Pt == op2->Pt) && (op2b != op2)) op2b = op2b->Prev; + if ((op2b->Pt.Y > op2->Pt.Y) || + !SlopesEqual(op2->Pt, op2b->Pt, j->OffPt, m_UseFullRange)) return false; + } + + if ((op1b == op1) || (op2b == op2) || (op1b == op2b) || + ((outRec1 == outRec2) && (Reverse1 == Reverse2))) return false; + + if (Reverse1) + { + op1b = DupOutPt(op1, false); + op2b = DupOutPt(op2, true); + op1->Prev = op2; + op2->Next = op1; + op1b->Next = op2b; + op2b->Prev = op1b; + j->OutPt1 = op1; + j->OutPt2 = op1b; + return true; + } else + { + op1b = DupOutPt(op1, true); + op2b = DupOutPt(op2, false); + op1->Next = op2; + op2->Prev = op1; + op1b->Prev = op2b; + op2b->Next = op1b; + j->OutPt1 = op1; + j->OutPt2 = op1b; + return true; + } + } +} +//---------------------------------------------------------------------- + +static OutRec* ParseFirstLeft(OutRec* FirstLeft) +{ + while (FirstLeft && !FirstLeft->Pts) + FirstLeft = FirstLeft->FirstLeft; + return FirstLeft; +} +//------------------------------------------------------------------------------ + +void Clipper::FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec) +{ + //tests if NewOutRec contains the polygon before reassigning FirstLeft + for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) + { + OutRec* outRec = m_PolyOuts[i]; + OutRec* firstLeft = ParseFirstLeft(outRec->FirstLeft); + if (outRec->Pts && firstLeft == OldOutRec) + { + if (Poly2ContainsPoly1(outRec->Pts, NewOutRec->Pts)) + outRec->FirstLeft = NewOutRec; + } + } +} +//---------------------------------------------------------------------- + +void Clipper::FixupFirstLefts2(OutRec* InnerOutRec, OutRec* OuterOutRec) +{ + //A polygon has split into two such that one is now the inner of the other. + //It's possible that these polygons now wrap around other polygons, so check + //every polygon that's also contained by OuterOutRec's FirstLeft container + //(including 0) to see if they've become inner to the new inner polygon ... + OutRec* orfl = OuterOutRec->FirstLeft; + for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) + { + OutRec* outRec = m_PolyOuts[i]; + + if (!outRec->Pts || outRec == OuterOutRec || outRec == InnerOutRec) + continue; + OutRec* firstLeft = ParseFirstLeft(outRec->FirstLeft); + if (firstLeft != orfl && firstLeft != InnerOutRec && firstLeft != OuterOutRec) + continue; + if (Poly2ContainsPoly1(outRec->Pts, InnerOutRec->Pts)) + outRec->FirstLeft = InnerOutRec; + else if (Poly2ContainsPoly1(outRec->Pts, OuterOutRec->Pts)) + outRec->FirstLeft = OuterOutRec; + else if (outRec->FirstLeft == InnerOutRec || outRec->FirstLeft == OuterOutRec) + outRec->FirstLeft = orfl; + } +} +//---------------------------------------------------------------------- +void Clipper::FixupFirstLefts3(OutRec* OldOutRec, OutRec* NewOutRec) +{ + //reassigns FirstLeft WITHOUT testing if NewOutRec contains the polygon + for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) + { + OutRec* outRec = m_PolyOuts[i]; + OutRec* firstLeft = ParseFirstLeft(outRec->FirstLeft); + if (outRec->Pts && outRec->FirstLeft == OldOutRec) + outRec->FirstLeft = NewOutRec; + } +} +//---------------------------------------------------------------------- + +void Clipper::JoinCommonEdges() +{ + for (JoinList::size_type i = 0; i < m_Joins.size(); i++) + { + Join* join = m_Joins[i]; + + OutRec *outRec1 = GetOutRec(join->OutPt1->Idx); + OutRec *outRec2 = GetOutRec(join->OutPt2->Idx); + + if (!outRec1->Pts || !outRec2->Pts) continue; + if (outRec1->IsOpen || outRec2->IsOpen) continue; + + //get the polygon fragment with the correct hole state (FirstLeft) + //before calling JoinPoints() ... + OutRec *holeStateRec; + if (outRec1 == outRec2) holeStateRec = outRec1; + else if (OutRec1RightOfOutRec2(outRec1, outRec2)) holeStateRec = outRec2; + else if (OutRec1RightOfOutRec2(outRec2, outRec1)) holeStateRec = outRec1; + else holeStateRec = GetLowermostRec(outRec1, outRec2); + + if (!JoinPoints(join, outRec1, outRec2)) continue; + + if (outRec1 == outRec2) + { + //instead of joining two polygons, we've just created a new one by + //splitting one polygon into two. + outRec1->Pts = join->OutPt1; + outRec1->BottomPt = 0; + outRec2 = CreateOutRec(); + outRec2->Pts = join->OutPt2; + + //update all OutRec2.Pts Idx's ... + UpdateOutPtIdxs(*outRec2); + + if (Poly2ContainsPoly1(outRec2->Pts, outRec1->Pts)) + { + //outRec1 contains outRec2 ... + outRec2->IsHole = !outRec1->IsHole; + outRec2->FirstLeft = outRec1; + + if (m_UsingPolyTree) FixupFirstLefts2(outRec2, outRec1); + + if ((outRec2->IsHole ^ m_ReverseOutput) == (Area(*outRec2) > 0)) + ReversePolyPtLinks(outRec2->Pts); + + } else if (Poly2ContainsPoly1(outRec1->Pts, outRec2->Pts)) + { + //outRec2 contains outRec1 ... + outRec2->IsHole = outRec1->IsHole; + outRec1->IsHole = !outRec2->IsHole; + outRec2->FirstLeft = outRec1->FirstLeft; + outRec1->FirstLeft = outRec2; + + if (m_UsingPolyTree) FixupFirstLefts2(outRec1, outRec2); + + if ((outRec1->IsHole ^ m_ReverseOutput) == (Area(*outRec1) > 0)) + ReversePolyPtLinks(outRec1->Pts); + } + else + { + //the 2 polygons are completely separate ... + outRec2->IsHole = outRec1->IsHole; + outRec2->FirstLeft = outRec1->FirstLeft; + + //fixup FirstLeft pointers that may need reassigning to OutRec2 + if (m_UsingPolyTree) FixupFirstLefts1(outRec1, outRec2); + } + + } else + { + //joined 2 polygons together ... + + outRec2->Pts = 0; + outRec2->BottomPt = 0; + outRec2->Idx = outRec1->Idx; + + outRec1->IsHole = holeStateRec->IsHole; + if (holeStateRec == outRec2) + outRec1->FirstLeft = outRec2->FirstLeft; + outRec2->FirstLeft = outRec1; + + if (m_UsingPolyTree) FixupFirstLefts3(outRec2, outRec1); + } + } +} + +//------------------------------------------------------------------------------ +// ClipperOffset support functions ... +//------------------------------------------------------------------------------ + +DoublePoint GetUnitNormal(const IntPoint &pt1, const IntPoint &pt2) +{ + if(pt2.X == pt1.X && pt2.Y == pt1.Y) + return DoublePoint(0, 0); + + double Dx = (double)(pt2.X - pt1.X); + double dy = (double)(pt2.Y - pt1.Y); + double f = 1 *1.0/ std::sqrt( Dx*Dx + dy*dy ); + Dx *= f; + dy *= f; + return DoublePoint(dy, -Dx); +} + +//------------------------------------------------------------------------------ +// ClipperOffset class +//------------------------------------------------------------------------------ + +ClipperOffset::ClipperOffset(double miterLimit, double arcTolerance) +{ + this->MiterLimit = miterLimit; + this->ArcTolerance = arcTolerance; + m_lowest.X = -1; +} +//------------------------------------------------------------------------------ + +ClipperOffset::~ClipperOffset() +{ + Clear(); +} +//------------------------------------------------------------------------------ + +void ClipperOffset::Clear() +{ + for (int i = 0; i < m_polyNodes.ChildCount(); ++i) + delete m_polyNodes.Childs[i]; + m_polyNodes.Childs.clear(); + m_lowest.X = -1; +} +//------------------------------------------------------------------------------ + +void ClipperOffset::AddPath(const Path& path, JoinType joinType, EndType endType) +{ + int highI = (int)path.size() - 1; + if (highI < 0) return; + PolyNode* newNode = new PolyNode(); + newNode->m_jointype = joinType; + newNode->m_endtype = endType; + + //strip duplicate points from path and also get index to the lowest point ... + if (endType == etClosedLine || endType == etClosedPolygon) + while (highI > 0 && path[0] == path[highI]) highI--; + newNode->Contour.reserve(highI + 1); + newNode->Contour.push_back(path[0]); + int j = 0, k = 0; + for (int i = 1; i <= highI; i++) + if (newNode->Contour[j] != path[i]) + { + j++; + newNode->Contour.push_back(path[i]); + if (path[i].Y > newNode->Contour[k].Y || + (path[i].Y == newNode->Contour[k].Y && + path[i].X < newNode->Contour[k].X)) k = j; + } + if (endType == etClosedPolygon && j < 2) + { + delete newNode; + return; + } + m_polyNodes.AddChild(*newNode); + + //if this path's lowest pt is lower than all the others then update m_lowest + if (endType != etClosedPolygon) return; + if (m_lowest.X < 0) + m_lowest = IntPoint(m_polyNodes.ChildCount() - 1, k); + else + { + IntPoint ip = m_polyNodes.Childs[(int)m_lowest.X]->Contour[(int)m_lowest.Y]; + if (newNode->Contour[k].Y > ip.Y || + (newNode->Contour[k].Y == ip.Y && + newNode->Contour[k].X < ip.X)) + m_lowest = IntPoint(m_polyNodes.ChildCount() - 1, k); + } +} +//------------------------------------------------------------------------------ + +void ClipperOffset::AddPaths(const Paths& paths, JoinType joinType, EndType endType) +{ + for (Paths::size_type i = 0; i < paths.size(); ++i) + AddPath(paths[i], joinType, endType); +} +//------------------------------------------------------------------------------ + +void ClipperOffset::FixOrientations() +{ + //fixup orientations of all closed paths if the orientation of the + //closed path with the lowermost vertex is wrong ... + if (m_lowest.X >= 0 && + !Orientation(m_polyNodes.Childs[(int)m_lowest.X]->Contour)) + { + for (int i = 0; i < m_polyNodes.ChildCount(); ++i) + { + PolyNode& node = *m_polyNodes.Childs[i]; + if (node.m_endtype == etClosedPolygon || + (node.m_endtype == etClosedLine && Orientation(node.Contour))) + ReversePath(node.Contour); + } + } else + { + for (int i = 0; i < m_polyNodes.ChildCount(); ++i) + { + PolyNode& node = *m_polyNodes.Childs[i]; + if (node.m_endtype == etClosedLine && !Orientation(node.Contour)) + ReversePath(node.Contour); + } + } +} +//------------------------------------------------------------------------------ + +void ClipperOffset::Execute(Paths& solution, double delta) +{ + solution.clear(); + FixOrientations(); + DoOffset(delta); + + //now clean up 'corners' ... + Clipper clpr; + clpr.AddPaths(m_destPolys, ptSubject, true); + if (delta > 0) + { + clpr.Execute(ctUnion, solution, pftPositive, pftPositive); + } + else + { + IntRect r = clpr.GetBounds(); + Path outer(4); + outer[0] = IntPoint(r.left - 10, r.bottom + 10); + outer[1] = IntPoint(r.right + 10, r.bottom + 10); + outer[2] = IntPoint(r.right + 10, r.top - 10); + outer[3] = IntPoint(r.left - 10, r.top - 10); + + clpr.AddPath(outer, ptSubject, true); + clpr.ReverseSolution(true); + clpr.Execute(ctUnion, solution, pftNegative, pftNegative); + if (solution.size() > 0) solution.erase(solution.begin()); + } +} +//------------------------------------------------------------------------------ + +void ClipperOffset::Execute(PolyTree& solution, double delta) +{ + solution.Clear(); + FixOrientations(); + DoOffset(delta); + + //now clean up 'corners' ... + Clipper clpr; + clpr.AddPaths(m_destPolys, ptSubject, true); + if (delta > 0) + { + clpr.Execute(ctUnion, solution, pftPositive, pftPositive); + } + else + { + IntRect r = clpr.GetBounds(); + Path outer(4); + outer[0] = IntPoint(r.left - 10, r.bottom + 10); + outer[1] = IntPoint(r.right + 10, r.bottom + 10); + outer[2] = IntPoint(r.right + 10, r.top - 10); + outer[3] = IntPoint(r.left - 10, r.top - 10); + + clpr.AddPath(outer, ptSubject, true); + clpr.ReverseSolution(true); + clpr.Execute(ctUnion, solution, pftNegative, pftNegative); + //remove the outer PolyNode rectangle ... + if (solution.ChildCount() == 1 && solution.Childs[0]->ChildCount() > 0) + { + PolyNode* outerNode = solution.Childs[0]; + solution.Childs.reserve(outerNode->ChildCount()); + solution.Childs[0] = outerNode->Childs[0]; + solution.Childs[0]->Parent = outerNode->Parent; + for (int i = 1; i < outerNode->ChildCount(); ++i) + solution.AddChild(*outerNode->Childs[i]); + } + else + solution.Clear(); + } +} +//------------------------------------------------------------------------------ + +void ClipperOffset::DoOffset(double delta) +{ + m_destPolys.clear(); + m_delta = delta; + + //if Zero offset, just copy any CLOSED polygons to m_p and return ... + if (NEAR_ZERO(delta)) + { + m_destPolys.reserve(m_polyNodes.ChildCount()); + for (int i = 0; i < m_polyNodes.ChildCount(); i++) + { + PolyNode& node = *m_polyNodes.Childs[i]; + if (node.m_endtype == etClosedPolygon) + m_destPolys.push_back(node.Contour); + } + return; + } + + //see offset_triginometry3.svg in the documentation folder ... + if (MiterLimit > 2) m_miterLim = 2/(MiterLimit * MiterLimit); + else m_miterLim = 0.5; + + double y; + if (ArcTolerance <= 0.0) y = def_arc_tolerance; + else if (ArcTolerance > std::fabs(delta) * def_arc_tolerance) + y = std::fabs(delta) * def_arc_tolerance; + else y = ArcTolerance; + //see offset_triginometry2.svg in the documentation folder ... + double steps = pi / std::acos(1 - y / std::fabs(delta)); + if (steps > std::fabs(delta) * pi) + steps = std::fabs(delta) * pi; //ie excessive precision check + m_sin = std::sin(two_pi / steps); + m_cos = std::cos(two_pi / steps); + m_StepsPerRad = steps / two_pi; + if (delta < 0.0) m_sin = -m_sin; + + m_destPolys.reserve(m_polyNodes.ChildCount() * 2); + for (int i = 0; i < m_polyNodes.ChildCount(); i++) + { + PolyNode& node = *m_polyNodes.Childs[i]; + m_srcPoly = node.Contour; + + int len = (int)m_srcPoly.size(); + if (len == 0 || (delta <= 0 && (len < 3 || node.m_endtype != etClosedPolygon))) + continue; + + m_destPoly.clear(); + if (len == 1) + { + if (node.m_jointype == jtRound) + { + double X = 1.0, Y = 0.0; + for (cInt j = 1; j <= steps; j++) + { + m_destPoly.push_back(IntPoint( + Round(m_srcPoly[0].X + X * delta), + Round(m_srcPoly[0].Y + Y * delta))); + double X2 = X; + X = X * m_cos - m_sin * Y; + Y = X2 * m_sin + Y * m_cos; + } + } + else + { + double X = -1.0, Y = -1.0; + for (int j = 0; j < 4; ++j) + { + m_destPoly.push_back(IntPoint( + Round(m_srcPoly[0].X + X * delta), + Round(m_srcPoly[0].Y + Y * delta))); + if (X < 0) X = 1; + else if (Y < 0) Y = 1; + else X = -1; + } + } + m_destPolys.push_back(m_destPoly); + continue; + } + //build m_normals ... + m_normals.clear(); + m_normals.reserve(len); + for (int j = 0; j < len - 1; ++j) + m_normals.push_back(GetUnitNormal(m_srcPoly[j], m_srcPoly[j + 1])); + if (node.m_endtype == etClosedLine || node.m_endtype == etClosedPolygon) + m_normals.push_back(GetUnitNormal(m_srcPoly[len - 1], m_srcPoly[0])); + else + m_normals.push_back(DoublePoint(m_normals[len - 2])); + + if (node.m_endtype == etClosedPolygon) + { + int k = len - 1; + for (int j = 0; j < len; ++j) + OffsetPoint(j, k, node.m_jointype); + m_destPolys.push_back(m_destPoly); + } + else if (node.m_endtype == etClosedLine) + { + int k = len - 1; + for (int j = 0; j < len; ++j) + OffsetPoint(j, k, node.m_jointype); + m_destPolys.push_back(m_destPoly); + m_destPoly.clear(); + //re-build m_normals ... + DoublePoint n = m_normals[len -1]; + for (int j = len - 1; j > 0; j--) + m_normals[j] = DoublePoint(-m_normals[j - 1].X, -m_normals[j - 1].Y); + m_normals[0] = DoublePoint(-n.X, -n.Y); + k = 0; + for (int j = len - 1; j >= 0; j--) + OffsetPoint(j, k, node.m_jointype); + m_destPolys.push_back(m_destPoly); + } + else + { + int k = 0; + for (int j = 1; j < len - 1; ++j) + OffsetPoint(j, k, node.m_jointype); + + IntPoint pt1; + if (node.m_endtype == etOpenButt) + { + int j = len - 1; + pt1 = IntPoint((cInt)Round(m_srcPoly[j].X + m_normals[j].X * + delta), (cInt)Round(m_srcPoly[j].Y + m_normals[j].Y * delta)); + m_destPoly.push_back(pt1); + pt1 = IntPoint((cInt)Round(m_srcPoly[j].X - m_normals[j].X * + delta), (cInt)Round(m_srcPoly[j].Y - m_normals[j].Y * delta)); + m_destPoly.push_back(pt1); + } + else + { + int j = len - 1; + k = len - 2; + m_sinA = 0; + m_normals[j] = DoublePoint(-m_normals[j].X, -m_normals[j].Y); + if (node.m_endtype == etOpenSquare) + DoSquare(j, k); + else + DoRound(j, k); + } + + //re-build m_normals ... + for (int j = len - 1; j > 0; j--) + m_normals[j] = DoublePoint(-m_normals[j - 1].X, -m_normals[j - 1].Y); + m_normals[0] = DoublePoint(-m_normals[1].X, -m_normals[1].Y); + + k = len - 1; + for (int j = k - 1; j > 0; --j) OffsetPoint(j, k, node.m_jointype); + + if (node.m_endtype == etOpenButt) + { + pt1 = IntPoint((cInt)Round(m_srcPoly[0].X - m_normals[0].X * delta), + (cInt)Round(m_srcPoly[0].Y - m_normals[0].Y * delta)); + m_destPoly.push_back(pt1); + pt1 = IntPoint((cInt)Round(m_srcPoly[0].X + m_normals[0].X * delta), + (cInt)Round(m_srcPoly[0].Y + m_normals[0].Y * delta)); + m_destPoly.push_back(pt1); + } + else + { + k = 1; + m_sinA = 0; + if (node.m_endtype == etOpenSquare) + DoSquare(0, 1); + else + DoRound(0, 1); + } + m_destPolys.push_back(m_destPoly); + } + } +} +//------------------------------------------------------------------------------ + +void ClipperOffset::OffsetPoint(int j, int& k, JoinType jointype) +{ + //cross product ... + m_sinA = (m_normals[k].X * m_normals[j].Y - m_normals[j].X * m_normals[k].Y); + if (std::fabs(m_sinA * m_delta) < 1.0) + { + //dot product ... + double cosA = (m_normals[k].X * m_normals[j].X + m_normals[j].Y * m_normals[k].Y ); + if (cosA > 0) // angle => 0 degrees + { + m_destPoly.push_back(IntPoint(Round(m_srcPoly[j].X + m_normals[k].X * m_delta), + Round(m_srcPoly[j].Y + m_normals[k].Y * m_delta))); + return; + } + //else angle => 180 degrees + } + else if (m_sinA > 1.0) m_sinA = 1.0; + else if (m_sinA < -1.0) m_sinA = -1.0; + + if (m_sinA * m_delta < 0) + { + m_destPoly.push_back(IntPoint(Round(m_srcPoly[j].X + m_normals[k].X * m_delta), + Round(m_srcPoly[j].Y + m_normals[k].Y * m_delta))); + m_destPoly.push_back(m_srcPoly[j]); + m_destPoly.push_back(IntPoint(Round(m_srcPoly[j].X + m_normals[j].X * m_delta), + Round(m_srcPoly[j].Y + m_normals[j].Y * m_delta))); + } + else + switch (jointype) + { + case jtMiter: + { + double r = 1 + (m_normals[j].X * m_normals[k].X + + m_normals[j].Y * m_normals[k].Y); + if (r >= m_miterLim) DoMiter(j, k, r); else DoSquare(j, k); + break; + } + case jtSquare: DoSquare(j, k); break; + case jtRound: DoRound(j, k); break; + } + k = j; +} +//------------------------------------------------------------------------------ + +void ClipperOffset::DoSquare(int j, int k) +{ + double dx = std::tan(std::atan2(m_sinA, + m_normals[k].X * m_normals[j].X + m_normals[k].Y * m_normals[j].Y) / 4); + m_destPoly.push_back(IntPoint( + Round(m_srcPoly[j].X + m_delta * (m_normals[k].X - m_normals[k].Y * dx)), + Round(m_srcPoly[j].Y + m_delta * (m_normals[k].Y + m_normals[k].X * dx)))); + m_destPoly.push_back(IntPoint( + Round(m_srcPoly[j].X + m_delta * (m_normals[j].X + m_normals[j].Y * dx)), + Round(m_srcPoly[j].Y + m_delta * (m_normals[j].Y - m_normals[j].X * dx)))); +} +//------------------------------------------------------------------------------ + +void ClipperOffset::DoMiter(int j, int k, double r) +{ + double q = m_delta / r; + m_destPoly.push_back(IntPoint(Round(m_srcPoly[j].X + (m_normals[k].X + m_normals[j].X) * q), + Round(m_srcPoly[j].Y + (m_normals[k].Y + m_normals[j].Y) * q))); +} +//------------------------------------------------------------------------------ + +void ClipperOffset::DoRound(int j, int k) +{ + double a = std::atan2(m_sinA, + m_normals[k].X * m_normals[j].X + m_normals[k].Y * m_normals[j].Y); + int steps = std::max((int)Round(m_StepsPerRad * std::fabs(a)), 1); + + double X = m_normals[k].X, Y = m_normals[k].Y, X2; + for (int i = 0; i < steps; ++i) + { + m_destPoly.push_back(IntPoint( + Round(m_srcPoly[j].X + X * m_delta), + Round(m_srcPoly[j].Y + Y * m_delta))); + X2 = X; + X = X * m_cos - m_sin * Y; + Y = X2 * m_sin + Y * m_cos; + } + m_destPoly.push_back(IntPoint( + Round(m_srcPoly[j].X + m_normals[j].X * m_delta), + Round(m_srcPoly[j].Y + m_normals[j].Y * m_delta))); +} + +//------------------------------------------------------------------------------ +// Miscellaneous public functions +//------------------------------------------------------------------------------ + +void Clipper::DoSimplePolygons() +{ + PolyOutList::size_type i = 0; + while (i < m_PolyOuts.size()) + { + OutRec* outrec = m_PolyOuts[i++]; + OutPt* op = outrec->Pts; + if (!op || outrec->IsOpen) continue; + do //for each Pt in Polygon until duplicate found do ... + { + OutPt* op2 = op->Next; + while (op2 != outrec->Pts) + { + if ((op->Pt == op2->Pt) && op2->Next != op && op2->Prev != op) + { + //split the polygon into two ... + OutPt* op3 = op->Prev; + OutPt* op4 = op2->Prev; + op->Prev = op4; + op4->Next = op; + op2->Prev = op3; + op3->Next = op2; + + outrec->Pts = op; + OutRec* outrec2 = CreateOutRec(); + outrec2->Pts = op2; + UpdateOutPtIdxs(*outrec2); + if (Poly2ContainsPoly1(outrec2->Pts, outrec->Pts)) + { + //OutRec2 is contained by OutRec1 ... + outrec2->IsHole = !outrec->IsHole; + outrec2->FirstLeft = outrec; + if (m_UsingPolyTree) FixupFirstLefts2(outrec2, outrec); + } + else + if (Poly2ContainsPoly1(outrec->Pts, outrec2->Pts)) + { + //OutRec1 is contained by OutRec2 ... + outrec2->IsHole = outrec->IsHole; + outrec->IsHole = !outrec2->IsHole; + outrec2->FirstLeft = outrec->FirstLeft; + outrec->FirstLeft = outrec2; + if (m_UsingPolyTree) FixupFirstLefts2(outrec, outrec2); + } + else + { + //the 2 polygons are separate ... + outrec2->IsHole = outrec->IsHole; + outrec2->FirstLeft = outrec->FirstLeft; + if (m_UsingPolyTree) FixupFirstLefts1(outrec, outrec2); + } + op2 = op; //ie get ready for the Next iteration + } + op2 = op2->Next; + } + op = op->Next; + } + while (op != outrec->Pts); + } +} +//------------------------------------------------------------------------------ + +void ReversePath(Path& p) +{ + std::reverse(p.begin(), p.end()); +} +//------------------------------------------------------------------------------ + +void ReversePaths(Paths& p) +{ + for (Paths::size_type i = 0; i < p.size(); ++i) + ReversePath(p[i]); +} +//------------------------------------------------------------------------------ + +void SimplifyPolygon(const Path &in_poly, Paths &out_polys, PolyFillType fillType) +{ + Clipper c; + c.StrictlySimple(true); + c.AddPath(in_poly, ptSubject, true); + c.Execute(ctUnion, out_polys, fillType, fillType); +} +//------------------------------------------------------------------------------ + +void SimplifyPolygons(const Paths &in_polys, Paths &out_polys, PolyFillType fillType) +{ + Clipper c; + c.StrictlySimple(true); + c.AddPaths(in_polys, ptSubject, true); + c.Execute(ctUnion, out_polys, fillType, fillType); +} +//------------------------------------------------------------------------------ + +void SimplifyPolygons(Paths &polys, PolyFillType fillType) +{ + SimplifyPolygons(polys, polys, fillType); +} +//------------------------------------------------------------------------------ + +inline double DistanceSqrd(const IntPoint& pt1, const IntPoint& pt2) +{ + double Dx = ((double)pt1.X - pt2.X); + double dy = ((double)pt1.Y - pt2.Y); + return (Dx*Dx + dy*dy); +} +//------------------------------------------------------------------------------ + +double DistanceFromLineSqrd( + const IntPoint& pt, const IntPoint& ln1, const IntPoint& ln2) +{ + //The equation of a line in general form (Ax + By + C = 0) + //given 2 points (x,y) & (x,y) is ... + //(y - y)x + (x - x)y + (y - y)x - (x - x)y = 0 + //A = (y - y); B = (x - x); C = (y - y)x - (x - x)y + //perpendicular distance of point (x,y) = (Ax + By + C)/Sqrt(A + B) + //see http://en.wikipedia.org/wiki/Perpendicular_distance + double A = double(ln1.Y - ln2.Y); + double B = double(ln2.X - ln1.X); + double C = A * ln1.X + B * ln1.Y; + C = A * pt.X + B * pt.Y - C; + return (C * C) / (A * A + B * B); +} +//--------------------------------------------------------------------------- + +bool SlopesNearCollinear(const IntPoint& pt1, + const IntPoint& pt2, const IntPoint& pt3, double distSqrd) +{ + //this function is more accurate when the point that's geometrically + //between the other 2 points is the one that's tested for distance. + //ie makes it more likely to pick up 'spikes' ... + if (Abs(pt1.X - pt2.X) > Abs(pt1.Y - pt2.Y)) + { + if ((pt1.X > pt2.X) == (pt1.X < pt3.X)) + return DistanceFromLineSqrd(pt1, pt2, pt3) < distSqrd; + else if ((pt2.X > pt1.X) == (pt2.X < pt3.X)) + return DistanceFromLineSqrd(pt2, pt1, pt3) < distSqrd; + else + return DistanceFromLineSqrd(pt3, pt1, pt2) < distSqrd; + } + else + { + if ((pt1.Y > pt2.Y) == (pt1.Y < pt3.Y)) + return DistanceFromLineSqrd(pt1, pt2, pt3) < distSqrd; + else if ((pt2.Y > pt1.Y) == (pt2.Y < pt3.Y)) + return DistanceFromLineSqrd(pt2, pt1, pt3) < distSqrd; + else + return DistanceFromLineSqrd(pt3, pt1, pt2) < distSqrd; + } +} +//------------------------------------------------------------------------------ + +bool PointsAreClose(IntPoint pt1, IntPoint pt2, double distSqrd) +{ + double Dx = (double)pt1.X - pt2.X; + double dy = (double)pt1.Y - pt2.Y; + return ((Dx * Dx) + (dy * dy) <= distSqrd); +} +//------------------------------------------------------------------------------ + +OutPt* ExcludeOp(OutPt* op) +{ + OutPt* result = op->Prev; + result->Next = op->Next; + op->Next->Prev = result; + result->Idx = 0; + return result; +} +//------------------------------------------------------------------------------ + +void CleanPolygon(const Path& in_poly, Path& out_poly, double distance) +{ + //distance = proximity in units/pixels below which vertices + //will be stripped. Default ~= sqrt(2). + + size_t size = in_poly.size(); + + if (size == 0) + { + out_poly.clear(); + return; + } + + OutPt* outPts = new OutPt[size]; + for (size_t i = 0; i < size; ++i) + { + outPts[i].Pt = in_poly[i]; + outPts[i].Next = &outPts[(i + 1) % size]; + outPts[i].Next->Prev = &outPts[i]; + outPts[i].Idx = 0; + } + + double distSqrd = distance * distance; + OutPt* op = &outPts[0]; + while (op->Idx == 0 && op->Next != op->Prev) + { + if (PointsAreClose(op->Pt, op->Prev->Pt, distSqrd)) + { + op = ExcludeOp(op); + size--; + } + else if (PointsAreClose(op->Prev->Pt, op->Next->Pt, distSqrd)) + { + ExcludeOp(op->Next); + op = ExcludeOp(op); + size -= 2; + } + else if (SlopesNearCollinear(op->Prev->Pt, op->Pt, op->Next->Pt, distSqrd)) + { + op = ExcludeOp(op); + size--; + } + else + { + op->Idx = 1; + op = op->Next; + } + } + + if (size < 3) size = 0; + out_poly.resize(size); + for (size_t i = 0; i < size; ++i) + { + out_poly[i] = op->Pt; + op = op->Next; + } + delete [] outPts; +} +//------------------------------------------------------------------------------ + +void CleanPolygon(Path& poly, double distance) +{ + CleanPolygon(poly, poly, distance); +} +//------------------------------------------------------------------------------ + +void CleanPolygons(const Paths& in_polys, Paths& out_polys, double distance) +{ + out_polys.resize(in_polys.size()); + for (Paths::size_type i = 0; i < in_polys.size(); ++i) + CleanPolygon(in_polys[i], out_polys[i], distance); +} +//------------------------------------------------------------------------------ + +void CleanPolygons(Paths& polys, double distance) +{ + CleanPolygons(polys, polys, distance); +} +//------------------------------------------------------------------------------ + +void Minkowski(const Path& poly, const Path& path, + Paths& solution, bool isSum, bool isClosed) +{ + int delta = (isClosed ? 1 : 0); + size_t polyCnt = poly.size(); + size_t pathCnt = path.size(); + Paths pp; + pp.reserve(pathCnt); + if (isSum) + for (size_t i = 0; i < pathCnt; ++i) + { + Path p; + p.reserve(polyCnt); + for (size_t j = 0; j < poly.size(); ++j) + p.push_back(IntPoint(path[i].X + poly[j].X, path[i].Y + poly[j].Y)); + pp.push_back(p); + } + else + for (size_t i = 0; i < pathCnt; ++i) + { + Path p; + p.reserve(polyCnt); + for (size_t j = 0; j < poly.size(); ++j) + p.push_back(IntPoint(path[i].X - poly[j].X, path[i].Y - poly[j].Y)); + pp.push_back(p); + } + + solution.clear(); + solution.reserve((pathCnt + delta) * (polyCnt + 1)); + for (size_t i = 0; i < pathCnt - 1 + delta; ++i) + for (size_t j = 0; j < polyCnt; ++j) + { + Path quad; + quad.reserve(4); + quad.push_back(pp[i % pathCnt][j % polyCnt]); + quad.push_back(pp[(i + 1) % pathCnt][j % polyCnt]); + quad.push_back(pp[(i + 1) % pathCnt][(j + 1) % polyCnt]); + quad.push_back(pp[i % pathCnt][(j + 1) % polyCnt]); + if (!Orientation(quad)) ReversePath(quad); + solution.push_back(quad); + } +} +//------------------------------------------------------------------------------ + +void MinkowskiSum(const Path& pattern, const Path& path, Paths& solution, bool pathIsClosed) +{ + Minkowski(pattern, path, solution, true, pathIsClosed); + Clipper c; + c.AddPaths(solution, ptSubject, true); + c.Execute(ctUnion, solution, pftNonZero, pftNonZero); +} +//------------------------------------------------------------------------------ + +void TranslatePath(const Path& input, Path& output, const IntPoint delta) +{ + //precondition: input != output + output.resize(input.size()); + for (size_t i = 0; i < input.size(); ++i) + output[i] = IntPoint(input[i].X + delta.X, input[i].Y + delta.Y); +} +//------------------------------------------------------------------------------ + +void MinkowskiSum(const Path& pattern, const Paths& paths, Paths& solution, bool pathIsClosed) +{ + Clipper c; + for (size_t i = 0; i < paths.size(); ++i) + { + Paths tmp; + Minkowski(pattern, paths[i], tmp, true, pathIsClosed); + c.AddPaths(tmp, ptSubject, true); + if (pathIsClosed) + { + Path tmp2; + TranslatePath(paths[i], tmp2, pattern[0]); + c.AddPath(tmp2, ptClip, true); + } + } + c.Execute(ctUnion, solution, pftNonZero, pftNonZero); +} +//------------------------------------------------------------------------------ + +void MinkowskiDiff(const Path& poly1, const Path& poly2, Paths& solution) +{ + Minkowski(poly1, poly2, solution, false, true); + Clipper c; + c.AddPaths(solution, ptSubject, true); + c.Execute(ctUnion, solution, pftNonZero, pftNonZero); +} +//------------------------------------------------------------------------------ + +enum NodeType {ntAny, ntOpen, ntClosed}; + +void AddPolyNodeToPaths(const PolyNode& polynode, NodeType nodetype, Paths& paths) +{ + bool match = true; + if (nodetype == ntClosed) match = !polynode.IsOpen(); + else if (nodetype == ntOpen) return; + + if (!polynode.Contour.empty() && match) + paths.push_back(polynode.Contour); + for (int i = 0; i < polynode.ChildCount(); ++i) + AddPolyNodeToPaths(*polynode.Childs[i], nodetype, paths); +} +//------------------------------------------------------------------------------ + +void PolyTreeToPaths(const PolyTree& polytree, Paths& paths) +{ + paths.resize(0); + paths.reserve(polytree.Total()); + AddPolyNodeToPaths(polytree, ntAny, paths); +} +//------------------------------------------------------------------------------ + +void ClosedPathsFromPolyTree(const PolyTree& polytree, Paths& paths) +{ + paths.resize(0); + paths.reserve(polytree.Total()); + AddPolyNodeToPaths(polytree, ntClosed, paths); +} +//------------------------------------------------------------------------------ + +void OpenPathsFromPolyTree(PolyTree& polytree, Paths& paths) +{ + paths.resize(0); + paths.reserve(polytree.Total()); + //Open paths are top level only, so ... + for (int i = 0; i < polytree.ChildCount(); ++i) + if (polytree.Childs[i]->IsOpen()) + paths.push_back(polytree.Childs[i]->Contour); +} +//------------------------------------------------------------------------------ + +std::ostream& operator <<(std::ostream &s, const IntPoint &p) +{ + s << "(" << p.X << "," << p.Y << ")"; + return s; +} +//------------------------------------------------------------------------------ + +std::ostream& operator <<(std::ostream &s, const Path &p) +{ + if (p.empty()) return s; + Path::size_type last = p.size() -1; + for (Path::size_type i = 0; i < last; i++) + s << "(" << p[i].X << "," << p[i].Y << "), "; + s << "(" << p[last].X << "," << p[last].Y << ")\n"; + return s; +} +//------------------------------------------------------------------------------ + +std::ostream& operator <<(std::ostream &s, const Paths &p) +{ + for (Paths::size_type i = 0; i < p.size(); i++) + s << p[i]; + s << "\n"; + return s; +} +//------------------------------------------------------------------------------ + +} //ClipperLib namespace diff --git a/ptocr/postprocess/lanms/include/clipper/clipper.hpp b/ptocr/postprocess/lanms/include/clipper/clipper.hpp new file mode 100644 index 0000000..1c38371 --- /dev/null +++ b/ptocr/postprocess/lanms/include/clipper/clipper.hpp @@ -0,0 +1,404 @@ +/******************************************************************************* +* * +* Author : Angus Johnson * +* Version : 6.4.0 * +* Date : 2 July 2015 * +* Website : http://www.angusj.com * +* Copyright : Angus Johnson 2010-2015 * +* * +* License: * +* Use, modification & distribution is subject to Boost Software License Ver 1. * +* http://www.boost.org/LICENSE_1_0.txt * +* * +* Attributions: * +* The code in this library is an extension of Bala Vatti's clipping algorithm: * +* "A generic solution to polygon clipping" * +* Communications of the ACM, Vol 35, Issue 7 (July 1992) pp 56-63. * +* http://portal.acm.org/citation.cfm?id=129906 * +* * +* Computer graphics and geometric modeling: implementation and algorithms * +* By Max K. Agoston * +* Springer; 1 edition (January 4, 2005) * +* http://books.google.com/books?q=vatti+clipping+agoston * +* * +* See also: * +* "Polygon Offsetting by Computing Winding Numbers" * +* Paper no. DETC2005-85513 pp. 565-575 * +* ASME 2005 International Design Engineering Technical Conferences * +* and Computers and Information in Engineering Conference (IDETC/CIE2005) * +* September 24-28, 2005 , Long Beach, California, USA * +* http://www.me.berkeley.edu/~mcmains/pubs/DAC05OffsetPolygon.pdf * +* * +*******************************************************************************/ + +#ifndef clipper_hpp +#define clipper_hpp + +#define CLIPPER_VERSION "6.2.6" + +//use_int32: When enabled 32bit ints are used instead of 64bit ints. This +//improve performance but coordinate values are limited to the range +/- 46340 +//#define use_int32 + +//use_xyz: adds a Z member to IntPoint. Adds a minor cost to perfomance. +//#define use_xyz + +//use_lines: Enables line clipping. Adds a very minor cost to performance. +#define use_lines + +//use_deprecated: Enables temporary support for the obsolete functions +//#define use_deprecated + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace ClipperLib { + +enum ClipType { ctIntersection, ctUnion, ctDifference, ctXor }; +enum PolyType { ptSubject, ptClip }; +//By far the most widely used winding rules for polygon filling are +//EvenOdd & NonZero (GDI, GDI+, XLib, OpenGL, Cairo, AGG, Quartz, SVG, Gr32) +//Others rules include Positive, Negative and ABS_GTR_EQ_TWO (only in OpenGL) +//see http://glprogramming.com/red/chapter11.html +enum PolyFillType { pftEvenOdd, pftNonZero, pftPositive, pftNegative }; + +#ifdef use_int32 + typedef int cInt; + static cInt const loRange = 0x7FFF; + static cInt const hiRange = 0x7FFF; +#else + typedef signed long long cInt; + static cInt const loRange = 0x3FFFFFFF; + static cInt const hiRange = 0x3FFFFFFFFFFFFFFFLL; + typedef signed long long long64; //used by Int128 class + typedef unsigned long long ulong64; + +#endif + +struct IntPoint { + cInt X; + cInt Y; +#ifdef use_xyz + cInt Z; + IntPoint(cInt x = 0, cInt y = 0, cInt z = 0): X(x), Y(y), Z(z) {}; +#else + IntPoint(cInt x = 0, cInt y = 0): X(x), Y(y) {}; +#endif + + friend inline bool operator== (const IntPoint& a, const IntPoint& b) + { + return a.X == b.X && a.Y == b.Y; + } + friend inline bool operator!= (const IntPoint& a, const IntPoint& b) + { + return a.X != b.X || a.Y != b.Y; + } +}; +//------------------------------------------------------------------------------ + +typedef std::vector< IntPoint > Path; +typedef std::vector< Path > Paths; + +inline Path& operator <<(Path& poly, const IntPoint& p) {poly.push_back(p); return poly;} +inline Paths& operator <<(Paths& polys, const Path& p) {polys.push_back(p); return polys;} + +std::ostream& operator <<(std::ostream &s, const IntPoint &p); +std::ostream& operator <<(std::ostream &s, const Path &p); +std::ostream& operator <<(std::ostream &s, const Paths &p); + +struct DoublePoint +{ + double X; + double Y; + DoublePoint(double x = 0, double y = 0) : X(x), Y(y) {} + DoublePoint(IntPoint ip) : X((double)ip.X), Y((double)ip.Y) {} +}; +//------------------------------------------------------------------------------ + +#ifdef use_xyz +typedef void (*ZFillCallback)(IntPoint& e1bot, IntPoint& e1top, IntPoint& e2bot, IntPoint& e2top, IntPoint& pt); +#endif + +enum InitOptions {ioReverseSolution = 1, ioStrictlySimple = 2, ioPreserveCollinear = 4}; +enum JoinType {jtSquare, jtRound, jtMiter}; +enum EndType {etClosedPolygon, etClosedLine, etOpenButt, etOpenSquare, etOpenRound}; + +class PolyNode; +typedef std::vector< PolyNode* > PolyNodes; + +class PolyNode +{ +public: + PolyNode(); + virtual ~PolyNode(){}; + Path Contour; + PolyNodes Childs; + PolyNode* Parent; + PolyNode* GetNext() const; + bool IsHole() const; + bool IsOpen() const; + int ChildCount() const; +private: + unsigned Index; //node index in Parent.Childs + bool m_IsOpen; + JoinType m_jointype; + EndType m_endtype; + PolyNode* GetNextSiblingUp() const; + void AddChild(PolyNode& child); + friend class Clipper; //to access Index + friend class ClipperOffset; +}; + +class PolyTree: public PolyNode +{ +public: + ~PolyTree(){Clear();}; + PolyNode* GetFirst() const; + void Clear(); + int Total() const; +private: + PolyNodes AllNodes; + friend class Clipper; //to access AllNodes +}; + +bool Orientation(const Path &poly); +double Area(const Path &poly); +int PointInPolygon(const IntPoint &pt, const Path &path); + +void SimplifyPolygon(const Path &in_poly, Paths &out_polys, PolyFillType fillType = pftEvenOdd); +void SimplifyPolygons(const Paths &in_polys, Paths &out_polys, PolyFillType fillType = pftEvenOdd); +void SimplifyPolygons(Paths &polys, PolyFillType fillType = pftEvenOdd); + +void CleanPolygon(const Path& in_poly, Path& out_poly, double distance = 1.415); +void CleanPolygon(Path& poly, double distance = 1.415); +void CleanPolygons(const Paths& in_polys, Paths& out_polys, double distance = 1.415); +void CleanPolygons(Paths& polys, double distance = 1.415); + +void MinkowskiSum(const Path& pattern, const Path& path, Paths& solution, bool pathIsClosed); +void MinkowskiSum(const Path& pattern, const Paths& paths, Paths& solution, bool pathIsClosed); +void MinkowskiDiff(const Path& poly1, const Path& poly2, Paths& solution); + +void PolyTreeToPaths(const PolyTree& polytree, Paths& paths); +void ClosedPathsFromPolyTree(const PolyTree& polytree, Paths& paths); +void OpenPathsFromPolyTree(PolyTree& polytree, Paths& paths); + +void ReversePath(Path& p); +void ReversePaths(Paths& p); + +struct IntRect { cInt left; cInt top; cInt right; cInt bottom; }; + +//enums that are used internally ... +enum EdgeSide { esLeft = 1, esRight = 2}; + +//forward declarations (for stuff used internally) ... +struct TEdge; +struct IntersectNode; +struct LocalMinimum; +struct OutPt; +struct OutRec; +struct Join; + +typedef std::vector < OutRec* > PolyOutList; +typedef std::vector < TEdge* > EdgeList; +typedef std::vector < Join* > JoinList; +typedef std::vector < IntersectNode* > IntersectList; + +//------------------------------------------------------------------------------ + +//ClipperBase is the ancestor to the Clipper class. It should not be +//instantiated directly. This class simply abstracts the conversion of sets of +//polygon coordinates into edge objects that are stored in a LocalMinima list. +class ClipperBase +{ +public: + ClipperBase(); + virtual ~ClipperBase(); + virtual bool AddPath(const Path &pg, PolyType PolyTyp, bool Closed); + bool AddPaths(const Paths &ppg, PolyType PolyTyp, bool Closed); + virtual void Clear(); + IntRect GetBounds(); + bool PreserveCollinear() {return m_PreserveCollinear;}; + void PreserveCollinear(bool value) {m_PreserveCollinear = value;}; +protected: + void DisposeLocalMinimaList(); + TEdge* AddBoundsToLML(TEdge *e, bool IsClosed); + virtual void Reset(); + TEdge* ProcessBound(TEdge* E, bool IsClockwise); + void InsertScanbeam(const cInt Y); + bool PopScanbeam(cInt &Y); + bool LocalMinimaPending(); + bool PopLocalMinima(cInt Y, const LocalMinimum *&locMin); + OutRec* CreateOutRec(); + void DisposeAllOutRecs(); + void DisposeOutRec(PolyOutList::size_type index); + void SwapPositionsInAEL(TEdge *edge1, TEdge *edge2); + void DeleteFromAEL(TEdge *e); + void UpdateEdgeIntoAEL(TEdge *&e); + + typedef std::vector MinimaList; + MinimaList::iterator m_CurrentLM; + MinimaList m_MinimaList; + + bool m_UseFullRange; + EdgeList m_edges; + bool m_PreserveCollinear; + bool m_HasOpenPaths; + PolyOutList m_PolyOuts; + TEdge *m_ActiveEdges; + + typedef std::priority_queue ScanbeamList; + ScanbeamList m_Scanbeam; +}; +//------------------------------------------------------------------------------ + +class Clipper : public virtual ClipperBase +{ +public: + Clipper(int initOptions = 0); + bool Execute(ClipType clipType, + Paths &solution, + PolyFillType fillType = pftEvenOdd); + bool Execute(ClipType clipType, + Paths &solution, + PolyFillType subjFillType, + PolyFillType clipFillType); + bool Execute(ClipType clipType, + PolyTree &polytree, + PolyFillType fillType = pftEvenOdd); + bool Execute(ClipType clipType, + PolyTree &polytree, + PolyFillType subjFillType, + PolyFillType clipFillType); + bool ReverseSolution() { return m_ReverseOutput; }; + void ReverseSolution(bool value) {m_ReverseOutput = value;}; + bool StrictlySimple() {return m_StrictSimple;}; + void StrictlySimple(bool value) {m_StrictSimple = value;}; + //set the callback function for z value filling on intersections (otherwise Z is 0) +#ifdef use_xyz + void ZFillFunction(ZFillCallback zFillFunc); +#endif +protected: + virtual bool ExecuteInternal(); +private: + JoinList m_Joins; + JoinList m_GhostJoins; + IntersectList m_IntersectList; + ClipType m_ClipType; + typedef std::list MaximaList; + MaximaList m_Maxima; + TEdge *m_SortedEdges; + bool m_ExecuteLocked; + PolyFillType m_ClipFillType; + PolyFillType m_SubjFillType; + bool m_ReverseOutput; + bool m_UsingPolyTree; + bool m_StrictSimple; +#ifdef use_xyz + ZFillCallback m_ZFill; //custom callback +#endif + void SetWindingCount(TEdge& edge); + bool IsEvenOddFillType(const TEdge& edge) const; + bool IsEvenOddAltFillType(const TEdge& edge) const; + void InsertLocalMinimaIntoAEL(const cInt botY); + void InsertEdgeIntoAEL(TEdge *edge, TEdge* startEdge); + void AddEdgeToSEL(TEdge *edge); + bool PopEdgeFromSEL(TEdge *&edge); + void CopyAELToSEL(); + void DeleteFromSEL(TEdge *e); + void SwapPositionsInSEL(TEdge *edge1, TEdge *edge2); + bool IsContributing(const TEdge& edge) const; + bool IsTopHorz(const cInt XPos); + void DoMaxima(TEdge *e); + void ProcessHorizontals(); + void ProcessHorizontal(TEdge *horzEdge); + void AddLocalMaxPoly(TEdge *e1, TEdge *e2, const IntPoint &pt); + OutPt* AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &pt); + OutRec* GetOutRec(int idx); + void AppendPolygon(TEdge *e1, TEdge *e2); + void IntersectEdges(TEdge *e1, TEdge *e2, IntPoint &pt); + OutPt* AddOutPt(TEdge *e, const IntPoint &pt); + OutPt* GetLastOutPt(TEdge *e); + bool ProcessIntersections(const cInt topY); + void BuildIntersectList(const cInt topY); + void ProcessIntersectList(); + void ProcessEdgesAtTopOfScanbeam(const cInt topY); + void BuildResult(Paths& polys); + void BuildResult2(PolyTree& polytree); + void SetHoleState(TEdge *e, OutRec *outrec); + void DisposeIntersectNodes(); + bool FixupIntersectionOrder(); + void FixupOutPolygon(OutRec &outrec); + void FixupOutPolyline(OutRec &outrec); + bool IsHole(TEdge *e); + bool FindOwnerFromSplitRecs(OutRec &outRec, OutRec *&currOrfl); + void FixHoleLinkage(OutRec &outrec); + void AddJoin(OutPt *op1, OutPt *op2, const IntPoint offPt); + void ClearJoins(); + void ClearGhostJoins(); + void AddGhostJoin(OutPt *op, const IntPoint offPt); + bool JoinPoints(Join *j, OutRec* outRec1, OutRec* outRec2); + void JoinCommonEdges(); + void DoSimplePolygons(); + void FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec); + void FixupFirstLefts2(OutRec* InnerOutRec, OutRec* OuterOutRec); + void FixupFirstLefts3(OutRec* OldOutRec, OutRec* NewOutRec); +#ifdef use_xyz + void SetZ(IntPoint& pt, TEdge& e1, TEdge& e2); +#endif +}; +//------------------------------------------------------------------------------ + +class ClipperOffset +{ +public: + ClipperOffset(double miterLimit = 2.0, double roundPrecision = 0.25); + ~ClipperOffset(); + void AddPath(const Path& path, JoinType joinType, EndType endType); + void AddPaths(const Paths& paths, JoinType joinType, EndType endType); + void Execute(Paths& solution, double delta); + void Execute(PolyTree& solution, double delta); + void Clear(); + double MiterLimit; + double ArcTolerance; +private: + Paths m_destPolys; + Path m_srcPoly; + Path m_destPoly; + std::vector m_normals; + double m_delta, m_sinA, m_sin, m_cos; + double m_miterLim, m_StepsPerRad; + IntPoint m_lowest; + PolyNode m_polyNodes; + + void FixOrientations(); + void DoOffset(double delta); + void OffsetPoint(int j, int& k, JoinType jointype); + void DoSquare(int j, int k); + void DoMiter(int j, int k, double r); + void DoRound(int j, int k); +}; +//------------------------------------------------------------------------------ + +class clipperException : public std::exception +{ + public: + clipperException(const char* description): m_descr(description) {} + virtual ~clipperException() throw() {} + virtual const char* what() const throw() {return m_descr.c_str();} + private: + std::string m_descr; +}; +//------------------------------------------------------------------------------ + +} //ClipperLib namespace + +#endif //clipper_hpp + + diff --git a/ptocr/postprocess/lanms/include/pybind11/attr.h b/ptocr/postprocess/lanms/include/pybind11/attr.h new file mode 100644 index 0000000..b4137cb --- /dev/null +++ b/ptocr/postprocess/lanms/include/pybind11/attr.h @@ -0,0 +1,471 @@ +/* + pybind11/attr.h: Infrastructure for processing custom + type and function attributes + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "cast.h" + +NAMESPACE_BEGIN(pybind11) + +/// \addtogroup annotations +/// @{ + +/// Annotation for methods +struct is_method { handle class_; is_method(const handle &c) : class_(c) { } }; + +/// Annotation for operators +struct is_operator { }; + +/// Annotation for parent scope +struct scope { handle value; scope(const handle &s) : value(s) { } }; + +/// Annotation for documentation +struct doc { const char *value; doc(const char *value) : value(value) { } }; + +/// Annotation for function names +struct name { const char *value; name(const char *value) : value(value) { } }; + +/// Annotation indicating that a function is an overload associated with a given "sibling" +struct sibling { handle value; sibling(const handle &value) : value(value.ptr()) { } }; + +/// Annotation indicating that a class derives from another given type +template struct base { + PYBIND11_DEPRECATED("base() was deprecated in favor of specifying 'T' as a template argument to class_") + base() { } +}; + +/// Keep patient alive while nurse lives +template struct keep_alive { }; + +/// Annotation indicating that a class is involved in a multiple inheritance relationship +struct multiple_inheritance { }; + +/// Annotation which enables dynamic attributes, i.e. adds `__dict__` to a class +struct dynamic_attr { }; + +/// Annotation which enables the buffer protocol for a type +struct buffer_protocol { }; + +/// Annotation which requests that a special metaclass is created for a type +struct metaclass { + handle value; + + PYBIND11_DEPRECATED("py::metaclass() is no longer required. It's turned on by default now.") + metaclass() {} + + /// Override pybind11's default metaclass + explicit metaclass(handle value) : value(value) { } +}; + +/// Annotation to mark enums as an arithmetic type +struct arithmetic { }; + +/** \rst + A call policy which places one or more guard variables (``Ts...``) around the function call. + + For example, this definition: + + .. code-block:: cpp + + m.def("foo", foo, py::call_guard()); + + is equivalent to the following pseudocode: + + .. code-block:: cpp + + m.def("foo", [](args...) { + T scope_guard; + return foo(args...); // forwarded arguments + }); + \endrst */ +template struct call_guard; + +template <> struct call_guard<> { using type = detail::void_type; }; + +template +struct call_guard { + static_assert(std::is_default_constructible::value, + "The guard type must be default constructible"); + + using type = T; +}; + +template +struct call_guard { + struct type { + T guard{}; // Compose multiple guard types with left-to-right default-constructor order + typename call_guard::type next{}; + }; +}; + +/// @} annotations + +NAMESPACE_BEGIN(detail) +/* Forward declarations */ +enum op_id : int; +enum op_type : int; +struct undefined_t; +template struct op_; +template struct init; +template struct init_alias; +inline void keep_alive_impl(size_t Nurse, size_t Patient, function_call &call, handle ret); + +/// Internal data structure which holds metadata about a keyword argument +struct argument_record { + const char *name; ///< Argument name + const char *descr; ///< Human-readable version of the argument value + handle value; ///< Associated Python object + bool convert : 1; ///< True if the argument is allowed to convert when loading + bool none : 1; ///< True if None is allowed when loading + + argument_record(const char *name, const char *descr, handle value, bool convert, bool none) + : name(name), descr(descr), value(value), convert(convert), none(none) { } +}; + +/// Internal data structure which holds metadata about a bound function (signature, overloads, etc.) +struct function_record { + function_record() + : is_constructor(false), is_stateless(false), is_operator(false), + has_args(false), has_kwargs(false), is_method(false) { } + + /// Function name + char *name = nullptr; /* why no C++ strings? They generate heavier code.. */ + + // User-specified documentation string + char *doc = nullptr; + + /// Human-readable version of the function signature + char *signature = nullptr; + + /// List of registered keyword arguments + std::vector args; + + /// Pointer to lambda function which converts arguments and performs the actual call + handle (*impl) (function_call &) = nullptr; + + /// Storage for the wrapped function pointer and captured data, if any + void *data[3] = { }; + + /// Pointer to custom destructor for 'data' (if needed) + void (*free_data) (function_record *ptr) = nullptr; + + /// Return value policy associated with this function + return_value_policy policy = return_value_policy::automatic; + + /// True if name == '__init__' + bool is_constructor : 1; + + /// True if this is a stateless function pointer + bool is_stateless : 1; + + /// True if this is an operator (__add__), etc. + bool is_operator : 1; + + /// True if the function has a '*args' argument + bool has_args : 1; + + /// True if the function has a '**kwargs' argument + bool has_kwargs : 1; + + /// True if this is a method + bool is_method : 1; + + /// Number of arguments (including py::args and/or py::kwargs, if present) + std::uint16_t nargs; + + /// Python method object + PyMethodDef *def = nullptr; + + /// Python handle to the parent scope (a class or a module) + handle scope; + + /// Python handle to the sibling function representing an overload chain + handle sibling; + + /// Pointer to next overload + function_record *next = nullptr; +}; + +/// Special data structure which (temporarily) holds metadata about a bound class +struct type_record { + PYBIND11_NOINLINE type_record() + : multiple_inheritance(false), dynamic_attr(false), buffer_protocol(false) { } + + /// Handle to the parent scope + handle scope; + + /// Name of the class + const char *name = nullptr; + + // Pointer to RTTI type_info data structure + const std::type_info *type = nullptr; + + /// How large is the underlying C++ type? + size_t type_size = 0; + + /// How large is the type's holder? + size_t holder_size = 0; + + /// The global operator new can be overridden with a class-specific variant + void *(*operator_new)(size_t) = ::operator new; + + /// Function pointer to class_<..>::init_instance + void (*init_instance)(instance *, const void *) = nullptr; + + /// Function pointer to class_<..>::dealloc + void (*dealloc)(const detail::value_and_holder &) = nullptr; + + /// List of base classes of the newly created type + list bases; + + /// Optional docstring + const char *doc = nullptr; + + /// Custom metaclass (optional) + handle metaclass; + + /// Multiple inheritance marker + bool multiple_inheritance : 1; + + /// Does the class manage a __dict__? + bool dynamic_attr : 1; + + /// Does the class implement the buffer protocol? + bool buffer_protocol : 1; + + /// Is the default (unique_ptr) holder type used? + bool default_holder : 1; + + PYBIND11_NOINLINE void add_base(const std::type_info &base, void *(*caster)(void *)) { + auto base_info = detail::get_type_info(base, false); + if (!base_info) { + std::string tname(base.name()); + detail::clean_type_id(tname); + pybind11_fail("generic_type: type \"" + std::string(name) + + "\" referenced unknown base type \"" + tname + "\""); + } + + if (default_holder != base_info->default_holder) { + std::string tname(base.name()); + detail::clean_type_id(tname); + pybind11_fail("generic_type: type \"" + std::string(name) + "\" " + + (default_holder ? "does not have" : "has") + + " a non-default holder type while its base \"" + tname + "\" " + + (base_info->default_holder ? "does not" : "does")); + } + + bases.append((PyObject *) base_info->type); + + if (base_info->type->tp_dictoffset != 0) + dynamic_attr = true; + + if (caster) + base_info->implicit_casts.emplace_back(type, caster); + } +}; + +inline function_call::function_call(function_record &f, handle p) : + func(f), parent(p) { + args.reserve(f.nargs); + args_convert.reserve(f.nargs); +} + +/** + * Partial template specializations to process custom attributes provided to + * cpp_function_ and class_. These are either used to initialize the respective + * fields in the type_record and function_record data structures or executed at + * runtime to deal with custom call policies (e.g. keep_alive). + */ +template struct process_attribute; + +template struct process_attribute_default { + /// Default implementation: do nothing + static void init(const T &, function_record *) { } + static void init(const T &, type_record *) { } + static void precall(function_call &) { } + static void postcall(function_call &, handle) { } +}; + +/// Process an attribute specifying the function's name +template <> struct process_attribute : process_attribute_default { + static void init(const name &n, function_record *r) { r->name = const_cast(n.value); } +}; + +/// Process an attribute specifying the function's docstring +template <> struct process_attribute : process_attribute_default { + static void init(const doc &n, function_record *r) { r->doc = const_cast(n.value); } +}; + +/// Process an attribute specifying the function's docstring (provided as a C-style string) +template <> struct process_attribute : process_attribute_default { + static void init(const char *d, function_record *r) { r->doc = const_cast(d); } + static void init(const char *d, type_record *r) { r->doc = const_cast(d); } +}; +template <> struct process_attribute : process_attribute { }; + +/// Process an attribute indicating the function's return value policy +template <> struct process_attribute : process_attribute_default { + static void init(const return_value_policy &p, function_record *r) { r->policy = p; } +}; + +/// Process an attribute which indicates that this is an overloaded function associated with a given sibling +template <> struct process_attribute : process_attribute_default { + static void init(const sibling &s, function_record *r) { r->sibling = s.value; } +}; + +/// Process an attribute which indicates that this function is a method +template <> struct process_attribute : process_attribute_default { + static void init(const is_method &s, function_record *r) { r->is_method = true; r->scope = s.class_; } +}; + +/// Process an attribute which indicates the parent scope of a method +template <> struct process_attribute : process_attribute_default { + static void init(const scope &s, function_record *r) { r->scope = s.value; } +}; + +/// Process an attribute which indicates that this function is an operator +template <> struct process_attribute : process_attribute_default { + static void init(const is_operator &, function_record *r) { r->is_operator = true; } +}; + +/// Process a keyword argument attribute (*without* a default value) +template <> struct process_attribute : process_attribute_default { + static void init(const arg &a, function_record *r) { + if (r->is_method && r->args.empty()) + r->args.emplace_back("self", nullptr, handle(), true /*convert*/, false /*none not allowed*/); + r->args.emplace_back(a.name, nullptr, handle(), !a.flag_noconvert, a.flag_none); + } +}; + +/// Process a keyword argument attribute (*with* a default value) +template <> struct process_attribute : process_attribute_default { + static void init(const arg_v &a, function_record *r) { + if (r->is_method && r->args.empty()) + r->args.emplace_back("self", nullptr /*descr*/, handle() /*parent*/, true /*convert*/, false /*none not allowed*/); + + if (!a.value) { +#if !defined(NDEBUG) + std::string descr("'"); + if (a.name) descr += std::string(a.name) + ": "; + descr += a.type + "'"; + if (r->is_method) { + if (r->name) + descr += " in method '" + (std::string) str(r->scope) + "." + (std::string) r->name + "'"; + else + descr += " in method of '" + (std::string) str(r->scope) + "'"; + } else if (r->name) { + descr += " in function '" + (std::string) r->name + "'"; + } + pybind11_fail("arg(): could not convert default argument " + + descr + " into a Python object (type not registered yet?)"); +#else + pybind11_fail("arg(): could not convert default argument " + "into a Python object (type not registered yet?). " + "Compile in debug mode for more information."); +#endif + } + r->args.emplace_back(a.name, a.descr, a.value.inc_ref(), !a.flag_noconvert, a.flag_none); + } +}; + +/// Process a parent class attribute. Single inheritance only (class_ itself already guarantees that) +template +struct process_attribute::value>> : process_attribute_default { + static void init(const handle &h, type_record *r) { r->bases.append(h); } +}; + +/// Process a parent class attribute (deprecated, does not support multiple inheritance) +template +struct process_attribute> : process_attribute_default> { + static void init(const base &, type_record *r) { r->add_base(typeid(T), nullptr); } +}; + +/// Process a multiple inheritance attribute +template <> +struct process_attribute : process_attribute_default { + static void init(const multiple_inheritance &, type_record *r) { r->multiple_inheritance = true; } +}; + +template <> +struct process_attribute : process_attribute_default { + static void init(const dynamic_attr &, type_record *r) { r->dynamic_attr = true; } +}; + +template <> +struct process_attribute : process_attribute_default { + static void init(const buffer_protocol &, type_record *r) { r->buffer_protocol = true; } +}; + +template <> +struct process_attribute : process_attribute_default { + static void init(const metaclass &m, type_record *r) { r->metaclass = m.value; } +}; + + +/// Process an 'arithmetic' attribute for enums (does nothing here) +template <> +struct process_attribute : process_attribute_default {}; + +template +struct process_attribute> : process_attribute_default> { }; + +/** + * Process a keep_alive call policy -- invokes keep_alive_impl during the + * pre-call handler if both Nurse, Patient != 0 and use the post-call handler + * otherwise + */ +template struct process_attribute> : public process_attribute_default> { + template = 0> + static void precall(function_call &call) { keep_alive_impl(Nurse, Patient, call, handle()); } + template = 0> + static void postcall(function_call &, handle) { } + template = 0> + static void precall(function_call &) { } + template = 0> + static void postcall(function_call &call, handle ret) { keep_alive_impl(Nurse, Patient, call, ret); } +}; + +/// Recursively iterate over variadic template arguments +template struct process_attributes { + static void init(const Args&... args, function_record *r) { + int unused[] = { 0, (process_attribute::type>::init(args, r), 0) ... }; + ignore_unused(unused); + } + static void init(const Args&... args, type_record *r) { + int unused[] = { 0, (process_attribute::type>::init(args, r), 0) ... }; + ignore_unused(unused); + } + static void precall(function_call &call) { + int unused[] = { 0, (process_attribute::type>::precall(call), 0) ... }; + ignore_unused(unused); + } + static void postcall(function_call &call, handle fn_ret) { + int unused[] = { 0, (process_attribute::type>::postcall(call, fn_ret), 0) ... }; + ignore_unused(unused); + } +}; + +template +using is_call_guard = is_instantiation; + +/// Extract the ``type`` from the first `call_guard` in `Extras...` (or `void_type` if none found) +template +using extract_guard_t = typename exactly_one_t, Extra...>::type; + +/// Check the number of named arguments at compile time +template ::value...), + size_t self = constexpr_sum(std::is_same::value...)> +constexpr bool expected_num_args(size_t nargs, bool has_args, bool has_kwargs) { + return named == 0 || (self + named + has_args + has_kwargs) == nargs; +} + +NAMESPACE_END(detail) +NAMESPACE_END(pybind11) diff --git a/ptocr/postprocess/lanms/include/pybind11/buffer_info.h b/ptocr/postprocess/lanms/include/pybind11/buffer_info.h new file mode 100644 index 0000000..6d1167d --- /dev/null +++ b/ptocr/postprocess/lanms/include/pybind11/buffer_info.h @@ -0,0 +1,108 @@ +/* + pybind11/buffer_info.h: Python buffer object interface + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "common.h" + +NAMESPACE_BEGIN(pybind11) + +/// Information record describing a Python buffer object +struct buffer_info { + void *ptr = nullptr; // Pointer to the underlying storage + ssize_t itemsize = 0; // Size of individual items in bytes + ssize_t size = 0; // Total number of entries + std::string format; // For homogeneous buffers, this should be set to format_descriptor::format() + ssize_t ndim = 0; // Number of dimensions + std::vector shape; // Shape of the tensor (1 entry per dimension) + std::vector strides; // Number of entries between adjacent entries (for each per dimension) + + buffer_info() { } + + buffer_info(void *ptr, ssize_t itemsize, const std::string &format, ssize_t ndim, + detail::any_container shape_in, detail::any_container strides_in) + : ptr(ptr), itemsize(itemsize), size(1), format(format), ndim(ndim), + shape(std::move(shape_in)), strides(std::move(strides_in)) { + if (ndim != (ssize_t) shape.size() || ndim != (ssize_t) strides.size()) + pybind11_fail("buffer_info: ndim doesn't match shape and/or strides length"); + for (size_t i = 0; i < (size_t) ndim; ++i) + size *= shape[i]; + } + + template + buffer_info(T *ptr, detail::any_container shape_in, detail::any_container strides_in) + : buffer_info(private_ctr_tag(), ptr, sizeof(T), format_descriptor::format(), static_cast(shape_in->size()), std::move(shape_in), std::move(strides_in)) { } + + buffer_info(void *ptr, ssize_t itemsize, const std::string &format, ssize_t size) + : buffer_info(ptr, itemsize, format, 1, {size}, {itemsize}) { } + + template + buffer_info(T *ptr, ssize_t size) + : buffer_info(ptr, sizeof(T), format_descriptor::format(), size) { } + + explicit buffer_info(Py_buffer *view, bool ownview = true) + : buffer_info(view->buf, view->itemsize, view->format, view->ndim, + {view->shape, view->shape + view->ndim}, {view->strides, view->strides + view->ndim}) { + this->view = view; + this->ownview = ownview; + } + + buffer_info(const buffer_info &) = delete; + buffer_info& operator=(const buffer_info &) = delete; + + buffer_info(buffer_info &&other) { + (*this) = std::move(other); + } + + buffer_info& operator=(buffer_info &&rhs) { + ptr = rhs.ptr; + itemsize = rhs.itemsize; + size = rhs.size; + format = std::move(rhs.format); + ndim = rhs.ndim; + shape = std::move(rhs.shape); + strides = std::move(rhs.strides); + std::swap(view, rhs.view); + std::swap(ownview, rhs.ownview); + return *this; + } + + ~buffer_info() { + if (view && ownview) { PyBuffer_Release(view); delete view; } + } + +private: + struct private_ctr_tag { }; + + buffer_info(private_ctr_tag, void *ptr, ssize_t itemsize, const std::string &format, ssize_t ndim, + detail::any_container &&shape_in, detail::any_container &&strides_in) + : buffer_info(ptr, itemsize, format, ndim, std::move(shape_in), std::move(strides_in)) { } + + Py_buffer *view = nullptr; + bool ownview = false; +}; + +NAMESPACE_BEGIN(detail) + +template struct compare_buffer_info { + static bool compare(const buffer_info& b) { + return b.format == format_descriptor::format() && b.itemsize == (ssize_t) sizeof(T); + } +}; + +template struct compare_buffer_info::value>> { + static bool compare(const buffer_info& b) { + return (size_t) b.itemsize == sizeof(T) && (b.format == format_descriptor::value || + ((sizeof(T) == sizeof(long)) && b.format == (std::is_unsigned::value ? "L" : "l")) || + ((sizeof(T) == sizeof(size_t)) && b.format == (std::is_unsigned::value ? "N" : "n"))); + } +}; + +NAMESPACE_END(detail) +NAMESPACE_END(pybind11) diff --git a/ptocr/postprocess/lanms/include/pybind11/cast.h b/ptocr/postprocess/lanms/include/pybind11/cast.h new file mode 100644 index 0000000..5db03e2 --- /dev/null +++ b/ptocr/postprocess/lanms/include/pybind11/cast.h @@ -0,0 +1,2058 @@ +/* + pybind11/cast.h: Partial template specializations to cast between + C++ and Python types + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "pytypes.h" +#include "typeid.h" +#include "descr.h" +#include +#include +#include + +#if defined(PYBIND11_CPP17) +# if defined(__has_include) +# if __has_include() +# define PYBIND11_HAS_STRING_VIEW +# endif +# elif defined(_MSC_VER) +# define PYBIND11_HAS_STRING_VIEW +# endif +#endif +#ifdef PYBIND11_HAS_STRING_VIEW +#include +#endif + +NAMESPACE_BEGIN(pybind11) +NAMESPACE_BEGIN(detail) +// Forward declarations: +inline PyTypeObject *make_static_property_type(); +inline PyTypeObject *make_default_metaclass(); +inline PyObject *make_object_base_type(PyTypeObject *metaclass); +struct value_and_holder; + +/// Additional type information which does not fit into the PyTypeObject +struct type_info { + PyTypeObject *type; + const std::type_info *cpptype; + size_t type_size, holder_size_in_ptrs; + void *(*operator_new)(size_t); + void (*init_instance)(instance *, const void *); + void (*dealloc)(const value_and_holder &v_h); + std::vector implicit_conversions; + std::vector> implicit_casts; + std::vector *direct_conversions; + buffer_info *(*get_buffer)(PyObject *, void *) = nullptr; + void *get_buffer_data = nullptr; + /* A simple type never occurs as a (direct or indirect) parent + * of a class that makes use of multiple inheritance */ + bool simple_type : 1; + /* True if there is no multiple inheritance in this type's inheritance tree */ + bool simple_ancestors : 1; + /* for base vs derived holder_type checks */ + bool default_holder : 1; +}; + +// Store the static internals pointer in a version-specific function so that we're guaranteed it +// will be distinct for modules compiled for different pybind11 versions. Without this, some +// compilers (i.e. gcc) can use the same static pointer storage location across different .so's, +// even though the `get_internals()` function itself is local to each shared object. +template +internals *&get_internals_ptr() { static internals *internals_ptr = nullptr; return internals_ptr; } + +PYBIND11_NOINLINE inline internals &get_internals() { + internals *&internals_ptr = get_internals_ptr(); + if (internals_ptr) + return *internals_ptr; + handle builtins(PyEval_GetBuiltins()); + const char *id = PYBIND11_INTERNALS_ID; + if (builtins.contains(id) && isinstance(builtins[id])) { + internals_ptr = *static_cast(capsule(builtins[id])); + } else { + internals_ptr = new internals(); + #if defined(WITH_THREAD) + PyEval_InitThreads(); + PyThreadState *tstate = PyThreadState_Get(); + internals_ptr->tstate = PyThread_create_key(); + PyThread_set_key_value(internals_ptr->tstate, tstate); + internals_ptr->istate = tstate->interp; + #endif + builtins[id] = capsule(&internals_ptr); + internals_ptr->registered_exception_translators.push_front( + [](std::exception_ptr p) -> void { + try { + if (p) std::rethrow_exception(p); + } catch (error_already_set &e) { e.restore(); return; + } catch (const builtin_exception &e) { e.set_error(); return; + } catch (const std::bad_alloc &e) { PyErr_SetString(PyExc_MemoryError, e.what()); return; + } catch (const std::domain_error &e) { PyErr_SetString(PyExc_ValueError, e.what()); return; + } catch (const std::invalid_argument &e) { PyErr_SetString(PyExc_ValueError, e.what()); return; + } catch (const std::length_error &e) { PyErr_SetString(PyExc_ValueError, e.what()); return; + } catch (const std::out_of_range &e) { PyErr_SetString(PyExc_IndexError, e.what()); return; + } catch (const std::range_error &e) { PyErr_SetString(PyExc_ValueError, e.what()); return; + } catch (const std::exception &e) { PyErr_SetString(PyExc_RuntimeError, e.what()); return; + } catch (...) { + PyErr_SetString(PyExc_RuntimeError, "Caught an unknown exception!"); + return; + } + } + ); + internals_ptr->static_property_type = make_static_property_type(); + internals_ptr->default_metaclass = make_default_metaclass(); + internals_ptr->instance_base = make_object_base_type(internals_ptr->default_metaclass); + } + return *internals_ptr; +} + +/// A life support system for temporary objects created by `type_caster::load()`. +/// Adding a patient will keep it alive up until the enclosing function returns. +class loader_life_support { +public: + /// A new patient frame is created when a function is entered + loader_life_support() { + get_internals().loader_patient_stack.push_back(nullptr); + } + + /// ... and destroyed after it returns + ~loader_life_support() { + auto &stack = get_internals().loader_patient_stack; + if (stack.empty()) + pybind11_fail("loader_life_support: internal error"); + + auto ptr = stack.back(); + stack.pop_back(); + Py_CLEAR(ptr); + + // A heuristic to reduce the stack's capacity (e.g. after long recursive calls) + if (stack.capacity() > 16 && stack.size() != 0 && stack.capacity() / stack.size() > 2) + stack.shrink_to_fit(); + } + + /// This can only be used inside a pybind11-bound function, either by `argument_loader` + /// at argument preparation time or by `py::cast()` at execution time. + PYBIND11_NOINLINE static void add_patient(handle h) { + auto &stack = get_internals().loader_patient_stack; + if (stack.empty()) + throw cast_error("When called outside a bound function, py::cast() cannot " + "do Python -> C++ conversions which require the creation " + "of temporary values"); + + auto &list_ptr = stack.back(); + if (list_ptr == nullptr) { + list_ptr = PyList_New(1); + if (!list_ptr) + pybind11_fail("loader_life_support: error allocating list"); + PyList_SET_ITEM(list_ptr, 0, h.inc_ref().ptr()); + } else { + auto result = PyList_Append(list_ptr, h.ptr()); + if (result == -1) + pybind11_fail("loader_life_support: error adding patient"); + } + } +}; + +// Gets the cache entry for the given type, creating it if necessary. The return value is the pair +// returned by emplace, i.e. an iterator for the entry and a bool set to `true` if the entry was +// just created. +inline std::pair all_type_info_get_cache(PyTypeObject *type); + +// Populates a just-created cache entry. +PYBIND11_NOINLINE inline void all_type_info_populate(PyTypeObject *t, std::vector &bases) { + std::vector check; + for (handle parent : reinterpret_borrow(t->tp_bases)) + check.push_back((PyTypeObject *) parent.ptr()); + + auto const &type_dict = get_internals().registered_types_py; + for (size_t i = 0; i < check.size(); i++) { + auto type = check[i]; + // Ignore Python2 old-style class super type: + if (!PyType_Check((PyObject *) type)) continue; + + // Check `type` in the current set of registered python types: + auto it = type_dict.find(type); + if (it != type_dict.end()) { + // We found a cache entry for it, so it's either pybind-registered or has pre-computed + // pybind bases, but we have to make sure we haven't already seen the type(s) before: we + // want to follow Python/virtual C++ rules that there should only be one instance of a + // common base. + for (auto *tinfo : it->second) { + // NB: Could use a second set here, rather than doing a linear search, but since + // having a large number of immediate pybind11-registered types seems fairly + // unlikely, that probably isn't worthwhile. + bool found = false; + for (auto *known : bases) { + if (known == tinfo) { found = true; break; } + } + if (!found) bases.push_back(tinfo); + } + } + else if (type->tp_bases) { + // It's some python type, so keep follow its bases classes to look for one or more + // registered types + if (i + 1 == check.size()) { + // When we're at the end, we can pop off the current element to avoid growing + // `check` when adding just one base (which is typical--.e. when there is no + // multiple inheritance) + check.pop_back(); + i--; + } + for (handle parent : reinterpret_borrow(type->tp_bases)) + check.push_back((PyTypeObject *) parent.ptr()); + } + } +} + +/** + * Extracts vector of type_info pointers of pybind-registered roots of the given Python type. Will + * be just 1 pybind type for the Python type of a pybind-registered class, or for any Python-side + * derived class that uses single inheritance. Will contain as many types as required for a Python + * class that uses multiple inheritance to inherit (directly or indirectly) from multiple + * pybind-registered classes. Will be empty if neither the type nor any base classes are + * pybind-registered. + * + * The value is cached for the lifetime of the Python type. + */ +inline const std::vector &all_type_info(PyTypeObject *type) { + auto ins = all_type_info_get_cache(type); + if (ins.second) + // New cache entry: populate it + all_type_info_populate(type, ins.first->second); + + return ins.first->second; +} + +/** + * Gets a single pybind11 type info for a python type. Returns nullptr if neither the type nor any + * ancestors are pybind11-registered. Throws an exception if there are multiple bases--use + * `all_type_info` instead if you want to support multiple bases. + */ +PYBIND11_NOINLINE inline detail::type_info* get_type_info(PyTypeObject *type) { + auto &bases = all_type_info(type); + if (bases.size() == 0) + return nullptr; + if (bases.size() > 1) + pybind11_fail("pybind11::detail::get_type_info: type has multiple pybind11-registered bases"); + return bases.front(); +} + +PYBIND11_NOINLINE inline detail::type_info *get_type_info(const std::type_info &tp, + bool throw_if_missing = false) { + auto &types = get_internals().registered_types_cpp; + + auto it = types.find(std::type_index(tp)); + if (it != types.end()) + return (detail::type_info *) it->second; + if (throw_if_missing) { + std::string tname = tp.name(); + detail::clean_type_id(tname); + pybind11_fail("pybind11::detail::get_type_info: unable to find type info for \"" + tname + "\""); + } + return nullptr; +} + +PYBIND11_NOINLINE inline handle get_type_handle(const std::type_info &tp, bool throw_if_missing) { + detail::type_info *type_info = get_type_info(tp, throw_if_missing); + return handle(type_info ? ((PyObject *) type_info->type) : nullptr); +} + +struct value_and_holder { + instance *inst; + size_t index; + const detail::type_info *type; + void **vh; + + value_and_holder(instance *i, const detail::type_info *type, size_t vpos, size_t index) : + inst{i}, index{index}, type{type}, + vh{inst->simple_layout ? inst->simple_value_holder : &inst->nonsimple.values_and_holders[vpos]} + {} + + // Used for past-the-end iterator + value_and_holder(size_t index) : index{index} {} + + template V *&value_ptr() const { + return reinterpret_cast(vh[0]); + } + // True if this `value_and_holder` has a non-null value pointer + explicit operator bool() const { return value_ptr(); } + + template H &holder() const { + return reinterpret_cast(vh[1]); + } + bool holder_constructed() const { + return inst->simple_layout + ? inst->simple_holder_constructed + : inst->nonsimple.status[index] & instance::status_holder_constructed; + } + void set_holder_constructed() { + if (inst->simple_layout) + inst->simple_holder_constructed = true; + else + inst->nonsimple.status[index] |= instance::status_holder_constructed; + } + bool instance_registered() const { + return inst->simple_layout + ? inst->simple_instance_registered + : inst->nonsimple.status[index] & instance::status_instance_registered; + } + void set_instance_registered() { + if (inst->simple_layout) + inst->simple_instance_registered = true; + else + inst->nonsimple.status[index] |= instance::status_instance_registered; + } +}; + +// Container for accessing and iterating over an instance's values/holders +struct values_and_holders { +private: + instance *inst; + using type_vec = std::vector; + const type_vec &tinfo; + +public: + values_and_holders(instance *inst) : inst{inst}, tinfo(all_type_info(Py_TYPE(inst))) {} + + struct iterator { + private: + instance *inst; + const type_vec *types; + value_and_holder curr; + friend struct values_and_holders; + iterator(instance *inst, const type_vec *tinfo) + : inst{inst}, types{tinfo}, + curr(inst /* instance */, + types->empty() ? nullptr : (*types)[0] /* type info */, + 0, /* vpos: (non-simple types only): the first vptr comes first */ + 0 /* index */) + {} + // Past-the-end iterator: + iterator(size_t end) : curr(end) {} + public: + bool operator==(const iterator &other) { return curr.index == other.curr.index; } + bool operator!=(const iterator &other) { return curr.index != other.curr.index; } + iterator &operator++() { + if (!inst->simple_layout) + curr.vh += 1 + (*types)[curr.index]->holder_size_in_ptrs; + ++curr.index; + curr.type = curr.index < types->size() ? (*types)[curr.index] : nullptr; + return *this; + } + value_and_holder &operator*() { return curr; } + value_and_holder *operator->() { return &curr; } + }; + + iterator begin() { return iterator(inst, &tinfo); } + iterator end() { return iterator(tinfo.size()); } + + iterator find(const type_info *find_type) { + auto it = begin(), endit = end(); + while (it != endit && it->type != find_type) ++it; + return it; + } + + size_t size() { return tinfo.size(); } +}; + +/** + * Extracts C++ value and holder pointer references from an instance (which may contain multiple + * values/holders for python-side multiple inheritance) that match the given type. Throws an error + * if the given type (or ValueType, if omitted) is not a pybind11 base of the given instance. If + * `find_type` is omitted (or explicitly specified as nullptr) the first value/holder are returned, + * regardless of type (and the resulting .type will be nullptr). + * + * The returned object should be short-lived: in particular, it must not outlive the called-upon + * instance. + */ +PYBIND11_NOINLINE inline value_and_holder instance::get_value_and_holder(const type_info *find_type /*= nullptr default in common.h*/) { + // Optimize common case: + if (!find_type || Py_TYPE(this) == find_type->type) + return value_and_holder(this, find_type, 0, 0); + + detail::values_and_holders vhs(this); + auto it = vhs.find(find_type); + if (it != vhs.end()) + return *it; + +#if defined(NDEBUG) + pybind11_fail("pybind11::detail::instance::get_value_and_holder: " + "type is not a pybind11 base of the given instance " + "(compile in debug mode for type details)"); +#else + pybind11_fail("pybind11::detail::instance::get_value_and_holder: `" + + std::string(find_type->type->tp_name) + "' is not a pybind11 base of the given `" + + std::string(Py_TYPE(this)->tp_name) + "' instance"); +#endif +} + +PYBIND11_NOINLINE inline void instance::allocate_layout() { + auto &tinfo = all_type_info(Py_TYPE(this)); + + const size_t n_types = tinfo.size(); + + if (n_types == 0) + pybind11_fail("instance allocation failed: new instance has no pybind11-registered base types"); + + simple_layout = + n_types == 1 && tinfo.front()->holder_size_in_ptrs <= instance_simple_holder_in_ptrs(); + + // Simple path: no python-side multiple inheritance, and a small-enough holder + if (simple_layout) { + simple_value_holder[0] = nullptr; + simple_holder_constructed = false; + simple_instance_registered = false; + } + else { // multiple base types or a too-large holder + // Allocate space to hold: [v1*][h1][v2*][h2]...[bb...] where [vN*] is a value pointer, + // [hN] is the (uninitialized) holder instance for value N, and [bb...] is a set of bool + // values that tracks whether each associated holder has been initialized. Each [block] is + // padded, if necessary, to an integer multiple of sizeof(void *). + size_t space = 0; + for (auto t : tinfo) { + space += 1; // value pointer + space += t->holder_size_in_ptrs; // holder instance + } + size_t flags_at = space; + space += size_in_ptrs(n_types); // status bytes (holder_constructed and instance_registered) + + // Allocate space for flags, values, and holders, and initialize it to 0 (flags and values, + // in particular, need to be 0). Use Python's memory allocation functions: in Python 3.6 + // they default to using pymalloc, which is designed to be efficient for small allocations + // like the one we're doing here; in earlier versions (and for larger allocations) they are + // just wrappers around malloc. +#if PY_VERSION_HEX >= 0x03050000 + nonsimple.values_and_holders = (void **) PyMem_Calloc(space, sizeof(void *)); + if (!nonsimple.values_and_holders) throw std::bad_alloc(); +#else + nonsimple.values_and_holders = (void **) PyMem_New(void *, space); + if (!nonsimple.values_and_holders) throw std::bad_alloc(); + std::memset(nonsimple.values_and_holders, 0, space * sizeof(void *)); +#endif + nonsimple.status = reinterpret_cast(&nonsimple.values_and_holders[flags_at]); + } + owned = true; +} + +PYBIND11_NOINLINE inline void instance::deallocate_layout() { + if (!simple_layout) + PyMem_Free(nonsimple.values_and_holders); +} + +PYBIND11_NOINLINE inline bool isinstance_generic(handle obj, const std::type_info &tp) { + handle type = detail::get_type_handle(tp, false); + if (!type) + return false; + return isinstance(obj, type); +} + +PYBIND11_NOINLINE inline std::string error_string() { + if (!PyErr_Occurred()) { + PyErr_SetString(PyExc_RuntimeError, "Unknown internal error occurred"); + return "Unknown internal error occurred"; + } + + error_scope scope; // Preserve error state + + std::string errorString; + if (scope.type) { + errorString += handle(scope.type).attr("__name__").cast(); + errorString += ": "; + } + if (scope.value) + errorString += (std::string) str(scope.value); + + PyErr_NormalizeException(&scope.type, &scope.value, &scope.trace); + +#if PY_MAJOR_VERSION >= 3 + if (scope.trace != nullptr) + PyException_SetTraceback(scope.value, scope.trace); +#endif + +#if !defined(PYPY_VERSION) + if (scope.trace) { + PyTracebackObject *trace = (PyTracebackObject *) scope.trace; + + /* Get the deepest trace possible */ + while (trace->tb_next) + trace = trace->tb_next; + + PyFrameObject *frame = trace->tb_frame; + errorString += "\n\nAt:\n"; + while (frame) { + int lineno = PyFrame_GetLineNumber(frame); + errorString += + " " + handle(frame->f_code->co_filename).cast() + + "(" + std::to_string(lineno) + "): " + + handle(frame->f_code->co_name).cast() + "\n"; + frame = frame->f_back; + } + trace = trace->tb_next; + } +#endif + + return errorString; +} + +PYBIND11_NOINLINE inline handle get_object_handle(const void *ptr, const detail::type_info *type ) { + auto &instances = get_internals().registered_instances; + auto range = instances.equal_range(ptr); + for (auto it = range.first; it != range.second; ++it) { + for (auto vh : values_and_holders(it->second)) { + if (vh.type == type) + return handle((PyObject *) it->second); + } + } + return handle(); +} + +inline PyThreadState *get_thread_state_unchecked() { +#if defined(PYPY_VERSION) + return PyThreadState_GET(); +#elif PY_VERSION_HEX < 0x03000000 + return _PyThreadState_Current; +#elif PY_VERSION_HEX < 0x03050000 + return (PyThreadState*) _Py_atomic_load_relaxed(&_PyThreadState_Current); +#elif PY_VERSION_HEX < 0x03050200 + return (PyThreadState*) _PyThreadState_Current.value; +#else + return _PyThreadState_UncheckedGet(); +#endif +} + +// Forward declarations +inline void keep_alive_impl(handle nurse, handle patient); +inline PyObject *make_new_instance(PyTypeObject *type, bool allocate_value = true); + +class type_caster_generic { +public: + PYBIND11_NOINLINE type_caster_generic(const std::type_info &type_info) + : typeinfo(get_type_info(type_info)) { } + + bool load(handle src, bool convert) { + return load_impl(src, convert); + } + + PYBIND11_NOINLINE static handle cast(const void *_src, return_value_policy policy, handle parent, + const detail::type_info *tinfo, + void *(*copy_constructor)(const void *), + void *(*move_constructor)(const void *), + const void *existing_holder = nullptr) { + if (!tinfo) // no type info: error will be set already + return handle(); + + void *src = const_cast(_src); + if (src == nullptr) + return none().release(); + + auto it_instances = get_internals().registered_instances.equal_range(src); + for (auto it_i = it_instances.first; it_i != it_instances.second; ++it_i) { + for (auto instance_type : detail::all_type_info(Py_TYPE(it_i->second))) { + if (instance_type && instance_type == tinfo) + return handle((PyObject *) it_i->second).inc_ref(); + } + } + + auto inst = reinterpret_steal(make_new_instance(tinfo->type, false /* don't allocate value */)); + auto wrapper = reinterpret_cast(inst.ptr()); + wrapper->owned = false; + void *&valueptr = values_and_holders(wrapper).begin()->value_ptr(); + + switch (policy) { + case return_value_policy::automatic: + case return_value_policy::take_ownership: + valueptr = src; + wrapper->owned = true; + break; + + case return_value_policy::automatic_reference: + case return_value_policy::reference: + valueptr = src; + wrapper->owned = false; + break; + + case return_value_policy::copy: + if (copy_constructor) + valueptr = copy_constructor(src); + else + throw cast_error("return_value_policy = copy, but the " + "object is non-copyable!"); + wrapper->owned = true; + break; + + case return_value_policy::move: + if (move_constructor) + valueptr = move_constructor(src); + else if (copy_constructor) + valueptr = copy_constructor(src); + else + throw cast_error("return_value_policy = move, but the " + "object is neither movable nor copyable!"); + wrapper->owned = true; + break; + + case return_value_policy::reference_internal: + valueptr = src; + wrapper->owned = false; + keep_alive_impl(inst, parent); + break; + + default: + throw cast_error("unhandled return_value_policy: should not happen!"); + } + + tinfo->init_instance(wrapper, existing_holder); + + return inst.release(); + } + +protected: + + // Base methods for generic caster; there are overridden in copyable_holder_caster + void load_value(const value_and_holder &v_h) { + value = v_h.value_ptr(); + } + bool try_implicit_casts(handle src, bool convert) { + for (auto &cast : typeinfo->implicit_casts) { + type_caster_generic sub_caster(*cast.first); + if (sub_caster.load(src, convert)) { + value = cast.second(sub_caster.value); + return true; + } + } + return false; + } + bool try_direct_conversions(handle src) { + for (auto &converter : *typeinfo->direct_conversions) { + if (converter(src.ptr(), value)) + return true; + } + return false; + } + void check_holder_compat() {} + + // Implementation of `load`; this takes the type of `this` so that it can dispatch the relevant + // bits of code between here and copyable_holder_caster where the two classes need different + // logic (without having to resort to virtual inheritance). + template + PYBIND11_NOINLINE bool load_impl(handle src, bool convert) { + if (!src || !typeinfo) + return false; + if (src.is_none()) { + // Defer accepting None to other overloads (if we aren't in convert mode): + if (!convert) return false; + value = nullptr; + return true; + } + + auto &this_ = static_cast(*this); + this_.check_holder_compat(); + + PyTypeObject *srctype = Py_TYPE(src.ptr()); + + // Case 1: If src is an exact type match for the target type then we can reinterpret_cast + // the instance's value pointer to the target type: + if (srctype == typeinfo->type) { + this_.load_value(reinterpret_cast(src.ptr())->get_value_and_holder()); + return true; + } + // Case 2: We have a derived class + else if (PyType_IsSubtype(srctype, typeinfo->type)) { + auto &bases = all_type_info(srctype); + bool no_cpp_mi = typeinfo->simple_type; + + // Case 2a: the python type is a Python-inherited derived class that inherits from just + // one simple (no MI) pybind11 class, or is an exact match, so the C++ instance is of + // the right type and we can use reinterpret_cast. + // (This is essentially the same as case 2b, but because not using multiple inheritance + // is extremely common, we handle it specially to avoid the loop iterator and type + // pointer lookup overhead) + if (bases.size() == 1 && (no_cpp_mi || bases.front()->type == typeinfo->type)) { + this_.load_value(reinterpret_cast(src.ptr())->get_value_and_holder()); + return true; + } + // Case 2b: the python type inherits from multiple C++ bases. Check the bases to see if + // we can find an exact match (or, for a simple C++ type, an inherited match); if so, we + // can safely reinterpret_cast to the relevant pointer. + else if (bases.size() > 1) { + for (auto base : bases) { + if (no_cpp_mi ? PyType_IsSubtype(base->type, typeinfo->type) : base->type == typeinfo->type) { + this_.load_value(reinterpret_cast(src.ptr())->get_value_and_holder(base)); + return true; + } + } + } + + // Case 2c: C++ multiple inheritance is involved and we couldn't find an exact type match + // in the registered bases, above, so try implicit casting (needed for proper C++ casting + // when MI is involved). + if (this_.try_implicit_casts(src, convert)) + return true; + } + + // Perform an implicit conversion + if (convert) { + for (auto &converter : typeinfo->implicit_conversions) { + auto temp = reinterpret_steal(converter(src.ptr(), typeinfo->type)); + if (load_impl(temp, false)) { + loader_life_support::add_patient(temp); + return true; + } + } + if (this_.try_direct_conversions(src)) + return true; + } + return false; + } + + + // Called to do type lookup and wrap the pointer and type in a pair when a dynamic_cast + // isn't needed or can't be used. If the type is unknown, sets the error and returns a pair + // with .second = nullptr. (p.first = nullptr is not an error: it becomes None). + PYBIND11_NOINLINE static std::pair src_and_type( + const void *src, const std::type_info &cast_type, const std::type_info *rtti_type = nullptr) { + auto &internals = get_internals(); + auto it = internals.registered_types_cpp.find(std::type_index(cast_type)); + if (it != internals.registered_types_cpp.end()) + return {src, (const type_info *) it->second}; + + // Not found, set error: + std::string tname = rtti_type ? rtti_type->name() : cast_type.name(); + detail::clean_type_id(tname); + std::string msg = "Unregistered type : " + tname; + PyErr_SetString(PyExc_TypeError, msg.c_str()); + return {nullptr, nullptr}; + } + + const type_info *typeinfo = nullptr; + void *value = nullptr; +}; + +/** + * Determine suitable casting operator for pointer-or-lvalue-casting type casters. The type caster + * needs to provide `operator T*()` and `operator T&()` operators. + * + * If the type supports moving the value away via an `operator T&&() &&` method, it should use + * `movable_cast_op_type` instead. + */ +template +using cast_op_type = + conditional_t>::value, + typename std::add_pointer>::type, + typename std::add_lvalue_reference>::type>; + +/** + * Determine suitable casting operator for a type caster with a movable value. Such a type caster + * needs to provide `operator T*()`, `operator T&()`, and `operator T&&() &&`. The latter will be + * called in appropriate contexts where the value can be moved rather than copied. + * + * These operator are automatically provided when using the PYBIND11_TYPE_CASTER macro. + */ +template +using movable_cast_op_type = + conditional_t::type>::value, + typename std::add_pointer>::type, + conditional_t::value, + typename std::add_rvalue_reference>::type, + typename std::add_lvalue_reference>::type>>; + +// std::is_copy_constructible isn't quite enough: it lets std::vector (and similar) through when +// T is non-copyable, but code containing such a copy constructor fails to actually compile. +template struct is_copy_constructible : std::is_copy_constructible {}; + +// Specialization for types that appear to be copy constructible but also look like stl containers +// (we specifically check for: has `value_type` and `reference` with `reference = value_type&`): if +// so, copy constructability depends on whether the value_type is copy constructible. +template struct is_copy_constructible, + std::is_same + >::value>> : is_copy_constructible {}; + +#if !defined(PYBIND11_CPP17) +// Likewise for std::pair before C++17 (which mandates that the copy constructor not exist when the +// two types aren't themselves copy constructible). +template struct is_copy_constructible> + : all_of, is_copy_constructible> {}; +#endif + +/// Generic type caster for objects stored on the heap +template class type_caster_base : public type_caster_generic { + using itype = intrinsic_t; +public: + static PYBIND11_DESCR name() { return type_descr(_()); } + + type_caster_base() : type_caster_base(typeid(type)) { } + explicit type_caster_base(const std::type_info &info) : type_caster_generic(info) { } + + static handle cast(const itype &src, return_value_policy policy, handle parent) { + if (policy == return_value_policy::automatic || policy == return_value_policy::automatic_reference) + policy = return_value_policy::copy; + return cast(&src, policy, parent); + } + + static handle cast(itype &&src, return_value_policy, handle parent) { + return cast(&src, return_value_policy::move, parent); + } + + // Returns a (pointer, type_info) pair taking care of necessary RTTI type lookup for a + // polymorphic type. If the instance isn't derived, returns the non-RTTI base version. + template ::value, int> = 0> + static std::pair src_and_type(const itype *src) { + const void *vsrc = src; + auto &internals = get_internals(); + auto &cast_type = typeid(itype); + const std::type_info *instance_type = nullptr; + if (vsrc) { + instance_type = &typeid(*src); + if (!same_type(cast_type, *instance_type)) { + // This is a base pointer to a derived type; if it is a pybind11-registered type, we + // can get the correct derived pointer (which may be != base pointer) by a + // dynamic_cast to most derived type: + auto it = internals.registered_types_cpp.find(std::type_index(*instance_type)); + if (it != internals.registered_types_cpp.end()) + return {dynamic_cast(src), (const type_info *) it->second}; + } + } + // Otherwise we have either a nullptr, an `itype` pointer, or an unknown derived pointer, so + // don't do a cast + return type_caster_generic::src_and_type(vsrc, cast_type, instance_type); + } + + // Non-polymorphic type, so no dynamic casting; just call the generic version directly + template ::value, int> = 0> + static std::pair src_and_type(const itype *src) { + return type_caster_generic::src_and_type(src, typeid(itype)); + } + + static handle cast(const itype *src, return_value_policy policy, handle parent) { + auto st = src_and_type(src); + return type_caster_generic::cast( + st.first, policy, parent, st.second, + make_copy_constructor(src), make_move_constructor(src)); + } + + static handle cast_holder(const itype *src, const void *holder) { + auto st = src_and_type(src); + return type_caster_generic::cast( + st.first, return_value_policy::take_ownership, {}, st.second, + nullptr, nullptr, holder); + } + + template using cast_op_type = cast_op_type; + + operator itype*() { return (type *) value; } + operator itype&() { if (!value) throw reference_cast_error(); return *((itype *) value); } + +protected: + using Constructor = void *(*)(const void *); + + /* Only enabled when the types are {copy,move}-constructible *and* when the type + does not have a private operator new implementation. */ + template ::value>> + static auto make_copy_constructor(const T *x) -> decltype(new T(*x), Constructor{}) { + return [](const void *arg) -> void * { + return new T(*reinterpret_cast(arg)); + }; + } + + template ::value>> + static auto make_move_constructor(const T *x) -> decltype(new T(std::move(*const_cast(x))), Constructor{}) { + return [](const void *arg) -> void * { + return new T(std::move(*const_cast(reinterpret_cast(arg)))); + }; + } + + static Constructor make_copy_constructor(...) { return nullptr; } + static Constructor make_move_constructor(...) { return nullptr; } +}; + +template class type_caster : public type_caster_base { }; +template using make_caster = type_caster>; + +// Shortcut for calling a caster's `cast_op_type` cast operator for casting a type_caster to a T +template typename make_caster::template cast_op_type cast_op(make_caster &caster) { + return caster.operator typename make_caster::template cast_op_type(); +} +template typename make_caster::template cast_op_type::type> +cast_op(make_caster &&caster) { + return std::move(caster).operator + typename make_caster::template cast_op_type::type>(); +} + +template class type_caster> { +private: + using caster_t = make_caster; + caster_t subcaster; + using subcaster_cast_op_type = typename caster_t::template cast_op_type; + static_assert(std::is_same::type &, subcaster_cast_op_type>::value, + "std::reference_wrapper caster requires T to have a caster with an `T &` operator"); +public: + bool load(handle src, bool convert) { return subcaster.load(src, convert); } + static PYBIND11_DESCR name() { return caster_t::name(); } + static handle cast(const std::reference_wrapper &src, return_value_policy policy, handle parent) { + // It is definitely wrong to take ownership of this pointer, so mask that rvp + if (policy == return_value_policy::take_ownership || policy == return_value_policy::automatic) + policy = return_value_policy::automatic_reference; + return caster_t::cast(&src.get(), policy, parent); + } + template using cast_op_type = std::reference_wrapper; + operator std::reference_wrapper() { return subcaster.operator subcaster_cast_op_type&(); } +}; + +#define PYBIND11_TYPE_CASTER(type, py_name) \ + protected: \ + type value; \ + public: \ + static PYBIND11_DESCR name() { return type_descr(py_name); } \ + template >::value, int> = 0> \ + static handle cast(T_ *src, return_value_policy policy, handle parent) { \ + if (!src) return none().release(); \ + if (policy == return_value_policy::take_ownership) { \ + auto h = cast(std::move(*src), policy, parent); delete src; return h; \ + } else { \ + return cast(*src, policy, parent); \ + } \ + } \ + operator type*() { return &value; } \ + operator type&() { return value; } \ + operator type&&() && { return std::move(value); } \ + template using cast_op_type = pybind11::detail::movable_cast_op_type + + +template using is_std_char_type = any_of< + std::is_same, /* std::string */ + std::is_same, /* std::u16string */ + std::is_same, /* std::u32string */ + std::is_same /* std::wstring */ +>; + +template +struct type_caster::value && !is_std_char_type::value>> { + using _py_type_0 = conditional_t; + using _py_type_1 = conditional_t::value, _py_type_0, typename std::make_unsigned<_py_type_0>::type>; + using py_type = conditional_t::value, double, _py_type_1>; +public: + + bool load(handle src, bool convert) { + py_type py_value; + + if (!src) + return false; + + if (std::is_floating_point::value) { + if (convert || PyFloat_Check(src.ptr())) + py_value = (py_type) PyFloat_AsDouble(src.ptr()); + else + return false; + } else if (PyFloat_Check(src.ptr())) { + return false; + } else if (std::is_unsigned::value) { + py_value = as_unsigned(src.ptr()); + } else { // signed integer: + py_value = sizeof(T) <= sizeof(long) + ? (py_type) PyLong_AsLong(src.ptr()) + : (py_type) PYBIND11_LONG_AS_LONGLONG(src.ptr()); + } + + bool py_err = py_value == (py_type) -1 && PyErr_Occurred(); + if (py_err || (std::is_integral::value && sizeof(py_type) != sizeof(T) && + (py_value < (py_type) std::numeric_limits::min() || + py_value > (py_type) std::numeric_limits::max()))) { + bool type_error = py_err && PyErr_ExceptionMatches( +#if PY_VERSION_HEX < 0x03000000 && !defined(PYPY_VERSION) + PyExc_SystemError +#else + PyExc_TypeError +#endif + ); + PyErr_Clear(); + if (type_error && convert && PyNumber_Check(src.ptr())) { + auto tmp = reinterpret_borrow(std::is_floating_point::value + ? PyNumber_Float(src.ptr()) + : PyNumber_Long(src.ptr())); + PyErr_Clear(); + return load(tmp, false); + } + return false; + } + + value = (T) py_value; + return true; + } + + static handle cast(T src, return_value_policy /* policy */, handle /* parent */) { + if (std::is_floating_point::value) { + return PyFloat_FromDouble((double) src); + } else if (sizeof(T) <= sizeof(long)) { + if (std::is_signed::value) + return PyLong_FromLong((long) src); + else + return PyLong_FromUnsignedLong((unsigned long) src); + } else { + if (std::is_signed::value) + return PyLong_FromLongLong((long long) src); + else + return PyLong_FromUnsignedLongLong((unsigned long long) src); + } + } + + PYBIND11_TYPE_CASTER(T, _::value>("int", "float")); +}; + +template struct void_caster { +public: + bool load(handle src, bool) { + if (src && src.is_none()) + return true; + return false; + } + static handle cast(T, return_value_policy /* policy */, handle /* parent */) { + return none().inc_ref(); + } + PYBIND11_TYPE_CASTER(T, _("None")); +}; + +template <> class type_caster : public void_caster {}; + +template <> class type_caster : public type_caster { +public: + using type_caster::cast; + + bool load(handle h, bool) { + if (!h) { + return false; + } else if (h.is_none()) { + value = nullptr; + return true; + } + + /* Check if this is a capsule */ + if (isinstance(h)) { + value = reinterpret_borrow(h); + return true; + } + + /* Check if this is a C++ type */ + auto &bases = all_type_info((PyTypeObject *) h.get_type().ptr()); + if (bases.size() == 1) { // Only allowing loading from a single-value type + value = values_and_holders(reinterpret_cast(h.ptr())).begin()->value_ptr(); + return true; + } + + /* Fail */ + return false; + } + + static handle cast(const void *ptr, return_value_policy /* policy */, handle /* parent */) { + if (ptr) + return capsule(ptr).release(); + else + return none().inc_ref(); + } + + template using cast_op_type = void*&; + operator void *&() { return value; } + static PYBIND11_DESCR name() { return type_descr(_("capsule")); } +private: + void *value = nullptr; +}; + +template <> class type_caster : public void_caster { }; + +template <> class type_caster { +public: + bool load(handle src, bool convert) { + if (!src) return false; + else if (src.ptr() == Py_True) { value = true; return true; } + else if (src.ptr() == Py_False) { value = false; return true; } + else if (convert || !strcmp("numpy.bool_", Py_TYPE(src.ptr())->tp_name)) { + // (allow non-implicit conversion for numpy booleans) + + Py_ssize_t res = -1; + if (src.is_none()) { + res = 0; // None is implicitly converted to False + } + #if defined(PYPY_VERSION) + // On PyPy, check that "__bool__" (or "__nonzero__" on Python 2.7) attr exists + else if (hasattr(src, PYBIND11_BOOL_ATTR)) { + res = PyObject_IsTrue(src.ptr()); + } + #else + // Alternate approach for CPython: this does the same as the above, but optimized + // using the CPython API so as to avoid an unneeded attribute lookup. + else if (auto tp_as_number = src.ptr()->ob_type->tp_as_number) { + if (PYBIND11_NB_BOOL(tp_as_number)) { + res = (*PYBIND11_NB_BOOL(tp_as_number))(src.ptr()); + } + } + #endif + if (res == 0 || res == 1) { + value = (bool) res; + return true; + } + } + return false; + } + static handle cast(bool src, return_value_policy /* policy */, handle /* parent */) { + return handle(src ? Py_True : Py_False).inc_ref(); + } + PYBIND11_TYPE_CASTER(bool, _("bool")); +}; + +// Helper class for UTF-{8,16,32} C++ stl strings: +template struct string_caster { + using CharT = typename StringType::value_type; + + // Simplify life by being able to assume standard char sizes (the standard only guarantees + // minimums, but Python requires exact sizes) + static_assert(!std::is_same::value || sizeof(CharT) == 1, "Unsupported char size != 1"); + static_assert(!std::is_same::value || sizeof(CharT) == 2, "Unsupported char16_t size != 2"); + static_assert(!std::is_same::value || sizeof(CharT) == 4, "Unsupported char32_t size != 4"); + // wchar_t can be either 16 bits (Windows) or 32 (everywhere else) + static_assert(!std::is_same::value || sizeof(CharT) == 2 || sizeof(CharT) == 4, + "Unsupported wchar_t size != 2/4"); + static constexpr size_t UTF_N = 8 * sizeof(CharT); + + bool load(handle src, bool) { +#if PY_MAJOR_VERSION < 3 + object temp; +#endif + handle load_src = src; + if (!src) { + return false; + } else if (!PyUnicode_Check(load_src.ptr())) { +#if PY_MAJOR_VERSION >= 3 + return load_bytes(load_src); +#else + if (sizeof(CharT) == 1) { + return load_bytes(load_src); + } + + // The below is a guaranteed failure in Python 3 when PyUnicode_Check returns false + if (!PYBIND11_BYTES_CHECK(load_src.ptr())) + return false; + + temp = reinterpret_steal(PyUnicode_FromObject(load_src.ptr())); + if (!temp) { PyErr_Clear(); return false; } + load_src = temp; +#endif + } + + object utfNbytes = reinterpret_steal(PyUnicode_AsEncodedString( + load_src.ptr(), UTF_N == 8 ? "utf-8" : UTF_N == 16 ? "utf-16" : "utf-32", nullptr)); + if (!utfNbytes) { PyErr_Clear(); return false; } + + const CharT *buffer = reinterpret_cast(PYBIND11_BYTES_AS_STRING(utfNbytes.ptr())); + size_t length = (size_t) PYBIND11_BYTES_SIZE(utfNbytes.ptr()) / sizeof(CharT); + if (UTF_N > 8) { buffer++; length--; } // Skip BOM for UTF-16/32 + value = StringType(buffer, length); + + // If we're loading a string_view we need to keep the encoded Python object alive: + if (IsView) + loader_life_support::add_patient(utfNbytes); + + return true; + } + + static handle cast(const StringType &src, return_value_policy /* policy */, handle /* parent */) { + const char *buffer = reinterpret_cast(src.data()); + ssize_t nbytes = ssize_t(src.size() * sizeof(CharT)); + handle s = decode_utfN(buffer, nbytes); + if (!s) throw error_already_set(); + return s; + } + + PYBIND11_TYPE_CASTER(StringType, _(PYBIND11_STRING_NAME)); + +private: + static handle decode_utfN(const char *buffer, ssize_t nbytes) { +#if !defined(PYPY_VERSION) + return + UTF_N == 8 ? PyUnicode_DecodeUTF8(buffer, nbytes, nullptr) : + UTF_N == 16 ? PyUnicode_DecodeUTF16(buffer, nbytes, nullptr, nullptr) : + PyUnicode_DecodeUTF32(buffer, nbytes, nullptr, nullptr); +#else + // PyPy seems to have multiple problems related to PyUnicode_UTF*: the UTF8 version + // sometimes segfaults for unknown reasons, while the UTF16 and 32 versions require a + // non-const char * arguments, which is also a nuissance, so bypass the whole thing by just + // passing the encoding as a string value, which works properly: + return PyUnicode_Decode(buffer, nbytes, UTF_N == 8 ? "utf-8" : UTF_N == 16 ? "utf-16" : "utf-32", nullptr); +#endif + } + + // When loading into a std::string or char*, accept a bytes object as-is (i.e. + // without any encoding/decoding attempt). For other C++ char sizes this is a no-op. + // which supports loading a unicode from a str, doesn't take this path. + template + bool load_bytes(enable_if_t src) { + if (PYBIND11_BYTES_CHECK(src.ptr())) { + // We were passed a Python 3 raw bytes; accept it into a std::string or char* + // without any encoding attempt. + const char *bytes = PYBIND11_BYTES_AS_STRING(src.ptr()); + if (bytes) { + value = StringType(bytes, (size_t) PYBIND11_BYTES_SIZE(src.ptr())); + return true; + } + } + + return false; + } + + template + bool load_bytes(enable_if_t) { return false; } +}; + +template +struct type_caster, enable_if_t::value>> + : string_caster> {}; + +#ifdef PYBIND11_HAS_STRING_VIEW +template +struct type_caster, enable_if_t::value>> + : string_caster, true> {}; +#endif + +// Type caster for C-style strings. We basically use a std::string type caster, but also add the +// ability to use None as a nullptr char* (which the string caster doesn't allow). +template struct type_caster::value>> { + using StringType = std::basic_string; + using StringCaster = type_caster; + StringCaster str_caster; + bool none = false; +public: + bool load(handle src, bool convert) { + if (!src) return false; + if (src.is_none()) { + // Defer accepting None to other overloads (if we aren't in convert mode): + if (!convert) return false; + none = true; + return true; + } + return str_caster.load(src, convert); + } + + static handle cast(const CharT *src, return_value_policy policy, handle parent) { + if (src == nullptr) return pybind11::none().inc_ref(); + return StringCaster::cast(StringType(src), policy, parent); + } + + static handle cast(CharT src, return_value_policy policy, handle parent) { + if (std::is_same::value) { + handle s = PyUnicode_DecodeLatin1((const char *) &src, 1, nullptr); + if (!s) throw error_already_set(); + return s; + } + return StringCaster::cast(StringType(1, src), policy, parent); + } + + operator CharT*() { return none ? nullptr : const_cast(static_cast(str_caster).c_str()); } + operator CharT() { + if (none) + throw value_error("Cannot convert None to a character"); + + auto &value = static_cast(str_caster); + size_t str_len = value.size(); + if (str_len == 0) + throw value_error("Cannot convert empty string to a character"); + + // If we're in UTF-8 mode, we have two possible failures: one for a unicode character that + // is too high, and one for multiple unicode characters (caught later), so we need to figure + // out how long the first encoded character is in bytes to distinguish between these two + // errors. We also allow want to allow unicode characters U+0080 through U+00FF, as those + // can fit into a single char value. + if (StringCaster::UTF_N == 8 && str_len > 1 && str_len <= 4) { + unsigned char v0 = static_cast(value[0]); + size_t char0_bytes = !(v0 & 0x80) ? 1 : // low bits only: 0-127 + (v0 & 0xE0) == 0xC0 ? 2 : // 0b110xxxxx - start of 2-byte sequence + (v0 & 0xF0) == 0xE0 ? 3 : // 0b1110xxxx - start of 3-byte sequence + 4; // 0b11110xxx - start of 4-byte sequence + + if (char0_bytes == str_len) { + // If we have a 128-255 value, we can decode it into a single char: + if (char0_bytes == 2 && (v0 & 0xFC) == 0xC0) { // 0x110000xx 0x10xxxxxx + return static_cast(((v0 & 3) << 6) + (static_cast(value[1]) & 0x3F)); + } + // Otherwise we have a single character, but it's > U+00FF + throw value_error("Character code point not in range(0x100)"); + } + } + + // UTF-16 is much easier: we can only have a surrogate pair for values above U+FFFF, thus a + // surrogate pair with total length 2 instantly indicates a range error (but not a "your + // string was too long" error). + else if (StringCaster::UTF_N == 16 && str_len == 2) { + char16_t v0 = static_cast(value[0]); + if (v0 >= 0xD800 && v0 < 0xE000) + throw value_error("Character code point not in range(0x10000)"); + } + + if (str_len != 1) + throw value_error("Expected a character, but multi-character string found"); + + return value[0]; + } + + static PYBIND11_DESCR name() { return type_descr(_(PYBIND11_STRING_NAME)); } + template using cast_op_type = remove_reference_t>; +}; + +// Base implementation for std::tuple and std::pair +template class Tuple, typename... Ts> class tuple_caster { + using type = Tuple; + static constexpr auto size = sizeof...(Ts); + using indices = make_index_sequence; +public: + + bool load(handle src, bool convert) { + if (!isinstance(src)) + return false; + const auto seq = reinterpret_borrow(src); + if (seq.size() != size) + return false; + return load_impl(seq, convert, indices{}); + } + + template + static handle cast(T &&src, return_value_policy policy, handle parent) { + return cast_impl(std::forward(src), policy, parent, indices{}); + } + + static PYBIND11_DESCR name() { + return type_descr(_("Tuple[") + detail::concat(make_caster::name()...) + _("]")); + } + + template using cast_op_type = type; + + operator type() & { return implicit_cast(indices{}); } + operator type() && { return std::move(*this).implicit_cast(indices{}); } + +protected: + template + type implicit_cast(index_sequence) & { return type(cast_op(std::get(subcasters))...); } + template + type implicit_cast(index_sequence) && { return type(cast_op(std::move(std::get(subcasters)))...); } + + static constexpr bool load_impl(const sequence &, bool, index_sequence<>) { return true; } + + template + bool load_impl(const sequence &seq, bool convert, index_sequence) { + for (bool r : {std::get(subcasters).load(seq[Is], convert)...}) + if (!r) + return false; + return true; + } + + /* Implementation: Convert a C++ tuple into a Python tuple */ + template + static handle cast_impl(T &&src, return_value_policy policy, handle parent, index_sequence) { + std::array entries{{ + reinterpret_steal(make_caster::cast(std::get(std::forward(src)), policy, parent))... + }}; + for (const auto &entry: entries) + if (!entry) + return handle(); + tuple result(size); + int counter = 0; + for (auto & entry: entries) + PyTuple_SET_ITEM(result.ptr(), counter++, entry.release().ptr()); + return result.release(); + } + + Tuple...> subcasters; +}; + +template class type_caster> + : public tuple_caster {}; + +template class type_caster> + : public tuple_caster {}; + +/// Helper class which abstracts away certain actions. Users can provide specializations for +/// custom holders, but it's only necessary if the type has a non-standard interface. +template +struct holder_helper { + static auto get(const T &p) -> decltype(p.get()) { return p.get(); } +}; + +/// Type caster for holder types like std::shared_ptr, etc. +template +struct copyable_holder_caster : public type_caster_base { +public: + using base = type_caster_base; + static_assert(std::is_base_of>::value, + "Holder classes are only supported for custom types"); + using base::base; + using base::cast; + using base::typeinfo; + using base::value; + + bool load(handle src, bool convert) { + return base::template load_impl>(src, convert); + } + + explicit operator type*() { return this->value; } + explicit operator type&() { return *(this->value); } + explicit operator holder_type*() { return &holder; } + + // Workaround for Intel compiler bug + // see pybind11 issue 94 + #if defined(__ICC) || defined(__INTEL_COMPILER) + operator holder_type&() { return holder; } + #else + explicit operator holder_type&() { return holder; } + #endif + + static handle cast(const holder_type &src, return_value_policy, handle) { + const auto *ptr = holder_helper::get(src); + return type_caster_base::cast_holder(ptr, &src); + } + +protected: + friend class type_caster_generic; + void check_holder_compat() { + if (typeinfo->default_holder) + throw cast_error("Unable to load a custom holder type from a default-holder instance"); + } + + bool load_value(const value_and_holder &v_h) { + if (v_h.holder_constructed()) { + value = v_h.value_ptr(); + holder = v_h.holder(); + return true; + } else { + throw cast_error("Unable to cast from non-held to held instance (T& to Holder) " +#if defined(NDEBUG) + "(compile in debug mode for type information)"); +#else + "of type '" + type_id() + "''"); +#endif + } + } + + template ::value, int> = 0> + bool try_implicit_casts(handle, bool) { return false; } + + template ::value, int> = 0> + bool try_implicit_casts(handle src, bool convert) { + for (auto &cast : typeinfo->implicit_casts) { + copyable_holder_caster sub_caster(*cast.first); + if (sub_caster.load(src, convert)) { + value = cast.second(sub_caster.value); + holder = holder_type(sub_caster.holder, (type *) value); + return true; + } + } + return false; + } + + static bool try_direct_conversions(handle) { return false; } + + + holder_type holder; +}; + +/// Specialize for the common std::shared_ptr, so users don't need to +template +class type_caster> : public copyable_holder_caster> { }; + +template +struct move_only_holder_caster { + static_assert(std::is_base_of, type_caster>::value, + "Holder classes are only supported for custom types"); + + static handle cast(holder_type &&src, return_value_policy, handle) { + auto *ptr = holder_helper::get(src); + return type_caster_base::cast_holder(ptr, &src); + } + static PYBIND11_DESCR name() { return type_caster_base::name(); } +}; + +template +class type_caster> + : public move_only_holder_caster> { }; + +template +using type_caster_holder = conditional_t::value, + copyable_holder_caster, + move_only_holder_caster>; + +template struct always_construct_holder { static constexpr bool value = Value; }; + +/// Create a specialization for custom holder types (silently ignores std::shared_ptr) +#define PYBIND11_DECLARE_HOLDER_TYPE(type, holder_type, ...) \ + namespace pybind11 { namespace detail { \ + template \ + struct always_construct_holder : always_construct_holder { }; \ + template \ + class type_caster::value>> \ + : public type_caster_holder { }; \ + }} + +// PYBIND11_DECLARE_HOLDER_TYPE holder types: +template struct is_holder_type : + std::is_base_of, detail::type_caster> {}; +// Specialization for always-supported unique_ptr holders: +template struct is_holder_type> : + std::true_type {}; + +template struct handle_type_name { static PYBIND11_DESCR name() { return _(); } }; +template <> struct handle_type_name { static PYBIND11_DESCR name() { return _(PYBIND11_BYTES_NAME); } }; +template <> struct handle_type_name { static PYBIND11_DESCR name() { return _("*args"); } }; +template <> struct handle_type_name { static PYBIND11_DESCR name() { return _("**kwargs"); } }; + +template +struct pyobject_caster { + template ::value, int> = 0> + bool load(handle src, bool /* convert */) { value = src; return static_cast(value); } + + template ::value, int> = 0> + bool load(handle src, bool /* convert */) { + if (!isinstance(src)) + return false; + value = reinterpret_borrow(src); + return true; + } + + static handle cast(const handle &src, return_value_policy /* policy */, handle /* parent */) { + return src.inc_ref(); + } + PYBIND11_TYPE_CASTER(type, handle_type_name::name()); +}; + +template +class type_caster::value>> : public pyobject_caster { }; + +// Our conditions for enabling moving are quite restrictive: +// At compile time: +// - T needs to be a non-const, non-pointer, non-reference type +// - type_caster::operator T&() must exist +// - the type must be move constructible (obviously) +// At run-time: +// - if the type is non-copy-constructible, the object must be the sole owner of the type (i.e. it +// must have ref_count() == 1)h +// If any of the above are not satisfied, we fall back to copying. +template using move_is_plain_type = satisfies_none_of; +template struct move_always : std::false_type {}; +template struct move_always, + negation>, + std::is_move_constructible, + std::is_same>().operator T&()), T&> +>::value>> : std::true_type {}; +template struct move_if_unreferenced : std::false_type {}; +template struct move_if_unreferenced, + negation>, + std::is_move_constructible, + std::is_same>().operator T&()), T&> +>::value>> : std::true_type {}; +template using move_never = none_of, move_if_unreferenced>; + +// Detect whether returning a `type` from a cast on type's type_caster is going to result in a +// reference or pointer to a local variable of the type_caster. Basically, only +// non-reference/pointer `type`s and reference/pointers from a type_caster_generic are safe; +// everything else returns a reference/pointer to a local variable. +template using cast_is_temporary_value_reference = bool_constant< + (std::is_reference::value || std::is_pointer::value) && + !std::is_base_of>::value +>; + +// When a value returned from a C++ function is being cast back to Python, we almost always want to +// force `policy = move`, regardless of the return value policy the function/method was declared +// with. Some classes (most notably Eigen::Ref and related) need to avoid this, and so can do so by +// specializing this struct. +template struct return_value_policy_override { + static return_value_policy policy(return_value_policy p) { + return !std::is_lvalue_reference::value && !std::is_pointer::value + ? return_value_policy::move : p; + } +}; + +// Basic python -> C++ casting; throws if casting fails +template type_caster &load_type(type_caster &conv, const handle &handle) { + if (!conv.load(handle, true)) { +#if defined(NDEBUG) + throw cast_error("Unable to cast Python instance to C++ type (compile in debug mode for details)"); +#else + throw cast_error("Unable to cast Python instance of type " + + (std::string) str(handle.get_type()) + " to C++ type '" + type_id() + "''"); +#endif + } + return conv; +} +// Wrapper around the above that also constructs and returns a type_caster +template make_caster load_type(const handle &handle) { + make_caster conv; + load_type(conv, handle); + return conv; +} + +NAMESPACE_END(detail) + +// pytype -> C++ type +template ::value, int> = 0> +T cast(const handle &handle) { + using namespace detail; + static_assert(!cast_is_temporary_value_reference::value, + "Unable to cast type to reference: value is local to type caster"); + return cast_op(load_type(handle)); +} + +// pytype -> pytype (calls converting constructor) +template ::value, int> = 0> +T cast(const handle &handle) { return T(reinterpret_borrow(handle)); } + +// C++ type -> py::object +template ::value, int> = 0> +object cast(const T &value, return_value_policy policy = return_value_policy::automatic_reference, + handle parent = handle()) { + if (policy == return_value_policy::automatic) + policy = std::is_pointer::value ? return_value_policy::take_ownership : return_value_policy::copy; + else if (policy == return_value_policy::automatic_reference) + policy = std::is_pointer::value ? return_value_policy::reference : return_value_policy::copy; + return reinterpret_steal(detail::make_caster::cast(value, policy, parent)); +} + +template T handle::cast() const { return pybind11::cast(*this); } +template <> inline void handle::cast() const { return; } + +template +detail::enable_if_t::value, T> move(object &&obj) { + if (obj.ref_count() > 1) +#if defined(NDEBUG) + throw cast_error("Unable to cast Python instance to C++ rvalue: instance has multiple references" + " (compile in debug mode for details)"); +#else + throw cast_error("Unable to move from Python " + (std::string) str(obj.get_type()) + + " instance to C++ " + type_id() + " instance: instance has multiple references"); +#endif + + // Move into a temporary and return that, because the reference may be a local value of `conv` + T ret = std::move(detail::load_type(obj).operator T&()); + return ret; +} + +// Calling cast() on an rvalue calls pybind::cast with the object rvalue, which does: +// - If we have to move (because T has no copy constructor), do it. This will fail if the moved +// object has multiple references, but trying to copy will fail to compile. +// - If both movable and copyable, check ref count: if 1, move; otherwise copy +// - Otherwise (not movable), copy. +template detail::enable_if_t::value, T> cast(object &&object) { + return move(std::move(object)); +} +template detail::enable_if_t::value, T> cast(object &&object) { + if (object.ref_count() > 1) + return cast(object); + else + return move(std::move(object)); +} +template detail::enable_if_t::value, T> cast(object &&object) { + return cast(object); +} + +template T object::cast() const & { return pybind11::cast(*this); } +template T object::cast() && { return pybind11::cast(std::move(*this)); } +template <> inline void object::cast() const & { return; } +template <> inline void object::cast() && { return; } + +NAMESPACE_BEGIN(detail) + +// Declared in pytypes.h: +template ::value, int>> +object object_or_cast(T &&o) { return pybind11::cast(std::forward(o)); } + +struct overload_unused {}; // Placeholder type for the unneeded (and dead code) static variable in the OVERLOAD_INT macro +template using overload_caster_t = conditional_t< + cast_is_temporary_value_reference::value, make_caster, overload_unused>; + +// Trampoline use: for reference/pointer types to value-converted values, we do a value cast, then +// store the result in the given variable. For other types, this is a no-op. +template enable_if_t::value, T> cast_ref(object &&o, make_caster &caster) { + return cast_op(load_type(caster, o)); +} +template enable_if_t::value, T> cast_ref(object &&, overload_unused &) { + pybind11_fail("Internal error: cast_ref fallback invoked"); } + +// Trampoline use: Having a pybind11::cast with an invalid reference type is going to static_assert, even +// though if it's in dead code, so we provide a "trampoline" to pybind11::cast that only does anything in +// cases where pybind11::cast is valid. +template enable_if_t::value, T> cast_safe(object &&o) { + return pybind11::cast(std::move(o)); } +template enable_if_t::value, T> cast_safe(object &&) { + pybind11_fail("Internal error: cast_safe fallback invoked"); } +template <> inline void cast_safe(object &&) {} + +NAMESPACE_END(detail) + +template tuple make_tuple(Args&&... args_) { + constexpr size_t size = sizeof...(Args); + std::array args { + { reinterpret_steal(detail::make_caster::cast( + std::forward(args_), policy, nullptr))... } + }; + for (size_t i = 0; i < args.size(); i++) { + if (!args[i]) { +#if defined(NDEBUG) + throw cast_error("make_tuple(): unable to convert arguments to Python object (compile in debug mode for details)"); +#else + std::array argtypes { {type_id()...} }; + throw cast_error("make_tuple(): unable to convert argument of type '" + + argtypes[i] + "' to Python object"); +#endif + } + } + tuple result(size); + int counter = 0; + for (auto &arg_value : args) + PyTuple_SET_ITEM(result.ptr(), counter++, arg_value.release().ptr()); + return result; +} + +/// \ingroup annotations +/// Annotation for arguments +struct arg { + /// Constructs an argument with the name of the argument; if null or omitted, this is a positional argument. + constexpr explicit arg(const char *name = nullptr) : name(name), flag_noconvert(false), flag_none(true) { } + /// Assign a value to this argument + template arg_v operator=(T &&value) const; + /// Indicate that the type should not be converted in the type caster + arg &noconvert(bool flag = true) { flag_noconvert = flag; return *this; } + /// Indicates that the argument should/shouldn't allow None (e.g. for nullable pointer args) + arg &none(bool flag = true) { flag_none = flag; return *this; } + + const char *name; ///< If non-null, this is a named kwargs argument + bool flag_noconvert : 1; ///< If set, do not allow conversion (requires a supporting type caster!) + bool flag_none : 1; ///< If set (the default), allow None to be passed to this argument +}; + +/// \ingroup annotations +/// Annotation for arguments with values +struct arg_v : arg { +private: + template + arg_v(arg &&base, T &&x, const char *descr = nullptr) + : arg(base), + value(reinterpret_steal( + detail::make_caster::cast(x, return_value_policy::automatic, {}) + )), + descr(descr) +#if !defined(NDEBUG) + , type(type_id()) +#endif + { } + +public: + /// Direct construction with name, default, and description + template + arg_v(const char *name, T &&x, const char *descr = nullptr) + : arg_v(arg(name), std::forward(x), descr) { } + + /// Called internally when invoking `py::arg("a") = value` + template + arg_v(const arg &base, T &&x, const char *descr = nullptr) + : arg_v(arg(base), std::forward(x), descr) { } + + /// Same as `arg::noconvert()`, but returns *this as arg_v&, not arg& + arg_v &noconvert(bool flag = true) { arg::noconvert(flag); return *this; } + + /// Same as `arg::nonone()`, but returns *this as arg_v&, not arg& + arg_v &none(bool flag = true) { arg::none(flag); return *this; } + + /// The default value + object value; + /// The (optional) description of the default value + const char *descr; +#if !defined(NDEBUG) + /// The C++ type name of the default value (only available when compiled in debug mode) + std::string type; +#endif +}; + +template +arg_v arg::operator=(T &&value) const { return {std::move(*this), std::forward(value)}; } + +/// Alias for backward compatibility -- to be removed in version 2.0 +template using arg_t = arg_v; + +inline namespace literals { +/** \rst + String literal version of `arg` + \endrst */ +constexpr arg operator"" _a(const char *name, size_t) { return arg(name); } +} + +NAMESPACE_BEGIN(detail) + +// forward declaration (definition in attr.h) +struct function_record; + +/// Internal data associated with a single function call +struct function_call { + function_call(function_record &f, handle p); // Implementation in attr.h + + /// The function data: + const function_record &func; + + /// Arguments passed to the function: + std::vector args; + + /// The `convert` value the arguments should be loaded with + std::vector args_convert; + + /// The parent, if any + handle parent; +}; + + +/// Helper class which loads arguments for C++ functions called from Python +template +class argument_loader { + using indices = make_index_sequence; + + template using argument_is_args = std::is_same, args>; + template using argument_is_kwargs = std::is_same, kwargs>; + // Get args/kwargs argument positions relative to the end of the argument list: + static constexpr auto args_pos = constexpr_first() - (int) sizeof...(Args), + kwargs_pos = constexpr_first() - (int) sizeof...(Args); + + static constexpr bool args_kwargs_are_last = kwargs_pos >= - 1 && args_pos >= kwargs_pos - 1; + + static_assert(args_kwargs_are_last, "py::args/py::kwargs are only permitted as the last argument(s) of a function"); + +public: + static constexpr bool has_kwargs = kwargs_pos < 0; + static constexpr bool has_args = args_pos < 0; + + static PYBIND11_DESCR arg_names() { return detail::concat(make_caster::name()...); } + + bool load_args(function_call &call) { + return load_impl_sequence(call, indices{}); + } + + template + enable_if_t::value, Return> call(Func &&f) && { + return std::move(*this).template call_impl(std::forward(f), indices{}, Guard{}); + } + + template + enable_if_t::value, void_type> call(Func &&f) && { + std::move(*this).template call_impl(std::forward(f), indices{}, Guard{}); + return void_type(); + } + +private: + + static bool load_impl_sequence(function_call &, index_sequence<>) { return true; } + + template + bool load_impl_sequence(function_call &call, index_sequence) { + for (bool r : {std::get(argcasters).load(call.args[Is], call.args_convert[Is])...}) + if (!r) + return false; + return true; + } + + template + Return call_impl(Func &&f, index_sequence, Guard &&) { + return std::forward(f)(cast_op(std::move(std::get(argcasters)))...); + } + + std::tuple...> argcasters; +}; + +/// Helper class which collects only positional arguments for a Python function call. +/// A fancier version below can collect any argument, but this one is optimal for simple calls. +template +class simple_collector { +public: + template + explicit simple_collector(Ts &&...values) + : m_args(pybind11::make_tuple(std::forward(values)...)) { } + + const tuple &args() const & { return m_args; } + dict kwargs() const { return {}; } + + tuple args() && { return std::move(m_args); } + + /// Call a Python function and pass the collected arguments + object call(PyObject *ptr) const { + PyObject *result = PyObject_CallObject(ptr, m_args.ptr()); + if (!result) + throw error_already_set(); + return reinterpret_steal(result); + } + +private: + tuple m_args; +}; + +/// Helper class which collects positional, keyword, * and ** arguments for a Python function call +template +class unpacking_collector { +public: + template + explicit unpacking_collector(Ts &&...values) { + // Tuples aren't (easily) resizable so a list is needed for collection, + // but the actual function call strictly requires a tuple. + auto args_list = list(); + int _[] = { 0, (process(args_list, std::forward(values)), 0)... }; + ignore_unused(_); + + m_args = std::move(args_list); + } + + const tuple &args() const & { return m_args; } + const dict &kwargs() const & { return m_kwargs; } + + tuple args() && { return std::move(m_args); } + dict kwargs() && { return std::move(m_kwargs); } + + /// Call a Python function and pass the collected arguments + object call(PyObject *ptr) const { + PyObject *result = PyObject_Call(ptr, m_args.ptr(), m_kwargs.ptr()); + if (!result) + throw error_already_set(); + return reinterpret_steal(result); + } + +private: + template + void process(list &args_list, T &&x) { + auto o = reinterpret_steal(detail::make_caster::cast(std::forward(x), policy, {})); + if (!o) { +#if defined(NDEBUG) + argument_cast_error(); +#else + argument_cast_error(std::to_string(args_list.size()), type_id()); +#endif + } + args_list.append(o); + } + + void process(list &args_list, detail::args_proxy ap) { + for (const auto &a : ap) + args_list.append(a); + } + + void process(list &/*args_list*/, arg_v a) { + if (!a.name) +#if defined(NDEBUG) + nameless_argument_error(); +#else + nameless_argument_error(a.type); +#endif + + if (m_kwargs.contains(a.name)) { +#if defined(NDEBUG) + multiple_values_error(); +#else + multiple_values_error(a.name); +#endif + } + if (!a.value) { +#if defined(NDEBUG) + argument_cast_error(); +#else + argument_cast_error(a.name, a.type); +#endif + } + m_kwargs[a.name] = a.value; + } + + void process(list &/*args_list*/, detail::kwargs_proxy kp) { + if (!kp) + return; + for (const auto &k : reinterpret_borrow(kp)) { + if (m_kwargs.contains(k.first)) { +#if defined(NDEBUG) + multiple_values_error(); +#else + multiple_values_error(str(k.first)); +#endif + } + m_kwargs[k.first] = k.second; + } + } + + [[noreturn]] static void nameless_argument_error() { + throw type_error("Got kwargs without a name; only named arguments " + "may be passed via py::arg() to a python function call. " + "(compile in debug mode for details)"); + } + [[noreturn]] static void nameless_argument_error(std::string type) { + throw type_error("Got kwargs without a name of type '" + type + "'; only named " + "arguments may be passed via py::arg() to a python function call. "); + } + [[noreturn]] static void multiple_values_error() { + throw type_error("Got multiple values for keyword argument " + "(compile in debug mode for details)"); + } + + [[noreturn]] static void multiple_values_error(std::string name) { + throw type_error("Got multiple values for keyword argument '" + name + "'"); + } + + [[noreturn]] static void argument_cast_error() { + throw cast_error("Unable to convert call argument to Python object " + "(compile in debug mode for details)"); + } + + [[noreturn]] static void argument_cast_error(std::string name, std::string type) { + throw cast_error("Unable to convert call argument '" + name + + "' of type '" + type + "' to Python object"); + } + +private: + tuple m_args; + dict m_kwargs; +}; + +/// Collect only positional arguments for a Python function call +template ...>::value>> +simple_collector collect_arguments(Args &&...args) { + return simple_collector(std::forward(args)...); +} + +/// Collect all arguments, including keywords and unpacking (only instantiated when needed) +template ...>::value>> +unpacking_collector collect_arguments(Args &&...args) { + // Following argument order rules for generalized unpacking according to PEP 448 + static_assert( + constexpr_last() < constexpr_first() + && constexpr_last() < constexpr_first(), + "Invalid function call: positional args must precede keywords and ** unpacking; " + "* unpacking must precede ** unpacking" + ); + return unpacking_collector(std::forward(args)...); +} + +template +template +object object_api::operator()(Args &&...args) const { + return detail::collect_arguments(std::forward(args)...).call(derived().ptr()); +} + +template +template +object object_api::call(Args &&...args) const { + return operator()(std::forward(args)...); +} + +NAMESPACE_END(detail) + +#define PYBIND11_MAKE_OPAQUE(Type) \ + namespace pybind11 { namespace detail { \ + template<> class type_caster : public type_caster_base { }; \ + }} + +NAMESPACE_END(pybind11) diff --git a/ptocr/postprocess/lanms/include/pybind11/chrono.h b/ptocr/postprocess/lanms/include/pybind11/chrono.h new file mode 100644 index 0000000..8a41d08 --- /dev/null +++ b/ptocr/postprocess/lanms/include/pybind11/chrono.h @@ -0,0 +1,162 @@ +/* + pybind11/chrono.h: Transparent conversion between std::chrono and python's datetime + + Copyright (c) 2016 Trent Houliston and + Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "pybind11.h" +#include +#include +#include +#include + +// Backport the PyDateTime_DELTA functions from Python3.3 if required +#ifndef PyDateTime_DELTA_GET_DAYS +#define PyDateTime_DELTA_GET_DAYS(o) (((PyDateTime_Delta*)o)->days) +#endif +#ifndef PyDateTime_DELTA_GET_SECONDS +#define PyDateTime_DELTA_GET_SECONDS(o) (((PyDateTime_Delta*)o)->seconds) +#endif +#ifndef PyDateTime_DELTA_GET_MICROSECONDS +#define PyDateTime_DELTA_GET_MICROSECONDS(o) (((PyDateTime_Delta*)o)->microseconds) +#endif + +NAMESPACE_BEGIN(pybind11) +NAMESPACE_BEGIN(detail) + +template class duration_caster { +public: + typedef typename type::rep rep; + typedef typename type::period period; + + typedef std::chrono::duration> days; + + bool load(handle src, bool) { + using namespace std::chrono; + + // Lazy initialise the PyDateTime import + if (!PyDateTimeAPI) { PyDateTime_IMPORT; } + + if (!src) return false; + // If invoked with datetime.delta object + if (PyDelta_Check(src.ptr())) { + value = type(duration_cast>( + days(PyDateTime_DELTA_GET_DAYS(src.ptr())) + + seconds(PyDateTime_DELTA_GET_SECONDS(src.ptr())) + + microseconds(PyDateTime_DELTA_GET_MICROSECONDS(src.ptr())))); + return true; + } + // If invoked with a float we assume it is seconds and convert + else if (PyFloat_Check(src.ptr())) { + value = type(duration_cast>(duration(PyFloat_AsDouble(src.ptr())))); + return true; + } + else return false; + } + + // If this is a duration just return it back + static const std::chrono::duration& get_duration(const std::chrono::duration &src) { + return src; + } + + // If this is a time_point get the time_since_epoch + template static std::chrono::duration get_duration(const std::chrono::time_point> &src) { + return src.time_since_epoch(); + } + + static handle cast(const type &src, return_value_policy /* policy */, handle /* parent */) { + using namespace std::chrono; + + // Use overloaded function to get our duration from our source + // Works out if it is a duration or time_point and get the duration + auto d = get_duration(src); + + // Lazy initialise the PyDateTime import + if (!PyDateTimeAPI) { PyDateTime_IMPORT; } + + // Declare these special duration types so the conversions happen with the correct primitive types (int) + using dd_t = duration>; + using ss_t = duration>; + using us_t = duration; + + auto dd = duration_cast(d); + auto subd = d - dd; + auto ss = duration_cast(subd); + auto us = duration_cast(subd - ss); + return PyDelta_FromDSU(dd.count(), ss.count(), us.count()); + } + + PYBIND11_TYPE_CASTER(type, _("datetime.timedelta")); +}; + +// This is for casting times on the system clock into datetime.datetime instances +template class type_caster> { +public: + typedef std::chrono::time_point type; + bool load(handle src, bool) { + using namespace std::chrono; + + // Lazy initialise the PyDateTime import + if (!PyDateTimeAPI) { PyDateTime_IMPORT; } + + if (!src) return false; + if (PyDateTime_Check(src.ptr())) { + std::tm cal; + cal.tm_sec = PyDateTime_DATE_GET_SECOND(src.ptr()); + cal.tm_min = PyDateTime_DATE_GET_MINUTE(src.ptr()); + cal.tm_hour = PyDateTime_DATE_GET_HOUR(src.ptr()); + cal.tm_mday = PyDateTime_GET_DAY(src.ptr()); + cal.tm_mon = PyDateTime_GET_MONTH(src.ptr()) - 1; + cal.tm_year = PyDateTime_GET_YEAR(src.ptr()) - 1900; + cal.tm_isdst = -1; + + value = system_clock::from_time_t(std::mktime(&cal)) + microseconds(PyDateTime_DATE_GET_MICROSECOND(src.ptr())); + return true; + } + else return false; + } + + static handle cast(const std::chrono::time_point &src, return_value_policy /* policy */, handle /* parent */) { + using namespace std::chrono; + + // Lazy initialise the PyDateTime import + if (!PyDateTimeAPI) { PyDateTime_IMPORT; } + + std::time_t tt = system_clock::to_time_t(src); + // this function uses static memory so it's best to copy it out asap just in case + // otherwise other code that is using localtime may break this (not just python code) + std::tm localtime = *std::localtime(&tt); + + // Declare these special duration types so the conversions happen with the correct primitive types (int) + using us_t = duration; + + return PyDateTime_FromDateAndTime(localtime.tm_year + 1900, + localtime.tm_mon + 1, + localtime.tm_mday, + localtime.tm_hour, + localtime.tm_min, + localtime.tm_sec, + (duration_cast(src.time_since_epoch() % seconds(1))).count()); + } + PYBIND11_TYPE_CASTER(type, _("datetime.datetime")); +}; + +// Other clocks that are not the system clock are not measured as datetime.datetime objects +// since they are not measured on calendar time. So instead we just make them timedeltas +// Or if they have passed us a time as a float we convert that +template class type_caster> +: public duration_caster> { +}; + +template class type_caster> +: public duration_caster> { +}; + +NAMESPACE_END(detail) +NAMESPACE_END(pybind11) diff --git a/ptocr/postprocess/lanms/include/pybind11/class_support.h b/ptocr/postprocess/lanms/include/pybind11/class_support.h new file mode 100644 index 0000000..8e18c4c --- /dev/null +++ b/ptocr/postprocess/lanms/include/pybind11/class_support.h @@ -0,0 +1,603 @@ +/* + pybind11/class_support.h: Python C API implementation details for py::class_ + + Copyright (c) 2017 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "attr.h" + +NAMESPACE_BEGIN(pybind11) +NAMESPACE_BEGIN(detail) + +inline PyTypeObject *type_incref(PyTypeObject *type) { + Py_INCREF(type); + return type; +} + +#if !defined(PYPY_VERSION) + +/// `pybind11_static_property.__get__()`: Always pass the class instead of the instance. +extern "C" inline PyObject *pybind11_static_get(PyObject *self, PyObject * /*ob*/, PyObject *cls) { + return PyProperty_Type.tp_descr_get(self, cls, cls); +} + +/// `pybind11_static_property.__set__()`: Just like the above `__get__()`. +extern "C" inline int pybind11_static_set(PyObject *self, PyObject *obj, PyObject *value) { + PyObject *cls = PyType_Check(obj) ? obj : (PyObject *) Py_TYPE(obj); + return PyProperty_Type.tp_descr_set(self, cls, value); +} + +/** A `static_property` is the same as a `property` but the `__get__()` and `__set__()` + methods are modified to always use the object type instead of a concrete instance. + Return value: New reference. */ +inline PyTypeObject *make_static_property_type() { + constexpr auto *name = "pybind11_static_property"; + auto name_obj = reinterpret_steal(PYBIND11_FROM_STRING(name)); + + /* Danger zone: from now (and until PyType_Ready), make sure to + issue no Python C API calls which could potentially invoke the + garbage collector (the GC will call type_traverse(), which will in + turn find the newly constructed type in an invalid state) */ + auto heap_type = (PyHeapTypeObject *) PyType_Type.tp_alloc(&PyType_Type, 0); + if (!heap_type) + pybind11_fail("make_static_property_type(): error allocating type!"); + + heap_type->ht_name = name_obj.inc_ref().ptr(); +#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 3 + heap_type->ht_qualname = name_obj.inc_ref().ptr(); +#endif + + auto type = &heap_type->ht_type; + type->tp_name = name; + type->tp_base = type_incref(&PyProperty_Type); + type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE; + type->tp_descr_get = pybind11_static_get; + type->tp_descr_set = pybind11_static_set; + + if (PyType_Ready(type) < 0) + pybind11_fail("make_static_property_type(): failure in PyType_Ready()!"); + + setattr((PyObject *) type, "__module__", str("pybind11_builtins")); + + return type; +} + +#else // PYPY + +/** PyPy has some issues with the above C API, so we evaluate Python code instead. + This function will only be called once so performance isn't really a concern. + Return value: New reference. */ +inline PyTypeObject *make_static_property_type() { + auto d = dict(); + PyObject *result = PyRun_String(R"(\ + class pybind11_static_property(property): + def __get__(self, obj, cls): + return property.__get__(self, cls, cls) + + def __set__(self, obj, value): + cls = obj if isinstance(obj, type) else type(obj) + property.__set__(self, cls, value) + )", Py_file_input, d.ptr(), d.ptr() + ); + if (result == nullptr) + throw error_already_set(); + Py_DECREF(result); + return (PyTypeObject *) d["pybind11_static_property"].cast().release().ptr(); +} + +#endif // PYPY + +/** Types with static properties need to handle `Type.static_prop = x` in a specific way. + By default, Python replaces the `static_property` itself, but for wrapped C++ types + we need to call `static_property.__set__()` in order to propagate the new value to + the underlying C++ data structure. */ +extern "C" inline int pybind11_meta_setattro(PyObject* obj, PyObject* name, PyObject* value) { + // Use `_PyType_Lookup()` instead of `PyObject_GetAttr()` in order to get the raw + // descriptor (`property`) instead of calling `tp_descr_get` (`property.__get__()`). + PyObject *descr = _PyType_Lookup((PyTypeObject *) obj, name); + + // The following assignment combinations are possible: + // 1. `Type.static_prop = value` --> descr_set: `Type.static_prop.__set__(value)` + // 2. `Type.static_prop = other_static_prop` --> setattro: replace existing `static_prop` + // 3. `Type.regular_attribute = value` --> setattro: regular attribute assignment + const auto static_prop = (PyObject *) get_internals().static_property_type; + const auto call_descr_set = descr && PyObject_IsInstance(descr, static_prop) + && !PyObject_IsInstance(value, static_prop); + if (call_descr_set) { + // Call `static_property.__set__()` instead of replacing the `static_property`. +#if !defined(PYPY_VERSION) + return Py_TYPE(descr)->tp_descr_set(descr, obj, value); +#else + if (PyObject *result = PyObject_CallMethod(descr, "__set__", "OO", obj, value)) { + Py_DECREF(result); + return 0; + } else { + return -1; + } +#endif + } else { + // Replace existing attribute. + return PyType_Type.tp_setattro(obj, name, value); + } +} + +#if PY_MAJOR_VERSION >= 3 +/** + * Python 3's PyInstanceMethod_Type hides itself via its tp_descr_get, which prevents aliasing + * methods via cls.attr("m2") = cls.attr("m1"): instead the tp_descr_get returns a plain function, + * when called on a class, or a PyMethod, when called on an instance. Override that behaviour here + * to do a special case bypass for PyInstanceMethod_Types. + */ +extern "C" inline PyObject *pybind11_meta_getattro(PyObject *obj, PyObject *name) { + PyObject *descr = _PyType_Lookup((PyTypeObject *) obj, name); + if (descr && PyInstanceMethod_Check(descr)) { + Py_INCREF(descr); + return descr; + } + else { + return PyType_Type.tp_getattro(obj, name); + } +} +#endif + +/** This metaclass is assigned by default to all pybind11 types and is required in order + for static properties to function correctly. Users may override this using `py::metaclass`. + Return value: New reference. */ +inline PyTypeObject* make_default_metaclass() { + constexpr auto *name = "pybind11_type"; + auto name_obj = reinterpret_steal(PYBIND11_FROM_STRING(name)); + + /* Danger zone: from now (and until PyType_Ready), make sure to + issue no Python C API calls which could potentially invoke the + garbage collector (the GC will call type_traverse(), which will in + turn find the newly constructed type in an invalid state) */ + auto heap_type = (PyHeapTypeObject *) PyType_Type.tp_alloc(&PyType_Type, 0); + if (!heap_type) + pybind11_fail("make_default_metaclass(): error allocating metaclass!"); + + heap_type->ht_name = name_obj.inc_ref().ptr(); +#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 3 + heap_type->ht_qualname = name_obj.inc_ref().ptr(); +#endif + + auto type = &heap_type->ht_type; + type->tp_name = name; + type->tp_base = type_incref(&PyType_Type); + type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE; + + type->tp_setattro = pybind11_meta_setattro; +#if PY_MAJOR_VERSION >= 3 + type->tp_getattro = pybind11_meta_getattro; +#endif + + if (PyType_Ready(type) < 0) + pybind11_fail("make_default_metaclass(): failure in PyType_Ready()!"); + + setattr((PyObject *) type, "__module__", str("pybind11_builtins")); + + return type; +} + +/// For multiple inheritance types we need to recursively register/deregister base pointers for any +/// base classes with pointers that are difference from the instance value pointer so that we can +/// correctly recognize an offset base class pointer. This calls a function with any offset base ptrs. +inline void traverse_offset_bases(void *valueptr, const detail::type_info *tinfo, instance *self, + bool (*f)(void * /*parentptr*/, instance * /*self*/)) { + for (handle h : reinterpret_borrow(tinfo->type->tp_bases)) { + if (auto parent_tinfo = get_type_info((PyTypeObject *) h.ptr())) { + for (auto &c : parent_tinfo->implicit_casts) { + if (c.first == tinfo->cpptype) { + auto *parentptr = c.second(valueptr); + if (parentptr != valueptr) + f(parentptr, self); + traverse_offset_bases(parentptr, parent_tinfo, self, f); + break; + } + } + } + } +} + +inline bool register_instance_impl(void *ptr, instance *self) { + get_internals().registered_instances.emplace(ptr, self); + return true; // unused, but gives the same signature as the deregister func +} +inline bool deregister_instance_impl(void *ptr, instance *self) { + auto ®istered_instances = get_internals().registered_instances; + auto range = registered_instances.equal_range(ptr); + for (auto it = range.first; it != range.second; ++it) { + if (Py_TYPE(self) == Py_TYPE(it->second)) { + registered_instances.erase(it); + return true; + } + } + return false; +} + +inline void register_instance(instance *self, void *valptr, const type_info *tinfo) { + register_instance_impl(valptr, self); + if (!tinfo->simple_ancestors) + traverse_offset_bases(valptr, tinfo, self, register_instance_impl); +} + +inline bool deregister_instance(instance *self, void *valptr, const type_info *tinfo) { + bool ret = deregister_instance_impl(valptr, self); + if (!tinfo->simple_ancestors) + traverse_offset_bases(valptr, tinfo, self, deregister_instance_impl); + return ret; +} + +/// Instance creation function for all pybind11 types. It only allocates space for the C++ object +/// (or multiple objects, for Python-side inheritance from multiple pybind11 types), but doesn't +/// call the constructor -- an `__init__` function must do that (followed by an `init_instance` +/// to set up the holder and register the instance). +inline PyObject *make_new_instance(PyTypeObject *type, bool allocate_value /*= true (in cast.h)*/) { +#if defined(PYPY_VERSION) + // PyPy gets tp_basicsize wrong (issue 2482) under multiple inheritance when the first inherited + // object is a a plain Python type (i.e. not derived from an extension type). Fix it. + ssize_t instance_size = static_cast(sizeof(instance)); + if (type->tp_basicsize < instance_size) { + type->tp_basicsize = instance_size; + } +#endif + PyObject *self = type->tp_alloc(type, 0); + auto inst = reinterpret_cast(self); + // Allocate the value/holder internals: + inst->allocate_layout(); + + inst->owned = true; + // Allocate (if requested) the value pointers; otherwise leave them as nullptr + if (allocate_value) { + for (auto &v_h : values_and_holders(inst)) { + void *&vptr = v_h.value_ptr(); + vptr = v_h.type->operator_new(v_h.type->type_size); + } + } + + return self; +} + +/// Instance creation function for all pybind11 types. It only allocates space for the +/// C++ object, but doesn't call the constructor -- an `__init__` function must do that. +extern "C" inline PyObject *pybind11_object_new(PyTypeObject *type, PyObject *, PyObject *) { + return make_new_instance(type); +} + +/// An `__init__` function constructs the C++ object. Users should provide at least one +/// of these using `py::init` or directly with `.def(__init__, ...)`. Otherwise, the +/// following default function will be used which simply throws an exception. +extern "C" inline int pybind11_object_init(PyObject *self, PyObject *, PyObject *) { + PyTypeObject *type = Py_TYPE(self); + std::string msg; +#if defined(PYPY_VERSION) + msg += handle((PyObject *) type).attr("__module__").cast() + "."; +#endif + msg += type->tp_name; + msg += ": No constructor defined!"; + PyErr_SetString(PyExc_TypeError, msg.c_str()); + return -1; +} + +inline void add_patient(PyObject *nurse, PyObject *patient) { + auto &internals = get_internals(); + auto instance = reinterpret_cast(nurse); + instance->has_patients = true; + Py_INCREF(patient); + internals.patients[nurse].push_back(patient); +} + +inline void clear_patients(PyObject *self) { + auto instance = reinterpret_cast(self); + auto &internals = get_internals(); + auto pos = internals.patients.find(self); + assert(pos != internals.patients.end()); + // Clearing the patients can cause more Python code to run, which + // can invalidate the iterator. Extract the vector of patients + // from the unordered_map first. + auto patients = std::move(pos->second); + internals.patients.erase(pos); + instance->has_patients = false; + for (PyObject *&patient : patients) + Py_CLEAR(patient); +} + +/// Clears all internal data from the instance and removes it from registered instances in +/// preparation for deallocation. +inline void clear_instance(PyObject *self) { + auto instance = reinterpret_cast(self); + + // Deallocate any values/holders, if present: + for (auto &v_h : values_and_holders(instance)) { + if (v_h) { + + // We have to deregister before we call dealloc because, for virtual MI types, we still + // need to be able to get the parent pointers. + if (v_h.instance_registered() && !deregister_instance(instance, v_h.value_ptr(), v_h.type)) + pybind11_fail("pybind11_object_dealloc(): Tried to deallocate unregistered instance!"); + + if (instance->owned || v_h.holder_constructed()) + v_h.type->dealloc(v_h); + } + } + // Deallocate the value/holder layout internals: + instance->deallocate_layout(); + + if (instance->weakrefs) + PyObject_ClearWeakRefs(self); + + PyObject **dict_ptr = _PyObject_GetDictPtr(self); + if (dict_ptr) + Py_CLEAR(*dict_ptr); + + if (instance->has_patients) + clear_patients(self); +} + +/// Instance destructor function for all pybind11 types. It calls `type_info.dealloc` +/// to destroy the C++ object itself, while the rest is Python bookkeeping. +extern "C" inline void pybind11_object_dealloc(PyObject *self) { + clear_instance(self); + Py_TYPE(self)->tp_free(self); +} + +/** Create the type which can be used as a common base for all classes. This is + needed in order to satisfy Python's requirements for multiple inheritance. + Return value: New reference. */ +inline PyObject *make_object_base_type(PyTypeObject *metaclass) { + constexpr auto *name = "pybind11_object"; + auto name_obj = reinterpret_steal(PYBIND11_FROM_STRING(name)); + + /* Danger zone: from now (and until PyType_Ready), make sure to + issue no Python C API calls which could potentially invoke the + garbage collector (the GC will call type_traverse(), which will in + turn find the newly constructed type in an invalid state) */ + auto heap_type = (PyHeapTypeObject *) metaclass->tp_alloc(metaclass, 0); + if (!heap_type) + pybind11_fail("make_object_base_type(): error allocating type!"); + + heap_type->ht_name = name_obj.inc_ref().ptr(); +#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 3 + heap_type->ht_qualname = name_obj.inc_ref().ptr(); +#endif + + auto type = &heap_type->ht_type; + type->tp_name = name; + type->tp_base = type_incref(&PyBaseObject_Type); + type->tp_basicsize = static_cast(sizeof(instance)); + type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE; + + type->tp_new = pybind11_object_new; + type->tp_init = pybind11_object_init; + type->tp_dealloc = pybind11_object_dealloc; + + /* Support weak references (needed for the keep_alive feature) */ + type->tp_weaklistoffset = offsetof(instance, weakrefs); + + if (PyType_Ready(type) < 0) + pybind11_fail("PyType_Ready failed in make_object_base_type():" + error_string()); + + setattr((PyObject *) type, "__module__", str("pybind11_builtins")); + + assert(!PyType_HasFeature(type, Py_TPFLAGS_HAVE_GC)); + return (PyObject *) heap_type; +} + +/// dynamic_attr: Support for `d = instance.__dict__`. +extern "C" inline PyObject *pybind11_get_dict(PyObject *self, void *) { + PyObject *&dict = *_PyObject_GetDictPtr(self); + if (!dict) + dict = PyDict_New(); + Py_XINCREF(dict); + return dict; +} + +/// dynamic_attr: Support for `instance.__dict__ = dict()`. +extern "C" inline int pybind11_set_dict(PyObject *self, PyObject *new_dict, void *) { + if (!PyDict_Check(new_dict)) { + PyErr_Format(PyExc_TypeError, "__dict__ must be set to a dictionary, not a '%.200s'", + Py_TYPE(new_dict)->tp_name); + return -1; + } + PyObject *&dict = *_PyObject_GetDictPtr(self); + Py_INCREF(new_dict); + Py_CLEAR(dict); + dict = new_dict; + return 0; +} + +/// dynamic_attr: Allow the garbage collector to traverse the internal instance `__dict__`. +extern "C" inline int pybind11_traverse(PyObject *self, visitproc visit, void *arg) { + PyObject *&dict = *_PyObject_GetDictPtr(self); + Py_VISIT(dict); + return 0; +} + +/// dynamic_attr: Allow the GC to clear the dictionary. +extern "C" inline int pybind11_clear(PyObject *self) { + PyObject *&dict = *_PyObject_GetDictPtr(self); + Py_CLEAR(dict); + return 0; +} + +/// Give instances of this type a `__dict__` and opt into garbage collection. +inline void enable_dynamic_attributes(PyHeapTypeObject *heap_type) { + auto type = &heap_type->ht_type; +#if defined(PYPY_VERSION) + pybind11_fail(std::string(type->tp_name) + ": dynamic attributes are " + "currently not supported in " + "conjunction with PyPy!"); +#endif + type->tp_flags |= Py_TPFLAGS_HAVE_GC; + type->tp_dictoffset = type->tp_basicsize; // place dict at the end + type->tp_basicsize += (ssize_t)sizeof(PyObject *); // and allocate enough space for it + type->tp_traverse = pybind11_traverse; + type->tp_clear = pybind11_clear; + + static PyGetSetDef getset[] = { + {const_cast("__dict__"), pybind11_get_dict, pybind11_set_dict, nullptr, nullptr}, + {nullptr, nullptr, nullptr, nullptr, nullptr} + }; + type->tp_getset = getset; +} + +/// buffer_protocol: Fill in the view as specified by flags. +extern "C" inline int pybind11_getbuffer(PyObject *obj, Py_buffer *view, int flags) { + // Look for a `get_buffer` implementation in this type's info or any bases (following MRO). + type_info *tinfo = nullptr; + for (auto type : reinterpret_borrow(Py_TYPE(obj)->tp_mro)) { + tinfo = get_type_info((PyTypeObject *) type.ptr()); + if (tinfo && tinfo->get_buffer) + break; + } + if (view == nullptr || obj == nullptr || !tinfo || !tinfo->get_buffer) { + if (view) + view->obj = nullptr; + PyErr_SetString(PyExc_BufferError, "pybind11_getbuffer(): Internal error"); + return -1; + } + std::memset(view, 0, sizeof(Py_buffer)); + buffer_info *info = tinfo->get_buffer(obj, tinfo->get_buffer_data); + view->obj = obj; + view->ndim = 1; + view->internal = info; + view->buf = info->ptr; + view->itemsize = info->itemsize; + view->len = view->itemsize; + for (auto s : info->shape) + view->len *= s; + if ((flags & PyBUF_FORMAT) == PyBUF_FORMAT) + view->format = const_cast(info->format.c_str()); + if ((flags & PyBUF_STRIDES) == PyBUF_STRIDES) { + view->ndim = (int) info->ndim; + view->strides = &info->strides[0]; + view->shape = &info->shape[0]; + } + Py_INCREF(view->obj); + return 0; +} + +/// buffer_protocol: Release the resources of the buffer. +extern "C" inline void pybind11_releasebuffer(PyObject *, Py_buffer *view) { + delete (buffer_info *) view->internal; +} + +/// Give this type a buffer interface. +inline void enable_buffer_protocol(PyHeapTypeObject *heap_type) { + heap_type->ht_type.tp_as_buffer = &heap_type->as_buffer; +#if PY_MAJOR_VERSION < 3 + heap_type->ht_type.tp_flags |= Py_TPFLAGS_HAVE_NEWBUFFER; +#endif + + heap_type->as_buffer.bf_getbuffer = pybind11_getbuffer; + heap_type->as_buffer.bf_releasebuffer = pybind11_releasebuffer; +} + +/** Create a brand new Python type according to the `type_record` specification. + Return value: New reference. */ +inline PyObject* make_new_python_type(const type_record &rec) { + auto name = reinterpret_steal(PYBIND11_FROM_STRING(rec.name)); + +#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 3 + auto ht_qualname = name; + if (rec.scope && hasattr(rec.scope, "__qualname__")) { + ht_qualname = reinterpret_steal( + PyUnicode_FromFormat("%U.%U", rec.scope.attr("__qualname__").ptr(), name.ptr())); + } +#endif + + object module; + if (rec.scope) { + if (hasattr(rec.scope, "__module__")) + module = rec.scope.attr("__module__"); + else if (hasattr(rec.scope, "__name__")) + module = rec.scope.attr("__name__"); + } + +#if !defined(PYPY_VERSION) + const auto full_name = module ? str(module).cast() + "." + rec.name + : std::string(rec.name); +#else + const auto full_name = std::string(rec.name); +#endif + + char *tp_doc = nullptr; + if (rec.doc && options::show_user_defined_docstrings()) { + /* Allocate memory for docstring (using PyObject_MALLOC, since + Python will free this later on) */ + size_t size = strlen(rec.doc) + 1; + tp_doc = (char *) PyObject_MALLOC(size); + memcpy((void *) tp_doc, rec.doc, size); + } + + auto &internals = get_internals(); + auto bases = tuple(rec.bases); + auto base = (bases.size() == 0) ? internals.instance_base + : bases[0].ptr(); + + /* Danger zone: from now (and until PyType_Ready), make sure to + issue no Python C API calls which could potentially invoke the + garbage collector (the GC will call type_traverse(), which will in + turn find the newly constructed type in an invalid state) */ + auto metaclass = rec.metaclass.ptr() ? (PyTypeObject *) rec.metaclass.ptr() + : internals.default_metaclass; + + auto heap_type = (PyHeapTypeObject *) metaclass->tp_alloc(metaclass, 0); + if (!heap_type) + pybind11_fail(std::string(rec.name) + ": Unable to create type object!"); + + heap_type->ht_name = name.release().ptr(); +#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 3 + heap_type->ht_qualname = ht_qualname.release().ptr(); +#endif + + auto type = &heap_type->ht_type; + type->tp_name = strdup(full_name.c_str()); + type->tp_doc = tp_doc; + type->tp_base = type_incref((PyTypeObject *)base); + type->tp_basicsize = static_cast(sizeof(instance)); + if (bases.size() > 0) + type->tp_bases = bases.release().ptr(); + + /* Don't inherit base __init__ */ + type->tp_init = pybind11_object_init; + + /* Supported protocols */ + type->tp_as_number = &heap_type->as_number; + type->tp_as_sequence = &heap_type->as_sequence; + type->tp_as_mapping = &heap_type->as_mapping; + + /* Flags */ + type->tp_flags |= Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE; +#if PY_MAJOR_VERSION < 3 + type->tp_flags |= Py_TPFLAGS_CHECKTYPES; +#endif + + if (rec.dynamic_attr) + enable_dynamic_attributes(heap_type); + + if (rec.buffer_protocol) + enable_buffer_protocol(heap_type); + + if (PyType_Ready(type) < 0) + pybind11_fail(std::string(rec.name) + ": PyType_Ready failed (" + error_string() + ")!"); + + assert(rec.dynamic_attr ? PyType_HasFeature(type, Py_TPFLAGS_HAVE_GC) + : !PyType_HasFeature(type, Py_TPFLAGS_HAVE_GC)); + + /* Register type with the parent scope */ + if (rec.scope) + setattr(rec.scope, rec.name, (PyObject *) type); + + if (module) // Needed by pydoc + setattr((PyObject *) type, "__module__", module); + + return (PyObject *) type; +} + +NAMESPACE_END(detail) +NAMESPACE_END(pybind11) diff --git a/ptocr/postprocess/lanms/include/pybind11/common.h b/ptocr/postprocess/lanms/include/pybind11/common.h new file mode 100644 index 0000000..240f6d8 --- /dev/null +++ b/ptocr/postprocess/lanms/include/pybind11/common.h @@ -0,0 +1,857 @@ +/* + pybind11/common.h -- Basic macros + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#if !defined(NAMESPACE_BEGIN) +# define NAMESPACE_BEGIN(name) namespace name { +#endif +#if !defined(NAMESPACE_END) +# define NAMESPACE_END(name) } +#endif + +#if !defined(_MSC_VER) && !defined(__INTEL_COMPILER) +# if __cplusplus >= 201402L +# define PYBIND11_CPP14 +# if __cplusplus > 201402L /* Temporary: should be updated to >= the final C++17 value once known */ +# define PYBIND11_CPP17 +# endif +# endif +#elif defined(_MSC_VER) +// MSVC sets _MSVC_LANG rather than __cplusplus (supposedly until the standard is fully implemented) +# if _MSVC_LANG >= 201402L +# define PYBIND11_CPP14 +# if _MSVC_LANG > 201402L && _MSC_VER >= 1910 +# define PYBIND11_CPP17 +# endif +# endif +#endif + +// Compiler version assertions +#if defined(__INTEL_COMPILER) +# if __INTEL_COMPILER < 1500 +# error pybind11 requires Intel C++ compiler v15 or newer +# endif +#elif defined(__clang__) && !defined(__apple_build_version__) +# if __clang_major__ < 3 || (__clang_major__ == 3 && __clang_minor__ < 3) +# error pybind11 requires clang 3.3 or newer +# endif +#elif defined(__clang__) +// Apple changes clang version macros to its Xcode version; the first Xcode release based on +// (upstream) clang 3.3 was Xcode 5: +# if __clang_major__ < 5 +# error pybind11 requires Xcode/clang 5.0 or newer +# endif +#elif defined(__GNUG__) +# if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 8) +# error pybind11 requires gcc 4.8 or newer +# endif +#elif defined(_MSC_VER) +// Pybind hits various compiler bugs in 2015u2 and earlier, and also makes use of some stl features +// (e.g. std::negation) added in 2015u3: +# if _MSC_FULL_VER < 190024210 +# error pybind11 requires MSVC 2015 update 3 or newer +# endif +#endif + +#if !defined(PYBIND11_EXPORT) +# if defined(WIN32) || defined(_WIN32) +# define PYBIND11_EXPORT __declspec(dllexport) +# else +# define PYBIND11_EXPORT __attribute__ ((visibility("default"))) +# endif +#endif + +#if defined(_MSC_VER) +# define PYBIND11_NOINLINE __declspec(noinline) +#else +# define PYBIND11_NOINLINE __attribute__ ((noinline)) +#endif + +#if defined(PYBIND11_CPP14) +# define PYBIND11_DEPRECATED(reason) [[deprecated(reason)]] +#else +# define PYBIND11_DEPRECATED(reason) __attribute__((deprecated(reason))) +#endif + +#define PYBIND11_VERSION_MAJOR 2 +#define PYBIND11_VERSION_MINOR 2 +#define PYBIND11_VERSION_PATCH dev0 + +/// Include Python header, disable linking to pythonX_d.lib on Windows in debug mode +#if defined(_MSC_VER) +# if (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION < 4) +# define HAVE_ROUND 1 +# endif +# pragma warning(push) +# pragma warning(disable: 4510 4610 4512 4005) +# if defined(_DEBUG) +# define PYBIND11_DEBUG_MARKER +# undef _DEBUG +# endif +#endif + +#include +#include +#include + +#if defined(_WIN32) && (defined(min) || defined(max)) +# error Macro clash with min and max -- define NOMINMAX when compiling your program on Windows +#endif + +#if defined(isalnum) +# undef isalnum +# undef isalpha +# undef islower +# undef isspace +# undef isupper +# undef tolower +# undef toupper +#endif + +#if defined(_MSC_VER) +# if defined(PYBIND11_DEBUG_MARKER) +# define _DEBUG +# undef PYBIND11_DEBUG_MARKER +# endif +# pragma warning(pop) +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if PY_MAJOR_VERSION >= 3 /// Compatibility macros for various Python versions +#define PYBIND11_INSTANCE_METHOD_NEW(ptr, class_) PyInstanceMethod_New(ptr) +#define PYBIND11_INSTANCE_METHOD_CHECK PyInstanceMethod_Check +#define PYBIND11_INSTANCE_METHOD_GET_FUNCTION PyInstanceMethod_GET_FUNCTION +#define PYBIND11_BYTES_CHECK PyBytes_Check +#define PYBIND11_BYTES_FROM_STRING PyBytes_FromString +#define PYBIND11_BYTES_FROM_STRING_AND_SIZE PyBytes_FromStringAndSize +#define PYBIND11_BYTES_AS_STRING_AND_SIZE PyBytes_AsStringAndSize +#define PYBIND11_BYTES_AS_STRING PyBytes_AsString +#define PYBIND11_BYTES_SIZE PyBytes_Size +#define PYBIND11_LONG_CHECK(o) PyLong_Check(o) +#define PYBIND11_LONG_AS_LONGLONG(o) PyLong_AsLongLong(o) +#define PYBIND11_BYTES_NAME "bytes" +#define PYBIND11_STRING_NAME "str" +#define PYBIND11_SLICE_OBJECT PyObject +#define PYBIND11_FROM_STRING PyUnicode_FromString +#define PYBIND11_STR_TYPE ::pybind11::str +#define PYBIND11_BOOL_ATTR "__bool__" +#define PYBIND11_NB_BOOL(ptr) ((ptr)->nb_bool) +#define PYBIND11_PLUGIN_IMPL(name) \ + extern "C" PYBIND11_EXPORT PyObject *PyInit_##name() + +#else +#define PYBIND11_INSTANCE_METHOD_NEW(ptr, class_) PyMethod_New(ptr, nullptr, class_) +#define PYBIND11_INSTANCE_METHOD_CHECK PyMethod_Check +#define PYBIND11_INSTANCE_METHOD_GET_FUNCTION PyMethod_GET_FUNCTION +#define PYBIND11_BYTES_CHECK PyString_Check +#define PYBIND11_BYTES_FROM_STRING PyString_FromString +#define PYBIND11_BYTES_FROM_STRING_AND_SIZE PyString_FromStringAndSize +#define PYBIND11_BYTES_AS_STRING_AND_SIZE PyString_AsStringAndSize +#define PYBIND11_BYTES_AS_STRING PyString_AsString +#define PYBIND11_BYTES_SIZE PyString_Size +#define PYBIND11_LONG_CHECK(o) (PyInt_Check(o) || PyLong_Check(o)) +#define PYBIND11_LONG_AS_LONGLONG(o) (PyInt_Check(o) ? (long long) PyLong_AsLong(o) : PyLong_AsLongLong(o)) +#define PYBIND11_BYTES_NAME "str" +#define PYBIND11_STRING_NAME "unicode" +#define PYBIND11_SLICE_OBJECT PySliceObject +#define PYBIND11_FROM_STRING PyString_FromString +#define PYBIND11_STR_TYPE ::pybind11::bytes +#define PYBIND11_BOOL_ATTR "__nonzero__" +#define PYBIND11_NB_BOOL(ptr) ((ptr)->nb_nonzero) +#define PYBIND11_PLUGIN_IMPL(name) \ + static PyObject *pybind11_init_wrapper(); \ + extern "C" PYBIND11_EXPORT void init##name() { \ + (void)pybind11_init_wrapper(); \ + } \ + PyObject *pybind11_init_wrapper() +#endif + +#if PY_VERSION_HEX >= 0x03050000 && PY_VERSION_HEX < 0x03050200 +extern "C" { + struct _Py_atomic_address { void *value; }; + PyAPI_DATA(_Py_atomic_address) _PyThreadState_Current; +} +#endif + +#define PYBIND11_TRY_NEXT_OVERLOAD ((PyObject *) 1) // special failure return code +#define PYBIND11_STRINGIFY(x) #x +#define PYBIND11_TOSTRING(x) PYBIND11_STRINGIFY(x) +#define PYBIND11_INTERNALS_ID "__pybind11_" \ + PYBIND11_TOSTRING(PYBIND11_VERSION_MAJOR) "_" PYBIND11_TOSTRING(PYBIND11_VERSION_MINOR) "__" + +/** \rst + ***Deprecated in favor of PYBIND11_MODULE*** + + This macro creates the entry point that will be invoked when the Python interpreter + imports a plugin library. Please create a `module` in the function body and return + the pointer to its underlying Python object at the end. + + .. code-block:: cpp + + PYBIND11_PLUGIN(example) { + pybind11::module m("example", "pybind11 example plugin"); + /// Set up bindings here + return m.ptr(); + } +\endrst */ +#define PYBIND11_PLUGIN(name) \ + PYBIND11_DEPRECATED("PYBIND11_PLUGIN is deprecated, use PYBIND11_MODULE") \ + static PyObject *pybind11_init(); \ + PYBIND11_PLUGIN_IMPL(name) { \ + int major, minor; \ + if (sscanf(Py_GetVersion(), "%i.%i", &major, &minor) != 2) { \ + PyErr_SetString(PyExc_ImportError, "Can't parse Python version."); \ + return nullptr; \ + } else if (major != PY_MAJOR_VERSION || minor != PY_MINOR_VERSION) { \ + PyErr_Format(PyExc_ImportError, \ + "Python version mismatch: module was compiled for " \ + "version %i.%i, while the interpreter is running " \ + "version %i.%i.", PY_MAJOR_VERSION, PY_MINOR_VERSION, \ + major, minor); \ + return nullptr; \ + } \ + try { \ + return pybind11_init(); \ + } catch (pybind11::error_already_set &e) { \ + PyErr_SetString(PyExc_ImportError, e.what()); \ + return nullptr; \ + } catch (const std::exception &e) { \ + PyErr_SetString(PyExc_ImportError, e.what()); \ + return nullptr; \ + } \ + } \ + PyObject *pybind11_init() + +/** \rst + This macro creates the entry point that will be invoked when the Python interpreter + imports an extension module. The module name is given as the fist argument and it + should not be in quotes. The second macro argument defines a variable of type + `py::module` which can be used to initialize the module. + + .. code-block:: cpp + + PYBIND11_MODULE(example, m) { + m.doc() = "pybind11 example module"; + + // Add bindings here + m.def("foo", []() { + return "Hello, World!"; + }); + } +\endrst */ +#define PYBIND11_MODULE(name, variable) \ + static void pybind11_init_##name(pybind11::module &); \ + PYBIND11_PLUGIN_IMPL(name) { \ + int major, minor; \ + if (sscanf(Py_GetVersion(), "%i.%i", &major, &minor) != 2) { \ + PyErr_SetString(PyExc_ImportError, "Can't parse Python version."); \ + return nullptr; \ + } else if (major != PY_MAJOR_VERSION || minor != PY_MINOR_VERSION) { \ + PyErr_Format(PyExc_ImportError, \ + "Python version mismatch: module was compiled for " \ + "version %i.%i, while the interpreter is running " \ + "version %i.%i.", PY_MAJOR_VERSION, PY_MINOR_VERSION, \ + major, minor); \ + return nullptr; \ + } \ + auto m = pybind11::module(#name); \ + try { \ + pybind11_init_##name(m); \ + return m.ptr(); \ + } catch (pybind11::error_already_set &e) { \ + PyErr_SetString(PyExc_ImportError, e.what()); \ + return nullptr; \ + } catch (const std::exception &e) { \ + PyErr_SetString(PyExc_ImportError, e.what()); \ + return nullptr; \ + } \ + } \ + void pybind11_init_##name(pybind11::module &variable) + + +NAMESPACE_BEGIN(pybind11) + +using ssize_t = Py_ssize_t; +using size_t = std::size_t; + +/// Approach used to cast a previously unknown C++ instance into a Python object +enum class return_value_policy : uint8_t { + /** This is the default return value policy, which falls back to the policy + return_value_policy::take_ownership when the return value is a pointer. + Otherwise, it uses return_value::move or return_value::copy for rvalue + and lvalue references, respectively. See below for a description of what + all of these different policies do. */ + automatic = 0, + + /** As above, but use policy return_value_policy::reference when the return + value is a pointer. This is the default conversion policy for function + arguments when calling Python functions manually from C++ code (i.e. via + handle::operator()). You probably won't need to use this. */ + automatic_reference, + + /** Reference an existing object (i.e. do not create a new copy) and take + ownership. Python will call the destructor and delete operator when the + object’s reference count reaches zero. Undefined behavior ensues when + the C++ side does the same.. */ + take_ownership, + + /** Create a new copy of the returned object, which will be owned by + Python. This policy is comparably safe because the lifetimes of the two + instances are decoupled. */ + copy, + + /** Use std::move to move the return value contents into a new instance + that will be owned by Python. This policy is comparably safe because the + lifetimes of the two instances (move source and destination) are + decoupled. */ + move, + + /** Reference an existing object, but do not take ownership. The C++ side + is responsible for managing the object’s lifetime and deallocating it + when it is no longer used. Warning: undefined behavior will ensue when + the C++ side deletes an object that is still referenced and used by + Python. */ + reference, + + /** This policy only applies to methods and properties. It references the + object without taking ownership similar to the above + return_value_policy::reference policy. In contrast to that policy, the + function or property’s implicit this argument (called the parent) is + considered to be the the owner of the return value (the child). + pybind11 then couples the lifetime of the parent to the child via a + reference relationship that ensures that the parent cannot be garbage + collected while Python is still using the child. More advanced + variations of this scheme are also possible using combinations of + return_value_policy::reference and the keep_alive call policy */ + reference_internal +}; + +NAMESPACE_BEGIN(detail) + +inline static constexpr int log2(size_t n, int k = 0) { return (n <= 1) ? k : log2(n >> 1, k + 1); } + +// Returns the size as a multiple of sizeof(void *), rounded up. +inline static constexpr size_t size_in_ptrs(size_t s) { return 1 + ((s - 1) >> log2(sizeof(void *))); } + +/** + * The space to allocate for simple layout instance holders (see below) in multiple of the size of + * a pointer (e.g. 2 means 16 bytes on 64-bit architectures). The default is the minimum required + * to holder either a std::unique_ptr or std::shared_ptr (which is almost always + * sizeof(std::shared_ptr)). + */ +constexpr size_t instance_simple_holder_in_ptrs() { + static_assert(sizeof(std::shared_ptr) >= sizeof(std::unique_ptr), + "pybind assumes std::shared_ptrs are at least as big as std::unique_ptrs"); + return size_in_ptrs(sizeof(std::shared_ptr)); +} + +// Forward declarations +struct type_info; +struct value_and_holder; + +/// The 'instance' type which needs to be standard layout (need to be able to use 'offsetof') +struct instance { + PyObject_HEAD + /// Storage for pointers and holder; see simple_layout, below, for a description + union { + void *simple_value_holder[1 + instance_simple_holder_in_ptrs()]; + struct { + void **values_and_holders; + uint8_t *status; + } nonsimple; + }; + /// Weak references (needed for keep alive): + PyObject *weakrefs; + /// If true, the pointer is owned which means we're free to manage it with a holder. + bool owned : 1; + /** + * An instance has two possible value/holder layouts. + * + * Simple layout (when this flag is true), means the `simple_value_holder` is set with a pointer + * and the holder object governing that pointer, i.e. [val1*][holder]. This layout is applied + * whenever there is no python-side multiple inheritance of bound C++ types *and* the type's + * holder will fit in the default space (which is large enough to hold either a std::unique_ptr + * or std::shared_ptr). + * + * Non-simple layout applies when using custom holders that require more space than `shared_ptr` + * (which is typically the size of two pointers), or when multiple inheritance is used on the + * python side. Non-simple layout allocates the required amount of memory to have multiple + * bound C++ classes as parents. Under this layout, `nonsimple.values_and_holders` is set to a + * pointer to allocated space of the required space to hold a a sequence of value pointers and + * holders followed `status`, a set of bit flags (1 byte each), i.e. + * [val1*][holder1][val2*][holder2]...[bb...] where each [block] is rounded up to a multiple of + * `sizeof(void *)`. `nonsimple.holder_constructed` is, for convenience, a pointer to the + * beginning of the [bb...] block (but not independently allocated). + * + * Status bits indicate whether the associated holder is constructed (& + * status_holder_constructed) and whether the value pointer is registered (& + * status_instance_registered) in `registered_instances`. + */ + bool simple_layout : 1; + /// For simple layout, tracks whether the holder has been constructed + bool simple_holder_constructed : 1; + /// For simple layout, tracks whether the instance is registered in `registered_instances` + bool simple_instance_registered : 1; + /// If true, get_internals().patients has an entry for this object + bool has_patients : 1; + + /// Initializes all of the above type/values/holders data + void allocate_layout(); + + /// Destroys/deallocates all of the above + void deallocate_layout(); + + /// Returns the value_and_holder wrapper for the given type (or the first, if `find_type` + /// omitted) + value_and_holder get_value_and_holder(const type_info *find_type = nullptr); + + /// Bit values for the non-simple status flags + static constexpr uint8_t status_holder_constructed = 1; + static constexpr uint8_t status_instance_registered = 2; +}; + +static_assert(std::is_standard_layout::value, "Internal error: `pybind11::detail::instance` is not standard layout!"); + +struct overload_hash { + inline size_t operator()(const std::pair& v) const { + size_t value = std::hash()(v.first); + value ^= std::hash()(v.second) + 0x9e3779b9 + (value<<6) + (value>>2); + return value; + } +}; + +// Python loads modules by default with dlopen with the RTLD_LOCAL flag; under libc++ and possibly +// other stls, this means `typeid(A)` from one module won't equal `typeid(A)` from another module +// even when `A` is the same, non-hidden-visibility type (e.g. from a common include). Under +// stdlibc++, this doesn't happen: equality and the type_index hash are based on the type name, +// which works. If not under a known-good stl, provide our own name-based hasher and equality +// functions that use the type name. +#if defined(__GLIBCXX__) +inline bool same_type(const std::type_info &lhs, const std::type_info &rhs) { return lhs == rhs; } +using type_hash = std::hash; +using type_equal_to = std::equal_to; +#else +inline bool same_type(const std::type_info &lhs, const std::type_info &rhs) { + return lhs.name() == rhs.name() || + std::strcmp(lhs.name(), rhs.name()) == 0; +} +struct type_hash { + size_t operator()(const std::type_index &t) const { + size_t hash = 5381; + const char *ptr = t.name(); + while (auto c = static_cast(*ptr++)) + hash = (hash * 33) ^ c; + return hash; + } +}; +struct type_equal_to { + bool operator()(const std::type_index &lhs, const std::type_index &rhs) const { + return lhs.name() == rhs.name() || + std::strcmp(lhs.name(), rhs.name()) == 0; + } +}; +#endif + +template +using type_map = std::unordered_map; + +/// Internal data structure used to track registered instances and types +struct internals { + type_map registered_types_cpp; // std::type_index -> type_info + std::unordered_map> registered_types_py; // PyTypeObject* -> base type_info(s) + std::unordered_multimap registered_instances; // void * -> instance* + std::unordered_set, overload_hash> inactive_overload_cache; + type_map> direct_conversions; + std::unordered_map> patients; + std::forward_list registered_exception_translators; + std::unordered_map shared_data; // Custom data to be shared across extensions + std::vector loader_patient_stack; // Used by `loader_life_support` + PyTypeObject *static_property_type; + PyTypeObject *default_metaclass; + PyObject *instance_base; +#if defined(WITH_THREAD) + decltype(PyThread_create_key()) tstate = 0; // Usually an int but a long on Cygwin64 with Python 3.x + PyInterpreterState *istate = nullptr; +#endif +}; + +/// Return a reference to the current 'internals' information +inline internals &get_internals(); + +/// from __cpp_future__ import (convenient aliases from C++14/17) +#if defined(PYBIND11_CPP14) && (!defined(_MSC_VER) || _MSC_VER >= 1910) +using std::enable_if_t; +using std::conditional_t; +using std::remove_cv_t; +using std::remove_reference_t; +#else +template using enable_if_t = typename std::enable_if::type; +template using conditional_t = typename std::conditional::type; +template using remove_cv_t = typename std::remove_cv::type; +template using remove_reference_t = typename std::remove_reference::type; +#endif + +/// Index sequences +#if defined(PYBIND11_CPP14) +using std::index_sequence; +using std::make_index_sequence; +#else +template struct index_sequence { }; +template struct make_index_sequence_impl : make_index_sequence_impl { }; +template struct make_index_sequence_impl <0, S...> { typedef index_sequence type; }; +template using make_index_sequence = typename make_index_sequence_impl::type; +#endif + +/// Make an index sequence of the indices of true arguments +template struct select_indices_impl { using type = ISeq; }; +template struct select_indices_impl, I, B, Bs...> + : select_indices_impl, index_sequence>, I + 1, Bs...> {}; +template using select_indices = typename select_indices_impl, 0, Bs...>::type; + +/// Backports of std::bool_constant and std::negation to accomodate older compilers +template using bool_constant = std::integral_constant; +template struct negation : bool_constant { }; + +template struct void_t_impl { using type = void; }; +template using void_t = typename void_t_impl::type; + +/// Compile-time all/any/none of that check the boolean value of all template types +#ifdef __cpp_fold_expressions +template using all_of = bool_constant<(Ts::value && ...)>; +template using any_of = bool_constant<(Ts::value || ...)>; +#elif !defined(_MSC_VER) +template struct bools {}; +template using all_of = std::is_same< + bools, + bools>; +template using any_of = negation...>>; +#else +// MSVC has trouble with the above, but supports std::conjunction, which we can use instead (albeit +// at a slight loss of compilation efficiency). +template using all_of = std::conjunction; +template using any_of = std::disjunction; +#endif +template using none_of = negation>; + +template class... Predicates> using satisfies_all_of = all_of...>; +template class... Predicates> using satisfies_any_of = any_of...>; +template class... Predicates> using satisfies_none_of = none_of...>; + +/// Strip the class from a method type +template struct remove_class { }; +template struct remove_class { typedef R type(A...); }; +template struct remove_class { typedef R type(A...); }; + +/// Helper template to strip away type modifiers +template struct intrinsic_type { typedef T type; }; +template struct intrinsic_type { typedef typename intrinsic_type::type type; }; +template struct intrinsic_type { typedef typename intrinsic_type::type type; }; +template struct intrinsic_type { typedef typename intrinsic_type::type type; }; +template struct intrinsic_type { typedef typename intrinsic_type::type type; }; +template struct intrinsic_type { typedef typename intrinsic_type::type type; }; +template struct intrinsic_type { typedef typename intrinsic_type::type type; }; +template using intrinsic_t = typename intrinsic_type::type; + +/// Helper type to replace 'void' in some expressions +struct void_type { }; + +/// Helper template which holds a list of types +template struct type_list { }; + +/// Compile-time integer sum +#ifdef __cpp_fold_expressions +template constexpr size_t constexpr_sum(Ts... ns) { return (0 + ... + size_t{ns}); } +#else +constexpr size_t constexpr_sum() { return 0; } +template +constexpr size_t constexpr_sum(T n, Ts... ns) { return size_t{n} + constexpr_sum(ns...); } +#endif + +NAMESPACE_BEGIN(constexpr_impl) +/// Implementation details for constexpr functions +constexpr int first(int i) { return i; } +template +constexpr int first(int i, T v, Ts... vs) { return v ? i : first(i + 1, vs...); } + +constexpr int last(int /*i*/, int result) { return result; } +template +constexpr int last(int i, int result, T v, Ts... vs) { return last(i + 1, v ? i : result, vs...); } +NAMESPACE_END(constexpr_impl) + +/// Return the index of the first type in Ts which satisfies Predicate. Returns sizeof...(Ts) if +/// none match. +template class Predicate, typename... Ts> +constexpr int constexpr_first() { return constexpr_impl::first(0, Predicate::value...); } + +/// Return the index of the last type in Ts which satisfies Predicate, or -1 if none match. +template class Predicate, typename... Ts> +constexpr int constexpr_last() { return constexpr_impl::last(0, -1, Predicate::value...); } + +/// Return the Nth element from the parameter pack +template +struct pack_element { using type = typename pack_element::type; }; +template +struct pack_element<0, T, Ts...> { using type = T; }; + +/// Return the one and only type which matches the predicate, or Default if none match. +/// If more than one type matches the predicate, fail at compile-time. +template class Predicate, typename Default, typename... Ts> +struct exactly_one { + static constexpr auto found = constexpr_sum(Predicate::value...); + static_assert(found <= 1, "Found more than one type matching the predicate"); + + static constexpr auto index = found ? constexpr_first() : 0; + using type = conditional_t::type, Default>; +}; +template class P, typename Default> +struct exactly_one { using type = Default; }; + +template class Predicate, typename Default, typename... Ts> +using exactly_one_t = typename exactly_one::type; + +/// Defer the evaluation of type T until types Us are instantiated +template struct deferred_type { using type = T; }; +template using deferred_t = typename deferred_type::type; + +/// Like is_base_of, but requires a strict base (i.e. `is_strict_base_of::value == false`, +/// unlike `std::is_base_of`) +template using is_strict_base_of = bool_constant< + std::is_base_of::value && !std::is_same::value>; + +template class Base> +struct is_template_base_of_impl { + template static std::true_type check(Base *); + static std::false_type check(...); +}; + +/// Check if a template is the base of a type. For example: +/// `is_template_base_of` is true if `struct T : Base {}` where U can be anything +template class Base, typename T> +#if !defined(_MSC_VER) +using is_template_base_of = decltype(is_template_base_of_impl::check((remove_cv_t*)nullptr)); +#else // MSVC2015 has trouble with decltype in template aliases +struct is_template_base_of : decltype(is_template_base_of_impl::check((remove_cv_t*)nullptr)) { }; +#endif + +/// Check if T is an instantiation of the template `Class`. For example: +/// `is_instantiation` is true if `T == shared_ptr` where U can be anything. +template class Class, typename T> +struct is_instantiation : std::false_type { }; +template class Class, typename... Us> +struct is_instantiation> : std::true_type { }; + +/// Check if T is std::shared_ptr where U can be anything +template using is_shared_ptr = is_instantiation; + +/// Check if T looks like an input iterator +template struct is_input_iterator : std::false_type {}; +template +struct is_input_iterator()), decltype(++std::declval())>> + : std::true_type {}; + +/// Ignore that a variable is unused in compiler warnings +inline void ignore_unused(const int *) { } + +/// Apply a function over each element of a parameter pack +#ifdef __cpp_fold_expressions +#define PYBIND11_EXPAND_SIDE_EFFECTS(PATTERN) (((PATTERN), void()), ...) +#else +using expand_side_effects = bool[]; +#define PYBIND11_EXPAND_SIDE_EFFECTS(PATTERN) pybind11::detail::expand_side_effects{ ((PATTERN), void(), false)..., false } +#endif + +NAMESPACE_END(detail) + +/// Returns a named pointer that is shared among all extension modules (using the same +/// pybind11 version) running in the current interpreter. Names starting with underscores +/// are reserved for internal usage. Returns `nullptr` if no matching entry was found. +inline PYBIND11_NOINLINE void* get_shared_data(const std::string& name) { + auto& internals = detail::get_internals(); + auto it = internals.shared_data.find(name); + return it != internals.shared_data.end() ? it->second : nullptr; +} + +/// Set the shared data that can be later recovered by `get_shared_data()`. +inline PYBIND11_NOINLINE void *set_shared_data(const std::string& name, void *data) { + detail::get_internals().shared_data[name] = data; + return data; +} + +/// Returns a typed reference to a shared data entry (by using `get_shared_data()`) if +/// such entry exists. Otherwise, a new object of default-constructible type `T` is +/// added to the shared data under the given name and a reference to it is returned. +template T& get_or_create_shared_data(const std::string& name) { + auto& internals = detail::get_internals(); + auto it = internals.shared_data.find(name); + T* ptr = (T*) (it != internals.shared_data.end() ? it->second : nullptr); + if (!ptr) { + ptr = new T(); + internals.shared_data[name] = ptr; + } + return *ptr; +} + +/// C++ bindings of builtin Python exceptions +class builtin_exception : public std::runtime_error { +public: + using std::runtime_error::runtime_error; + /// Set the error using the Python C API + virtual void set_error() const = 0; +}; + +#define PYBIND11_RUNTIME_EXCEPTION(name, type) \ + class name : public builtin_exception { public: \ + using builtin_exception::builtin_exception; \ + name() : name("") { } \ + void set_error() const override { PyErr_SetString(type, what()); } \ + }; + +PYBIND11_RUNTIME_EXCEPTION(stop_iteration, PyExc_StopIteration) +PYBIND11_RUNTIME_EXCEPTION(index_error, PyExc_IndexError) +PYBIND11_RUNTIME_EXCEPTION(key_error, PyExc_KeyError) +PYBIND11_RUNTIME_EXCEPTION(value_error, PyExc_ValueError) +PYBIND11_RUNTIME_EXCEPTION(type_error, PyExc_TypeError) +PYBIND11_RUNTIME_EXCEPTION(cast_error, PyExc_RuntimeError) /// Thrown when pybind11::cast or handle::call fail due to a type casting error +PYBIND11_RUNTIME_EXCEPTION(reference_cast_error, PyExc_RuntimeError) /// Used internally + +[[noreturn]] PYBIND11_NOINLINE inline void pybind11_fail(const char *reason) { throw std::runtime_error(reason); } +[[noreturn]] PYBIND11_NOINLINE inline void pybind11_fail(const std::string &reason) { throw std::runtime_error(reason); } + +template struct format_descriptor { }; + +NAMESPACE_BEGIN(detail) +// Returns the index of the given type in the type char array below, and in the list in numpy.h +// The order here is: bool; 8 ints ((signed,unsigned)x(8,16,32,64)bits); float,double,long double; +// complex float,double,long double. Note that the long double types only participate when long +// double is actually longer than double (it isn't under MSVC). +// NB: not only the string below but also complex.h and numpy.h rely on this order. +template struct is_fmt_numeric { static constexpr bool value = false; }; +template struct is_fmt_numeric::value>> { + static constexpr bool value = true; + static constexpr int index = std::is_same::value ? 0 : 1 + ( + std::is_integral::value ? detail::log2(sizeof(T))*2 + std::is_unsigned::value : 8 + ( + std::is_same::value ? 1 : std::is_same::value ? 2 : 0)); +}; +NAMESPACE_END(detail) + +template struct format_descriptor::value>> { + static constexpr const char c = "?bBhHiIqQfdg"[detail::is_fmt_numeric::index]; + static constexpr const char value[2] = { c, '\0' }; + static std::string format() { return std::string(1, c); } +}; + +template constexpr const char format_descriptor< + T, detail::enable_if_t::value>>::value[2]; + +/// RAII wrapper that temporarily clears any Python error state +struct error_scope { + PyObject *type, *value, *trace; + error_scope() { PyErr_Fetch(&type, &value, &trace); } + ~error_scope() { PyErr_Restore(type, value, trace); } +}; + +/// Dummy destructor wrapper that can be used to expose classes with a private destructor +struct nodelete { template void operator()(T*) { } }; + +// overload_cast requires variable templates: C++14 +#if defined(PYBIND11_CPP14) +#define PYBIND11_OVERLOAD_CAST 1 + +NAMESPACE_BEGIN(detail) +template +struct overload_cast_impl { + template + constexpr auto operator()(Return (*pf)(Args...)) const noexcept + -> decltype(pf) { return pf; } + + template + constexpr auto operator()(Return (Class::*pmf)(Args...), std::false_type = {}) const noexcept + -> decltype(pmf) { return pmf; } + + template + constexpr auto operator()(Return (Class::*pmf)(Args...) const, std::true_type) const noexcept + -> decltype(pmf) { return pmf; } +}; +NAMESPACE_END(detail) + +/// Syntax sugar for resolving overloaded function pointers: +/// - regular: static_cast(&Class::func) +/// - sweet: overload_cast(&Class::func) +template +static constexpr detail::overload_cast_impl overload_cast = {}; +// MSVC 2015 only accepts this particular initialization syntax for this variable template. + +/// Const member function selector for overload_cast +/// - regular: static_cast(&Class::func) +/// - sweet: overload_cast(&Class::func, const_) +static constexpr auto const_ = std::true_type{}; + +#else // no overload_cast: providing something that static_assert-fails: +template struct overload_cast { + static_assert(detail::deferred_t::value, + "pybind11::overload_cast<...> requires compiling in C++14 mode"); +}; +#endif // overload_cast + +NAMESPACE_BEGIN(detail) + +// Adaptor for converting arbitrary container arguments into a vector; implicitly convertible from +// any standard container (or C-style array) supporting std::begin/std::end, any singleton +// arithmetic type (if T is arithmetic), or explicitly constructible from an iterator pair. +template +class any_container { + std::vector v; +public: + any_container() = default; + + // Can construct from a pair of iterators + template ::value>> + any_container(It first, It last) : v(first, last) { } + + // Implicit conversion constructor from any arbitrary container type with values convertible to T + template ())), T>::value>> + any_container(const Container &c) : any_container(std::begin(c), std::end(c)) { } + + // initializer_list's aren't deducible, so don't get matched by the above template; we need this + // to explicitly allow implicit conversion from one: + template ::value>> + any_container(const std::initializer_list &c) : any_container(c.begin(), c.end()) { } + + // Avoid copying if given an rvalue vector of the correct type. + any_container(std::vector &&v) : v(std::move(v)) { } + + // Moves the vector out of an rvalue any_container + operator std::vector &&() && { return std::move(v); } + + // Dereferencing obtains a reference to the underlying vector + std::vector &operator*() { return v; } + const std::vector &operator*() const { return v; } + + // -> lets you call methods on the underlying vector + std::vector *operator->() { return &v; } + const std::vector *operator->() const { return &v; } +}; + +NAMESPACE_END(detail) + + + +NAMESPACE_END(pybind11) diff --git a/ptocr/postprocess/lanms/include/pybind11/complex.h b/ptocr/postprocess/lanms/include/pybind11/complex.h new file mode 100644 index 0000000..7d422e2 --- /dev/null +++ b/ptocr/postprocess/lanms/include/pybind11/complex.h @@ -0,0 +1,61 @@ +/* + pybind11/complex.h: Complex number support + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "pybind11.h" +#include + +/// glibc defines I as a macro which breaks things, e.g., boost template names +#ifdef I +# undef I +#endif + +NAMESPACE_BEGIN(pybind11) + +template struct format_descriptor, detail::enable_if_t::value>> { + static constexpr const char c = format_descriptor::c; + static constexpr const char value[3] = { 'Z', c, '\0' }; + static std::string format() { return std::string(value); } +}; + +template constexpr const char format_descriptor< + std::complex, detail::enable_if_t::value>>::value[3]; + +NAMESPACE_BEGIN(detail) + +template struct is_fmt_numeric, detail::enable_if_t::value>> { + static constexpr bool value = true; + static constexpr int index = is_fmt_numeric::index + 3; +}; + +template class type_caster> { +public: + bool load(handle src, bool convert) { + if (!src) + return false; + if (!convert && !PyComplex_Check(src.ptr())) + return false; + Py_complex result = PyComplex_AsCComplex(src.ptr()); + if (result.real == -1.0 && PyErr_Occurred()) { + PyErr_Clear(); + return false; + } + value = std::complex((T) result.real, (T) result.imag); + return true; + } + + static handle cast(const std::complex &src, return_value_policy /* policy */, handle /* parent */) { + return PyComplex_FromDoubles((double) src.real(), (double) src.imag()); + } + + PYBIND11_TYPE_CASTER(std::complex, _("complex")); +}; +NAMESPACE_END(detail) +NAMESPACE_END(pybind11) diff --git a/ptocr/postprocess/lanms/include/pybind11/descr.h b/ptocr/postprocess/lanms/include/pybind11/descr.h new file mode 100644 index 0000000..23a099c --- /dev/null +++ b/ptocr/postprocess/lanms/include/pybind11/descr.h @@ -0,0 +1,185 @@ +/* + pybind11/descr.h: Helper type for concatenating type signatures + either at runtime (C++11) or compile time (C++14) + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "common.h" + +NAMESPACE_BEGIN(pybind11) +NAMESPACE_BEGIN(detail) + +/* Concatenate type signatures at compile time using C++14 */ +#if defined(PYBIND11_CPP14) && !defined(_MSC_VER) +#define PYBIND11_CONSTEXPR_DESCR + +template class descr { + template friend class descr; +public: + constexpr descr(char const (&text) [Size1+1], const std::type_info * const (&types)[Size2+1]) + : descr(text, types, + make_index_sequence(), + make_index_sequence()) { } + + constexpr const char *text() const { return m_text; } + constexpr const std::type_info * const * types() const { return m_types; } + + template + constexpr descr operator+(const descr &other) const { + return concat(other, + make_index_sequence(), + make_index_sequence(), + make_index_sequence(), + make_index_sequence()); + } + +protected: + template + constexpr descr( + char const (&text) [Size1+1], + const std::type_info * const (&types) [Size2+1], + index_sequence, index_sequence) + : m_text{text[Indices1]..., '\0'}, + m_types{types[Indices2]..., nullptr } {} + + template + constexpr descr + concat(const descr &other, + index_sequence, index_sequence, + index_sequence, index_sequence) const { + return descr( + { m_text[Indices1]..., other.m_text[OtherIndices1]..., '\0' }, + { m_types[Indices2]..., other.m_types[OtherIndices2]..., nullptr } + ); + } + +protected: + char m_text[Size1 + 1]; + const std::type_info * m_types[Size2 + 1]; +}; + +template constexpr descr _(char const(&text)[Size]) { + return descr(text, { nullptr }); +} + +template struct int_to_str : int_to_str { }; +template struct int_to_str<0, Digits...> { + static constexpr auto digits = descr({ ('0' + Digits)..., '\0' }, { nullptr }); +}; + +// Ternary description (like std::conditional) +template +constexpr enable_if_t> _(char const(&text1)[Size1], char const(&)[Size2]) { + return _(text1); +} +template +constexpr enable_if_t> _(char const(&)[Size1], char const(&text2)[Size2]) { + return _(text2); +} +template +constexpr enable_if_t> _(descr d, descr) { return d; } +template +constexpr enable_if_t> _(descr, descr d) { return d; } + +template auto constexpr _() -> decltype(int_to_str::digits) { + return int_to_str::digits; +} + +template constexpr descr<1, 1> _() { + return descr<1, 1>({ '%', '\0' }, { &typeid(Type), nullptr }); +} + +inline constexpr descr<0, 0> concat() { return _(""); } +template auto constexpr concat(descr descr) { return descr; } +template auto constexpr concat(descr descr, Args&&... args) { return descr + _(", ") + concat(args...); } +template auto constexpr type_descr(descr descr) { return _("{") + descr + _("}"); } + +#define PYBIND11_DESCR constexpr auto + +#else /* Simpler C++11 implementation based on run-time memory allocation and copying */ + +class descr { +public: + PYBIND11_NOINLINE descr(const char *text, const std::type_info * const * types) { + size_t nChars = len(text), nTypes = len(types); + m_text = new char[nChars]; + m_types = new const std::type_info *[nTypes]; + memcpy(m_text, text, nChars * sizeof(char)); + memcpy(m_types, types, nTypes * sizeof(const std::type_info *)); + } + + PYBIND11_NOINLINE descr operator+(descr &&d2) && { + descr r; + + size_t nChars1 = len(m_text), nTypes1 = len(m_types); + size_t nChars2 = len(d2.m_text), nTypes2 = len(d2.m_types); + + r.m_text = new char[nChars1 + nChars2 - 1]; + r.m_types = new const std::type_info *[nTypes1 + nTypes2 - 1]; + memcpy(r.m_text, m_text, (nChars1-1) * sizeof(char)); + memcpy(r.m_text + nChars1 - 1, d2.m_text, nChars2 * sizeof(char)); + memcpy(r.m_types, m_types, (nTypes1-1) * sizeof(std::type_info *)); + memcpy(r.m_types + nTypes1 - 1, d2.m_types, nTypes2 * sizeof(std::type_info *)); + + delete[] m_text; delete[] m_types; + delete[] d2.m_text; delete[] d2.m_types; + + return r; + } + + char *text() { return m_text; } + const std::type_info * * types() { return m_types; } + +protected: + PYBIND11_NOINLINE descr() { } + + template static size_t len(const T *ptr) { // return length including null termination + const T *it = ptr; + while (*it++ != (T) 0) + ; + return static_cast(it - ptr); + } + + const std::type_info **m_types = nullptr; + char *m_text = nullptr; +}; + +/* The 'PYBIND11_NOINLINE inline' combinations below are intentional to get the desired linkage while producing as little object code as possible */ + +PYBIND11_NOINLINE inline descr _(const char *text) { + const std::type_info *types[1] = { nullptr }; + return descr(text, types); +} + +template PYBIND11_NOINLINE enable_if_t _(const char *text1, const char *) { return _(text1); } +template PYBIND11_NOINLINE enable_if_t _(char const *, const char *text2) { return _(text2); } +template PYBIND11_NOINLINE enable_if_t _(descr d, descr) { return d; } +template PYBIND11_NOINLINE enable_if_t _(descr, descr d) { return d; } + +template PYBIND11_NOINLINE descr _() { + const std::type_info *types[2] = { &typeid(Type), nullptr }; + return descr("%", types); +} + +template PYBIND11_NOINLINE descr _() { + const std::type_info *types[1] = { nullptr }; + return descr(std::to_string(Size).c_str(), types); +} + +PYBIND11_NOINLINE inline descr concat() { return _(""); } +PYBIND11_NOINLINE inline descr concat(descr &&d) { return d; } +template PYBIND11_NOINLINE descr concat(descr &&d, Args&&... args) { return std::move(d) + _(", ") + concat(std::forward(args)...); } +PYBIND11_NOINLINE inline descr type_descr(descr&& d) { return _("{") + std::move(d) + _("}"); } + +#define PYBIND11_DESCR ::pybind11::detail::descr +#endif + +NAMESPACE_END(detail) +NAMESPACE_END(pybind11) diff --git a/ptocr/postprocess/lanms/include/pybind11/eigen.h b/ptocr/postprocess/lanms/include/pybind11/eigen.h new file mode 100644 index 0000000..fc07051 --- /dev/null +++ b/ptocr/postprocess/lanms/include/pybind11/eigen.h @@ -0,0 +1,610 @@ +/* + pybind11/eigen.h: Transparent conversion for dense and sparse Eigen matrices + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "numpy.h" + +#if defined(__INTEL_COMPILER) +# pragma warning(disable: 1682) // implicit conversion of a 64-bit integral type to a smaller integral type (potential portability problem) +#elif defined(__GNUG__) || defined(__clang__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wconversion" +# pragma GCC diagnostic ignored "-Wdeprecated-declarations" +# if __GNUC__ >= 7 +# pragma GCC diagnostic ignored "-Wint-in-bool-context" +# endif +#endif + +#include +#include + +#if defined(_MSC_VER) +# pragma warning(push) +# pragma warning(disable: 4127) // warning C4127: Conditional expression is constant +#endif + +// Eigen prior to 3.2.7 doesn't have proper move constructors--but worse, some classes get implicit +// move constructors that break things. We could detect this an explicitly copy, but an extra copy +// of matrices seems highly undesirable. +static_assert(EIGEN_VERSION_AT_LEAST(3,2,7), "Eigen support in pybind11 requires Eigen >= 3.2.7"); + +NAMESPACE_BEGIN(pybind11) + +// Provide a convenience alias for easier pass-by-ref usage with fully dynamic strides: +using EigenDStride = Eigen::Stride; +template using EigenDRef = Eigen::Ref; +template using EigenDMap = Eigen::Map; + +NAMESPACE_BEGIN(detail) + +#if EIGEN_VERSION_AT_LEAST(3,3,0) +using EigenIndex = Eigen::Index; +#else +using EigenIndex = EIGEN_DEFAULT_DENSE_INDEX_TYPE; +#endif + +// Matches Eigen::Map, Eigen::Ref, blocks, etc: +template using is_eigen_dense_map = all_of, std::is_base_of, T>>; +template using is_eigen_mutable_map = std::is_base_of, T>; +template using is_eigen_dense_plain = all_of>, is_template_base_of>; +template using is_eigen_sparse = is_template_base_of; +// Test for objects inheriting from EigenBase that aren't captured by the above. This +// basically covers anything that can be assigned to a dense matrix but that don't have a typical +// matrix data layout that can be copied from their .data(). For example, DiagonalMatrix and +// SelfAdjointView fall into this category. +template using is_eigen_other = all_of< + is_template_base_of, + negation, is_eigen_dense_plain, is_eigen_sparse>> +>; + +// Captures numpy/eigen conformability status (returned by EigenProps::conformable()): +template struct EigenConformable { + bool conformable = false; + EigenIndex rows = 0, cols = 0; + EigenDStride stride{0, 0}; // Only valid if negativestrides is false! + bool negativestrides = false; // If true, do not use stride! + + EigenConformable(bool fits = false) : conformable{fits} {} + // Matrix type: + EigenConformable(EigenIndex r, EigenIndex c, + EigenIndex rstride, EigenIndex cstride) : + conformable{true}, rows{r}, cols{c} { + // TODO: when Eigen bug #747 is fixed, remove the tests for non-negativity. http://eigen.tuxfamily.org/bz/show_bug.cgi?id=747 + if (rstride < 0 || cstride < 0) { + negativestrides = true; + } else { + stride = {EigenRowMajor ? rstride : cstride /* outer stride */, + EigenRowMajor ? cstride : rstride /* inner stride */ }; + } + } + // Vector type: + EigenConformable(EigenIndex r, EigenIndex c, EigenIndex stride) + : EigenConformable(r, c, r == 1 ? c*stride : stride, c == 1 ? r : r*stride) {} + + template bool stride_compatible() const { + // To have compatible strides, we need (on both dimensions) one of fully dynamic strides, + // matching strides, or a dimension size of 1 (in which case the stride value is irrelevant) + return + !negativestrides && + (props::inner_stride == Eigen::Dynamic || props::inner_stride == stride.inner() || + (EigenRowMajor ? cols : rows) == 1) && + (props::outer_stride == Eigen::Dynamic || props::outer_stride == stride.outer() || + (EigenRowMajor ? rows : cols) == 1); + } + operator bool() const { return conformable; } +}; + +template struct eigen_extract_stride { using type = Type; }; +template +struct eigen_extract_stride> { using type = StrideType; }; +template +struct eigen_extract_stride> { using type = StrideType; }; + +// Helper struct for extracting information from an Eigen type +template struct EigenProps { + using Type = Type_; + using Scalar = typename Type::Scalar; + using StrideType = typename eigen_extract_stride::type; + static constexpr EigenIndex + rows = Type::RowsAtCompileTime, + cols = Type::ColsAtCompileTime, + size = Type::SizeAtCompileTime; + static constexpr bool + row_major = Type::IsRowMajor, + vector = Type::IsVectorAtCompileTime, // At least one dimension has fixed size 1 + fixed_rows = rows != Eigen::Dynamic, + fixed_cols = cols != Eigen::Dynamic, + fixed = size != Eigen::Dynamic, // Fully-fixed size + dynamic = !fixed_rows && !fixed_cols; // Fully-dynamic size + + template using if_zero = std::integral_constant; + static constexpr EigenIndex inner_stride = if_zero::value, + outer_stride = if_zero::value; + static constexpr bool dynamic_stride = inner_stride == Eigen::Dynamic && outer_stride == Eigen::Dynamic; + static constexpr bool requires_row_major = !dynamic_stride && !vector && (row_major ? inner_stride : outer_stride) == 1; + static constexpr bool requires_col_major = !dynamic_stride && !vector && (row_major ? outer_stride : inner_stride) == 1; + + // Takes an input array and determines whether we can make it fit into the Eigen type. If + // the array is a vector, we attempt to fit it into either an Eigen 1xN or Nx1 vector + // (preferring the latter if it will fit in either, i.e. for a fully dynamic matrix type). + static EigenConformable conformable(const array &a) { + const auto dims = a.ndim(); + if (dims < 1 || dims > 2) + return false; + + if (dims == 2) { // Matrix type: require exact match (or dynamic) + + EigenIndex + np_rows = a.shape(0), + np_cols = a.shape(1), + np_rstride = a.strides(0) / static_cast(sizeof(Scalar)), + np_cstride = a.strides(1) / static_cast(sizeof(Scalar)); + if ((fixed_rows && np_rows != rows) || (fixed_cols && np_cols != cols)) + return false; + + return {np_rows, np_cols, np_rstride, np_cstride}; + } + + // Otherwise we're storing an n-vector. Only one of the strides will be used, but whichever + // is used, we want the (single) numpy stride value. + const EigenIndex n = a.shape(0), + stride = a.strides(0) / static_cast(sizeof(Scalar)); + + if (vector) { // Eigen type is a compile-time vector + if (fixed && size != n) + return false; // Vector size mismatch + return {rows == 1 ? 1 : n, cols == 1 ? 1 : n, stride}; + } + else if (fixed) { + // The type has a fixed size, but is not a vector: abort + return false; + } + else if (fixed_cols) { + // Since this isn't a vector, cols must be != 1. We allow this only if it exactly + // equals the number of elements (rows is Dynamic, and so 1 row is allowed). + if (cols != n) return false; + return {1, n, stride}; + } + else { + // Otherwise it's either fully dynamic, or column dynamic; both become a column vector + if (fixed_rows && rows != n) return false; + return {n, 1, stride}; + } + } + + static PYBIND11_DESCR descriptor() { + constexpr bool show_writeable = is_eigen_dense_map::value && is_eigen_mutable_map::value; + constexpr bool show_order = is_eigen_dense_map::value; + constexpr bool show_c_contiguous = show_order && requires_row_major; + constexpr bool show_f_contiguous = !show_c_contiguous && show_order && requires_col_major; + + return type_descr(_("numpy.ndarray[") + npy_format_descriptor::name() + + _("[") + _(_<(size_t) rows>(), _("m")) + + _(", ") + _(_<(size_t) cols>(), _("n")) + + _("]") + + // For a reference type (e.g. Ref) we have other constraints that might need to be + // satisfied: writeable=True (for a mutable reference), and, depending on the map's stride + // options, possibly f_contiguous or c_contiguous. We include them in the descriptor output + // to provide some hint as to why a TypeError is occurring (otherwise it can be confusing to + // see that a function accepts a 'numpy.ndarray[float64[3,2]]' and an error message that you + // *gave* a numpy.ndarray of the right type and dimensions. + _(", flags.writeable", "") + + _(", flags.c_contiguous", "") + + _(", flags.f_contiguous", "") + + _("]") + ); + } +}; + +// Casts an Eigen type to numpy array. If given a base, the numpy array references the src data, +// otherwise it'll make a copy. writeable lets you turn off the writeable flag for the array. +template handle eigen_array_cast(typename props::Type const &src, handle base = handle(), bool writeable = true) { + constexpr ssize_t elem_size = sizeof(typename props::Scalar); + array a; + if (props::vector) + a = array({ src.size() }, { elem_size * src.innerStride() }, src.data(), base); + else + a = array({ src.rows(), src.cols() }, { elem_size * src.rowStride(), elem_size * src.colStride() }, + src.data(), base); + + if (!writeable) + array_proxy(a.ptr())->flags &= ~detail::npy_api::NPY_ARRAY_WRITEABLE_; + + return a.release(); +} + +// Takes an lvalue ref to some Eigen type and a (python) base object, creating a numpy array that +// reference the Eigen object's data with `base` as the python-registered base class (if omitted, +// the base will be set to None, and lifetime management is up to the caller). The numpy array is +// non-writeable if the given type is const. +template +handle eigen_ref_array(Type &src, handle parent = none()) { + // none here is to get past array's should-we-copy detection, which currently always + // copies when there is no base. Setting the base to None should be harmless. + return eigen_array_cast(src, parent, !std::is_const::value); +} + +// Takes a pointer to some dense, plain Eigen type, builds a capsule around it, then returns a numpy +// array that references the encapsulated data with a python-side reference to the capsule to tie +// its destruction to that of any dependent python objects. Const-ness is determined by whether or +// not the Type of the pointer given is const. +template ::value>> +handle eigen_encapsulate(Type *src) { + capsule base(src, [](void *o) { delete static_cast(o); }); + return eigen_ref_array(*src, base); +} + +// Type caster for regular, dense matrix types (e.g. MatrixXd), but not maps/refs/etc. of dense +// types. +template +struct type_caster::value>> { + using Scalar = typename Type::Scalar; + using props = EigenProps; + + bool load(handle src, bool convert) { + // If we're in no-convert mode, only load if given an array of the correct type + if (!convert && !isinstance>(src)) + return false; + + // Coerce into an array, but don't do type conversion yet; the copy below handles it. + auto buf = array::ensure(src); + + if (!buf) + return false; + + auto dims = buf.ndim(); + if (dims < 1 || dims > 2) + return false; + + auto fits = props::conformable(buf); + if (!fits) + return false; + + // Allocate the new type, then build a numpy reference into it + value = Type(fits.rows, fits.cols); + auto ref = reinterpret_steal(eigen_ref_array(value)); + if (dims == 1) ref = ref.squeeze(); + + int result = detail::npy_api::get().PyArray_CopyInto_(ref.ptr(), buf.ptr()); + + if (result < 0) { // Copy failed! + PyErr_Clear(); + return false; + } + + return true; + } + +private: + + // Cast implementation + template + static handle cast_impl(CType *src, return_value_policy policy, handle parent) { + switch (policy) { + case return_value_policy::take_ownership: + case return_value_policy::automatic: + return eigen_encapsulate(src); + case return_value_policy::move: + return eigen_encapsulate(new CType(std::move(*src))); + case return_value_policy::copy: + return eigen_array_cast(*src); + case return_value_policy::reference: + case return_value_policy::automatic_reference: + return eigen_ref_array(*src); + case return_value_policy::reference_internal: + return eigen_ref_array(*src, parent); + default: + throw cast_error("unhandled return_value_policy: should not happen!"); + }; + } + +public: + + // Normal returned non-reference, non-const value: + static handle cast(Type &&src, return_value_policy /* policy */, handle parent) { + return cast_impl(&src, return_value_policy::move, parent); + } + // If you return a non-reference const, we mark the numpy array readonly: + static handle cast(const Type &&src, return_value_policy /* policy */, handle parent) { + return cast_impl(&src, return_value_policy::move, parent); + } + // lvalue reference return; default (automatic) becomes copy + static handle cast(Type &src, return_value_policy policy, handle parent) { + if (policy == return_value_policy::automatic || policy == return_value_policy::automatic_reference) + policy = return_value_policy::copy; + return cast_impl(&src, policy, parent); + } + // const lvalue reference return; default (automatic) becomes copy + static handle cast(const Type &src, return_value_policy policy, handle parent) { + if (policy == return_value_policy::automatic || policy == return_value_policy::automatic_reference) + policy = return_value_policy::copy; + return cast(&src, policy, parent); + } + // non-const pointer return + static handle cast(Type *src, return_value_policy policy, handle parent) { + return cast_impl(src, policy, parent); + } + // const pointer return + static handle cast(const Type *src, return_value_policy policy, handle parent) { + return cast_impl(src, policy, parent); + } + + static PYBIND11_DESCR name() { return props::descriptor(); } + + operator Type*() { return &value; } + operator Type&() { return value; } + operator Type&&() && { return std::move(value); } + template using cast_op_type = movable_cast_op_type; + +private: + Type value; +}; + +// Eigen Ref/Map classes have slightly different policy requirements, meaning we don't want to force +// `move` when a Ref/Map rvalue is returned; we treat Ref<> sort of like a pointer (we care about +// the underlying data, not the outer shell). +template +struct return_value_policy_override::value>> { + static return_value_policy policy(return_value_policy p) { return p; } +}; + +// Base class for casting reference/map/block/etc. objects back to python. +template struct eigen_map_caster { +private: + using props = EigenProps; + +public: + + // Directly referencing a ref/map's data is a bit dangerous (whatever the map/ref points to has + // to stay around), but we'll allow it under the assumption that you know what you're doing (and + // have an appropriate keep_alive in place). We return a numpy array pointing directly at the + // ref's data (The numpy array ends up read-only if the ref was to a const matrix type.) Note + // that this means you need to ensure you don't destroy the object in some other way (e.g. with + // an appropriate keep_alive, or with a reference to a statically allocated matrix). + static handle cast(const MapType &src, return_value_policy policy, handle parent) { + switch (policy) { + case return_value_policy::copy: + return eigen_array_cast(src); + case return_value_policy::reference_internal: + return eigen_array_cast(src, parent, is_eigen_mutable_map::value); + case return_value_policy::reference: + case return_value_policy::automatic: + case return_value_policy::automatic_reference: + return eigen_array_cast(src, none(), is_eigen_mutable_map::value); + default: + // move, take_ownership don't make any sense for a ref/map: + pybind11_fail("Invalid return_value_policy for Eigen Map/Ref/Block type"); + } + } + + static PYBIND11_DESCR name() { return props::descriptor(); } + + // Explicitly delete these: support python -> C++ conversion on these (i.e. these can be return + // types but not bound arguments). We still provide them (with an explicitly delete) so that + // you end up here if you try anyway. + bool load(handle, bool) = delete; + operator MapType() = delete; + template using cast_op_type = MapType; +}; + +// We can return any map-like object (but can only load Refs, specialized next): +template struct type_caster::value>> + : eigen_map_caster {}; + +// Loader for Ref<...> arguments. See the documentation for info on how to make this work without +// copying (it requires some extra effort in many cases). +template +struct type_caster< + Eigen::Ref, + enable_if_t>::value> +> : public eigen_map_caster> { +private: + using Type = Eigen::Ref; + using props = EigenProps; + using Scalar = typename props::Scalar; + using MapType = Eigen::Map; + using Array = array_t; + static constexpr bool need_writeable = is_eigen_mutable_map::value; + // Delay construction (these have no default constructor) + std::unique_ptr map; + std::unique_ptr ref; + // Our array. When possible, this is just a numpy array pointing to the source data, but + // sometimes we can't avoid copying (e.g. input is not a numpy array at all, has an incompatible + // layout, or is an array of a type that needs to be converted). Using a numpy temporary + // (rather than an Eigen temporary) saves an extra copy when we need both type conversion and + // storage order conversion. (Note that we refuse to use this temporary copy when loading an + // argument for a Ref with M non-const, i.e. a read-write reference). + Array copy_or_ref; +public: + bool load(handle src, bool convert) { + // First check whether what we have is already an array of the right type. If not, we can't + // avoid a copy (because the copy is also going to do type conversion). + bool need_copy = !isinstance(src); + + EigenConformable fits; + if (!need_copy) { + // We don't need a converting copy, but we also need to check whether the strides are + // compatible with the Ref's stride requirements + Array aref = reinterpret_borrow(src); + + if (aref && (!need_writeable || aref.writeable())) { + fits = props::conformable(aref); + if (!fits) return false; // Incompatible dimensions + if (!fits.template stride_compatible()) + need_copy = true; + else + copy_or_ref = std::move(aref); + } + else { + need_copy = true; + } + } + + if (need_copy) { + // We need to copy: If we need a mutable reference, or we're not supposed to convert + // (either because we're in the no-convert overload pass, or because we're explicitly + // instructed not to copy (via `py::arg().noconvert()`) we have to fail loading. + if (!convert || need_writeable) return false; + + Array copy = Array::ensure(src); + if (!copy) return false; + fits = props::conformable(copy); + if (!fits || !fits.template stride_compatible()) + return false; + copy_or_ref = std::move(copy); + loader_life_support::add_patient(copy_or_ref); + } + + ref.reset(); + map.reset(new MapType(data(copy_or_ref), fits.rows, fits.cols, make_stride(fits.stride.outer(), fits.stride.inner()))); + ref.reset(new Type(*map)); + + return true; + } + + operator Type*() { return ref.get(); } + operator Type&() { return *ref; } + template using cast_op_type = pybind11::detail::cast_op_type<_T>; + +private: + template ::value, int> = 0> + Scalar *data(Array &a) { return a.mutable_data(); } + + template ::value, int> = 0> + const Scalar *data(Array &a) { return a.data(); } + + // Attempt to figure out a constructor of `Stride` that will work. + // If both strides are fixed, use a default constructor: + template using stride_ctor_default = bool_constant< + S::InnerStrideAtCompileTime != Eigen::Dynamic && S::OuterStrideAtCompileTime != Eigen::Dynamic && + std::is_default_constructible::value>; + // Otherwise, if there is a two-index constructor, assume it is (outer,inner) like + // Eigen::Stride, and use it: + template using stride_ctor_dual = bool_constant< + !stride_ctor_default::value && std::is_constructible::value>; + // Otherwise, if there is a one-index constructor, and just one of the strides is dynamic, use + // it (passing whichever stride is dynamic). + template using stride_ctor_outer = bool_constant< + !any_of, stride_ctor_dual>::value && + S::OuterStrideAtCompileTime == Eigen::Dynamic && S::InnerStrideAtCompileTime != Eigen::Dynamic && + std::is_constructible::value>; + template using stride_ctor_inner = bool_constant< + !any_of, stride_ctor_dual>::value && + S::InnerStrideAtCompileTime == Eigen::Dynamic && S::OuterStrideAtCompileTime != Eigen::Dynamic && + std::is_constructible::value>; + + template ::value, int> = 0> + static S make_stride(EigenIndex, EigenIndex) { return S(); } + template ::value, int> = 0> + static S make_stride(EigenIndex outer, EigenIndex inner) { return S(outer, inner); } + template ::value, int> = 0> + static S make_stride(EigenIndex outer, EigenIndex) { return S(outer); } + template ::value, int> = 0> + static S make_stride(EigenIndex, EigenIndex inner) { return S(inner); } + +}; + +// type_caster for special matrix types (e.g. DiagonalMatrix), which are EigenBase, but not +// EigenDense (i.e. they don't have a data(), at least not with the usual matrix layout). +// load() is not supported, but we can cast them into the python domain by first copying to a +// regular Eigen::Matrix, then casting that. +template +struct type_caster::value>> { +protected: + using Matrix = Eigen::Matrix; + using props = EigenProps; +public: + static handle cast(const Type &src, return_value_policy /* policy */, handle /* parent */) { + handle h = eigen_encapsulate(new Matrix(src)); + return h; + } + static handle cast(const Type *src, return_value_policy policy, handle parent) { return cast(*src, policy, parent); } + + static PYBIND11_DESCR name() { return props::descriptor(); } + + // Explicitly delete these: support python -> C++ conversion on these (i.e. these can be return + // types but not bound arguments). We still provide them (with an explicitly delete) so that + // you end up here if you try anyway. + bool load(handle, bool) = delete; + operator Type() = delete; + template using cast_op_type = Type; +}; + +template +struct type_caster::value>> { + typedef typename Type::Scalar Scalar; + typedef remove_reference_t().outerIndexPtr())> StorageIndex; + typedef typename Type::Index Index; + static constexpr bool rowMajor = Type::IsRowMajor; + + bool load(handle src, bool) { + if (!src) + return false; + + auto obj = reinterpret_borrow(src); + object sparse_module = module::import("scipy.sparse"); + object matrix_type = sparse_module.attr( + rowMajor ? "csr_matrix" : "csc_matrix"); + + if (!obj.get_type().is(matrix_type)) { + try { + obj = matrix_type(obj); + } catch (const error_already_set &) { + return false; + } + } + + auto values = array_t((object) obj.attr("data")); + auto innerIndices = array_t((object) obj.attr("indices")); + auto outerIndices = array_t((object) obj.attr("indptr")); + auto shape = pybind11::tuple((pybind11::object) obj.attr("shape")); + auto nnz = obj.attr("nnz").cast(); + + if (!values || !innerIndices || !outerIndices) + return false; + + value = Eigen::MappedSparseMatrix( + shape[0].cast(), shape[1].cast(), nnz, + outerIndices.mutable_data(), innerIndices.mutable_data(), values.mutable_data()); + + return true; + } + + static handle cast(const Type &src, return_value_policy /* policy */, handle /* parent */) { + const_cast(src).makeCompressed(); + + object matrix_type = module::import("scipy.sparse").attr( + rowMajor ? "csr_matrix" : "csc_matrix"); + + array data(src.nonZeros(), src.valuePtr()); + array outerIndices((rowMajor ? src.rows() : src.cols()) + 1, src.outerIndexPtr()); + array innerIndices(src.nonZeros(), src.innerIndexPtr()); + + return matrix_type( + std::make_tuple(data, innerIndices, outerIndices), + std::make_pair(src.rows(), src.cols()) + ).release(); + } + + PYBIND11_TYPE_CASTER(Type, _<(Type::IsRowMajor) != 0>("scipy.sparse.csr_matrix[", "scipy.sparse.csc_matrix[") + + npy_format_descriptor::name() + _("]")); +}; + +NAMESPACE_END(detail) +NAMESPACE_END(pybind11) + +#if defined(__GNUG__) || defined(__clang__) +# pragma GCC diagnostic pop +#elif defined(_MSC_VER) +# pragma warning(pop) +#endif diff --git a/ptocr/postprocess/lanms/include/pybind11/embed.h b/ptocr/postprocess/lanms/include/pybind11/embed.h new file mode 100644 index 0000000..0eb656b --- /dev/null +++ b/ptocr/postprocess/lanms/include/pybind11/embed.h @@ -0,0 +1,194 @@ +/* + pybind11/embed.h: Support for embedding the interpreter + + Copyright (c) 2017 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "pybind11.h" +#include "eval.h" + +#if defined(PYPY_VERSION) +# error Embedding the interpreter is not supported with PyPy +#endif + +#if PY_MAJOR_VERSION >= 3 +# define PYBIND11_EMBEDDED_MODULE_IMPL(name) \ + extern "C" PyObject *pybind11_init_impl_##name() { \ + return pybind11_init_wrapper_##name(); \ + } +#else +# define PYBIND11_EMBEDDED_MODULE_IMPL(name) \ + extern "C" void pybind11_init_impl_##name() { \ + pybind11_init_wrapper_##name(); \ + } +#endif + +/** \rst + Add a new module to the table of builtins for the interpreter. Must be + defined in global scope. The first macro parameter is the name of the + module (without quotes). The second parameter is the variable which will + be used as the interface to add functions and classes to the module. + + .. code-block:: cpp + + PYBIND11_EMBEDDED_MODULE(example, m) { + // ... initialize functions and classes here + m.def("foo", []() { + return "Hello, World!"; + }); + } + \endrst */ +#define PYBIND11_EMBEDDED_MODULE(name, variable) \ + static void pybind11_init_##name(pybind11::module &); \ + static PyObject *pybind11_init_wrapper_##name() { \ + auto m = pybind11::module(#name); \ + try { \ + pybind11_init_##name(m); \ + return m.ptr(); \ + } catch (pybind11::error_already_set &e) { \ + PyErr_SetString(PyExc_ImportError, e.what()); \ + return nullptr; \ + } catch (const std::exception &e) { \ + PyErr_SetString(PyExc_ImportError, e.what()); \ + return nullptr; \ + } \ + } \ + PYBIND11_EMBEDDED_MODULE_IMPL(name) \ + pybind11::detail::embedded_module name(#name, pybind11_init_impl_##name); \ + void pybind11_init_##name(pybind11::module &variable) + + +NAMESPACE_BEGIN(pybind11) +NAMESPACE_BEGIN(detail) + +/// Python 2.7/3.x compatible version of `PyImport_AppendInittab` and error checks. +struct embedded_module { +#if PY_MAJOR_VERSION >= 3 + using init_t = PyObject *(*)(); +#else + using init_t = void (*)(); +#endif + embedded_module(const char *name, init_t init) { + if (Py_IsInitialized()) + pybind11_fail("Can't add new modules after the interpreter has been initialized"); + + auto result = PyImport_AppendInittab(name, init); + if (result == -1) + pybind11_fail("Insufficient memory to add a new module"); + } +}; + +NAMESPACE_END(detail) + +/** \rst + Initialize the Python interpreter. No other pybind11 or CPython API functions can be + called before this is done; with the exception of `PYBIND11_EMBEDDED_MODULE`. The + optional parameter can be used to skip the registration of signal handlers (see the + Python documentation for details). Calling this function again after the interpreter + has already been initialized is a fatal error. + \endrst */ +inline void initialize_interpreter(bool init_signal_handlers = true) { + if (Py_IsInitialized()) + pybind11_fail("The interpreter is already running"); + + Py_InitializeEx(init_signal_handlers ? 1 : 0); + + // Make .py files in the working directory available by default + auto sys_path = reinterpret_borrow(module::import("sys").attr("path")); + sys_path.append("."); +} + +/** \rst + Shut down the Python interpreter. No pybind11 or CPython API functions can be called + after this. In addition, pybind11 objects must not outlive the interpreter: + + .. code-block:: cpp + + { // BAD + py::initialize_interpreter(); + auto hello = py::str("Hello, World!"); + py::finalize_interpreter(); + } // <-- BOOM, hello's destructor is called after interpreter shutdown + + { // GOOD + py::initialize_interpreter(); + { // scoped + auto hello = py::str("Hello, World!"); + } // <-- OK, hello is cleaned up properly + py::finalize_interpreter(); + } + + { // BETTER + py::scoped_interpreter guard{}; + auto hello = py::str("Hello, World!"); + } + + .. warning:: + + The interpreter can be restarted by calling `initialize_interpreter` again. + Modules created using pybind11 can be safely re-initialized. However, Python + itself cannot completely unload binary extension modules and there are several + caveats with regard to interpreter restarting. All the details can be found + in the CPython documentation. In short, not all interpreter memory may be + freed, either due to reference cycles or user-created global data. + + \endrst */ +inline void finalize_interpreter() { + handle builtins(PyEval_GetBuiltins()); + const char *id = PYBIND11_INTERNALS_ID; + + // Get the internals pointer (without creating it if it doesn't exist). It's possible for the + // internals to be created during Py_Finalize() (e.g. if a py::capsule calls `get_internals()` + // during destruction), so we get the pointer-pointer here and check it after Py_Finalize(). + detail::internals **internals_ptr_ptr = &detail::get_internals_ptr(); + // It could also be stashed in builtins, so look there too: + if (builtins.contains(id) && isinstance(builtins[id])) + internals_ptr_ptr = capsule(builtins[id]); + + Py_Finalize(); + + if (internals_ptr_ptr) { + delete *internals_ptr_ptr; + *internals_ptr_ptr = nullptr; + } +} + +/** \rst + Scope guard version of `initialize_interpreter` and `finalize_interpreter`. + This a move-only guard and only a single instance can exist. + + .. code-block:: cpp + + #include + + int main() { + py::scoped_interpreter guard{}; + py::print(Hello, World!); + } // <-- interpreter shutdown + \endrst */ +class scoped_interpreter { +public: + scoped_interpreter(bool init_signal_handlers = true) { + initialize_interpreter(init_signal_handlers); + } + + scoped_interpreter(const scoped_interpreter &) = delete; + scoped_interpreter(scoped_interpreter &&other) noexcept { other.is_valid = false; } + scoped_interpreter &operator=(const scoped_interpreter &) = delete; + scoped_interpreter &operator=(scoped_interpreter &&) = delete; + + ~scoped_interpreter() { + if (is_valid) + finalize_interpreter(); + } + +private: + bool is_valid = true; +}; + +NAMESPACE_END(pybind11) diff --git a/ptocr/postprocess/lanms/include/pybind11/eval.h b/ptocr/postprocess/lanms/include/pybind11/eval.h new file mode 100644 index 0000000..165003b --- /dev/null +++ b/ptocr/postprocess/lanms/include/pybind11/eval.h @@ -0,0 +1,117 @@ +/* + pybind11/exec.h: Support for evaluating Python expressions and statements + from strings and files + + Copyright (c) 2016 Klemens Morgenstern and + Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "pybind11.h" + +NAMESPACE_BEGIN(pybind11) + +enum eval_mode { + /// Evaluate a string containing an isolated expression + eval_expr, + + /// Evaluate a string containing a single statement. Returns \c none + eval_single_statement, + + /// Evaluate a string containing a sequence of statement. Returns \c none + eval_statements +}; + +template +object eval(str expr, object global = globals(), object local = object()) { + if (!local) + local = global; + + /* PyRun_String does not accept a PyObject / encoding specifier, + this seems to be the only alternative */ + std::string buffer = "# -*- coding: utf-8 -*-\n" + (std::string) expr; + + int start; + switch (mode) { + case eval_expr: start = Py_eval_input; break; + case eval_single_statement: start = Py_single_input; break; + case eval_statements: start = Py_file_input; break; + default: pybind11_fail("invalid evaluation mode"); + } + + PyObject *result = PyRun_String(buffer.c_str(), start, global.ptr(), local.ptr()); + if (!result) + throw error_already_set(); + return reinterpret_steal(result); +} + +template +object eval(const char (&s)[N], object global = globals(), object local = object()) { + /* Support raw string literals by removing common leading whitespace */ + auto expr = (s[0] == '\n') ? str(module::import("textwrap").attr("dedent")(s)) + : str(s); + return eval(expr, global, local); +} + +inline void exec(str expr, object global = globals(), object local = object()) { + eval(expr, global, local); +} + +template +void exec(const char (&s)[N], object global = globals(), object local = object()) { + eval(s, global, local); +} + +template +object eval_file(str fname, object global = globals(), object local = object()) { + if (!local) + local = global; + + int start; + switch (mode) { + case eval_expr: start = Py_eval_input; break; + case eval_single_statement: start = Py_single_input; break; + case eval_statements: start = Py_file_input; break; + default: pybind11_fail("invalid evaluation mode"); + } + + int closeFile = 1; + std::string fname_str = (std::string) fname; +#if PY_VERSION_HEX >= 0x03040000 + FILE *f = _Py_fopen_obj(fname.ptr(), "r"); +#elif PY_VERSION_HEX >= 0x03000000 + FILE *f = _Py_fopen(fname.ptr(), "r"); +#else + /* No unicode support in open() :( */ + auto fobj = reinterpret_steal(PyFile_FromString( + const_cast(fname_str.c_str()), + const_cast("r"))); + FILE *f = nullptr; + if (fobj) + f = PyFile_AsFile(fobj.ptr()); + closeFile = 0; +#endif + if (!f) { + PyErr_Clear(); + pybind11_fail("File \"" + fname_str + "\" could not be opened!"); + } + +#if PY_VERSION_HEX < 0x03000000 && defined(PYPY_VERSION) + PyObject *result = PyRun_File(f, fname_str.c_str(), start, global.ptr(), + local.ptr()); + (void) closeFile; +#else + PyObject *result = PyRun_FileEx(f, fname_str.c_str(), start, global.ptr(), + local.ptr(), closeFile); +#endif + + if (!result) + throw error_already_set(); + return reinterpret_steal(result); +} + +NAMESPACE_END(pybind11) diff --git a/ptocr/postprocess/lanms/include/pybind11/functional.h b/ptocr/postprocess/lanms/include/pybind11/functional.h new file mode 100644 index 0000000..fdb6b33 --- /dev/null +++ b/ptocr/postprocess/lanms/include/pybind11/functional.h @@ -0,0 +1,85 @@ +/* + pybind11/functional.h: std::function<> support + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "pybind11.h" +#include + +NAMESPACE_BEGIN(pybind11) +NAMESPACE_BEGIN(detail) + +template +struct type_caster> { + using type = std::function; + using retval_type = conditional_t::value, void_type, Return>; + using function_type = Return (*) (Args...); + +public: + bool load(handle src, bool convert) { + if (src.is_none()) { + // Defer accepting None to other overloads (if we aren't in convert mode): + if (!convert) return false; + return true; + } + + if (!isinstance(src)) + return false; + + auto func = reinterpret_borrow(src); + + /* + When passing a C++ function as an argument to another C++ + function via Python, every function call would normally involve + a full C++ -> Python -> C++ roundtrip, which can be prohibitive. + Here, we try to at least detect the case where the function is + stateless (i.e. function pointer or lambda function without + captured variables), in which case the roundtrip can be avoided. + */ + if (auto cfunc = func.cpp_function()) { + auto c = reinterpret_borrow(PyCFunction_GET_SELF(cfunc.ptr())); + auto rec = (function_record *) c; + + if (rec && rec->is_stateless && + same_type(typeid(function_type), *reinterpret_cast(rec->data[1]))) { + struct capture { function_type f; }; + value = ((capture *) &rec->data)->f; + return true; + } + } + + value = [func](Args... args) -> Return { + gil_scoped_acquire acq; + object retval(func(std::forward(args)...)); + /* Visual studio 2015 parser issue: need parentheses around this expression */ + return (retval.template cast()); + }; + return true; + } + + template + static handle cast(Func &&f_, return_value_policy policy, handle /* parent */) { + if (!f_) + return none().inc_ref(); + + auto result = f_.template target(); + if (result) + return cpp_function(*result, policy).release(); + else + return cpp_function(std::forward(f_), policy).release(); + } + + PYBIND11_TYPE_CASTER(type, _("Callable[[") + + argument_loader::arg_names() + _("], ") + + make_caster::name() + + _("]")); +}; + +NAMESPACE_END(detail) +NAMESPACE_END(pybind11) diff --git a/ptocr/postprocess/lanms/include/pybind11/numpy.h b/ptocr/postprocess/lanms/include/pybind11/numpy.h new file mode 100644 index 0000000..388e212 --- /dev/null +++ b/ptocr/postprocess/lanms/include/pybind11/numpy.h @@ -0,0 +1,1598 @@ +/* + pybind11/numpy.h: Basic NumPy support, vectorize() wrapper + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "pybind11.h" +#include "complex.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(_MSC_VER) +# pragma warning(push) +# pragma warning(disable: 4127) // warning C4127: Conditional expression is constant +#endif + +/* This will be true on all flat address space platforms and allows us to reduce the + whole npy_intp / ssize_t / Py_intptr_t business down to just ssize_t for all size + and dimension types (e.g. shape, strides, indexing), instead of inflicting this + upon the library user. */ +static_assert(sizeof(ssize_t) == sizeof(Py_intptr_t), "ssize_t != Py_intptr_t"); + +NAMESPACE_BEGIN(pybind11) + +class array; // Forward declaration + +NAMESPACE_BEGIN(detail) +template struct npy_format_descriptor; + +struct PyArrayDescr_Proxy { + PyObject_HEAD + PyObject *typeobj; + char kind; + char type; + char byteorder; + char flags; + int type_num; + int elsize; + int alignment; + char *subarray; + PyObject *fields; + PyObject *names; +}; + +struct PyArray_Proxy { + PyObject_HEAD + char *data; + int nd; + ssize_t *dimensions; + ssize_t *strides; + PyObject *base; + PyObject *descr; + int flags; +}; + +struct PyVoidScalarObject_Proxy { + PyObject_VAR_HEAD + char *obval; + PyArrayDescr_Proxy *descr; + int flags; + PyObject *base; +}; + +struct numpy_type_info { + PyObject* dtype_ptr; + std::string format_str; +}; + +struct numpy_internals { + std::unordered_map registered_dtypes; + + numpy_type_info *get_type_info(const std::type_info& tinfo, bool throw_if_missing = true) { + auto it = registered_dtypes.find(std::type_index(tinfo)); + if (it != registered_dtypes.end()) + return &(it->second); + if (throw_if_missing) + pybind11_fail(std::string("NumPy type info missing for ") + tinfo.name()); + return nullptr; + } + + template numpy_type_info *get_type_info(bool throw_if_missing = true) { + return get_type_info(typeid(typename std::remove_cv::type), throw_if_missing); + } +}; + +inline PYBIND11_NOINLINE void load_numpy_internals(numpy_internals* &ptr) { + ptr = &get_or_create_shared_data("_numpy_internals"); +} + +inline numpy_internals& get_numpy_internals() { + static numpy_internals* ptr = nullptr; + if (!ptr) + load_numpy_internals(ptr); + return *ptr; +} + +struct npy_api { + enum constants { + NPY_ARRAY_C_CONTIGUOUS_ = 0x0001, + NPY_ARRAY_F_CONTIGUOUS_ = 0x0002, + NPY_ARRAY_OWNDATA_ = 0x0004, + NPY_ARRAY_FORCECAST_ = 0x0010, + NPY_ARRAY_ENSUREARRAY_ = 0x0040, + NPY_ARRAY_ALIGNED_ = 0x0100, + NPY_ARRAY_WRITEABLE_ = 0x0400, + NPY_BOOL_ = 0, + NPY_BYTE_, NPY_UBYTE_, + NPY_SHORT_, NPY_USHORT_, + NPY_INT_, NPY_UINT_, + NPY_LONG_, NPY_ULONG_, + NPY_LONGLONG_, NPY_ULONGLONG_, + NPY_FLOAT_, NPY_DOUBLE_, NPY_LONGDOUBLE_, + NPY_CFLOAT_, NPY_CDOUBLE_, NPY_CLONGDOUBLE_, + NPY_OBJECT_ = 17, + NPY_STRING_, NPY_UNICODE_, NPY_VOID_ + }; + + typedef struct { + Py_intptr_t *ptr; + int len; + } PyArray_Dims; + + static npy_api& get() { + static npy_api api = lookup(); + return api; + } + + bool PyArray_Check_(PyObject *obj) const { + return (bool) PyObject_TypeCheck(obj, PyArray_Type_); + } + bool PyArrayDescr_Check_(PyObject *obj) const { + return (bool) PyObject_TypeCheck(obj, PyArrayDescr_Type_); + } + + unsigned int (*PyArray_GetNDArrayCFeatureVersion_)(); + PyObject *(*PyArray_DescrFromType_)(int); + PyObject *(*PyArray_NewFromDescr_) + (PyTypeObject *, PyObject *, int, Py_intptr_t *, + Py_intptr_t *, void *, int, PyObject *); + PyObject *(*PyArray_DescrNewFromType_)(int); + int (*PyArray_CopyInto_)(PyObject *, PyObject *); + PyObject *(*PyArray_NewCopy_)(PyObject *, int); + PyTypeObject *PyArray_Type_; + PyTypeObject *PyVoidArrType_Type_; + PyTypeObject *PyArrayDescr_Type_; + PyObject *(*PyArray_DescrFromScalar_)(PyObject *); + PyObject *(*PyArray_FromAny_) (PyObject *, PyObject *, int, int, int, PyObject *); + int (*PyArray_DescrConverter_) (PyObject *, PyObject **); + bool (*PyArray_EquivTypes_) (PyObject *, PyObject *); + int (*PyArray_GetArrayParamsFromObject_)(PyObject *, PyObject *, char, PyObject **, int *, + Py_ssize_t *, PyObject **, PyObject *); + PyObject *(*PyArray_Squeeze_)(PyObject *); + int (*PyArray_SetBaseObject_)(PyObject *, PyObject *); + PyObject* (*PyArray_Resize_)(PyObject*, PyArray_Dims*, int, int); +private: + enum functions { + API_PyArray_GetNDArrayCFeatureVersion = 211, + API_PyArray_Type = 2, + API_PyArrayDescr_Type = 3, + API_PyVoidArrType_Type = 39, + API_PyArray_DescrFromType = 45, + API_PyArray_DescrFromScalar = 57, + API_PyArray_FromAny = 69, + API_PyArray_Resize = 80, + API_PyArray_CopyInto = 82, + API_PyArray_NewCopy = 85, + API_PyArray_NewFromDescr = 94, + API_PyArray_DescrNewFromType = 9, + API_PyArray_DescrConverter = 174, + API_PyArray_EquivTypes = 182, + API_PyArray_GetArrayParamsFromObject = 278, + API_PyArray_Squeeze = 136, + API_PyArray_SetBaseObject = 282 + }; + + static npy_api lookup() { + module m = module::import("numpy.core.multiarray"); + auto c = m.attr("_ARRAY_API"); +#if PY_MAJOR_VERSION >= 3 + void **api_ptr = (void **) PyCapsule_GetPointer(c.ptr(), NULL); +#else + void **api_ptr = (void **) PyCObject_AsVoidPtr(c.ptr()); +#endif + npy_api api; +#define DECL_NPY_API(Func) api.Func##_ = (decltype(api.Func##_)) api_ptr[API_##Func]; + DECL_NPY_API(PyArray_GetNDArrayCFeatureVersion); + if (api.PyArray_GetNDArrayCFeatureVersion_() < 0x7) + pybind11_fail("pybind11 numpy support requires numpy >= 1.7.0"); + DECL_NPY_API(PyArray_Type); + DECL_NPY_API(PyVoidArrType_Type); + DECL_NPY_API(PyArrayDescr_Type); + DECL_NPY_API(PyArray_DescrFromType); + DECL_NPY_API(PyArray_DescrFromScalar); + DECL_NPY_API(PyArray_FromAny); + DECL_NPY_API(PyArray_Resize); + DECL_NPY_API(PyArray_CopyInto); + DECL_NPY_API(PyArray_NewCopy); + DECL_NPY_API(PyArray_NewFromDescr); + DECL_NPY_API(PyArray_DescrNewFromType); + DECL_NPY_API(PyArray_DescrConverter); + DECL_NPY_API(PyArray_EquivTypes); + DECL_NPY_API(PyArray_GetArrayParamsFromObject); + DECL_NPY_API(PyArray_Squeeze); + DECL_NPY_API(PyArray_SetBaseObject); +#undef DECL_NPY_API + return api; + } +}; + +inline PyArray_Proxy* array_proxy(void* ptr) { + return reinterpret_cast(ptr); +} + +inline const PyArray_Proxy* array_proxy(const void* ptr) { + return reinterpret_cast(ptr); +} + +inline PyArrayDescr_Proxy* array_descriptor_proxy(PyObject* ptr) { + return reinterpret_cast(ptr); +} + +inline const PyArrayDescr_Proxy* array_descriptor_proxy(const PyObject* ptr) { + return reinterpret_cast(ptr); +} + +inline bool check_flags(const void* ptr, int flag) { + return (flag == (array_proxy(ptr)->flags & flag)); +} + +template struct is_std_array : std::false_type { }; +template struct is_std_array> : std::true_type { }; +template struct is_complex : std::false_type { }; +template struct is_complex> : std::true_type { }; + +template struct array_info_scalar { + typedef T type; + static constexpr bool is_array = false; + static constexpr bool is_empty = false; + static PYBIND11_DESCR extents() { return _(""); } + static void append_extents(list& /* shape */) { } +}; +// Computes underlying type and a comma-separated list of extents for array +// types (any mix of std::array and built-in arrays). An array of char is +// treated as scalar because it gets special handling. +template struct array_info : array_info_scalar { }; +template struct array_info> { + using type = typename array_info::type; + static constexpr bool is_array = true; + static constexpr bool is_empty = (N == 0) || array_info::is_empty; + static constexpr size_t extent = N; + + // appends the extents to shape + static void append_extents(list& shape) { + shape.append(N); + array_info::append_extents(shape); + } + + template::is_array, int> = 0> + static PYBIND11_DESCR extents() { + return _(); + } + + template::is_array, int> = 0> + static PYBIND11_DESCR extents() { + return concat(_(), array_info::extents()); + } +}; +// For numpy we have special handling for arrays of characters, so we don't include +// the size in the array extents. +template struct array_info : array_info_scalar { }; +template struct array_info> : array_info_scalar> { }; +template struct array_info : array_info> { }; +template using remove_all_extents_t = typename array_info::type; + +template using is_pod_struct = all_of< + std::is_standard_layout, // since we're accessing directly in memory we need a standard layout type +#if !defined(__GNUG__) || defined(__clang__) || __GNUC__ >= 5 + std::is_trivially_copyable, +#else + // GCC 4 doesn't implement is_trivially_copyable, so approximate it + std::is_trivially_destructible, + satisfies_any_of, +#endif + satisfies_none_of +>; + +template ssize_t byte_offset_unsafe(const Strides &) { return 0; } +template +ssize_t byte_offset_unsafe(const Strides &strides, ssize_t i, Ix... index) { + return i * strides[Dim] + byte_offset_unsafe(strides, index...); +} + +/** + * Proxy class providing unsafe, unchecked const access to array data. This is constructed through + * the `unchecked()` method of `array` or the `unchecked()` method of `array_t`. `Dims` + * will be -1 for dimensions determined at runtime. + */ +template +class unchecked_reference { +protected: + static constexpr bool Dynamic = Dims < 0; + const unsigned char *data_; + // Storing the shape & strides in local variables (i.e. these arrays) allows the compiler to + // make large performance gains on big, nested loops, but requires compile-time dimensions + conditional_t> + shape_, strides_; + const ssize_t dims_; + + friend class pybind11::array; + // Constructor for compile-time dimensions: + template + unchecked_reference(const void *data, const ssize_t *shape, const ssize_t *strides, enable_if_t) + : data_{reinterpret_cast(data)}, dims_{Dims} { + for (size_t i = 0; i < (size_t) dims_; i++) { + shape_[i] = shape[i]; + strides_[i] = strides[i]; + } + } + // Constructor for runtime dimensions: + template + unchecked_reference(const void *data, const ssize_t *shape, const ssize_t *strides, enable_if_t dims) + : data_{reinterpret_cast(data)}, shape_{shape}, strides_{strides}, dims_{dims} {} + +public: + /** + * Unchecked const reference access to data at the given indices. For a compile-time known + * number of dimensions, this requires the correct number of arguments; for run-time + * dimensionality, this is not checked (and so is up to the caller to use safely). + */ + template const T &operator()(Ix... index) const { + static_assert(sizeof...(Ix) == Dims || Dynamic, + "Invalid number of indices for unchecked array reference"); + return *reinterpret_cast(data_ + byte_offset_unsafe(strides_, ssize_t(index)...)); + } + /** + * Unchecked const reference access to data; this operator only participates if the reference + * is to a 1-dimensional array. When present, this is exactly equivalent to `obj(index)`. + */ + template > + const T &operator[](ssize_t index) const { return operator()(index); } + + /// Pointer access to the data at the given indices. + template const T *data(Ix... ix) const { return &operator()(ssize_t(ix)...); } + + /// Returns the item size, i.e. sizeof(T) + constexpr static ssize_t itemsize() { return sizeof(T); } + + /// Returns the shape (i.e. size) of dimension `dim` + ssize_t shape(ssize_t dim) const { return shape_[(size_t) dim]; } + + /// Returns the number of dimensions of the array + ssize_t ndim() const { return dims_; } + + /// Returns the total number of elements in the referenced array, i.e. the product of the shapes + template + enable_if_t size() const { + return std::accumulate(shape_.begin(), shape_.end(), (ssize_t) 1, std::multiplies()); + } + template + enable_if_t size() const { + return std::accumulate(shape_, shape_ + ndim(), (ssize_t) 1, std::multiplies()); + } + + /// Returns the total number of bytes used by the referenced data. Note that the actual span in + /// memory may be larger if the referenced array has non-contiguous strides (e.g. for a slice). + ssize_t nbytes() const { + return size() * itemsize(); + } +}; + +template +class unchecked_mutable_reference : public unchecked_reference { + friend class pybind11::array; + using ConstBase = unchecked_reference; + using ConstBase::ConstBase; + using ConstBase::Dynamic; +public: + /// Mutable, unchecked access to data at the given indices. + template T& operator()(Ix... index) { + static_assert(sizeof...(Ix) == Dims || Dynamic, + "Invalid number of indices for unchecked array reference"); + return const_cast(ConstBase::operator()(index...)); + } + /** + * Mutable, unchecked access data at the given index; this operator only participates if the + * reference is to a 1-dimensional array (or has runtime dimensions). When present, this is + * exactly equivalent to `obj(index)`. + */ + template > + T &operator[](ssize_t index) { return operator()(index); } + + /// Mutable pointer access to the data at the given indices. + template T *mutable_data(Ix... ix) { return &operator()(ssize_t(ix)...); } +}; + +template +struct type_caster> { + static_assert(Dim == 0 && Dim > 0 /* always fail */, "unchecked array proxy object is not castable"); +}; +template +struct type_caster> : type_caster> {}; + +NAMESPACE_END(detail) + +class dtype : public object { +public: + PYBIND11_OBJECT_DEFAULT(dtype, object, detail::npy_api::get().PyArrayDescr_Check_); + + explicit dtype(const buffer_info &info) { + dtype descr(_dtype_from_pep3118()(PYBIND11_STR_TYPE(info.format))); + // If info.itemsize == 0, use the value calculated from the format string + m_ptr = descr.strip_padding(info.itemsize ? info.itemsize : descr.itemsize()).release().ptr(); + } + + explicit dtype(const std::string &format) { + m_ptr = from_args(pybind11::str(format)).release().ptr(); + } + + dtype(const char *format) : dtype(std::string(format)) { } + + dtype(list names, list formats, list offsets, ssize_t itemsize) { + dict args; + args["names"] = names; + args["formats"] = formats; + args["offsets"] = offsets; + args["itemsize"] = pybind11::int_(itemsize); + m_ptr = from_args(args).release().ptr(); + } + + /// This is essentially the same as calling numpy.dtype(args) in Python. + static dtype from_args(object args) { + PyObject *ptr = nullptr; + if (!detail::npy_api::get().PyArray_DescrConverter_(args.release().ptr(), &ptr) || !ptr) + throw error_already_set(); + return reinterpret_steal(ptr); + } + + /// Return dtype associated with a C++ type. + template static dtype of() { + return detail::npy_format_descriptor::type>::dtype(); + } + + /// Size of the data type in bytes. + ssize_t itemsize() const { + return detail::array_descriptor_proxy(m_ptr)->elsize; + } + + /// Returns true for structured data types. + bool has_fields() const { + return detail::array_descriptor_proxy(m_ptr)->names != nullptr; + } + + /// Single-character type code. + char kind() const { + return detail::array_descriptor_proxy(m_ptr)->kind; + } + +private: + static object _dtype_from_pep3118() { + static PyObject *obj = module::import("numpy.core._internal") + .attr("_dtype_from_pep3118").cast().release().ptr(); + return reinterpret_borrow(obj); + } + + dtype strip_padding(ssize_t itemsize) { + // Recursively strip all void fields with empty names that are generated for + // padding fields (as of NumPy v1.11). + if (!has_fields()) + return *this; + + struct field_descr { PYBIND11_STR_TYPE name; object format; pybind11::int_ offset; }; + std::vector field_descriptors; + + for (auto field : attr("fields").attr("items")()) { + auto spec = field.cast(); + auto name = spec[0].cast(); + auto format = spec[1].cast()[0].cast(); + auto offset = spec[1].cast()[1].cast(); + if (!len(name) && format.kind() == 'V') + continue; + field_descriptors.push_back({(PYBIND11_STR_TYPE) name, format.strip_padding(format.itemsize()), offset}); + } + + std::sort(field_descriptors.begin(), field_descriptors.end(), + [](const field_descr& a, const field_descr& b) { + return a.offset.cast() < b.offset.cast(); + }); + + list names, formats, offsets; + for (auto& descr : field_descriptors) { + names.append(descr.name); + formats.append(descr.format); + offsets.append(descr.offset); + } + return dtype(names, formats, offsets, itemsize); + } +}; + +class array : public buffer { +public: + PYBIND11_OBJECT_CVT(array, buffer, detail::npy_api::get().PyArray_Check_, raw_array) + + enum { + c_style = detail::npy_api::NPY_ARRAY_C_CONTIGUOUS_, + f_style = detail::npy_api::NPY_ARRAY_F_CONTIGUOUS_, + forcecast = detail::npy_api::NPY_ARRAY_FORCECAST_ + }; + + array() : array({{0}}, static_cast(nullptr)) {} + + using ShapeContainer = detail::any_container; + using StridesContainer = detail::any_container; + + // Constructs an array taking shape/strides from arbitrary container types + array(const pybind11::dtype &dt, ShapeContainer shape, StridesContainer strides, + const void *ptr = nullptr, handle base = handle()) { + + if (strides->empty()) + *strides = c_strides(*shape, dt.itemsize()); + + auto ndim = shape->size(); + if (ndim != strides->size()) + pybind11_fail("NumPy: shape ndim doesn't match strides ndim"); + auto descr = dt; + + int flags = 0; + if (base && ptr) { + if (isinstance(base)) + /* Copy flags from base (except ownership bit) */ + flags = reinterpret_borrow(base).flags() & ~detail::npy_api::NPY_ARRAY_OWNDATA_; + else + /* Writable by default, easy to downgrade later on if needed */ + flags = detail::npy_api::NPY_ARRAY_WRITEABLE_; + } + + auto &api = detail::npy_api::get(); + auto tmp = reinterpret_steal(api.PyArray_NewFromDescr_( + api.PyArray_Type_, descr.release().ptr(), (int) ndim, shape->data(), strides->data(), + const_cast(ptr), flags, nullptr)); + if (!tmp) + throw error_already_set(); + if (ptr) { + if (base) { + api.PyArray_SetBaseObject_(tmp.ptr(), base.inc_ref().ptr()); + } else { + tmp = reinterpret_steal(api.PyArray_NewCopy_(tmp.ptr(), -1 /* any order */)); + } + } + m_ptr = tmp.release().ptr(); + } + + array(const pybind11::dtype &dt, ShapeContainer shape, const void *ptr = nullptr, handle base = handle()) + : array(dt, std::move(shape), {}, ptr, base) { } + + template ::value && !std::is_same::value>> + array(const pybind11::dtype &dt, T count, const void *ptr = nullptr, handle base = handle()) + : array(dt, {{count}}, ptr, base) { } + + template + array(ShapeContainer shape, StridesContainer strides, const T *ptr, handle base = handle()) + : array(pybind11::dtype::of(), std::move(shape), std::move(strides), ptr, base) { } + + template + array(ShapeContainer shape, const T *ptr, handle base = handle()) + : array(std::move(shape), {}, ptr, base) { } + + template + explicit array(ssize_t count, const T *ptr, handle base = handle()) : array({count}, {}, ptr, base) { } + + explicit array(const buffer_info &info) + : array(pybind11::dtype(info), info.shape, info.strides, info.ptr) { } + + /// Array descriptor (dtype) + pybind11::dtype dtype() const { + return reinterpret_borrow(detail::array_proxy(m_ptr)->descr); + } + + /// Total number of elements + ssize_t size() const { + return std::accumulate(shape(), shape() + ndim(), (ssize_t) 1, std::multiplies()); + } + + /// Byte size of a single element + ssize_t itemsize() const { + return detail::array_descriptor_proxy(detail::array_proxy(m_ptr)->descr)->elsize; + } + + /// Total number of bytes + ssize_t nbytes() const { + return size() * itemsize(); + } + + /// Number of dimensions + ssize_t ndim() const { + return detail::array_proxy(m_ptr)->nd; + } + + /// Base object + object base() const { + return reinterpret_borrow(detail::array_proxy(m_ptr)->base); + } + + /// Dimensions of the array + const ssize_t* shape() const { + return detail::array_proxy(m_ptr)->dimensions; + } + + /// Dimension along a given axis + ssize_t shape(ssize_t dim) const { + if (dim >= ndim()) + fail_dim_check(dim, "invalid axis"); + return shape()[dim]; + } + + /// Strides of the array + const ssize_t* strides() const { + return detail::array_proxy(m_ptr)->strides; + } + + /// Stride along a given axis + ssize_t strides(ssize_t dim) const { + if (dim >= ndim()) + fail_dim_check(dim, "invalid axis"); + return strides()[dim]; + } + + /// Return the NumPy array flags + int flags() const { + return detail::array_proxy(m_ptr)->flags; + } + + /// If set, the array is writeable (otherwise the buffer is read-only) + bool writeable() const { + return detail::check_flags(m_ptr, detail::npy_api::NPY_ARRAY_WRITEABLE_); + } + + /// If set, the array owns the data (will be freed when the array is deleted) + bool owndata() const { + return detail::check_flags(m_ptr, detail::npy_api::NPY_ARRAY_OWNDATA_); + } + + /// Pointer to the contained data. If index is not provided, points to the + /// beginning of the buffer. May throw if the index would lead to out of bounds access. + template const void* data(Ix... index) const { + return static_cast(detail::array_proxy(m_ptr)->data + offset_at(index...)); + } + + /// Mutable pointer to the contained data. If index is not provided, points to the + /// beginning of the buffer. May throw if the index would lead to out of bounds access. + /// May throw if the array is not writeable. + template void* mutable_data(Ix... index) { + check_writeable(); + return static_cast(detail::array_proxy(m_ptr)->data + offset_at(index...)); + } + + /// Byte offset from beginning of the array to a given index (full or partial). + /// May throw if the index would lead to out of bounds access. + template ssize_t offset_at(Ix... index) const { + if ((ssize_t) sizeof...(index) > ndim()) + fail_dim_check(sizeof...(index), "too many indices for an array"); + return byte_offset(ssize_t(index)...); + } + + ssize_t offset_at() const { return 0; } + + /// Item count from beginning of the array to a given index (full or partial). + /// May throw if the index would lead to out of bounds access. + template ssize_t index_at(Ix... index) const { + return offset_at(index...) / itemsize(); + } + + /** + * Returns a proxy object that provides access to the array's data without bounds or + * dimensionality checking. Will throw if the array is missing the `writeable` flag. Use with + * care: the array must not be destroyed or reshaped for the duration of the returned object, + * and the caller must take care not to access invalid dimensions or dimension indices. + */ + template detail::unchecked_mutable_reference mutable_unchecked() { + if (Dims >= 0 && ndim() != Dims) + throw std::domain_error("array has incorrect number of dimensions: " + std::to_string(ndim()) + + "; expected " + std::to_string(Dims)); + return detail::unchecked_mutable_reference(mutable_data(), shape(), strides(), ndim()); + } + + /** + * Returns a proxy object that provides const access to the array's data without bounds or + * dimensionality checking. Unlike `mutable_unchecked()`, this does not require that the + * underlying array have the `writable` flag. Use with care: the array must not be destroyed or + * reshaped for the duration of the returned object, and the caller must take care not to access + * invalid dimensions or dimension indices. + */ + template detail::unchecked_reference unchecked() const { + if (Dims >= 0 && ndim() != Dims) + throw std::domain_error("array has incorrect number of dimensions: " + std::to_string(ndim()) + + "; expected " + std::to_string(Dims)); + return detail::unchecked_reference(data(), shape(), strides(), ndim()); + } + + /// Return a new view with all of the dimensions of length 1 removed + array squeeze() { + auto& api = detail::npy_api::get(); + return reinterpret_steal(api.PyArray_Squeeze_(m_ptr)); + } + + /// Resize array to given shape + /// If refcheck is true and more that one reference exist to this array + /// then resize will succeed only if it makes a reshape, i.e. original size doesn't change + void resize(ShapeContainer new_shape, bool refcheck = true) { + detail::npy_api::PyArray_Dims d = { + new_shape->data(), int(new_shape->size()) + }; + // try to resize, set ordering param to -1 cause it's not used anyway + object new_array = reinterpret_steal( + detail::npy_api::get().PyArray_Resize_(m_ptr, &d, int(refcheck), -1) + ); + if (!new_array) throw error_already_set(); + if (isinstance(new_array)) { *this = std::move(new_array); } + } + + /// Ensure that the argument is a NumPy array + /// In case of an error, nullptr is returned and the Python error is cleared. + static array ensure(handle h, int ExtraFlags = 0) { + auto result = reinterpret_steal(raw_array(h.ptr(), ExtraFlags)); + if (!result) + PyErr_Clear(); + return result; + } + +protected: + template friend struct detail::npy_format_descriptor; + + void fail_dim_check(ssize_t dim, const std::string& msg) const { + throw index_error(msg + ": " + std::to_string(dim) + + " (ndim = " + std::to_string(ndim()) + ")"); + } + + template ssize_t byte_offset(Ix... index) const { + check_dimensions(index...); + return detail::byte_offset_unsafe(strides(), ssize_t(index)...); + } + + void check_writeable() const { + if (!writeable()) + throw std::domain_error("array is not writeable"); + } + + // Default, C-style strides + static std::vector c_strides(const std::vector &shape, ssize_t itemsize) { + auto ndim = shape.size(); + std::vector strides(ndim, itemsize); + for (size_t i = ndim - 1; i > 0; --i) + strides[i - 1] = strides[i] * shape[i]; + return strides; + } + + // F-style strides; default when constructing an array_t with `ExtraFlags & f_style` + static std::vector f_strides(const std::vector &shape, ssize_t itemsize) { + auto ndim = shape.size(); + std::vector strides(ndim, itemsize); + for (size_t i = 1; i < ndim; ++i) + strides[i] = strides[i - 1] * shape[i - 1]; + return strides; + } + + template void check_dimensions(Ix... index) const { + check_dimensions_impl(ssize_t(0), shape(), ssize_t(index)...); + } + + void check_dimensions_impl(ssize_t, const ssize_t*) const { } + + template void check_dimensions_impl(ssize_t axis, const ssize_t* shape, ssize_t i, Ix... index) const { + if (i >= *shape) { + throw index_error(std::string("index ") + std::to_string(i) + + " is out of bounds for axis " + std::to_string(axis) + + " with size " + std::to_string(*shape)); + } + check_dimensions_impl(axis + 1, shape + 1, index...); + } + + /// Create array from any object -- always returns a new reference + static PyObject *raw_array(PyObject *ptr, int ExtraFlags = 0) { + if (ptr == nullptr) { + PyErr_SetString(PyExc_ValueError, "cannot create a pybind11::array from a nullptr"); + return nullptr; + } + return detail::npy_api::get().PyArray_FromAny_( + ptr, nullptr, 0, 0, detail::npy_api::NPY_ARRAY_ENSUREARRAY_ | ExtraFlags, nullptr); + } +}; + +template class array_t : public array { +private: + struct private_ctor {}; + // Delegating constructor needed when both moving and accessing in the same constructor + array_t(private_ctor, ShapeContainer &&shape, StridesContainer &&strides, const T *ptr, handle base) + : array(std::move(shape), std::move(strides), ptr, base) {} +public: + static_assert(!detail::array_info::is_array, "Array types cannot be used with array_t"); + + using value_type = T; + + array_t() : array(0, static_cast(nullptr)) {} + array_t(handle h, borrowed_t) : array(h, borrowed_t{}) { } + array_t(handle h, stolen_t) : array(h, stolen_t{}) { } + + PYBIND11_DEPRECATED("Use array_t::ensure() instead") + array_t(handle h, bool is_borrowed) : array(raw_array_t(h.ptr()), stolen_t{}) { + if (!m_ptr) PyErr_Clear(); + if (!is_borrowed) Py_XDECREF(h.ptr()); + } + + array_t(const object &o) : array(raw_array_t(o.ptr()), stolen_t{}) { + if (!m_ptr) throw error_already_set(); + } + + explicit array_t(const buffer_info& info) : array(info) { } + + array_t(ShapeContainer shape, StridesContainer strides, const T *ptr = nullptr, handle base = handle()) + : array(std::move(shape), std::move(strides), ptr, base) { } + + explicit array_t(ShapeContainer shape, const T *ptr = nullptr, handle base = handle()) + : array_t(private_ctor{}, std::move(shape), + ExtraFlags & f_style ? f_strides(*shape, itemsize()) : c_strides(*shape, itemsize()), + ptr, base) { } + + explicit array_t(size_t count, const T *ptr = nullptr, handle base = handle()) + : array({count}, {}, ptr, base) { } + + constexpr ssize_t itemsize() const { + return sizeof(T); + } + + template ssize_t index_at(Ix... index) const { + return offset_at(index...) / itemsize(); + } + + template const T* data(Ix... index) const { + return static_cast(array::data(index...)); + } + + template T* mutable_data(Ix... index) { + return static_cast(array::mutable_data(index...)); + } + + // Reference to element at a given index + template const T& at(Ix... index) const { + if (sizeof...(index) != ndim()) + fail_dim_check(sizeof...(index), "index dimension mismatch"); + return *(static_cast(array::data()) + byte_offset(ssize_t(index)...) / itemsize()); + } + + // Mutable reference to element at a given index + template T& mutable_at(Ix... index) { + if (sizeof...(index) != ndim()) + fail_dim_check(sizeof...(index), "index dimension mismatch"); + return *(static_cast(array::mutable_data()) + byte_offset(ssize_t(index)...) / itemsize()); + } + + /** + * Returns a proxy object that provides access to the array's data without bounds or + * dimensionality checking. Will throw if the array is missing the `writeable` flag. Use with + * care: the array must not be destroyed or reshaped for the duration of the returned object, + * and the caller must take care not to access invalid dimensions or dimension indices. + */ + template detail::unchecked_mutable_reference mutable_unchecked() { + return array::mutable_unchecked(); + } + + /** + * Returns a proxy object that provides const access to the array's data without bounds or + * dimensionality checking. Unlike `unchecked()`, this does not require that the underlying + * array have the `writable` flag. Use with care: the array must not be destroyed or reshaped + * for the duration of the returned object, and the caller must take care not to access invalid + * dimensions or dimension indices. + */ + template detail::unchecked_reference unchecked() const { + return array::unchecked(); + } + + /// Ensure that the argument is a NumPy array of the correct dtype (and if not, try to convert + /// it). In case of an error, nullptr is returned and the Python error is cleared. + static array_t ensure(handle h) { + auto result = reinterpret_steal(raw_array_t(h.ptr())); + if (!result) + PyErr_Clear(); + return result; + } + + static bool check_(handle h) { + const auto &api = detail::npy_api::get(); + return api.PyArray_Check_(h.ptr()) + && api.PyArray_EquivTypes_(detail::array_proxy(h.ptr())->descr, dtype::of().ptr()); + } + +protected: + /// Create array from any object -- always returns a new reference + static PyObject *raw_array_t(PyObject *ptr) { + if (ptr == nullptr) { + PyErr_SetString(PyExc_ValueError, "cannot create a pybind11::array_t from a nullptr"); + return nullptr; + } + return detail::npy_api::get().PyArray_FromAny_( + ptr, dtype::of().release().ptr(), 0, 0, + detail::npy_api::NPY_ARRAY_ENSUREARRAY_ | ExtraFlags, nullptr); + } +}; + +template +struct format_descriptor::value>> { + static std::string format() { + return detail::npy_format_descriptor::type>::format(); + } +}; + +template struct format_descriptor { + static std::string format() { return std::to_string(N) + "s"; } +}; +template struct format_descriptor> { + static std::string format() { return std::to_string(N) + "s"; } +}; + +template +struct format_descriptor::value>> { + static std::string format() { + return format_descriptor< + typename std::remove_cv::type>::type>::format(); + } +}; + +template +struct format_descriptor::is_array>> { + static std::string format() { + using detail::_; + PYBIND11_DESCR extents = _("(") + detail::array_info::extents() + _(")"); + return extents.text() + format_descriptor>::format(); + } +}; + +NAMESPACE_BEGIN(detail) +template +struct pyobject_caster> { + using type = array_t; + + bool load(handle src, bool convert) { + if (!convert && !type::check_(src)) + return false; + value = type::ensure(src); + return static_cast(value); + } + + static handle cast(const handle &src, return_value_policy /* policy */, handle /* parent */) { + return src.inc_ref(); + } + PYBIND11_TYPE_CASTER(type, handle_type_name::name()); +}; + +template +struct compare_buffer_info::value>> { + static bool compare(const buffer_info& b) { + return npy_api::get().PyArray_EquivTypes_(dtype::of().ptr(), dtype(b).ptr()); + } +}; + +template struct npy_format_descriptor::value>> { +private: + // NB: the order here must match the one in common.h + constexpr static const int values[15] = { + npy_api::NPY_BOOL_, + npy_api::NPY_BYTE_, npy_api::NPY_UBYTE_, npy_api::NPY_SHORT_, npy_api::NPY_USHORT_, + npy_api::NPY_INT_, npy_api::NPY_UINT_, npy_api::NPY_LONGLONG_, npy_api::NPY_ULONGLONG_, + npy_api::NPY_FLOAT_, npy_api::NPY_DOUBLE_, npy_api::NPY_LONGDOUBLE_, + npy_api::NPY_CFLOAT_, npy_api::NPY_CDOUBLE_, npy_api::NPY_CLONGDOUBLE_ + }; + +public: + static constexpr int value = values[detail::is_fmt_numeric::index]; + + static pybind11::dtype dtype() { + if (auto ptr = npy_api::get().PyArray_DescrFromType_(value)) + return reinterpret_borrow(ptr); + pybind11_fail("Unsupported buffer format!"); + } + template ::value, int> = 0> + static PYBIND11_DESCR name() { + return _::value>(_("bool"), + _::value>("int", "uint") + _()); + } + template ::value, int> = 0> + static PYBIND11_DESCR name() { + return _::value || std::is_same::value>( + _("float") + _(), _("longdouble")); + } + template ::value, int> = 0> + static PYBIND11_DESCR name() { + return _::value || std::is_same::value>( + _("complex") + _(), _("longcomplex")); + } +}; + +#define PYBIND11_DECL_CHAR_FMT \ + static PYBIND11_DESCR name() { return _("S") + _(); } \ + static pybind11::dtype dtype() { return pybind11::dtype(std::string("S") + std::to_string(N)); } +template struct npy_format_descriptor { PYBIND11_DECL_CHAR_FMT }; +template struct npy_format_descriptor> { PYBIND11_DECL_CHAR_FMT }; +#undef PYBIND11_DECL_CHAR_FMT + +template struct npy_format_descriptor::is_array>> { +private: + using base_descr = npy_format_descriptor::type>; +public: + static_assert(!array_info::is_empty, "Zero-sized arrays are not supported"); + + static PYBIND11_DESCR name() { return _("(") + array_info::extents() + _(")") + base_descr::name(); } + static pybind11::dtype dtype() { + list shape; + array_info::append_extents(shape); + return pybind11::dtype::from_args(pybind11::make_tuple(base_descr::dtype(), shape)); + } +}; + +template struct npy_format_descriptor::value>> { +private: + using base_descr = npy_format_descriptor::type>; +public: + static PYBIND11_DESCR name() { return base_descr::name(); } + static pybind11::dtype dtype() { return base_descr::dtype(); } +}; + +struct field_descriptor { + const char *name; + ssize_t offset; + ssize_t size; + std::string format; + dtype descr; +}; + +inline PYBIND11_NOINLINE void register_structured_dtype( + const std::initializer_list& fields, + const std::type_info& tinfo, ssize_t itemsize, + bool (*direct_converter)(PyObject *, void *&)) { + + auto& numpy_internals = get_numpy_internals(); + if (numpy_internals.get_type_info(tinfo, false)) + pybind11_fail("NumPy: dtype is already registered"); + + list names, formats, offsets; + for (auto field : fields) { + if (!field.descr) + pybind11_fail(std::string("NumPy: unsupported field dtype: `") + + field.name + "` @ " + tinfo.name()); + names.append(PYBIND11_STR_TYPE(field.name)); + formats.append(field.descr); + offsets.append(pybind11::int_(field.offset)); + } + auto dtype_ptr = pybind11::dtype(names, formats, offsets, itemsize).release().ptr(); + + // There is an existing bug in NumPy (as of v1.11): trailing bytes are + // not encoded explicitly into the format string. This will supposedly + // get fixed in v1.12; for further details, see these: + // - https://github.com/numpy/numpy/issues/7797 + // - https://github.com/numpy/numpy/pull/7798 + // Because of this, we won't use numpy's logic to generate buffer format + // strings and will just do it ourselves. + std::vector ordered_fields(fields); + std::sort(ordered_fields.begin(), ordered_fields.end(), + [](const field_descriptor &a, const field_descriptor &b) { return a.offset < b.offset; }); + ssize_t offset = 0; + std::ostringstream oss; + // mark the structure as unaligned with '^', because numpy and C++ don't + // always agree about alignment (particularly for complex), and we're + // explicitly listing all our padding. This depends on none of the fields + // overriding the endianness. Putting the ^ in front of individual fields + // isn't guaranteed to work due to https://github.com/numpy/numpy/issues/9049 + oss << "^T{"; + for (auto& field : ordered_fields) { + if (field.offset > offset) + oss << (field.offset - offset) << 'x'; + oss << field.format << ':' << field.name << ':'; + offset = field.offset + field.size; + } + if (itemsize > offset) + oss << (itemsize - offset) << 'x'; + oss << '}'; + auto format_str = oss.str(); + + // Sanity check: verify that NumPy properly parses our buffer format string + auto& api = npy_api::get(); + auto arr = array(buffer_info(nullptr, itemsize, format_str, 1)); + if (!api.PyArray_EquivTypes_(dtype_ptr, arr.dtype().ptr())) + pybind11_fail("NumPy: invalid buffer descriptor!"); + + auto tindex = std::type_index(tinfo); + numpy_internals.registered_dtypes[tindex] = { dtype_ptr, format_str }; + get_internals().direct_conversions[tindex].push_back(direct_converter); +} + +template struct npy_format_descriptor { + static_assert(is_pod_struct::value, "Attempt to use a non-POD or unimplemented POD type as a numpy dtype"); + + static PYBIND11_DESCR name() { return make_caster::name(); } + + static pybind11::dtype dtype() { + return reinterpret_borrow(dtype_ptr()); + } + + static std::string format() { + static auto format_str = get_numpy_internals().get_type_info(true)->format_str; + return format_str; + } + + static void register_dtype(const std::initializer_list& fields) { + register_structured_dtype(fields, typeid(typename std::remove_cv::type), + sizeof(T), &direct_converter); + } + +private: + static PyObject* dtype_ptr() { + static PyObject* ptr = get_numpy_internals().get_type_info(true)->dtype_ptr; + return ptr; + } + + static bool direct_converter(PyObject *obj, void*& value) { + auto& api = npy_api::get(); + if (!PyObject_TypeCheck(obj, api.PyVoidArrType_Type_)) + return false; + if (auto descr = reinterpret_steal(api.PyArray_DescrFromScalar_(obj))) { + if (api.PyArray_EquivTypes_(dtype_ptr(), descr.ptr())) { + value = ((PyVoidScalarObject_Proxy *) obj)->obval; + return true; + } + } + return false; + } +}; + +#ifdef __CLION_IDE__ // replace heavy macro with dummy code for the IDE (doesn't affect code) +# define PYBIND11_NUMPY_DTYPE(Type, ...) ((void)0) +# define PYBIND11_NUMPY_DTYPE_EX(Type, ...) ((void)0) +#else + +#define PYBIND11_FIELD_DESCRIPTOR_EX(T, Field, Name) \ + ::pybind11::detail::field_descriptor { \ + Name, offsetof(T, Field), sizeof(decltype(std::declval().Field)), \ + ::pybind11::format_descriptor().Field)>::format(), \ + ::pybind11::detail::npy_format_descriptor().Field)>::dtype() \ + } + +// Extract name, offset and format descriptor for a struct field +#define PYBIND11_FIELD_DESCRIPTOR(T, Field) PYBIND11_FIELD_DESCRIPTOR_EX(T, Field, #Field) + +// The main idea of this macro is borrowed from https://github.com/swansontec/map-macro +// (C) William Swanson, Paul Fultz +#define PYBIND11_EVAL0(...) __VA_ARGS__ +#define PYBIND11_EVAL1(...) PYBIND11_EVAL0 (PYBIND11_EVAL0 (PYBIND11_EVAL0 (__VA_ARGS__))) +#define PYBIND11_EVAL2(...) PYBIND11_EVAL1 (PYBIND11_EVAL1 (PYBIND11_EVAL1 (__VA_ARGS__))) +#define PYBIND11_EVAL3(...) PYBIND11_EVAL2 (PYBIND11_EVAL2 (PYBIND11_EVAL2 (__VA_ARGS__))) +#define PYBIND11_EVAL4(...) PYBIND11_EVAL3 (PYBIND11_EVAL3 (PYBIND11_EVAL3 (__VA_ARGS__))) +#define PYBIND11_EVAL(...) PYBIND11_EVAL4 (PYBIND11_EVAL4 (PYBIND11_EVAL4 (__VA_ARGS__))) +#define PYBIND11_MAP_END(...) +#define PYBIND11_MAP_OUT +#define PYBIND11_MAP_COMMA , +#define PYBIND11_MAP_GET_END() 0, PYBIND11_MAP_END +#define PYBIND11_MAP_NEXT0(test, next, ...) next PYBIND11_MAP_OUT +#define PYBIND11_MAP_NEXT1(test, next) PYBIND11_MAP_NEXT0 (test, next, 0) +#define PYBIND11_MAP_NEXT(test, next) PYBIND11_MAP_NEXT1 (PYBIND11_MAP_GET_END test, next) +#ifdef _MSC_VER // MSVC is not as eager to expand macros, hence this workaround +#define PYBIND11_MAP_LIST_NEXT1(test, next) \ + PYBIND11_EVAL0 (PYBIND11_MAP_NEXT0 (test, PYBIND11_MAP_COMMA next, 0)) +#else +#define PYBIND11_MAP_LIST_NEXT1(test, next) \ + PYBIND11_MAP_NEXT0 (test, PYBIND11_MAP_COMMA next, 0) +#endif +#define PYBIND11_MAP_LIST_NEXT(test, next) \ + PYBIND11_MAP_LIST_NEXT1 (PYBIND11_MAP_GET_END test, next) +#define PYBIND11_MAP_LIST0(f, t, x, peek, ...) \ + f(t, x) PYBIND11_MAP_LIST_NEXT (peek, PYBIND11_MAP_LIST1) (f, t, peek, __VA_ARGS__) +#define PYBIND11_MAP_LIST1(f, t, x, peek, ...) \ + f(t, x) PYBIND11_MAP_LIST_NEXT (peek, PYBIND11_MAP_LIST0) (f, t, peek, __VA_ARGS__) +// PYBIND11_MAP_LIST(f, t, a1, a2, ...) expands to f(t, a1), f(t, a2), ... +#define PYBIND11_MAP_LIST(f, t, ...) \ + PYBIND11_EVAL (PYBIND11_MAP_LIST1 (f, t, __VA_ARGS__, (), 0)) + +#define PYBIND11_NUMPY_DTYPE(Type, ...) \ + ::pybind11::detail::npy_format_descriptor::register_dtype \ + ({PYBIND11_MAP_LIST (PYBIND11_FIELD_DESCRIPTOR, Type, __VA_ARGS__)}) + +#ifdef _MSC_VER +#define PYBIND11_MAP2_LIST_NEXT1(test, next) \ + PYBIND11_EVAL0 (PYBIND11_MAP_NEXT0 (test, PYBIND11_MAP_COMMA next, 0)) +#else +#define PYBIND11_MAP2_LIST_NEXT1(test, next) \ + PYBIND11_MAP_NEXT0 (test, PYBIND11_MAP_COMMA next, 0) +#endif +#define PYBIND11_MAP2_LIST_NEXT(test, next) \ + PYBIND11_MAP2_LIST_NEXT1 (PYBIND11_MAP_GET_END test, next) +#define PYBIND11_MAP2_LIST0(f, t, x1, x2, peek, ...) \ + f(t, x1, x2) PYBIND11_MAP2_LIST_NEXT (peek, PYBIND11_MAP2_LIST1) (f, t, peek, __VA_ARGS__) +#define PYBIND11_MAP2_LIST1(f, t, x1, x2, peek, ...) \ + f(t, x1, x2) PYBIND11_MAP2_LIST_NEXT (peek, PYBIND11_MAP2_LIST0) (f, t, peek, __VA_ARGS__) +// PYBIND11_MAP2_LIST(f, t, a1, a2, ...) expands to f(t, a1, a2), f(t, a3, a4), ... +#define PYBIND11_MAP2_LIST(f, t, ...) \ + PYBIND11_EVAL (PYBIND11_MAP2_LIST1 (f, t, __VA_ARGS__, (), 0)) + +#define PYBIND11_NUMPY_DTYPE_EX(Type, ...) \ + ::pybind11::detail::npy_format_descriptor::register_dtype \ + ({PYBIND11_MAP2_LIST (PYBIND11_FIELD_DESCRIPTOR_EX, Type, __VA_ARGS__)}) + +#endif // __CLION_IDE__ + +template +using array_iterator = typename std::add_pointer::type; + +template +array_iterator array_begin(const buffer_info& buffer) { + return array_iterator(reinterpret_cast(buffer.ptr)); +} + +template +array_iterator array_end(const buffer_info& buffer) { + return array_iterator(reinterpret_cast(buffer.ptr) + buffer.size); +} + +class common_iterator { +public: + using container_type = std::vector; + using value_type = container_type::value_type; + using size_type = container_type::size_type; + + common_iterator() : p_ptr(0), m_strides() {} + + common_iterator(void* ptr, const container_type& strides, const container_type& shape) + : p_ptr(reinterpret_cast(ptr)), m_strides(strides.size()) { + m_strides.back() = static_cast(strides.back()); + for (size_type i = m_strides.size() - 1; i != 0; --i) { + size_type j = i - 1; + value_type s = static_cast(shape[i]); + m_strides[j] = strides[j] + m_strides[i] - strides[i] * s; + } + } + + void increment(size_type dim) { + p_ptr += m_strides[dim]; + } + + void* data() const { + return p_ptr; + } + +private: + char* p_ptr; + container_type m_strides; +}; + +template class multi_array_iterator { +public: + using container_type = std::vector; + + multi_array_iterator(const std::array &buffers, + const container_type &shape) + : m_shape(shape.size()), m_index(shape.size(), 0), + m_common_iterator() { + + // Manual copy to avoid conversion warning if using std::copy + for (size_t i = 0; i < shape.size(); ++i) + m_shape[i] = shape[i]; + + container_type strides(shape.size()); + for (size_t i = 0; i < N; ++i) + init_common_iterator(buffers[i], shape, m_common_iterator[i], strides); + } + + multi_array_iterator& operator++() { + for (size_t j = m_index.size(); j != 0; --j) { + size_t i = j - 1; + if (++m_index[i] != m_shape[i]) { + increment_common_iterator(i); + break; + } else { + m_index[i] = 0; + } + } + return *this; + } + + template T* data() const { + return reinterpret_cast(m_common_iterator[K].data()); + } + +private: + + using common_iter = common_iterator; + + void init_common_iterator(const buffer_info &buffer, + const container_type &shape, + common_iter &iterator, + container_type &strides) { + auto buffer_shape_iter = buffer.shape.rbegin(); + auto buffer_strides_iter = buffer.strides.rbegin(); + auto shape_iter = shape.rbegin(); + auto strides_iter = strides.rbegin(); + + while (buffer_shape_iter != buffer.shape.rend()) { + if (*shape_iter == *buffer_shape_iter) + *strides_iter = *buffer_strides_iter; + else + *strides_iter = 0; + + ++buffer_shape_iter; + ++buffer_strides_iter; + ++shape_iter; + ++strides_iter; + } + + std::fill(strides_iter, strides.rend(), 0); + iterator = common_iter(buffer.ptr, strides, shape); + } + + void increment_common_iterator(size_t dim) { + for (auto &iter : m_common_iterator) + iter.increment(dim); + } + + container_type m_shape; + container_type m_index; + std::array m_common_iterator; +}; + +enum class broadcast_trivial { non_trivial, c_trivial, f_trivial }; + +// Populates the shape and number of dimensions for the set of buffers. Returns a broadcast_trivial +// enum value indicating whether the broadcast is "trivial"--that is, has each buffer being either a +// singleton or a full-size, C-contiguous (`c_trivial`) or Fortran-contiguous (`f_trivial`) storage +// buffer; returns `non_trivial` otherwise. +template +broadcast_trivial broadcast(const std::array &buffers, ssize_t &ndim, std::vector &shape) { + ndim = std::accumulate(buffers.begin(), buffers.end(), ssize_t(0), [](ssize_t res, const buffer_info &buf) { + return std::max(res, buf.ndim); + }); + + shape.clear(); + shape.resize((size_t) ndim, 1); + + // Figure out the output size, and make sure all input arrays conform (i.e. are either size 1 or + // the full size). + for (size_t i = 0; i < N; ++i) { + auto res_iter = shape.rbegin(); + auto end = buffers[i].shape.rend(); + for (auto shape_iter = buffers[i].shape.rbegin(); shape_iter != end; ++shape_iter, ++res_iter) { + const auto &dim_size_in = *shape_iter; + auto &dim_size_out = *res_iter; + + // Each input dimension can either be 1 or `n`, but `n` values must match across buffers + if (dim_size_out == 1) + dim_size_out = dim_size_in; + else if (dim_size_in != 1 && dim_size_in != dim_size_out) + pybind11_fail("pybind11::vectorize: incompatible size/dimension of inputs!"); + } + } + + bool trivial_broadcast_c = true; + bool trivial_broadcast_f = true; + for (size_t i = 0; i < N && (trivial_broadcast_c || trivial_broadcast_f); ++i) { + if (buffers[i].size == 1) + continue; + + // Require the same number of dimensions: + if (buffers[i].ndim != ndim) + return broadcast_trivial::non_trivial; + + // Require all dimensions be full-size: + if (!std::equal(buffers[i].shape.cbegin(), buffers[i].shape.cend(), shape.cbegin())) + return broadcast_trivial::non_trivial; + + // Check for C contiguity (but only if previous inputs were also C contiguous) + if (trivial_broadcast_c) { + ssize_t expect_stride = buffers[i].itemsize; + auto end = buffers[i].shape.crend(); + for (auto shape_iter = buffers[i].shape.crbegin(), stride_iter = buffers[i].strides.crbegin(); + trivial_broadcast_c && shape_iter != end; ++shape_iter, ++stride_iter) { + if (expect_stride == *stride_iter) + expect_stride *= *shape_iter; + else + trivial_broadcast_c = false; + } + } + + // Check for Fortran contiguity (if previous inputs were also F contiguous) + if (trivial_broadcast_f) { + ssize_t expect_stride = buffers[i].itemsize; + auto end = buffers[i].shape.cend(); + for (auto shape_iter = buffers[i].shape.cbegin(), stride_iter = buffers[i].strides.cbegin(); + trivial_broadcast_f && shape_iter != end; ++shape_iter, ++stride_iter) { + if (expect_stride == *stride_iter) + expect_stride *= *shape_iter; + else + trivial_broadcast_f = false; + } + } + } + + return + trivial_broadcast_c ? broadcast_trivial::c_trivial : + trivial_broadcast_f ? broadcast_trivial::f_trivial : + broadcast_trivial::non_trivial; +} + +template +struct vectorize_arg { + static_assert(!std::is_rvalue_reference::value, "Functions with rvalue reference arguments cannot be vectorized"); + // The wrapped function gets called with this type: + using call_type = remove_reference_t; + // Is this a vectorized argument? + static constexpr bool vectorize = + satisfies_any_of::value && + satisfies_none_of::value && + (!std::is_reference::value || + (std::is_lvalue_reference::value && std::is_const::value)); + // Accept this type: an array for vectorized types, otherwise the type as-is: + using type = conditional_t, array::forcecast>, T>; +}; + +template +struct vectorize_helper { +private: + static constexpr size_t N = sizeof...(Args); + static constexpr size_t NVectorized = constexpr_sum(vectorize_arg::vectorize...); + static_assert(NVectorized >= 1, + "pybind11::vectorize(...) requires a function with at least one vectorizable argument"); + +public: + template + explicit vectorize_helper(T &&f) : f(std::forward(f)) { } + + object operator()(typename vectorize_arg::type... args) { + return run(args..., + make_index_sequence(), + select_indices::vectorize...>(), + make_index_sequence()); + } + +private: + remove_reference_t f; + + template using param_n_t = typename pack_element::call_type...>::type; + + // Runs a vectorized function given arguments tuple and three index sequences: + // - Index is the full set of 0 ... (N-1) argument indices; + // - VIndex is the subset of argument indices with vectorized parameters, letting us access + // vectorized arguments (anything not in this sequence is passed through) + // - BIndex is a incremental sequence (beginning at 0) of the same size as VIndex, so that + // we can store vectorized buffer_infos in an array (argument VIndex has its buffer at + // index BIndex in the array). + template object run( + typename vectorize_arg::type &...args, + index_sequence i_seq, index_sequence vi_seq, index_sequence bi_seq) { + + // Pointers to values the function was called with; the vectorized ones set here will start + // out as array_t pointers, but they will be changed them to T pointers before we make + // call the wrapped function. Non-vectorized pointers are left as-is. + std::array params{{ &args... }}; + + // The array of `buffer_info`s of vectorized arguments: + std::array buffers{{ reinterpret_cast(params[VIndex])->request()... }}; + + /* Determine dimensions parameters of output array */ + ssize_t nd = 0; + std::vector shape(0); + auto trivial = broadcast(buffers, nd, shape); + size_t ndim = (size_t) nd; + + size_t size = std::accumulate(shape.begin(), shape.end(), (size_t) 1, std::multiplies()); + + // If all arguments are 0-dimension arrays (i.e. single values) return a plain value (i.e. + // not wrapped in an array). + if (size == 1 && ndim == 0) { + PYBIND11_EXPAND_SIDE_EFFECTS(params[VIndex] = buffers[BIndex].ptr); + return cast(f(*reinterpret_cast *>(params[Index])...)); + } + + array_t result; + if (trivial == broadcast_trivial::f_trivial) result = array_t(shape); + else result = array_t(shape); + + if (size == 0) return result; + + /* Call the function */ + if (trivial == broadcast_trivial::non_trivial) + apply_broadcast(buffers, params, result, i_seq, vi_seq, bi_seq); + else + apply_trivial(buffers, params, result.mutable_data(), size, i_seq, vi_seq, bi_seq); + + return result; + } + + template + void apply_trivial(std::array &buffers, + std::array ¶ms, + Return *out, + size_t size, + index_sequence, index_sequence, index_sequence) { + + // Initialize an array of mutable byte references and sizes with references set to the + // appropriate pointer in `params`; as we iterate, we'll increment each pointer by its size + // (except for singletons, which get an increment of 0). + std::array, NVectorized> vecparams{{ + std::pair( + reinterpret_cast(params[VIndex] = buffers[BIndex].ptr), + buffers[BIndex].size == 1 ? 0 : sizeof(param_n_t) + )... + }}; + + for (size_t i = 0; i < size; ++i) { + out[i] = f(*reinterpret_cast *>(params[Index])...); + for (auto &x : vecparams) x.first += x.second; + } + } + + template + void apply_broadcast(std::array &buffers, + std::array ¶ms, + array_t &output_array, + index_sequence, index_sequence, index_sequence) { + + buffer_info output = output_array.request(); + multi_array_iterator input_iter(buffers, output.shape); + + for (array_iterator iter = array_begin(output), end = array_end(output); + iter != end; + ++iter, ++input_iter) { + PYBIND11_EXPAND_SIDE_EFFECTS(( + params[VIndex] = input_iter.template data() + )); + *iter = f(*reinterpret_cast *>(std::get(params))...); + } + } +}; + +template +vectorize_helper +vectorize_extractor(const Func &f, Return (*) (Args ...)) { + return detail::vectorize_helper(f); +} + +template struct handle_type_name> { + static PYBIND11_DESCR name() { + return _("numpy.ndarray[") + npy_format_descriptor::name() + _("]"); + } +}; + +NAMESPACE_END(detail) + +// Vanilla pointer vectorizer: +template +detail::vectorize_helper +vectorize(Return (*f) (Args ...)) { + return detail::vectorize_helper(f); +} + +// lambda vectorizer: +template ::operator())>::type> +auto vectorize(Func &&f) -> decltype( + detail::vectorize_extractor(std::forward(f), (FuncType *) nullptr)) { + return detail::vectorize_extractor(std::forward(f), (FuncType *) nullptr); +} + +// Vectorize a class method (non-const): +template ())), Return, Class *, Args...>> +Helper vectorize(Return (Class::*f)(Args...)) { + return Helper(std::mem_fn(f)); +} + +// Vectorize a class method (non-const): +template ())), Return, const Class *, Args...>> +Helper vectorize(Return (Class::*f)(Args...) const) { + return Helper(std::mem_fn(f)); +} + +NAMESPACE_END(pybind11) + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif diff --git a/ptocr/postprocess/lanms/include/pybind11/operators.h b/ptocr/postprocess/lanms/include/pybind11/operators.h new file mode 100644 index 0000000..562987b --- /dev/null +++ b/ptocr/postprocess/lanms/include/pybind11/operators.h @@ -0,0 +1,167 @@ +/* + pybind11/operator.h: Metatemplates for operator overloading + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "pybind11.h" + +#if defined(__clang__) && !defined(__INTEL_COMPILER) +# pragma clang diagnostic ignored "-Wunsequenced" // multiple unsequenced modifications to 'self' (when using def(py::self OP Type())) +#elif defined(_MSC_VER) +# pragma warning(push) +# pragma warning(disable: 4127) // warning C4127: Conditional expression is constant +#endif + +NAMESPACE_BEGIN(pybind11) +NAMESPACE_BEGIN(detail) + +/// Enumeration with all supported operator types +enum op_id : int { + op_add, op_sub, op_mul, op_div, op_mod, op_divmod, op_pow, op_lshift, + op_rshift, op_and, op_xor, op_or, op_neg, op_pos, op_abs, op_invert, + op_int, op_long, op_float, op_str, op_cmp, op_gt, op_ge, op_lt, op_le, + op_eq, op_ne, op_iadd, op_isub, op_imul, op_idiv, op_imod, op_ilshift, + op_irshift, op_iand, op_ixor, op_ior, op_complex, op_bool, op_nonzero, + op_repr, op_truediv, op_itruediv +}; + +enum op_type : int { + op_l, /* base type on left */ + op_r, /* base type on right */ + op_u /* unary operator */ +}; + +struct self_t { }; +static const self_t self = self_t(); + +/// Type for an unused type slot +struct undefined_t { }; + +/// Don't warn about an unused variable +inline self_t __self() { return self; } + +/// base template of operator implementations +template struct op_impl { }; + +/// Operator implementation generator +template struct op_ { + template void execute(Class &cl, const Extra&... extra) const { + using Base = typename Class::type; + using L_type = conditional_t::value, Base, L>; + using R_type = conditional_t::value, Base, R>; + using op = op_impl; + cl.def(op::name(), &op::execute, is_operator(), extra...); + #if PY_MAJOR_VERSION < 3 + if (id == op_truediv || id == op_itruediv) + cl.def(id == op_itruediv ? "__idiv__" : ot == op_l ? "__div__" : "__rdiv__", + &op::execute, is_operator(), extra...); + #endif + } + template void execute_cast(Class &cl, const Extra&... extra) const { + using Base = typename Class::type; + using L_type = conditional_t::value, Base, L>; + using R_type = conditional_t::value, Base, R>; + using op = op_impl; + cl.def(op::name(), &op::execute_cast, is_operator(), extra...); + #if PY_MAJOR_VERSION < 3 + if (id == op_truediv || id == op_itruediv) + cl.def(id == op_itruediv ? "__idiv__" : ot == op_l ? "__div__" : "__rdiv__", + &op::execute, is_operator(), extra...); + #endif + } +}; + +#define PYBIND11_BINARY_OPERATOR(id, rid, op, expr) \ +template struct op_impl { \ + static char const* name() { return "__" #id "__"; } \ + static auto execute(const L &l, const R &r) -> decltype(expr) { return (expr); } \ + static B execute_cast(const L &l, const R &r) { return B(expr); } \ +}; \ +template struct op_impl { \ + static char const* name() { return "__" #rid "__"; } \ + static auto execute(const R &r, const L &l) -> decltype(expr) { return (expr); } \ + static B execute_cast(const R &r, const L &l) { return B(expr); } \ +}; \ +inline op_ op(const self_t &, const self_t &) { \ + return op_(); \ +} \ +template op_ op(const self_t &, const T &) { \ + return op_(); \ +} \ +template op_ op(const T &, const self_t &) { \ + return op_(); \ +} + +#define PYBIND11_INPLACE_OPERATOR(id, op, expr) \ +template struct op_impl { \ + static char const* name() { return "__" #id "__"; } \ + static auto execute(L &l, const R &r) -> decltype(expr) { return expr; } \ + static B execute_cast(L &l, const R &r) { return B(expr); } \ +}; \ +template op_ op(const self_t &, const T &) { \ + return op_(); \ +} + +#define PYBIND11_UNARY_OPERATOR(id, op, expr) \ +template struct op_impl { \ + static char const* name() { return "__" #id "__"; } \ + static auto execute(const L &l) -> decltype(expr) { return expr; } \ + static B execute_cast(const L &l) { return B(expr); } \ +}; \ +inline op_ op(const self_t &) { \ + return op_(); \ +} + +PYBIND11_BINARY_OPERATOR(sub, rsub, operator-, l - r) +PYBIND11_BINARY_OPERATOR(add, radd, operator+, l + r) +PYBIND11_BINARY_OPERATOR(mul, rmul, operator*, l * r) +PYBIND11_BINARY_OPERATOR(truediv, rtruediv, operator/, l / r) +PYBIND11_BINARY_OPERATOR(mod, rmod, operator%, l % r) +PYBIND11_BINARY_OPERATOR(lshift, rlshift, operator<<, l << r) +PYBIND11_BINARY_OPERATOR(rshift, rrshift, operator>>, l >> r) +PYBIND11_BINARY_OPERATOR(and, rand, operator&, l & r) +PYBIND11_BINARY_OPERATOR(xor, rxor, operator^, l ^ r) +PYBIND11_BINARY_OPERATOR(eq, eq, operator==, l == r) +PYBIND11_BINARY_OPERATOR(ne, ne, operator!=, l != r) +PYBIND11_BINARY_OPERATOR(or, ror, operator|, l | r) +PYBIND11_BINARY_OPERATOR(gt, lt, operator>, l > r) +PYBIND11_BINARY_OPERATOR(ge, le, operator>=, l >= r) +PYBIND11_BINARY_OPERATOR(lt, gt, operator<, l < r) +PYBIND11_BINARY_OPERATOR(le, ge, operator<=, l <= r) +//PYBIND11_BINARY_OPERATOR(pow, rpow, pow, std::pow(l, r)) +PYBIND11_INPLACE_OPERATOR(iadd, operator+=, l += r) +PYBIND11_INPLACE_OPERATOR(isub, operator-=, l -= r) +PYBIND11_INPLACE_OPERATOR(imul, operator*=, l *= r) +PYBIND11_INPLACE_OPERATOR(itruediv, operator/=, l /= r) +PYBIND11_INPLACE_OPERATOR(imod, operator%=, l %= r) +PYBIND11_INPLACE_OPERATOR(ilshift, operator<<=, l <<= r) +PYBIND11_INPLACE_OPERATOR(irshift, operator>>=, l >>= r) +PYBIND11_INPLACE_OPERATOR(iand, operator&=, l &= r) +PYBIND11_INPLACE_OPERATOR(ixor, operator^=, l ^= r) +PYBIND11_INPLACE_OPERATOR(ior, operator|=, l |= r) +PYBIND11_UNARY_OPERATOR(neg, operator-, -l) +PYBIND11_UNARY_OPERATOR(pos, operator+, +l) +PYBIND11_UNARY_OPERATOR(abs, abs, std::abs(l)) +PYBIND11_UNARY_OPERATOR(invert, operator~, (~l)) +PYBIND11_UNARY_OPERATOR(bool, operator!, !!l) +PYBIND11_UNARY_OPERATOR(int, int_, (int) l) +PYBIND11_UNARY_OPERATOR(float, float_, (double) l) + +#undef PYBIND11_BINARY_OPERATOR +#undef PYBIND11_INPLACE_OPERATOR +#undef PYBIND11_UNARY_OPERATOR +NAMESPACE_END(detail) + +using detail::self; + +NAMESPACE_END(pybind11) + +#if defined(_MSC_VER) +# pragma warning(pop) +#endif diff --git a/ptocr/postprocess/lanms/include/pybind11/options.h b/ptocr/postprocess/lanms/include/pybind11/options.h new file mode 100644 index 0000000..3105551 --- /dev/null +++ b/ptocr/postprocess/lanms/include/pybind11/options.h @@ -0,0 +1,65 @@ +/* + pybind11/options.h: global settings that are configurable at runtime. + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "common.h" + +NAMESPACE_BEGIN(pybind11) + +class options { +public: + + // Default RAII constructor, which leaves settings as they currently are. + options() : previous_state(global_state()) {} + + // Class is non-copyable. + options(const options&) = delete; + options& operator=(const options&) = delete; + + // Destructor, which restores settings that were in effect before. + ~options() { + global_state() = previous_state; + } + + // Setter methods (affect the global state): + + options& disable_user_defined_docstrings() & { global_state().show_user_defined_docstrings = false; return *this; } + + options& enable_user_defined_docstrings() & { global_state().show_user_defined_docstrings = true; return *this; } + + options& disable_function_signatures() & { global_state().show_function_signatures = false; return *this; } + + options& enable_function_signatures() & { global_state().show_function_signatures = true; return *this; } + + // Getter methods (return the global state): + + static bool show_user_defined_docstrings() { return global_state().show_user_defined_docstrings; } + + static bool show_function_signatures() { return global_state().show_function_signatures; } + + // This type is not meant to be allocated on the heap. + void* operator new(size_t) = delete; + +private: + + struct state { + bool show_user_defined_docstrings = true; //< Include user-supplied texts in docstrings. + bool show_function_signatures = true; //< Include auto-generated function signatures in docstrings. + }; + + static state &global_state() { + static state instance; + return instance; + } + + state previous_state; +}; + +NAMESPACE_END(pybind11) diff --git a/ptocr/postprocess/lanms/include/pybind11/pybind11.h b/ptocr/postprocess/lanms/include/pybind11/pybind11.h new file mode 100644 index 0000000..d3f34ee --- /dev/null +++ b/ptocr/postprocess/lanms/include/pybind11/pybind11.h @@ -0,0 +1,1869 @@ +/* + pybind11/pybind11.h: Main header file of the C++11 python + binding generator library + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#if defined(_MSC_VER) +# pragma warning(push) +# pragma warning(disable: 4100) // warning C4100: Unreferenced formal parameter +# pragma warning(disable: 4127) // warning C4127: Conditional expression is constant +# pragma warning(disable: 4512) // warning C4512: Assignment operator was implicitly defined as deleted +# pragma warning(disable: 4800) // warning C4800: 'int': forcing value to bool 'true' or 'false' (performance warning) +# pragma warning(disable: 4996) // warning C4996: The POSIX name for this item is deprecated. Instead, use the ISO C and C++ conformant name +# pragma warning(disable: 4702) // warning C4702: unreachable code +# pragma warning(disable: 4522) // warning C4522: multiple assignment operators specified +#elif defined(__INTEL_COMPILER) +# pragma warning(push) +# pragma warning(disable: 68) // integer conversion resulted in a change of sign +# pragma warning(disable: 186) // pointless comparison of unsigned integer with zero +# pragma warning(disable: 878) // incompatible exception specifications +# pragma warning(disable: 1334) // the "template" keyword used for syntactic disambiguation may only be used within a template +# pragma warning(disable: 1682) // implicit conversion of a 64-bit integral type to a smaller integral type (potential portability problem) +# pragma warning(disable: 1875) // offsetof applied to non-POD (Plain Old Data) types is nonstandard +# pragma warning(disable: 2196) // warning #2196: routine is both "inline" and "noinline" +#elif defined(__GNUG__) && !defined(__clang__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wunused-but-set-parameter" +# pragma GCC diagnostic ignored "-Wunused-but-set-variable" +# pragma GCC diagnostic ignored "-Wmissing-field-initializers" +# pragma GCC diagnostic ignored "-Wstrict-aliasing" +# pragma GCC diagnostic ignored "-Wattributes" +# if __GNUC__ >= 7 +# pragma GCC diagnostic ignored "-Wnoexcept-type" +# endif +#endif + +#include "attr.h" +#include "options.h" +#include "class_support.h" + +NAMESPACE_BEGIN(pybind11) + +/// Wraps an arbitrary C++ function/method/lambda function/.. into a callable Python object +class cpp_function : public function { +public: + cpp_function() { } + + /// Construct a cpp_function from a vanilla function pointer + template + cpp_function(Return (*f)(Args...), const Extra&... extra) { + initialize(f, f, extra...); + } + + /// Construct a cpp_function from a lambda function (possibly with internal state) + template , + std::is_function, std::is_pointer, std::is_member_pointer + >::value> + > + cpp_function(Func &&f, const Extra&... extra) { + using FuncType = typename detail::remove_class::operator())>::type; + initialize(std::forward(f), + (FuncType *) nullptr, extra...); + } + + /// Construct a cpp_function from a class method (non-const) + template + cpp_function(Return (Class::*f)(Arg...), const Extra&... extra) { + initialize([f](Class *c, Arg... args) -> Return { return (c->*f)(args...); }, + (Return (*) (Class *, Arg...)) nullptr, extra...); + } + + /// Construct a cpp_function from a class method (const) + template + cpp_function(Return (Class::*f)(Arg...) const, const Extra&... extra) { + initialize([f](const Class *c, Arg... args) -> Return { return (c->*f)(args...); }, + (Return (*)(const Class *, Arg ...)) nullptr, extra...); + } + + /// Return the function name + object name() const { return attr("__name__"); } + +protected: + /// Space optimization: don't inline this frequently instantiated fragment + PYBIND11_NOINLINE detail::function_record *make_function_record() { + return new detail::function_record(); + } + + /// Special internal constructor for functors, lambda functions, etc. + template + void initialize(Func &&f, Return (*)(Args...), const Extra&... extra) { + + struct capture { detail::remove_reference_t f; }; + + /* Store the function including any extra state it might have (e.g. a lambda capture object) */ + auto rec = make_function_record(); + + /* Store the capture object directly in the function record if there is enough space */ + if (sizeof(capture) <= sizeof(rec->data)) { + /* Without these pragmas, GCC warns that there might not be + enough space to use the placement new operator. However, the + 'if' statement above ensures that this is the case. */ +#if defined(__GNUG__) && !defined(__clang__) && __GNUC__ >= 6 +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wplacement-new" +#endif + new ((capture *) &rec->data) capture { std::forward(f) }; +#if defined(__GNUG__) && !defined(__clang__) && __GNUC__ >= 6 +# pragma GCC diagnostic pop +#endif + if (!std::is_trivially_destructible::value) + rec->free_data = [](detail::function_record *r) { ((capture *) &r->data)->~capture(); }; + } else { + rec->data[0] = new capture { std::forward(f) }; + rec->free_data = [](detail::function_record *r) { delete ((capture *) r->data[0]); }; + } + + /* Type casters for the function arguments and return value */ + using cast_in = detail::argument_loader; + using cast_out = detail::make_caster< + detail::conditional_t::value, detail::void_type, Return> + >; + + static_assert(detail::expected_num_args(sizeof...(Args), cast_in::has_args, cast_in::has_kwargs), + "The number of argument annotations does not match the number of function arguments"); + + /* Dispatch code which converts function arguments and performs the actual function call */ + rec->impl = [](detail::function_call &call) -> handle { + cast_in args_converter; + + /* Try to cast the function arguments into the C++ domain */ + if (!args_converter.load_args(call)) + return PYBIND11_TRY_NEXT_OVERLOAD; + + /* Invoke call policy pre-call hook */ + detail::process_attributes::precall(call); + + /* Get a pointer to the capture object */ + auto data = (sizeof(capture) <= sizeof(call.func.data) + ? &call.func.data : call.func.data[0]); + capture *cap = const_cast(reinterpret_cast(data)); + + /* Override policy for rvalues -- usually to enforce rvp::move on an rvalue */ + const auto policy = detail::return_value_policy_override::policy(call.func.policy); + + /* Function scope guard -- defaults to the compile-to-nothing `void_type` */ + using Guard = detail::extract_guard_t; + + /* Perform the function call */ + handle result = cast_out::cast( + std::move(args_converter).template call(cap->f), policy, call.parent); + + /* Invoke call policy post-call hook */ + detail::process_attributes::postcall(call, result); + + return result; + }; + + /* Process any user-provided function attributes */ + detail::process_attributes::init(extra..., rec); + + /* Generate a readable signature describing the function's arguments and return value types */ + using detail::descr; using detail::_; + PYBIND11_DESCR signature = _("(") + cast_in::arg_names() + _(") -> ") + cast_out::name(); + + /* Register the function with Python from generic (non-templated) code */ + initialize_generic(rec, signature.text(), signature.types(), sizeof...(Args)); + + if (cast_in::has_args) rec->has_args = true; + if (cast_in::has_kwargs) rec->has_kwargs = true; + + /* Stash some additional information used by an important optimization in 'functional.h' */ + using FunctionType = Return (*)(Args...); + constexpr bool is_function_ptr = + std::is_convertible::value && + sizeof(capture) == sizeof(void *); + if (is_function_ptr) { + rec->is_stateless = true; + rec->data[1] = const_cast(reinterpret_cast(&typeid(FunctionType))); + } + } + + /// Register a function call with Python (generic non-templated code goes here) + void initialize_generic(detail::function_record *rec, const char *text, + const std::type_info *const *types, size_t args) { + + /* Create copies of all referenced C-style strings */ + rec->name = strdup(rec->name ? rec->name : ""); + if (rec->doc) rec->doc = strdup(rec->doc); + for (auto &a: rec->args) { + if (a.name) + a.name = strdup(a.name); + if (a.descr) + a.descr = strdup(a.descr); + else if (a.value) + a.descr = strdup(a.value.attr("__repr__")().cast().c_str()); + } + + /* Generate a proper function signature */ + std::string signature; + size_t type_depth = 0, char_index = 0, type_index = 0, arg_index = 0; + while (true) { + char c = text[char_index++]; + if (c == '\0') + break; + + if (c == '{') { + // Write arg name for everything except *args, **kwargs and return type. + if (type_depth == 0 && text[char_index] != '*' && arg_index < args) { + if (!rec->args.empty() && rec->args[arg_index].name) { + signature += rec->args[arg_index].name; + } else if (arg_index == 0 && rec->is_method) { + signature += "self"; + } else { + signature += "arg" + std::to_string(arg_index - (rec->is_method ? 1 : 0)); + } + signature += ": "; + } + ++type_depth; + } else if (c == '}') { + --type_depth; + if (type_depth == 0) { + if (arg_index < rec->args.size() && rec->args[arg_index].descr) { + signature += "="; + signature += rec->args[arg_index].descr; + } + arg_index++; + } + } else if (c == '%') { + const std::type_info *t = types[type_index++]; + if (!t) + pybind11_fail("Internal error while parsing type signature (1)"); + if (auto tinfo = detail::get_type_info(*t)) { +#if defined(PYPY_VERSION) + signature += handle((PyObject *) tinfo->type) + .attr("__module__") + .cast() + "."; +#endif + signature += tinfo->type->tp_name; + } else { + std::string tname(t->name()); + detail::clean_type_id(tname); + signature += tname; + } + } else { + signature += c; + } + } + if (type_depth != 0 || types[type_index] != nullptr) + pybind11_fail("Internal error while parsing type signature (2)"); + + #if !defined(PYBIND11_CONSTEXPR_DESCR) + delete[] types; + delete[] text; + #endif + +#if PY_MAJOR_VERSION < 3 + if (strcmp(rec->name, "__next__") == 0) { + std::free(rec->name); + rec->name = strdup("next"); + } else if (strcmp(rec->name, "__bool__") == 0) { + std::free(rec->name); + rec->name = strdup("__nonzero__"); + } +#endif + rec->signature = strdup(signature.c_str()); + rec->args.shrink_to_fit(); + rec->is_constructor = !strcmp(rec->name, "__init__") || !strcmp(rec->name, "__setstate__"); + rec->nargs = (std::uint16_t) args; + + if (rec->sibling && PYBIND11_INSTANCE_METHOD_CHECK(rec->sibling.ptr())) + rec->sibling = PYBIND11_INSTANCE_METHOD_GET_FUNCTION(rec->sibling.ptr()); + + detail::function_record *chain = nullptr, *chain_start = rec; + if (rec->sibling) { + if (PyCFunction_Check(rec->sibling.ptr())) { + auto rec_capsule = reinterpret_borrow(PyCFunction_GET_SELF(rec->sibling.ptr())); + chain = (detail::function_record *) rec_capsule; + /* Never append a method to an overload chain of a parent class; + instead, hide the parent's overloads in this case */ + if (!chain->scope.is(rec->scope)) + chain = nullptr; + } + // Don't trigger for things like the default __init__, which are wrapper_descriptors that we are intentionally replacing + else if (!rec->sibling.is_none() && rec->name[0] != '_') + pybind11_fail("Cannot overload existing non-function object \"" + std::string(rec->name) + + "\" with a function of the same name"); + } + + if (!chain) { + /* No existing overload was found, create a new function object */ + rec->def = new PyMethodDef(); + std::memset(rec->def, 0, sizeof(PyMethodDef)); + rec->def->ml_name = rec->name; + rec->def->ml_meth = reinterpret_cast(*dispatcher); + rec->def->ml_flags = METH_VARARGS | METH_KEYWORDS; + + capsule rec_capsule(rec, [](void *ptr) { + destruct((detail::function_record *) ptr); + }); + + object scope_module; + if (rec->scope) { + if (hasattr(rec->scope, "__module__")) { + scope_module = rec->scope.attr("__module__"); + } else if (hasattr(rec->scope, "__name__")) { + scope_module = rec->scope.attr("__name__"); + } + } + + m_ptr = PyCFunction_NewEx(rec->def, rec_capsule.ptr(), scope_module.ptr()); + if (!m_ptr) + pybind11_fail("cpp_function::cpp_function(): Could not allocate function object"); + } else { + /* Append at the end of the overload chain */ + m_ptr = rec->sibling.ptr(); + inc_ref(); + chain_start = chain; + if (chain->is_method != rec->is_method) + pybind11_fail("overloading a method with both static and instance methods is not supported; " + #if defined(NDEBUG) + "compile in debug mode for more details" + #else + "error while attempting to bind " + std::string(rec->is_method ? "instance" : "static") + " method " + + std::string(pybind11::str(rec->scope.attr("__name__"))) + "." + std::string(rec->name) + signature + #endif + ); + while (chain->next) + chain = chain->next; + chain->next = rec; + } + + std::string signatures; + int index = 0; + /* Create a nice pydoc rec including all signatures and + docstrings of the functions in the overload chain */ + if (chain && options::show_function_signatures()) { + // First a generic signature + signatures += rec->name; + signatures += "(*args, **kwargs)\n"; + signatures += "Overloaded function.\n\n"; + } + // Then specific overload signatures + bool first_user_def = true; + for (auto it = chain_start; it != nullptr; it = it->next) { + if (options::show_function_signatures()) { + if (index > 0) signatures += "\n"; + if (chain) + signatures += std::to_string(++index) + ". "; + signatures += rec->name; + signatures += it->signature; + signatures += "\n"; + } + if (it->doc && strlen(it->doc) > 0 && options::show_user_defined_docstrings()) { + // If we're appending another docstring, and aren't printing function signatures, we + // need to append a newline first: + if (!options::show_function_signatures()) { + if (first_user_def) first_user_def = false; + else signatures += "\n"; + } + if (options::show_function_signatures()) signatures += "\n"; + signatures += it->doc; + if (options::show_function_signatures()) signatures += "\n"; + } + } + + /* Install docstring */ + PyCFunctionObject *func = (PyCFunctionObject *) m_ptr; + if (func->m_ml->ml_doc) + std::free(const_cast(func->m_ml->ml_doc)); + func->m_ml->ml_doc = strdup(signatures.c_str()); + + if (rec->is_method) { + m_ptr = PYBIND11_INSTANCE_METHOD_NEW(m_ptr, rec->scope.ptr()); + if (!m_ptr) + pybind11_fail("cpp_function::cpp_function(): Could not allocate instance method object"); + Py_DECREF(func); + } + } + + /// When a cpp_function is GCed, release any memory allocated by pybind11 + static void destruct(detail::function_record *rec) { + while (rec) { + detail::function_record *next = rec->next; + if (rec->free_data) + rec->free_data(rec); + std::free((char *) rec->name); + std::free((char *) rec->doc); + std::free((char *) rec->signature); + for (auto &arg: rec->args) { + std::free(const_cast(arg.name)); + std::free(const_cast(arg.descr)); + arg.value.dec_ref(); + } + if (rec->def) { + std::free(const_cast(rec->def->ml_doc)); + delete rec->def; + } + delete rec; + rec = next; + } + } + + /// Main dispatch logic for calls to functions bound using pybind11 + static PyObject *dispatcher(PyObject *self, PyObject *args_in, PyObject *kwargs_in) { + using namespace detail; + + /* Iterator over the list of potentially admissible overloads */ + function_record *overloads = (function_record *) PyCapsule_GetPointer(self, nullptr), + *it = overloads; + + /* Need to know how many arguments + keyword arguments there are to pick the right overload */ + const size_t n_args_in = (size_t) PyTuple_GET_SIZE(args_in); + + handle parent = n_args_in > 0 ? PyTuple_GET_ITEM(args_in, 0) : nullptr, + result = PYBIND11_TRY_NEXT_OVERLOAD; + + try { + // We do this in two passes: in the first pass, we load arguments with `convert=false`; + // in the second, we allow conversion (except for arguments with an explicit + // py::arg().noconvert()). This lets us prefer calls without conversion, with + // conversion as a fallback. + std::vector second_pass; + + // However, if there are no overloads, we can just skip the no-convert pass entirely + const bool overloaded = it != nullptr && it->next != nullptr; + + for (; it != nullptr; it = it->next) { + + /* For each overload: + 1. Copy all positional arguments we were given, also checking to make sure that + named positional arguments weren't *also* specified via kwarg. + 2. If we weren't given enough, try to make up the omitted ones by checking + whether they were provided by a kwarg matching the `py::arg("name")` name. If + so, use it (and remove it from kwargs; if not, see if the function binding + provided a default that we can use. + 3. Ensure that either all keyword arguments were "consumed", or that the function + takes a kwargs argument to accept unconsumed kwargs. + 4. Any positional arguments still left get put into a tuple (for args), and any + leftover kwargs get put into a dict. + 5. Pack everything into a vector; if we have py::args or py::kwargs, they are an + extra tuple or dict at the end of the positional arguments. + 6. Call the function call dispatcher (function_record::impl) + + If one of these fail, move on to the next overload and keep trying until we get a + result other than PYBIND11_TRY_NEXT_OVERLOAD. + */ + + function_record &func = *it; + size_t pos_args = func.nargs; // Number of positional arguments that we need + if (func.has_args) --pos_args; // (but don't count py::args + if (func.has_kwargs) --pos_args; // or py::kwargs) + + if (!func.has_args && n_args_in > pos_args) + continue; // Too many arguments for this overload + + if (n_args_in < pos_args && func.args.size() < pos_args) + continue; // Not enough arguments given, and not enough defaults to fill in the blanks + + function_call call(func, parent); + + size_t args_to_copy = std::min(pos_args, n_args_in); + size_t args_copied = 0; + + // 1. Copy any position arguments given. + bool bad_arg = false; + for (; args_copied < args_to_copy; ++args_copied) { + argument_record *arg_rec = args_copied < func.args.size() ? &func.args[args_copied] : nullptr; + if (kwargs_in && arg_rec && arg_rec->name && PyDict_GetItemString(kwargs_in, arg_rec->name)) { + bad_arg = true; + break; + } + + handle arg(PyTuple_GET_ITEM(args_in, args_copied)); + if (arg_rec && !arg_rec->none && arg.is_none()) { + bad_arg = true; + break; + } + call.args.push_back(arg); + call.args_convert.push_back(arg_rec ? arg_rec->convert : true); + } + if (bad_arg) + continue; // Maybe it was meant for another overload (issue #688) + + // We'll need to copy this if we steal some kwargs for defaults + dict kwargs = reinterpret_borrow(kwargs_in); + + // 2. Check kwargs and, failing that, defaults that may help complete the list + if (args_copied < pos_args) { + bool copied_kwargs = false; + + for (; args_copied < pos_args; ++args_copied) { + const auto &arg = func.args[args_copied]; + + handle value; + if (kwargs_in && arg.name) + value = PyDict_GetItemString(kwargs.ptr(), arg.name); + + if (value) { + // Consume a kwargs value + if (!copied_kwargs) { + kwargs = reinterpret_steal(PyDict_Copy(kwargs.ptr())); + copied_kwargs = true; + } + PyDict_DelItemString(kwargs.ptr(), arg.name); + } else if (arg.value) { + value = arg.value; + } + + if (value) { + call.args.push_back(value); + call.args_convert.push_back(arg.convert); + } + else + break; + } + + if (args_copied < pos_args) + continue; // Not enough arguments, defaults, or kwargs to fill the positional arguments + } + + // 3. Check everything was consumed (unless we have a kwargs arg) + if (kwargs && kwargs.size() > 0 && !func.has_kwargs) + continue; // Unconsumed kwargs, but no py::kwargs argument to accept them + + // 4a. If we have a py::args argument, create a new tuple with leftovers + tuple extra_args; + if (func.has_args) { + if (args_to_copy == 0) { + // We didn't copy out any position arguments from the args_in tuple, so we + // can reuse it directly without copying: + extra_args = reinterpret_borrow(args_in); + } else if (args_copied >= n_args_in) { + extra_args = tuple(0); + } else { + size_t args_size = n_args_in - args_copied; + extra_args = tuple(args_size); + for (size_t i = 0; i < args_size; ++i) { + handle item = PyTuple_GET_ITEM(args_in, args_copied + i); + extra_args[i] = item.inc_ref().ptr(); + } + } + call.args.push_back(extra_args); + call.args_convert.push_back(false); + } + + // 4b. If we have a py::kwargs, pass on any remaining kwargs + if (func.has_kwargs) { + if (!kwargs.ptr()) + kwargs = dict(); // If we didn't get one, send an empty one + call.args.push_back(kwargs); + call.args_convert.push_back(false); + } + + // 5. Put everything in a vector. Not technically step 5, we've been building it + // in `call.args` all along. + #if !defined(NDEBUG) + if (call.args.size() != func.nargs || call.args_convert.size() != func.nargs) + pybind11_fail("Internal error: function call dispatcher inserted wrong number of arguments!"); + #endif + + std::vector second_pass_convert; + if (overloaded) { + // We're in the first no-convert pass, so swap out the conversion flags for a + // set of all-false flags. If the call fails, we'll swap the flags back in for + // the conversion-allowed call below. + second_pass_convert.resize(func.nargs, false); + call.args_convert.swap(second_pass_convert); + } + + // 6. Call the function. + try { + loader_life_support guard{}; + result = func.impl(call); + } catch (reference_cast_error &) { + result = PYBIND11_TRY_NEXT_OVERLOAD; + } + + if (result.ptr() != PYBIND11_TRY_NEXT_OVERLOAD) + break; + + if (overloaded) { + // The (overloaded) call failed; if the call has at least one argument that + // permits conversion (i.e. it hasn't been explicitly specified `.noconvert()`) + // then add this call to the list of second pass overloads to try. + for (size_t i = func.is_method ? 1 : 0; i < pos_args; i++) { + if (second_pass_convert[i]) { + // Found one: swap the converting flags back in and store the call for + // the second pass. + call.args_convert.swap(second_pass_convert); + second_pass.push_back(std::move(call)); + break; + } + } + } + } + + if (overloaded && !second_pass.empty() && result.ptr() == PYBIND11_TRY_NEXT_OVERLOAD) { + // The no-conversion pass finished without success, try again with conversion allowed + for (auto &call : second_pass) { + try { + loader_life_support guard{}; + result = call.func.impl(call); + } catch (reference_cast_error &) { + result = PYBIND11_TRY_NEXT_OVERLOAD; + } + + if (result.ptr() != PYBIND11_TRY_NEXT_OVERLOAD) + break; + } + } + } catch (error_already_set &e) { + e.restore(); + return nullptr; + } catch (...) { + /* When an exception is caught, give each registered exception + translator a chance to translate it to a Python exception + in reverse order of registration. + + A translator may choose to do one of the following: + + - catch the exception and call PyErr_SetString or PyErr_SetObject + to set a standard (or custom) Python exception, or + - do nothing and let the exception fall through to the next translator, or + - delegate translation to the next translator by throwing a new type of exception. */ + + auto last_exception = std::current_exception(); + auto ®istered_exception_translators = get_internals().registered_exception_translators; + for (auto& translator : registered_exception_translators) { + try { + translator(last_exception); + } catch (...) { + last_exception = std::current_exception(); + continue; + } + return nullptr; + } + PyErr_SetString(PyExc_SystemError, "Exception escaped from default exception translator!"); + return nullptr; + } + + if (result.ptr() == PYBIND11_TRY_NEXT_OVERLOAD) { + if (overloads->is_operator) + return handle(Py_NotImplemented).inc_ref().ptr(); + + std::string msg = std::string(overloads->name) + "(): incompatible " + + std::string(overloads->is_constructor ? "constructor" : "function") + + " arguments. The following argument types are supported:\n"; + + int ctr = 0; + for (function_record *it2 = overloads; it2 != nullptr; it2 = it2->next) { + msg += " "+ std::to_string(++ctr) + ". "; + + bool wrote_sig = false; + if (overloads->is_constructor) { + // For a constructor, rewrite `(self: Object, arg0, ...) -> NoneType` as `Object(arg0, ...)` + std::string sig = it2->signature; + size_t start = sig.find('(') + 7; // skip "(self: " + if (start < sig.size()) { + // End at the , for the next argument + size_t end = sig.find(", "), next = end + 2; + size_t ret = sig.rfind(" -> "); + // Or the ), if there is no comma: + if (end >= sig.size()) next = end = sig.find(')'); + if (start < end && next < sig.size()) { + msg.append(sig, start, end - start); + msg += '('; + msg.append(sig, next, ret - next); + wrote_sig = true; + } + } + } + if (!wrote_sig) msg += it2->signature; + + msg += "\n"; + } + msg += "\nInvoked with: "; + auto args_ = reinterpret_borrow(args_in); + bool some_args = false; + for (size_t ti = overloads->is_constructor ? 1 : 0; ti < args_.size(); ++ti) { + if (!some_args) some_args = true; + else msg += ", "; + msg += pybind11::repr(args_[ti]); + } + if (kwargs_in) { + auto kwargs = reinterpret_borrow(kwargs_in); + if (kwargs.size() > 0) { + if (some_args) msg += "; "; + msg += "kwargs: "; + bool first = true; + for (auto kwarg : kwargs) { + if (first) first = false; + else msg += ", "; + msg += pybind11::str("{}={!r}").format(kwarg.first, kwarg.second); + } + } + } + + PyErr_SetString(PyExc_TypeError, msg.c_str()); + return nullptr; + } else if (!result) { + std::string msg = "Unable to convert function return value to a " + "Python type! The signature was\n\t"; + msg += it->signature; + PyErr_SetString(PyExc_TypeError, msg.c_str()); + return nullptr; + } else { + if (overloads->is_constructor) { + auto tinfo = get_type_info((PyTypeObject *) overloads->scope.ptr()); + tinfo->init_instance(reinterpret_cast(parent.ptr()), nullptr); + } + return result.ptr(); + } + } +}; + +/// Wrapper for Python extension modules +class module : public object { +public: + PYBIND11_OBJECT_DEFAULT(module, object, PyModule_Check) + + /// Create a new top-level Python module with the given name and docstring + explicit module(const char *name, const char *doc = nullptr) { + if (!options::show_user_defined_docstrings()) doc = nullptr; +#if PY_MAJOR_VERSION >= 3 + PyModuleDef *def = new PyModuleDef(); + std::memset(def, 0, sizeof(PyModuleDef)); + def->m_name = name; + def->m_doc = doc; + def->m_size = -1; + Py_INCREF(def); + m_ptr = PyModule_Create(def); +#else + m_ptr = Py_InitModule3(name, nullptr, doc); +#endif + if (m_ptr == nullptr) + pybind11_fail("Internal error in module::module()"); + inc_ref(); + } + + /** \rst + Create Python binding for a new function within the module scope. ``Func`` + can be a plain C++ function, a function pointer, or a lambda function. For + details on the ``Extra&& ... extra`` argument, see section :ref:`extras`. + \endrst */ + template + module &def(const char *name_, Func &&f, const Extra& ... extra) { + cpp_function func(std::forward(f), name(name_), scope(*this), + sibling(getattr(*this, name_, none())), extra...); + // NB: allow overwriting here because cpp_function sets up a chain with the intention of + // overwriting (and has already checked internally that it isn't overwriting non-functions). + add_object(name_, func, true /* overwrite */); + return *this; + } + + /** \rst + Create and return a new Python submodule with the given name and docstring. + This also works recursively, i.e. + + .. code-block:: cpp + + py::module m("example", "pybind11 example plugin"); + py::module m2 = m.def_submodule("sub", "A submodule of 'example'"); + py::module m3 = m2.def_submodule("subsub", "A submodule of 'example.sub'"); + \endrst */ + module def_submodule(const char *name, const char *doc = nullptr) { + std::string full_name = std::string(PyModule_GetName(m_ptr)) + + std::string(".") + std::string(name); + auto result = reinterpret_borrow(PyImport_AddModule(full_name.c_str())); + if (doc && options::show_user_defined_docstrings()) + result.attr("__doc__") = pybind11::str(doc); + attr(name) = result; + return result; + } + + /// Import and return a module or throws `error_already_set`. + static module import(const char *name) { + PyObject *obj = PyImport_ImportModule(name); + if (!obj) + throw error_already_set(); + return reinterpret_steal(obj); + } + + // Adds an object to the module using the given name. Throws if an object with the given name + // already exists. + // + // overwrite should almost always be false: attempting to overwrite objects that pybind11 has + // established will, in most cases, break things. + PYBIND11_NOINLINE void add_object(const char *name, handle obj, bool overwrite = false) { + if (!overwrite && hasattr(*this, name)) + pybind11_fail("Error during initialization: multiple incompatible definitions with name \"" + + std::string(name) + "\""); + + PyModule_AddObject(ptr(), name, obj.inc_ref().ptr() /* steals a reference */); + } +}; + +/// \ingroup python_builtins +/// Return a dictionary representing the global variables in the current execution frame, +/// or ``__main__.__dict__`` if there is no frame (usually when the interpreter is embedded). +inline dict globals() { + PyObject *p = PyEval_GetGlobals(); + return reinterpret_borrow(p ? p : module::import("__main__").attr("__dict__").ptr()); +} + +NAMESPACE_BEGIN(detail) +/// Generic support for creating new Python heap types +class generic_type : public object { + template friend class class_; +public: + PYBIND11_OBJECT_DEFAULT(generic_type, object, PyType_Check) +protected: + void initialize(const type_record &rec) { + if (rec.scope && hasattr(rec.scope, rec.name)) + pybind11_fail("generic_type: cannot initialize type \"" + std::string(rec.name) + + "\": an object with that name is already defined"); + + if (get_type_info(*rec.type)) + pybind11_fail("generic_type: type \"" + std::string(rec.name) + + "\" is already registered!"); + + m_ptr = make_new_python_type(rec); + + /* Register supplemental type information in C++ dict */ + auto *tinfo = new detail::type_info(); + tinfo->type = (PyTypeObject *) m_ptr; + tinfo->cpptype = rec.type; + tinfo->type_size = rec.type_size; + tinfo->operator_new = rec.operator_new; + tinfo->holder_size_in_ptrs = size_in_ptrs(rec.holder_size); + tinfo->init_instance = rec.init_instance; + tinfo->dealloc = rec.dealloc; + tinfo->simple_type = true; + tinfo->simple_ancestors = true; + + auto &internals = get_internals(); + auto tindex = std::type_index(*rec.type); + tinfo->direct_conversions = &internals.direct_conversions[tindex]; + tinfo->default_holder = rec.default_holder; + internals.registered_types_cpp[tindex] = tinfo; + internals.registered_types_py[(PyTypeObject *) m_ptr] = { tinfo }; + + if (rec.bases.size() > 1 || rec.multiple_inheritance) { + mark_parents_nonsimple(tinfo->type); + tinfo->simple_ancestors = false; + } + else if (rec.bases.size() == 1) { + auto parent_tinfo = get_type_info((PyTypeObject *) rec.bases[0].ptr()); + tinfo->simple_ancestors = parent_tinfo->simple_ancestors; + } + } + + /// Helper function which tags all parents of a type using mult. inheritance + void mark_parents_nonsimple(PyTypeObject *value) { + auto t = reinterpret_borrow(value->tp_bases); + for (handle h : t) { + auto tinfo2 = get_type_info((PyTypeObject *) h.ptr()); + if (tinfo2) + tinfo2->simple_type = false; + mark_parents_nonsimple((PyTypeObject *) h.ptr()); + } + } + + void install_buffer_funcs( + buffer_info *(*get_buffer)(PyObject *, void *), + void *get_buffer_data) { + PyHeapTypeObject *type = (PyHeapTypeObject*) m_ptr; + auto tinfo = detail::get_type_info(&type->ht_type); + + if (!type->ht_type.tp_as_buffer) + pybind11_fail( + "To be able to register buffer protocol support for the type '" + + std::string(tinfo->type->tp_name) + + "' the associated class<>(..) invocation must " + "include the pybind11::buffer_protocol() annotation!"); + + tinfo->get_buffer = get_buffer; + tinfo->get_buffer_data = get_buffer_data; + } + + void def_property_static_impl(const char *name, + handle fget, handle fset, + detail::function_record *rec_fget) { + const auto is_static = !(rec_fget->is_method && rec_fget->scope); + const auto has_doc = rec_fget->doc && pybind11::options::show_user_defined_docstrings(); + + auto property = handle((PyObject *) (is_static ? get_internals().static_property_type + : &PyProperty_Type)); + attr(name) = property(fget.ptr() ? fget : none(), + fset.ptr() ? fset : none(), + /*deleter*/none(), + pybind11::str(has_doc ? rec_fget->doc : "")); + } +}; + +/// Set the pointer to operator new if it exists. The cast is needed because it can be overloaded. +template (T::operator new))>> +void set_operator_new(type_record *r) { r->operator_new = &T::operator new; } + +template void set_operator_new(...) { } + +template struct has_operator_delete : std::false_type { }; +template struct has_operator_delete(T::operator delete))>> + : std::true_type { }; +template struct has_operator_delete_size : std::false_type { }; +template struct has_operator_delete_size(T::operator delete))>> + : std::true_type { }; +/// Call class-specific delete if it exists or global otherwise. Can also be an overload set. +template ::value, int> = 0> +void call_operator_delete(T *p, size_t) { T::operator delete(p); } +template ::value && has_operator_delete_size::value, int> = 0> +void call_operator_delete(T *p, size_t s) { T::operator delete(p, s); } + +inline void call_operator_delete(void *p, size_t) { ::operator delete(p); } + +NAMESPACE_END(detail) + +/// Given a pointer to a member function, cast it to its `Derived` version. +/// Forward everything else unchanged. +template +auto method_adaptor(F &&f) -> decltype(std::forward(f)) { return std::forward(f); } + +template +auto method_adaptor(Return (Class::*pmf)(Args...)) -> Return (Derived::*)(Args...) { return pmf; } + +template +auto method_adaptor(Return (Class::*pmf)(Args...) const) -> Return (Derived::*)(Args...) const { return pmf; } + +template +class class_ : public detail::generic_type { + template using is_holder = detail::is_holder_type; + template using is_subtype = detail::is_strict_base_of; + template using is_base = detail::is_strict_base_of; + // struct instead of using here to help MSVC: + template struct is_valid_class_option : + detail::any_of, is_subtype, is_base> {}; + +public: + using type = type_; + using type_alias = detail::exactly_one_t; + constexpr static bool has_alias = !std::is_void::value; + using holder_type = detail::exactly_one_t, options...>; + + static_assert(detail::all_of...>::value, + "Unknown/invalid class_ template parameters provided"); + + PYBIND11_OBJECT(class_, generic_type, PyType_Check) + + template + class_(handle scope, const char *name, const Extra &... extra) { + using namespace detail; + + // MI can only be specified via class_ template options, not constructor parameters + static_assert( + none_of...>::value || // no base class arguments, or: + ( constexpr_sum(is_pyobject::value...) == 1 && // Exactly one base + constexpr_sum(is_base::value...) == 0 && // no template option bases + none_of...>::value), // no multiple_inheritance attr + "Error: multiple inheritance bases must be specified via class_ template options"); + + type_record record; + record.scope = scope; + record.name = name; + record.type = &typeid(type); + record.type_size = sizeof(conditional_t); + record.holder_size = sizeof(holder_type); + record.init_instance = init_instance; + record.dealloc = dealloc; + record.default_holder = std::is_same>::value; + + set_operator_new(&record); + + /* Register base classes specified via template arguments to class_, if any */ + PYBIND11_EXPAND_SIDE_EFFECTS(add_base(record)); + + /* Process optional arguments, if any */ + process_attributes::init(extra..., &record); + + generic_type::initialize(record); + + if (has_alias) { + auto &instances = get_internals().registered_types_cpp; + instances[std::type_index(typeid(type_alias))] = instances[std::type_index(typeid(type))]; + } + } + + template ::value, int> = 0> + static void add_base(detail::type_record &rec) { + rec.add_base(typeid(Base), [](void *src) -> void * { + return static_cast(reinterpret_cast(src)); + }); + } + + template ::value, int> = 0> + static void add_base(detail::type_record &) { } + + template + class_ &def(const char *name_, Func&& f, const Extra&... extra) { + cpp_function cf(method_adaptor(std::forward(f)), name(name_), is_method(*this), + sibling(getattr(*this, name_, none())), extra...); + attr(cf.name()) = cf; + return *this; + } + + template class_ & + def_static(const char *name_, Func &&f, const Extra&... extra) { + static_assert(!std::is_member_function_pointer::value, + "def_static(...) called with a non-static member function pointer"); + cpp_function cf(std::forward(f), name(name_), scope(*this), + sibling(getattr(*this, name_, none())), extra...); + attr(cf.name()) = cf; + return *this; + } + + template + class_ &def(const detail::op_ &op, const Extra&... extra) { + op.execute(*this, extra...); + return *this; + } + + template + class_ & def_cast(const detail::op_ &op, const Extra&... extra) { + op.execute_cast(*this, extra...); + return *this; + } + + template + class_ &def(const detail::init &init, const Extra&... extra) { + init.execute(*this, extra...); + return *this; + } + + template + class_ &def(const detail::init_alias &init, const Extra&... extra) { + init.execute(*this, extra...); + return *this; + } + + template class_& def_buffer(Func &&func) { + struct capture { Func func; }; + capture *ptr = new capture { std::forward(func) }; + install_buffer_funcs([](PyObject *obj, void *ptr) -> buffer_info* { + detail::make_caster caster; + if (!caster.load(obj, false)) + return nullptr; + return new buffer_info(((capture *) ptr)->func(caster)); + }, ptr); + return *this; + } + + template + class_ &def_buffer(Return (Class::*func)(Args...)) { + return def_buffer([func] (type &obj) { return (obj.*func)(); }); + } + + template + class_ &def_buffer(Return (Class::*func)(Args...) const) { + return def_buffer([func] (const type &obj) { return (obj.*func)(); }); + } + + template + class_ &def_readwrite(const char *name, D C::*pm, const Extra&... extra) { + static_assert(std::is_base_of::value, "def_readwrite() requires a class member (or base class member)"); + cpp_function fget([pm](const type &c) -> const D &{ return c.*pm; }, is_method(*this)), + fset([pm](type &c, const D &value) { c.*pm = value; }, is_method(*this)); + def_property(name, fget, fset, return_value_policy::reference_internal, extra...); + return *this; + } + + template + class_ &def_readonly(const char *name, const D C::*pm, const Extra& ...extra) { + static_assert(std::is_base_of::value, "def_readonly() requires a class member (or base class member)"); + cpp_function fget([pm](const type &c) -> const D &{ return c.*pm; }, is_method(*this)); + def_property_readonly(name, fget, return_value_policy::reference_internal, extra...); + return *this; + } + + template + class_ &def_readwrite_static(const char *name, D *pm, const Extra& ...extra) { + cpp_function fget([pm](object) -> const D &{ return *pm; }, scope(*this)), + fset([pm](object, const D &value) { *pm = value; }, scope(*this)); + def_property_static(name, fget, fset, return_value_policy::reference, extra...); + return *this; + } + + template + class_ &def_readonly_static(const char *name, const D *pm, const Extra& ...extra) { + cpp_function fget([pm](object) -> const D &{ return *pm; }, scope(*this)); + def_property_readonly_static(name, fget, return_value_policy::reference, extra...); + return *this; + } + + /// Uses return_value_policy::reference_internal by default + template + class_ &def_property_readonly(const char *name, const Getter &fget, const Extra& ...extra) { + return def_property_readonly(name, cpp_function(method_adaptor(fget)), + return_value_policy::reference_internal, extra...); + } + + /// Uses cpp_function's return_value_policy by default + template + class_ &def_property_readonly(const char *name, const cpp_function &fget, const Extra& ...extra) { + return def_property(name, fget, cpp_function(), extra...); + } + + /// Uses return_value_policy::reference by default + template + class_ &def_property_readonly_static(const char *name, const Getter &fget, const Extra& ...extra) { + return def_property_readonly_static(name, cpp_function(fget), return_value_policy::reference, extra...); + } + + /// Uses cpp_function's return_value_policy by default + template + class_ &def_property_readonly_static(const char *name, const cpp_function &fget, const Extra& ...extra) { + return def_property_static(name, fget, cpp_function(), extra...); + } + + /// Uses return_value_policy::reference_internal by default + template + class_ &def_property(const char *name, const Getter &fget, const Setter &fset, const Extra& ...extra) { + return def_property(name, fget, cpp_function(method_adaptor(fset)), extra...); + } + template + class_ &def_property(const char *name, const Getter &fget, const cpp_function &fset, const Extra& ...extra) { + return def_property(name, cpp_function(method_adaptor(fget)), fset, + return_value_policy::reference_internal, extra...); + } + + /// Uses cpp_function's return_value_policy by default + template + class_ &def_property(const char *name, const cpp_function &fget, const cpp_function &fset, const Extra& ...extra) { + return def_property_static(name, fget, fset, is_method(*this), extra...); + } + + /// Uses return_value_policy::reference by default + template + class_ &def_property_static(const char *name, const Getter &fget, const cpp_function &fset, const Extra& ...extra) { + return def_property_static(name, cpp_function(fget), fset, return_value_policy::reference, extra...); + } + + /// Uses cpp_function's return_value_policy by default + template + class_ &def_property_static(const char *name, const cpp_function &fget, const cpp_function &fset, const Extra& ...extra) { + auto rec_fget = get_function_record(fget), rec_fset = get_function_record(fset); + char *doc_prev = rec_fget->doc; /* 'extra' field may include a property-specific documentation string */ + detail::process_attributes::init(extra..., rec_fget); + if (rec_fget->doc && rec_fget->doc != doc_prev) { + free(doc_prev); + rec_fget->doc = strdup(rec_fget->doc); + } + if (rec_fset) { + doc_prev = rec_fset->doc; + detail::process_attributes::init(extra..., rec_fset); + if (rec_fset->doc && rec_fset->doc != doc_prev) { + free(doc_prev); + rec_fset->doc = strdup(rec_fset->doc); + } + } + def_property_static_impl(name, fget, fset, rec_fget); + return *this; + } + +private: + /// Initialize holder object, variant 1: object derives from enable_shared_from_this + template + static void init_holder(detail::instance *inst, detail::value_and_holder &v_h, + const holder_type * /* unused */, const std::enable_shared_from_this * /* dummy */) { + try { + auto sh = std::dynamic_pointer_cast( + v_h.value_ptr()->shared_from_this()); + if (sh) { + new (&v_h.holder()) holder_type(std::move(sh)); + v_h.set_holder_constructed(); + } + } catch (const std::bad_weak_ptr &) {} + + if (!v_h.holder_constructed() && inst->owned) { + new (&v_h.holder()) holder_type(v_h.value_ptr()); + v_h.set_holder_constructed(); + } + } + + static void init_holder_from_existing(const detail::value_and_holder &v_h, + const holder_type *holder_ptr, std::true_type /*is_copy_constructible*/) { + new (&v_h.holder()) holder_type(*reinterpret_cast(holder_ptr)); + } + + static void init_holder_from_existing(const detail::value_and_holder &v_h, + const holder_type *holder_ptr, std::false_type /*is_copy_constructible*/) { + new (&v_h.holder()) holder_type(std::move(*const_cast(holder_ptr))); + } + + /// Initialize holder object, variant 2: try to construct from existing holder object, if possible + static void init_holder(detail::instance *inst, detail::value_and_holder &v_h, + const holder_type *holder_ptr, const void * /* dummy -- not enable_shared_from_this) */) { + if (holder_ptr) { + init_holder_from_existing(v_h, holder_ptr, std::is_copy_constructible()); + v_h.set_holder_constructed(); + } else if (inst->owned || detail::always_construct_holder::value) { + new (&v_h.holder()) holder_type(v_h.value_ptr()); + v_h.set_holder_constructed(); + } + } + + /// Performs instance initialization including constructing a holder and registering the known + /// instance. Should be called as soon as the `type` value_ptr is set for an instance. Takes an + /// optional pointer to an existing holder to use; if not specified and the instance is + /// `.owned`, a new holder will be constructed to manage the value pointer. + static void init_instance(detail::instance *inst, const void *holder_ptr) { + auto v_h = inst->get_value_and_holder(detail::get_type_info(typeid(type))); + if (!v_h.instance_registered()) { + register_instance(inst, v_h.value_ptr(), v_h.type); + v_h.set_instance_registered(); + } + init_holder(inst, v_h, (const holder_type *) holder_ptr, v_h.value_ptr()); + } + + /// Deallocates an instance; via holder, if constructed; otherwise via operator delete. + static void dealloc(const detail::value_and_holder &v_h) { + if (v_h.holder_constructed()) + v_h.holder().~holder_type(); + else + detail::call_operator_delete(v_h.value_ptr(), v_h.type->type_size); + } + + static detail::function_record *get_function_record(handle h) { + h = detail::get_function(h); + return h ? (detail::function_record *) reinterpret_borrow(PyCFunction_GET_SELF(h.ptr())) + : nullptr; + } +}; + +/// Binds C++ enumerations and enumeration classes to Python +template class enum_ : public class_ { +public: + using class_::def; + using class_::def_property_readonly_static; + using Scalar = typename std::underlying_type::type; + + template + enum_(const handle &scope, const char *name, const Extra&... extra) + : class_(scope, name, extra...), m_entries(), m_parent(scope) { + + constexpr bool is_arithmetic = detail::any_of...>::value; + + auto m_entries_ptr = m_entries.inc_ref().ptr(); + def("__repr__", [name, m_entries_ptr](Type value) -> pybind11::str { + for (const auto &kv : reinterpret_borrow(m_entries_ptr)) { + if (pybind11::cast(kv.second) == value) + return pybind11::str("{}.{}").format(name, kv.first); + } + return pybind11::str("{}.???").format(name); + }); + def_property_readonly_static("__members__", [m_entries_ptr](object /* self */) { + dict m; + for (const auto &kv : reinterpret_borrow(m_entries_ptr)) + m[kv.first] = kv.second; + return m; + }, return_value_policy::copy); + def("__init__", [](Type& value, Scalar i) { value = (Type)i; }); + def("__int__", [](Type value) { return (Scalar) value; }); + #if PY_MAJOR_VERSION < 3 + def("__long__", [](Type value) { return (Scalar) value; }); + #endif + def("__eq__", [](const Type &value, Type *value2) { return value2 && value == *value2; }); + def("__ne__", [](const Type &value, Type *value2) { return !value2 || value != *value2; }); + if (is_arithmetic) { + def("__lt__", [](const Type &value, Type *value2) { return value2 && value < *value2; }); + def("__gt__", [](const Type &value, Type *value2) { return value2 && value > *value2; }); + def("__le__", [](const Type &value, Type *value2) { return value2 && value <= *value2; }); + def("__ge__", [](const Type &value, Type *value2) { return value2 && value >= *value2; }); + } + if (std::is_convertible::value) { + // Don't provide comparison with the underlying type if the enum isn't convertible, + // i.e. if Type is a scoped enum, mirroring the C++ behaviour. (NB: we explicitly + // convert Type to Scalar below anyway because this needs to compile). + def("__eq__", [](const Type &value, Scalar value2) { return (Scalar) value == value2; }); + def("__ne__", [](const Type &value, Scalar value2) { return (Scalar) value != value2; }); + if (is_arithmetic) { + def("__lt__", [](const Type &value, Scalar value2) { return (Scalar) value < value2; }); + def("__gt__", [](const Type &value, Scalar value2) { return (Scalar) value > value2; }); + def("__le__", [](const Type &value, Scalar value2) { return (Scalar) value <= value2; }); + def("__ge__", [](const Type &value, Scalar value2) { return (Scalar) value >= value2; }); + def("__invert__", [](const Type &value) { return ~((Scalar) value); }); + def("__and__", [](const Type &value, Scalar value2) { return (Scalar) value & value2; }); + def("__or__", [](const Type &value, Scalar value2) { return (Scalar) value | value2; }); + def("__xor__", [](const Type &value, Scalar value2) { return (Scalar) value ^ value2; }); + def("__rand__", [](const Type &value, Scalar value2) { return (Scalar) value & value2; }); + def("__ror__", [](const Type &value, Scalar value2) { return (Scalar) value | value2; }); + def("__rxor__", [](const Type &value, Scalar value2) { return (Scalar) value ^ value2; }); + def("__and__", [](const Type &value, const Type &value2) { return (Scalar) value & (Scalar) value2; }); + def("__or__", [](const Type &value, const Type &value2) { return (Scalar) value | (Scalar) value2; }); + def("__xor__", [](const Type &value, const Type &value2) { return (Scalar) value ^ (Scalar) value2; }); + } + } + def("__hash__", [](const Type &value) { return (Scalar) value; }); + // Pickling and unpickling -- needed for use with the 'multiprocessing' module + def("__getstate__", [](const Type &value) { return pybind11::make_tuple((Scalar) value); }); + def("__setstate__", [](Type &p, tuple t) { new (&p) Type((Type) t[0].cast()); }); + } + + /// Export enumeration entries into the parent scope + enum_& export_values() { + for (const auto &kv : m_entries) + m_parent.attr(kv.first) = kv.second; + return *this; + } + + /// Add an enumeration entry + enum_& value(char const* name, Type value) { + auto v = pybind11::cast(value, return_value_policy::copy); + this->attr(name) = v; + m_entries[pybind11::str(name)] = v; + return *this; + } + +private: + dict m_entries; + handle m_parent; +}; + +NAMESPACE_BEGIN(detail) +template struct init { + template = 0> + static void execute(Class &cl, const Extra&... extra) { + using Base = typename Class::type; + /// Function which calls a specific C++ in-place constructor + cl.def("__init__", [](Base *self_, Args... args) { new (self_) Base(args...); }, extra...); + } + + template ::value, int> = 0> + static void execute(Class &cl, const Extra&... extra) { + using Base = typename Class::type; + using Alias = typename Class::type_alias; + handle cl_type = cl; + cl.def("__init__", [cl_type](handle self_, Args... args) { + if (self_.get_type().is(cl_type)) + new (self_.cast()) Base(args...); + else + new (self_.cast()) Alias(args...); + }, extra...); + } + + template ::value, int> = 0> + static void execute(Class &cl, const Extra&... extra) { + init_alias::execute(cl, extra...); + } +}; +template struct init_alias { + template ::value, int> = 0> + static void execute(Class &cl, const Extra&... extra) { + using Alias = typename Class::type_alias; + cl.def("__init__", [](Alias *self_, Args... args) { new (self_) Alias(args...); }, extra...); + } +}; + + +inline void keep_alive_impl(handle nurse, handle patient) { + if (!nurse || !patient) + pybind11_fail("Could not activate keep_alive!"); + + if (patient.is_none() || nurse.is_none()) + return; /* Nothing to keep alive or nothing to be kept alive by */ + + auto tinfo = all_type_info(Py_TYPE(nurse.ptr())); + if (!tinfo.empty()) { + /* It's a pybind-registered type, so we can store the patient in the + * internal list. */ + add_patient(nurse.ptr(), patient.ptr()); + } + else { + /* Fall back to clever approach based on weak references taken from + * Boost.Python. This is not used for pybind-registered types because + * the objects can be destroyed out-of-order in a GC pass. */ + cpp_function disable_lifesupport( + [patient](handle weakref) { patient.dec_ref(); weakref.dec_ref(); }); + + weakref wr(nurse, disable_lifesupport); + + patient.inc_ref(); /* reference patient and leak the weak reference */ + (void) wr.release(); + } +} + +PYBIND11_NOINLINE inline void keep_alive_impl(size_t Nurse, size_t Patient, function_call &call, handle ret) { + keep_alive_impl( + Nurse == 0 ? ret : Nurse <= call.args.size() ? call.args[Nurse - 1] : handle(), + Patient == 0 ? ret : Patient <= call.args.size() ? call.args[Patient - 1] : handle() + ); +} + +inline std::pair all_type_info_get_cache(PyTypeObject *type) { + auto res = get_internals().registered_types_py +#ifdef __cpp_lib_unordered_map_try_emplace + .try_emplace(type); +#else + .emplace(type, std::vector()); +#endif + if (res.second) { + // New cache entry created; set up a weak reference to automatically remove it if the type + // gets destroyed: + weakref((PyObject *) type, cpp_function([type](handle wr) { + get_internals().registered_types_py.erase(type); + wr.dec_ref(); + })).release(); + } + + return res; +} + +template +struct iterator_state { + Iterator it; + Sentinel end; + bool first_or_done; +}; + +NAMESPACE_END(detail) + +template detail::init init() { return detail::init(); } +template detail::init_alias init_alias() { return detail::init_alias(); } + +/// Makes a python iterator from a first and past-the-end C++ InputIterator. +template ()), + typename... Extra> +iterator make_iterator(Iterator first, Sentinel last, Extra &&... extra) { + typedef detail::iterator_state state; + + if (!detail::get_type_info(typeid(state), false)) { + class_(handle(), "iterator") + .def("__iter__", [](state &s) -> state& { return s; }) + .def("__next__", [](state &s) -> ValueType { + if (!s.first_or_done) + ++s.it; + else + s.first_or_done = false; + if (s.it == s.end) { + s.first_or_done = true; + throw stop_iteration(); + } + return *s.it; + }, std::forward(extra)..., Policy); + } + + return cast(state{first, last, true}); +} + +/// Makes an python iterator over the keys (`.first`) of a iterator over pairs from a +/// first and past-the-end InputIterator. +template ()).first), + typename... Extra> +iterator make_key_iterator(Iterator first, Sentinel last, Extra &&... extra) { + typedef detail::iterator_state state; + + if (!detail::get_type_info(typeid(state), false)) { + class_(handle(), "iterator") + .def("__iter__", [](state &s) -> state& { return s; }) + .def("__next__", [](state &s) -> KeyType { + if (!s.first_or_done) + ++s.it; + else + s.first_or_done = false; + if (s.it == s.end) { + s.first_or_done = true; + throw stop_iteration(); + } + return (*s.it).first; + }, std::forward(extra)..., Policy); + } + + return cast(state{first, last, true}); +} + +/// Makes an iterator over values of an stl container or other container supporting +/// `std::begin()`/`std::end()` +template iterator make_iterator(Type &value, Extra&&... extra) { + return make_iterator(std::begin(value), std::end(value), extra...); +} + +/// Makes an iterator over the keys (`.first`) of a stl map-like container supporting +/// `std::begin()`/`std::end()` +template iterator make_key_iterator(Type &value, Extra&&... extra) { + return make_key_iterator(std::begin(value), std::end(value), extra...); +} + +template void implicitly_convertible() { + auto implicit_caster = [](PyObject *obj, PyTypeObject *type) -> PyObject * { + if (!detail::make_caster().load(obj, false)) + return nullptr; + tuple args(1); + args[0] = obj; + PyObject *result = PyObject_Call((PyObject *) type, args.ptr(), nullptr); + if (result == nullptr) + PyErr_Clear(); + return result; + }; + + if (auto tinfo = detail::get_type_info(typeid(OutputType))) + tinfo->implicit_conversions.push_back(implicit_caster); + else + pybind11_fail("implicitly_convertible: Unable to find type " + type_id()); +} + +template +void register_exception_translator(ExceptionTranslator&& translator) { + detail::get_internals().registered_exception_translators.push_front( + std::forward(translator)); +} + +/** + * Wrapper to generate a new Python exception type. + * + * This should only be used with PyErr_SetString for now. + * It is not (yet) possible to use as a py::base. + * Template type argument is reserved for future use. + */ +template +class exception : public object { +public: + exception(handle scope, const char *name, PyObject *base = PyExc_Exception) { + std::string full_name = scope.attr("__name__").cast() + + std::string(".") + name; + m_ptr = PyErr_NewException(const_cast(full_name.c_str()), base, NULL); + if (hasattr(scope, name)) + pybind11_fail("Error during initialization: multiple incompatible " + "definitions with name \"" + std::string(name) + "\""); + scope.attr(name) = *this; + } + + // Sets the current python exception to this exception object with the given message + void operator()(const char *message) { + PyErr_SetString(m_ptr, message); + } +}; + +/** + * Registers a Python exception in `m` of the given `name` and installs an exception translator to + * translate the C++ exception to the created Python exception using the exceptions what() method. + * This is intended for simple exception translations; for more complex translation, register the + * exception object and translator directly. + */ +template +exception ®ister_exception(handle scope, + const char *name, + PyObject *base = PyExc_Exception) { + static exception ex(scope, name, base); + register_exception_translator([](std::exception_ptr p) { + if (!p) return; + try { + std::rethrow_exception(p); + } catch (const CppException &e) { + ex(e.what()); + } + }); + return ex; +} + +NAMESPACE_BEGIN(detail) +PYBIND11_NOINLINE inline void print(tuple args, dict kwargs) { + auto strings = tuple(args.size()); + for (size_t i = 0; i < args.size(); ++i) { + strings[i] = str(args[i]); + } + auto sep = kwargs.contains("sep") ? kwargs["sep"] : cast(" "); + auto line = sep.attr("join")(strings); + + object file; + if (kwargs.contains("file")) { + file = kwargs["file"].cast(); + } else { + try { + file = module::import("sys").attr("stdout"); + } catch (const error_already_set &) { + /* If print() is called from code that is executed as + part of garbage collection during interpreter shutdown, + importing 'sys' can fail. Give up rather than crashing the + interpreter in this case. */ + return; + } + } + + auto write = file.attr("write"); + write(line); + write(kwargs.contains("end") ? kwargs["end"] : cast("\n")); + + if (kwargs.contains("flush") && kwargs["flush"].cast()) + file.attr("flush")(); +} +NAMESPACE_END(detail) + +template +void print(Args &&...args) { + auto c = detail::collect_arguments(std::forward(args)...); + detail::print(c.args(), c.kwargs()); +} + +#if defined(WITH_THREAD) && !defined(PYPY_VERSION) + +/* The functions below essentially reproduce the PyGILState_* API using a RAII + * pattern, but there are a few important differences: + * + * 1. When acquiring the GIL from an non-main thread during the finalization + * phase, the GILState API blindly terminates the calling thread, which + * is often not what is wanted. This API does not do this. + * + * 2. The gil_scoped_release function can optionally cut the relationship + * of a PyThreadState and its associated thread, which allows moving it to + * another thread (this is a fairly rare/advanced use case). + * + * 3. The reference count of an acquired thread state can be controlled. This + * can be handy to prevent cases where callbacks issued from an external + * thread would otherwise constantly construct and destroy thread state data + * structures. + * + * See the Python bindings of NanoGUI (http://github.com/wjakob/nanogui) for an + * example which uses features 2 and 3 to migrate the Python thread of + * execution to another thread (to run the event loop on the original thread, + * in this case). + */ + +class gil_scoped_acquire { +public: + PYBIND11_NOINLINE gil_scoped_acquire() { + auto const &internals = detail::get_internals(); + tstate = (PyThreadState *) PyThread_get_key_value(internals.tstate); + + if (!tstate) { + tstate = PyThreadState_New(internals.istate); + #if !defined(NDEBUG) + if (!tstate) + pybind11_fail("scoped_acquire: could not create thread state!"); + #endif + tstate->gilstate_counter = 0; + #if PY_MAJOR_VERSION < 3 + PyThread_delete_key_value(internals.tstate); + #endif + PyThread_set_key_value(internals.tstate, tstate); + } else { + release = detail::get_thread_state_unchecked() != tstate; + } + + if (release) { + /* Work around an annoying assertion in PyThreadState_Swap */ + #if defined(Py_DEBUG) + PyInterpreterState *interp = tstate->interp; + tstate->interp = nullptr; + #endif + PyEval_AcquireThread(tstate); + #if defined(Py_DEBUG) + tstate->interp = interp; + #endif + } + + inc_ref(); + } + + void inc_ref() { + ++tstate->gilstate_counter; + } + + PYBIND11_NOINLINE void dec_ref() { + --tstate->gilstate_counter; + #if !defined(NDEBUG) + if (detail::get_thread_state_unchecked() != tstate) + pybind11_fail("scoped_acquire::dec_ref(): thread state must be current!"); + if (tstate->gilstate_counter < 0) + pybind11_fail("scoped_acquire::dec_ref(): reference count underflow!"); + #endif + if (tstate->gilstate_counter == 0) { + #if !defined(NDEBUG) + if (!release) + pybind11_fail("scoped_acquire::dec_ref(): internal error!"); + #endif + PyThreadState_Clear(tstate); + PyThreadState_DeleteCurrent(); + PyThread_delete_key_value(detail::get_internals().tstate); + release = false; + } + } + + PYBIND11_NOINLINE ~gil_scoped_acquire() { + dec_ref(); + if (release) + PyEval_SaveThread(); + } +private: + PyThreadState *tstate = nullptr; + bool release = true; +}; + +class gil_scoped_release { +public: + explicit gil_scoped_release(bool disassoc = false) : disassoc(disassoc) { + // `get_internals()` must be called here unconditionally in order to initialize + // `internals.tstate` for subsequent `gil_scoped_acquire` calls. Otherwise, an + // initialization race could occur as multiple threads try `gil_scoped_acquire`. + const auto &internals = detail::get_internals(); + tstate = PyEval_SaveThread(); + if (disassoc) { + auto key = internals.tstate; + #if PY_MAJOR_VERSION < 3 + PyThread_delete_key_value(key); + #else + PyThread_set_key_value(key, nullptr); + #endif + } + } + ~gil_scoped_release() { + if (!tstate) + return; + PyEval_RestoreThread(tstate); + if (disassoc) { + auto key = detail::get_internals().tstate; + #if PY_MAJOR_VERSION < 3 + PyThread_delete_key_value(key); + #endif + PyThread_set_key_value(key, tstate); + } + } +private: + PyThreadState *tstate; + bool disassoc; +}; +#elif defined(PYPY_VERSION) +class gil_scoped_acquire { + PyGILState_STATE state; +public: + gil_scoped_acquire() { state = PyGILState_Ensure(); } + ~gil_scoped_acquire() { PyGILState_Release(state); } +}; + +class gil_scoped_release { + PyThreadState *state; +public: + gil_scoped_release() { state = PyEval_SaveThread(); } + ~gil_scoped_release() { PyEval_RestoreThread(state); } +}; +#else +class gil_scoped_acquire { }; +class gil_scoped_release { }; +#endif + +error_already_set::~error_already_set() { + if (type) { + gil_scoped_acquire gil; + type.release().dec_ref(); + value.release().dec_ref(); + trace.release().dec_ref(); + } +} + +inline function get_type_overload(const void *this_ptr, const detail::type_info *this_type, const char *name) { + handle self = detail::get_object_handle(this_ptr, this_type); + if (!self) + return function(); + handle type = self.get_type(); + auto key = std::make_pair(type.ptr(), name); + + /* Cache functions that aren't overloaded in Python to avoid + many costly Python dictionary lookups below */ + auto &cache = detail::get_internals().inactive_overload_cache; + if (cache.find(key) != cache.end()) + return function(); + + function overload = getattr(self, name, function()); + if (overload.is_cpp_function()) { + cache.insert(key); + return function(); + } + + /* Don't call dispatch code if invoked from overridden function. + Unfortunately this doesn't work on PyPy. */ +#if !defined(PYPY_VERSION) + PyFrameObject *frame = PyThreadState_Get()->frame; + if (frame && (std::string) str(frame->f_code->co_name) == name && + frame->f_code->co_argcount > 0) { + PyFrame_FastToLocals(frame); + PyObject *self_caller = PyDict_GetItem( + frame->f_locals, PyTuple_GET_ITEM(frame->f_code->co_varnames, 0)); + if (self_caller == self.ptr()) + return function(); + } +#else + /* PyPy currently doesn't provide a detailed cpyext emulation of + frame objects, so we have to emulate this using Python. This + is going to be slow..*/ + dict d; d["self"] = self; d["name"] = pybind11::str(name); + PyObject *result = PyRun_String( + "import inspect\n" + "frame = inspect.currentframe()\n" + "if frame is not None:\n" + " frame = frame.f_back\n" + " if frame is not None and str(frame.f_code.co_name) == name and " + "frame.f_code.co_argcount > 0:\n" + " self_caller = frame.f_locals[frame.f_code.co_varnames[0]]\n" + " if self_caller == self:\n" + " self = None\n", + Py_file_input, d.ptr(), d.ptr()); + if (result == nullptr) + throw error_already_set(); + if (d["self"].is_none()) + return function(); + Py_DECREF(result); +#endif + + return overload; +} + +template function get_overload(const T *this_ptr, const char *name) { + auto tinfo = detail::get_type_info(typeid(T)); + return tinfo ? get_type_overload(this_ptr, tinfo, name) : function(); +} + +#define PYBIND11_OVERLOAD_INT(ret_type, cname, name, ...) { \ + pybind11::gil_scoped_acquire gil; \ + pybind11::function overload = pybind11::get_overload(static_cast(this), name); \ + if (overload) { \ + auto o = overload(__VA_ARGS__); \ + if (pybind11::detail::cast_is_temporary_value_reference::value) { \ + static pybind11::detail::overload_caster_t caster; \ + return pybind11::detail::cast_ref(std::move(o), caster); \ + } \ + else return pybind11::detail::cast_safe(std::move(o)); \ + } \ + } + +#define PYBIND11_OVERLOAD_NAME(ret_type, cname, name, fn, ...) \ + PYBIND11_OVERLOAD_INT(ret_type, cname, name, __VA_ARGS__) \ + return cname::fn(__VA_ARGS__) + +#define PYBIND11_OVERLOAD_PURE_NAME(ret_type, cname, name, fn, ...) \ + PYBIND11_OVERLOAD_INT(ret_type, cname, name, __VA_ARGS__) \ + pybind11::pybind11_fail("Tried to call pure virtual function \"" #cname "::" name "\""); + +#define PYBIND11_OVERLOAD(ret_type, cname, fn, ...) \ + PYBIND11_OVERLOAD_NAME(ret_type, cname, #fn, fn, __VA_ARGS__) + +#define PYBIND11_OVERLOAD_PURE(ret_type, cname, fn, ...) \ + PYBIND11_OVERLOAD_PURE_NAME(ret_type, cname, #fn, fn, __VA_ARGS__) + +NAMESPACE_END(pybind11) + +#if defined(_MSC_VER) +# pragma warning(pop) +#elif defined(__INTEL_COMPILER) +/* Leave ignored warnings on */ +#elif defined(__GNUG__) && !defined(__clang__) +# pragma GCC diagnostic pop +#endif diff --git a/ptocr/postprocess/lanms/include/pybind11/pytypes.h b/ptocr/postprocess/lanms/include/pybind11/pytypes.h new file mode 100644 index 0000000..095d40f --- /dev/null +++ b/ptocr/postprocess/lanms/include/pybind11/pytypes.h @@ -0,0 +1,1318 @@ +/* + pybind11/typeid.h: Convenience wrapper classes for basic Python types + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "common.h" +#include "buffer_info.h" +#include +#include + +NAMESPACE_BEGIN(pybind11) + +/* A few forward declarations */ +class handle; class object; +class str; class iterator; +struct arg; struct arg_v; + +NAMESPACE_BEGIN(detail) +class args_proxy; +inline bool isinstance_generic(handle obj, const std::type_info &tp); + +// Accessor forward declarations +template class accessor; +namespace accessor_policies { + struct obj_attr; + struct str_attr; + struct generic_item; + struct sequence_item; + struct list_item; + struct tuple_item; +} +using obj_attr_accessor = accessor; +using str_attr_accessor = accessor; +using item_accessor = accessor; +using sequence_accessor = accessor; +using list_accessor = accessor; +using tuple_accessor = accessor; + +/// Tag and check to identify a class which implements the Python object API +class pyobject_tag { }; +template using is_pyobject = std::is_base_of>; + +/** \rst + A mixin class which adds common functions to `handle`, `object` and various accessors. + The only requirement for `Derived` is to implement ``PyObject *Derived::ptr() const``. +\endrst */ +template +class object_api : public pyobject_tag { + const Derived &derived() const { return static_cast(*this); } + +public: + /** \rst + Return an iterator equivalent to calling ``iter()`` in Python. The object + must be a collection which supports the iteration protocol. + \endrst */ + iterator begin() const; + /// Return a sentinel which ends iteration. + iterator end() const; + + /** \rst + Return an internal functor to invoke the object's sequence protocol. Casting + the returned ``detail::item_accessor`` instance to a `handle` or `object` + subclass causes a corresponding call to ``__getitem__``. Assigning a `handle` + or `object` subclass causes a call to ``__setitem__``. + \endrst */ + item_accessor operator[](handle key) const; + /// See above (the only difference is that they key is provided as a string literal) + item_accessor operator[](const char *key) const; + + /** \rst + Return an internal functor to access the object's attributes. Casting the + returned ``detail::obj_attr_accessor`` instance to a `handle` or `object` + subclass causes a corresponding call to ``getattr``. Assigning a `handle` + or `object` subclass causes a call to ``setattr``. + \endrst */ + obj_attr_accessor attr(handle key) const; + /// See above (the only difference is that they key is provided as a string literal) + str_attr_accessor attr(const char *key) const; + + /** \rst + Matches * unpacking in Python, e.g. to unpack arguments out of a ``tuple`` + or ``list`` for a function call. Applying another * to the result yields + ** unpacking, e.g. to unpack a dict as function keyword arguments. + See :ref:`calling_python_functions`. + \endrst */ + args_proxy operator*() const; + + /// Check if the given item is contained within this object, i.e. ``item in obj``. + template bool contains(T &&item) const; + + /** \rst + Assuming the Python object is a function or implements the ``__call__`` + protocol, ``operator()`` invokes the underlying function, passing an + arbitrary set of parameters. The result is returned as a `object` and + may need to be converted back into a Python object using `handle::cast()`. + + When some of the arguments cannot be converted to Python objects, the + function will throw a `cast_error` exception. When the Python function + call fails, a `error_already_set` exception is thrown. + \endrst */ + template + object operator()(Args &&...args) const; + template + PYBIND11_DEPRECATED("call(...) was deprecated in favor of operator()(...)") + object call(Args&&... args) const; + + /// Equivalent to ``obj is other`` in Python. + bool is(object_api const& other) const { return derived().ptr() == other.derived().ptr(); } + /// Equivalent to ``obj is None`` in Python. + bool is_none() const { return derived().ptr() == Py_None; } + PYBIND11_DEPRECATED("Use py::str(obj) instead") + pybind11::str str() const; + + /// Get or set the object's docstring, i.e. ``obj.__doc__``. + str_attr_accessor doc() const; + + /// Return the object's current reference count + int ref_count() const { return static_cast(Py_REFCNT(derived().ptr())); } + /// Return a handle to the Python type object underlying the instance + handle get_type() const; +}; + +NAMESPACE_END(detail) + +/** \rst + Holds a reference to a Python object (no reference counting) + + The `handle` class is a thin wrapper around an arbitrary Python object (i.e. a + ``PyObject *`` in Python's C API). It does not perform any automatic reference + counting and merely provides a basic C++ interface to various Python API functions. + + .. seealso:: + The `object` class inherits from `handle` and adds automatic reference + counting features. +\endrst */ +class handle : public detail::object_api { +public: + /// The default constructor creates a handle with a ``nullptr``-valued pointer + handle() = default; + /// Creates a ``handle`` from the given raw Python object pointer + handle(PyObject *ptr) : m_ptr(ptr) { } // Allow implicit conversion from PyObject* + + /// Return the underlying ``PyObject *`` pointer + PyObject *ptr() const { return m_ptr; } + PyObject *&ptr() { return m_ptr; } + + /** \rst + Manually increase the reference count of the Python object. Usually, it is + preferable to use the `object` class which derives from `handle` and calls + this function automatically. Returns a reference to itself. + \endrst */ + const handle& inc_ref() const & { Py_XINCREF(m_ptr); return *this; } + + /** \rst + Manually decrease the reference count of the Python object. Usually, it is + preferable to use the `object` class which derives from `handle` and calls + this function automatically. Returns a reference to itself. + \endrst */ + const handle& dec_ref() const & { Py_XDECREF(m_ptr); return *this; } + + /** \rst + Attempt to cast the Python object into the given C++ type. A `cast_error` + will be throw upon failure. + \endrst */ + template T cast() const; + /// Return ``true`` when the `handle` wraps a valid Python object + explicit operator bool() const { return m_ptr != nullptr; } + /** \rst + Deprecated: Check that the underlying pointers are the same. + Equivalent to ``obj1 is obj2`` in Python. + \endrst */ + PYBIND11_DEPRECATED("Use obj1.is(obj2) instead") + bool operator==(const handle &h) const { return m_ptr == h.m_ptr; } + PYBIND11_DEPRECATED("Use !obj1.is(obj2) instead") + bool operator!=(const handle &h) const { return m_ptr != h.m_ptr; } + PYBIND11_DEPRECATED("Use handle::operator bool() instead") + bool check() const { return m_ptr != nullptr; } +protected: + PyObject *m_ptr = nullptr; +}; + +/** \rst + Holds a reference to a Python object (with reference counting) + + Like `handle`, the `object` class is a thin wrapper around an arbitrary Python + object (i.e. a ``PyObject *`` in Python's C API). In contrast to `handle`, it + optionally increases the object's reference count upon construction, and it + *always* decreases the reference count when the `object` instance goes out of + scope and is destructed. When using `object` instances consistently, it is much + easier to get reference counting right at the first attempt. +\endrst */ +class object : public handle { +public: + object() = default; + PYBIND11_DEPRECATED("Use reinterpret_borrow() or reinterpret_steal()") + object(handle h, bool is_borrowed) : handle(h) { if (is_borrowed) inc_ref(); } + /// Copy constructor; always increases the reference count + object(const object &o) : handle(o) { inc_ref(); } + /// Move constructor; steals the object from ``other`` and preserves its reference count + object(object &&other) noexcept { m_ptr = other.m_ptr; other.m_ptr = nullptr; } + /// Destructor; automatically calls `handle::dec_ref()` + ~object() { dec_ref(); } + + /** \rst + Resets the internal pointer to ``nullptr`` without without decreasing the + object's reference count. The function returns a raw handle to the original + Python object. + \endrst */ + handle release() { + PyObject *tmp = m_ptr; + m_ptr = nullptr; + return handle(tmp); + } + + object& operator=(const object &other) { + other.inc_ref(); + dec_ref(); + m_ptr = other.m_ptr; + return *this; + } + + object& operator=(object &&other) noexcept { + if (this != &other) { + handle temp(m_ptr); + m_ptr = other.m_ptr; + other.m_ptr = nullptr; + temp.dec_ref(); + } + return *this; + } + + // Calling cast() on an object lvalue just copies (via handle::cast) + template T cast() const &; + // Calling on an object rvalue does a move, if needed and/or possible + template T cast() &&; + +protected: + // Tags for choosing constructors from raw PyObject * + struct borrowed_t { }; + struct stolen_t { }; + + template friend T reinterpret_borrow(handle); + template friend T reinterpret_steal(handle); + +public: + // Only accessible from derived classes and the reinterpret_* functions + object(handle h, borrowed_t) : handle(h) { inc_ref(); } + object(handle h, stolen_t) : handle(h) { } +}; + +/** \rst + Declare that a `handle` or ``PyObject *`` is a certain type and borrow the reference. + The target type ``T`` must be `object` or one of its derived classes. The function + doesn't do any conversions or checks. It's up to the user to make sure that the + target type is correct. + + .. code-block:: cpp + + PyObject *p = PyList_GetItem(obj, index); + py::object o = reinterpret_borrow(p); + // or + py::tuple t = reinterpret_borrow(p); // <-- `p` must be already be a `tuple` +\endrst */ +template T reinterpret_borrow(handle h) { return {h, object::borrowed_t{}}; } + +/** \rst + Like `reinterpret_borrow`, but steals the reference. + + .. code-block:: cpp + + PyObject *p = PyObject_Str(obj); + py::str s = reinterpret_steal(p); // <-- `p` must be already be a `str` +\endrst */ +template T reinterpret_steal(handle h) { return {h, object::stolen_t{}}; } + +NAMESPACE_BEGIN(detail) +inline std::string error_string(); +NAMESPACE_END(detail) + +/// Fetch and hold an error which was already set in Python. An instance of this is typically +/// thrown to propagate python-side errors back through C++ which can either be caught manually or +/// else falls back to the function dispatcher (which then raises the captured error back to +/// python). +class error_already_set : public std::runtime_error { +public: + /// Constructs a new exception from the current Python error indicator, if any. The current + /// Python error indicator will be cleared. + error_already_set() : std::runtime_error(detail::error_string()) { + PyErr_Fetch(&type.ptr(), &value.ptr(), &trace.ptr()); + } + + inline ~error_already_set(); + + /// Give the currently-held error back to Python, if any. If there is currently a Python error + /// already set it is cleared first. After this call, the current object no longer stores the + /// error variables (but the `.what()` string is still available). + void restore() { PyErr_Restore(type.release().ptr(), value.release().ptr(), trace.release().ptr()); } + + // Does nothing; provided for backwards compatibility. + PYBIND11_DEPRECATED("Use of error_already_set.clear() is deprecated") + void clear() {} + + /// Check if the currently trapped error type matches the given Python exception class (or a + /// subclass thereof). May also be passed a tuple to search for any exception class matches in + /// the given tuple. + bool matches(handle ex) const { return PyErr_GivenExceptionMatches(ex.ptr(), type.ptr()); } + +private: + object type, value, trace; +}; + +/** \defgroup python_builtins _ + Unless stated otherwise, the following C++ functions behave the same + as their Python counterparts. + */ + +/** \ingroup python_builtins + \rst + Return true if ``obj`` is an instance of ``T``. Type ``T`` must be a subclass of + `object` or a class which was exposed to Python as ``py::class_``. +\endrst */ +template ::value, int> = 0> +bool isinstance(handle obj) { return T::check_(obj); } + +template ::value, int> = 0> +bool isinstance(handle obj) { return detail::isinstance_generic(obj, typeid(T)); } + +template <> inline bool isinstance(handle obj) = delete; +template <> inline bool isinstance(handle obj) { return obj.ptr() != nullptr; } + +/// \ingroup python_builtins +/// Return true if ``obj`` is an instance of the ``type``. +inline bool isinstance(handle obj, handle type) { + const auto result = PyObject_IsInstance(obj.ptr(), type.ptr()); + if (result == -1) + throw error_already_set(); + return result != 0; +} + +/// \addtogroup python_builtins +/// @{ +inline bool hasattr(handle obj, handle name) { + return PyObject_HasAttr(obj.ptr(), name.ptr()) == 1; +} + +inline bool hasattr(handle obj, const char *name) { + return PyObject_HasAttrString(obj.ptr(), name) == 1; +} + +inline object getattr(handle obj, handle name) { + PyObject *result = PyObject_GetAttr(obj.ptr(), name.ptr()); + if (!result) { throw error_already_set(); } + return reinterpret_steal(result); +} + +inline object getattr(handle obj, const char *name) { + PyObject *result = PyObject_GetAttrString(obj.ptr(), name); + if (!result) { throw error_already_set(); } + return reinterpret_steal(result); +} + +inline object getattr(handle obj, handle name, handle default_) { + if (PyObject *result = PyObject_GetAttr(obj.ptr(), name.ptr())) { + return reinterpret_steal(result); + } else { + PyErr_Clear(); + return reinterpret_borrow(default_); + } +} + +inline object getattr(handle obj, const char *name, handle default_) { + if (PyObject *result = PyObject_GetAttrString(obj.ptr(), name)) { + return reinterpret_steal(result); + } else { + PyErr_Clear(); + return reinterpret_borrow(default_); + } +} + +inline void setattr(handle obj, handle name, handle value) { + if (PyObject_SetAttr(obj.ptr(), name.ptr(), value.ptr()) != 0) { throw error_already_set(); } +} + +inline void setattr(handle obj, const char *name, handle value) { + if (PyObject_SetAttrString(obj.ptr(), name, value.ptr()) != 0) { throw error_already_set(); } +} +/// @} python_builtins + +NAMESPACE_BEGIN(detail) +inline handle get_function(handle value) { + if (value) { +#if PY_MAJOR_VERSION >= 3 + if (PyInstanceMethod_Check(value.ptr())) + value = PyInstanceMethod_GET_FUNCTION(value.ptr()); + else +#endif + if (PyMethod_Check(value.ptr())) + value = PyMethod_GET_FUNCTION(value.ptr()); + } + return value; +} + +// Helper aliases/functions to support implicit casting of values given to python accessors/methods. +// When given a pyobject, this simply returns the pyobject as-is; for other C++ type, the value goes +// through pybind11::cast(obj) to convert it to an `object`. +template ::value, int> = 0> +auto object_or_cast(T &&o) -> decltype(std::forward(o)) { return std::forward(o); } +// The following casting version is implemented in cast.h: +template ::value, int> = 0> +object object_or_cast(T &&o); +// Match a PyObject*, which we want to convert directly to handle via its converting constructor +inline handle object_or_cast(PyObject *ptr) { return ptr; } + + +template +class accessor : public object_api> { + using key_type = typename Policy::key_type; + +public: + accessor(handle obj, key_type key) : obj(obj), key(std::move(key)) { } + accessor(const accessor &a) = default; + accessor(accessor &&a) = default; + + // accessor overload required to override default assignment operator (templates are not allowed + // to replace default compiler-generated assignments). + void operator=(const accessor &a) && { std::move(*this).operator=(handle(a)); } + void operator=(const accessor &a) & { operator=(handle(a)); } + + template void operator=(T &&value) && { + Policy::set(obj, key, object_or_cast(std::forward(value))); + } + template void operator=(T &&value) & { + get_cache() = reinterpret_borrow(object_or_cast(std::forward(value))); + } + + template + PYBIND11_DEPRECATED("Use of obj.attr(...) as bool is deprecated in favor of pybind11::hasattr(obj, ...)") + explicit operator enable_if_t::value || + std::is_same::value, bool>() const { + return hasattr(obj, key); + } + template + PYBIND11_DEPRECATED("Use of obj[key] as bool is deprecated in favor of obj.contains(key)") + explicit operator enable_if_t::value, bool>() const { + return obj.contains(key); + } + + operator object() const { return get_cache(); } + PyObject *ptr() const { return get_cache().ptr(); } + template T cast() const { return get_cache().template cast(); } + +private: + object &get_cache() const { + if (!cache) { cache = Policy::get(obj, key); } + return cache; + } + +private: + handle obj; + key_type key; + mutable object cache; +}; + +NAMESPACE_BEGIN(accessor_policies) +struct obj_attr { + using key_type = object; + static object get(handle obj, handle key) { return getattr(obj, key); } + static void set(handle obj, handle key, handle val) { setattr(obj, key, val); } +}; + +struct str_attr { + using key_type = const char *; + static object get(handle obj, const char *key) { return getattr(obj, key); } + static void set(handle obj, const char *key, handle val) { setattr(obj, key, val); } +}; + +struct generic_item { + using key_type = object; + + static object get(handle obj, handle key) { + PyObject *result = PyObject_GetItem(obj.ptr(), key.ptr()); + if (!result) { throw error_already_set(); } + return reinterpret_steal(result); + } + + static void set(handle obj, handle key, handle val) { + if (PyObject_SetItem(obj.ptr(), key.ptr(), val.ptr()) != 0) { throw error_already_set(); } + } +}; + +struct sequence_item { + using key_type = size_t; + + static object get(handle obj, size_t index) { + PyObject *result = PySequence_GetItem(obj.ptr(), static_cast(index)); + if (!result) { throw error_already_set(); } + return reinterpret_steal(result); + } + + static void set(handle obj, size_t index, handle val) { + // PySequence_SetItem does not steal a reference to 'val' + if (PySequence_SetItem(obj.ptr(), static_cast(index), val.ptr()) != 0) { + throw error_already_set(); + } + } +}; + +struct list_item { + using key_type = size_t; + + static object get(handle obj, size_t index) { + PyObject *result = PyList_GetItem(obj.ptr(), static_cast(index)); + if (!result) { throw error_already_set(); } + return reinterpret_borrow(result); + } + + static void set(handle obj, size_t index, handle val) { + // PyList_SetItem steals a reference to 'val' + if (PyList_SetItem(obj.ptr(), static_cast(index), val.inc_ref().ptr()) != 0) { + throw error_already_set(); + } + } +}; + +struct tuple_item { + using key_type = size_t; + + static object get(handle obj, size_t index) { + PyObject *result = PyTuple_GetItem(obj.ptr(), static_cast(index)); + if (!result) { throw error_already_set(); } + return reinterpret_borrow(result); + } + + static void set(handle obj, size_t index, handle val) { + // PyTuple_SetItem steals a reference to 'val' + if (PyTuple_SetItem(obj.ptr(), static_cast(index), val.inc_ref().ptr()) != 0) { + throw error_already_set(); + } + } +}; +NAMESPACE_END(accessor_policies) + +/// STL iterator template used for tuple, list, sequence and dict +template +class generic_iterator : public Policy { + using It = generic_iterator; + +public: + using difference_type = ssize_t; + using iterator_category = typename Policy::iterator_category; + using value_type = typename Policy::value_type; + using reference = typename Policy::reference; + using pointer = typename Policy::pointer; + + generic_iterator() = default; + generic_iterator(handle seq, ssize_t index) : Policy(seq, index) { } + + reference operator*() const { return Policy::dereference(); } + reference operator[](difference_type n) const { return *(*this + n); } + pointer operator->() const { return **this; } + + It &operator++() { Policy::increment(); return *this; } + It operator++(int) { auto copy = *this; Policy::increment(); return copy; } + It &operator--() { Policy::decrement(); return *this; } + It operator--(int) { auto copy = *this; Policy::decrement(); return copy; } + It &operator+=(difference_type n) { Policy::advance(n); return *this; } + It &operator-=(difference_type n) { Policy::advance(-n); return *this; } + + friend It operator+(const It &a, difference_type n) { auto copy = a; return copy += n; } + friend It operator+(difference_type n, const It &b) { return b + n; } + friend It operator-(const It &a, difference_type n) { auto copy = a; return copy -= n; } + friend difference_type operator-(const It &a, const It &b) { return a.distance_to(b); } + + friend bool operator==(const It &a, const It &b) { return a.equal(b); } + friend bool operator!=(const It &a, const It &b) { return !(a == b); } + friend bool operator< (const It &a, const It &b) { return b - a > 0; } + friend bool operator> (const It &a, const It &b) { return b < a; } + friend bool operator>=(const It &a, const It &b) { return !(a < b); } + friend bool operator<=(const It &a, const It &b) { return !(a > b); } +}; + +NAMESPACE_BEGIN(iterator_policies) +/// Quick proxy class needed to implement ``operator->`` for iterators which can't return pointers +template +struct arrow_proxy { + T value; + + arrow_proxy(T &&value) : value(std::move(value)) { } + T *operator->() const { return &value; } +}; + +/// Lightweight iterator policy using just a simple pointer: see ``PySequence_Fast_ITEMS`` +class sequence_fast_readonly { +protected: + using iterator_category = std::random_access_iterator_tag; + using value_type = handle; + using reference = const handle; + using pointer = arrow_proxy; + + sequence_fast_readonly(handle obj, ssize_t n) : ptr(PySequence_Fast_ITEMS(obj.ptr()) + n) { } + + reference dereference() const { return *ptr; } + void increment() { ++ptr; } + void decrement() { --ptr; } + void advance(ssize_t n) { ptr += n; } + bool equal(const sequence_fast_readonly &b) const { return ptr == b.ptr; } + ssize_t distance_to(const sequence_fast_readonly &b) const { return ptr - b.ptr; } + +private: + PyObject **ptr; +}; + +/// Full read and write access using the sequence protocol: see ``detail::sequence_accessor`` +class sequence_slow_readwrite { +protected: + using iterator_category = std::random_access_iterator_tag; + using value_type = object; + using reference = sequence_accessor; + using pointer = arrow_proxy; + + sequence_slow_readwrite(handle obj, ssize_t index) : obj(obj), index(index) { } + + reference dereference() const { return {obj, static_cast(index)}; } + void increment() { ++index; } + void decrement() { --index; } + void advance(ssize_t n) { index += n; } + bool equal(const sequence_slow_readwrite &b) const { return index == b.index; } + ssize_t distance_to(const sequence_slow_readwrite &b) const { return index - b.index; } + +private: + handle obj; + ssize_t index; +}; + +/// Python's dictionary protocol permits this to be a forward iterator +class dict_readonly { +protected: + using iterator_category = std::forward_iterator_tag; + using value_type = std::pair; + using reference = const value_type; + using pointer = arrow_proxy; + + dict_readonly() = default; + dict_readonly(handle obj, ssize_t pos) : obj(obj), pos(pos) { increment(); } + + reference dereference() const { return {key, value}; } + void increment() { if (!PyDict_Next(obj.ptr(), &pos, &key, &value)) { pos = -1; } } + bool equal(const dict_readonly &b) const { return pos == b.pos; } + +private: + handle obj; + PyObject *key, *value; + ssize_t pos = -1; +}; +NAMESPACE_END(iterator_policies) + +#if !defined(PYPY_VERSION) +using tuple_iterator = generic_iterator; +using list_iterator = generic_iterator; +#else +using tuple_iterator = generic_iterator; +using list_iterator = generic_iterator; +#endif + +using sequence_iterator = generic_iterator; +using dict_iterator = generic_iterator; + +inline bool PyIterable_Check(PyObject *obj) { + PyObject *iter = PyObject_GetIter(obj); + if (iter) { + Py_DECREF(iter); + return true; + } else { + PyErr_Clear(); + return false; + } +} + +inline bool PyNone_Check(PyObject *o) { return o == Py_None; } + +inline bool PyUnicode_Check_Permissive(PyObject *o) { return PyUnicode_Check(o) || PYBIND11_BYTES_CHECK(o); } + +class kwargs_proxy : public handle { +public: + explicit kwargs_proxy(handle h) : handle(h) { } +}; + +class args_proxy : public handle { +public: + explicit args_proxy(handle h) : handle(h) { } + kwargs_proxy operator*() const { return kwargs_proxy(*this); } +}; + +/// Python argument categories (using PEP 448 terms) +template using is_keyword = std::is_base_of; +template using is_s_unpacking = std::is_same; // * unpacking +template using is_ds_unpacking = std::is_same; // ** unpacking +template using is_positional = satisfies_none_of; +template using is_keyword_or_ds = satisfies_any_of; + +// Call argument collector forward declarations +template +class simple_collector; +template +class unpacking_collector; + +NAMESPACE_END(detail) + +// TODO: After the deprecated constructors are removed, this macro can be simplified by +// inheriting ctors: `using Parent::Parent`. It's not an option right now because +// the `using` statement triggers the parent deprecation warning even if the ctor +// isn't even used. +#define PYBIND11_OBJECT_COMMON(Name, Parent, CheckFun) \ + public: \ + PYBIND11_DEPRECATED("Use reinterpret_borrow<"#Name">() or reinterpret_steal<"#Name">()") \ + Name(handle h, bool is_borrowed) : Parent(is_borrowed ? Parent(h, borrowed_t{}) : Parent(h, stolen_t{})) { } \ + Name(handle h, borrowed_t) : Parent(h, borrowed_t{}) { } \ + Name(handle h, stolen_t) : Parent(h, stolen_t{}) { } \ + PYBIND11_DEPRECATED("Use py::isinstance(obj) instead") \ + bool check() const { return m_ptr != nullptr && (bool) CheckFun(m_ptr); } \ + static bool check_(handle h) { return h.ptr() != nullptr && CheckFun(h.ptr()); } + +#define PYBIND11_OBJECT_CVT(Name, Parent, CheckFun, ConvertFun) \ + PYBIND11_OBJECT_COMMON(Name, Parent, CheckFun) \ + /* This is deliberately not 'explicit' to allow implicit conversion from object: */ \ + Name(const object &o) : Parent(ConvertFun(o.ptr()), stolen_t{}) { if (!m_ptr) throw error_already_set(); } + +#define PYBIND11_OBJECT(Name, Parent, CheckFun) \ + PYBIND11_OBJECT_COMMON(Name, Parent, CheckFun) \ + /* This is deliberately not 'explicit' to allow implicit conversion from object: */ \ + Name(const object &o) : Parent(o) { } \ + Name(object &&o) : Parent(std::move(o)) { } + +#define PYBIND11_OBJECT_DEFAULT(Name, Parent, CheckFun) \ + PYBIND11_OBJECT(Name, Parent, CheckFun) \ + Name() : Parent() { } + +/// \addtogroup pytypes +/// @{ + +/** \rst + Wraps a Python iterator so that it can also be used as a C++ input iterator + + Caveat: copying an iterator does not (and cannot) clone the internal + state of the Python iterable. This also applies to the post-increment + operator. This iterator should only be used to retrieve the current + value using ``operator*()``. +\endrst */ +class iterator : public object { +public: + using iterator_category = std::input_iterator_tag; + using difference_type = ssize_t; + using value_type = handle; + using reference = const handle; + using pointer = const handle *; + + PYBIND11_OBJECT_DEFAULT(iterator, object, PyIter_Check) + + iterator& operator++() { + advance(); + return *this; + } + + iterator operator++(int) { + auto rv = *this; + advance(); + return rv; + } + + reference operator*() const { + if (m_ptr && !value.ptr()) { + auto& self = const_cast(*this); + self.advance(); + } + return value; + } + + pointer operator->() const { operator*(); return &value; } + + /** \rst + The value which marks the end of the iteration. ``it == iterator::sentinel()`` + is equivalent to catching ``StopIteration`` in Python. + + .. code-block:: cpp + + void foo(py::iterator it) { + while (it != py::iterator::sentinel()) { + // use `*it` + ++it; + } + } + \endrst */ + static iterator sentinel() { return {}; } + + friend bool operator==(const iterator &a, const iterator &b) { return a->ptr() == b->ptr(); } + friend bool operator!=(const iterator &a, const iterator &b) { return a->ptr() != b->ptr(); } + +private: + void advance() { + value = reinterpret_steal(PyIter_Next(m_ptr)); + if (PyErr_Occurred()) { throw error_already_set(); } + } + +private: + object value = {}; +}; + +class iterable : public object { +public: + PYBIND11_OBJECT_DEFAULT(iterable, object, detail::PyIterable_Check) +}; + +class bytes; + +class str : public object { +public: + PYBIND11_OBJECT_CVT(str, object, detail::PyUnicode_Check_Permissive, raw_str) + + str(const char *c, size_t n) + : object(PyUnicode_FromStringAndSize(c, (ssize_t) n), stolen_t{}) { + if (!m_ptr) pybind11_fail("Could not allocate string object!"); + } + + // 'explicit' is explicitly omitted from the following constructors to allow implicit conversion to py::str from C++ string-like objects + str(const char *c = "") + : object(PyUnicode_FromString(c), stolen_t{}) { + if (!m_ptr) pybind11_fail("Could not allocate string object!"); + } + + str(const std::string &s) : str(s.data(), s.size()) { } + + explicit str(const bytes &b); + + /** \rst + Return a string representation of the object. This is analogous to + the ``str()`` function in Python. + \endrst */ + explicit str(handle h) : object(raw_str(h.ptr()), stolen_t{}) { } + + operator std::string() const { + object temp = *this; + if (PyUnicode_Check(m_ptr)) { + temp = reinterpret_steal(PyUnicode_AsUTF8String(m_ptr)); + if (!temp) + pybind11_fail("Unable to extract string contents! (encoding issue)"); + } + char *buffer; + ssize_t length; + if (PYBIND11_BYTES_AS_STRING_AND_SIZE(temp.ptr(), &buffer, &length)) + pybind11_fail("Unable to extract string contents! (invalid type)"); + return std::string(buffer, (size_t) length); + } + + template + str format(Args &&...args) const { + return attr("format")(std::forward(args)...); + } + +private: + /// Return string representation -- always returns a new reference, even if already a str + static PyObject *raw_str(PyObject *op) { + PyObject *str_value = PyObject_Str(op); +#if PY_MAJOR_VERSION < 3 + if (!str_value) throw error_already_set(); + PyObject *unicode = PyUnicode_FromEncodedObject(str_value, "utf-8", nullptr); + Py_XDECREF(str_value); str_value = unicode; +#endif + return str_value; + } +}; +/// @} pytypes + +inline namespace literals { +/** \rst + String literal version of `str` + \endrst */ +inline str operator"" _s(const char *s, size_t size) { return {s, size}; } +} + +/// \addtogroup pytypes +/// @{ +class bytes : public object { +public: + PYBIND11_OBJECT(bytes, object, PYBIND11_BYTES_CHECK) + + // Allow implicit conversion: + bytes(const char *c = "") + : object(PYBIND11_BYTES_FROM_STRING(c), stolen_t{}) { + if (!m_ptr) pybind11_fail("Could not allocate bytes object!"); + } + + bytes(const char *c, size_t n) + : object(PYBIND11_BYTES_FROM_STRING_AND_SIZE(c, (ssize_t) n), stolen_t{}) { + if (!m_ptr) pybind11_fail("Could not allocate bytes object!"); + } + + // Allow implicit conversion: + bytes(const std::string &s) : bytes(s.data(), s.size()) { } + + explicit bytes(const pybind11::str &s); + + operator std::string() const { + char *buffer; + ssize_t length; + if (PYBIND11_BYTES_AS_STRING_AND_SIZE(m_ptr, &buffer, &length)) + pybind11_fail("Unable to extract bytes contents!"); + return std::string(buffer, (size_t) length); + } +}; + +inline bytes::bytes(const pybind11::str &s) { + object temp = s; + if (PyUnicode_Check(s.ptr())) { + temp = reinterpret_steal(PyUnicode_AsUTF8String(s.ptr())); + if (!temp) + pybind11_fail("Unable to extract string contents! (encoding issue)"); + } + char *buffer; + ssize_t length; + if (PYBIND11_BYTES_AS_STRING_AND_SIZE(temp.ptr(), &buffer, &length)) + pybind11_fail("Unable to extract string contents! (invalid type)"); + auto obj = reinterpret_steal(PYBIND11_BYTES_FROM_STRING_AND_SIZE(buffer, length)); + if (!obj) + pybind11_fail("Could not allocate bytes object!"); + m_ptr = obj.release().ptr(); +} + +inline str::str(const bytes& b) { + char *buffer; + ssize_t length; + if (PYBIND11_BYTES_AS_STRING_AND_SIZE(b.ptr(), &buffer, &length)) + pybind11_fail("Unable to extract bytes contents!"); + auto obj = reinterpret_steal(PyUnicode_FromStringAndSize(buffer, (ssize_t) length)); + if (!obj) + pybind11_fail("Could not allocate string object!"); + m_ptr = obj.release().ptr(); +} + +class none : public object { +public: + PYBIND11_OBJECT(none, object, detail::PyNone_Check) + none() : object(Py_None, borrowed_t{}) { } +}; + +class bool_ : public object { +public: + PYBIND11_OBJECT_CVT(bool_, object, PyBool_Check, raw_bool) + bool_() : object(Py_False, borrowed_t{}) { } + // Allow implicit conversion from and to `bool`: + bool_(bool value) : object(value ? Py_True : Py_False, borrowed_t{}) { } + operator bool() const { return m_ptr && PyLong_AsLong(m_ptr) != 0; } + +private: + /// Return the truth value of an object -- always returns a new reference + static PyObject *raw_bool(PyObject *op) { + const auto value = PyObject_IsTrue(op); + if (value == -1) return nullptr; + return handle(value ? Py_True : Py_False).inc_ref().ptr(); + } +}; + +NAMESPACE_BEGIN(detail) +// Converts a value to the given unsigned type. If an error occurs, you get back (Unsigned) -1; +// otherwise you get back the unsigned long or unsigned long long value cast to (Unsigned). +// (The distinction is critically important when casting a returned -1 error value to some other +// unsigned type: (A)-1 != (B)-1 when A and B are unsigned types of different sizes). +template +Unsigned as_unsigned(PyObject *o) { + if (sizeof(Unsigned) <= sizeof(unsigned long) +#if PY_VERSION_HEX < 0x03000000 + || PyInt_Check(o) +#endif + ) { + unsigned long v = PyLong_AsUnsignedLong(o); + return v == (unsigned long) -1 && PyErr_Occurred() ? (Unsigned) -1 : (Unsigned) v; + } + else { + unsigned long long v = PyLong_AsUnsignedLongLong(o); + return v == (unsigned long long) -1 && PyErr_Occurred() ? (Unsigned) -1 : (Unsigned) v; + } +} +NAMESPACE_END(detail) + +class int_ : public object { +public: + PYBIND11_OBJECT_CVT(int_, object, PYBIND11_LONG_CHECK, PyNumber_Long) + int_() : object(PyLong_FromLong(0), stolen_t{}) { } + // Allow implicit conversion from C++ integral types: + template ::value, int> = 0> + int_(T value) { + if (sizeof(T) <= sizeof(long)) { + if (std::is_signed::value) + m_ptr = PyLong_FromLong((long) value); + else + m_ptr = PyLong_FromUnsignedLong((unsigned long) value); + } else { + if (std::is_signed::value) + m_ptr = PyLong_FromLongLong((long long) value); + else + m_ptr = PyLong_FromUnsignedLongLong((unsigned long long) value); + } + if (!m_ptr) pybind11_fail("Could not allocate int object!"); + } + + template ::value, int> = 0> + operator T() const { + return std::is_unsigned::value + ? detail::as_unsigned(m_ptr) + : sizeof(T) <= sizeof(long) + ? (T) PyLong_AsLong(m_ptr) + : (T) PYBIND11_LONG_AS_LONGLONG(m_ptr); + } +}; + +class float_ : public object { +public: + PYBIND11_OBJECT_CVT(float_, object, PyFloat_Check, PyNumber_Float) + // Allow implicit conversion from float/double: + float_(float value) : object(PyFloat_FromDouble((double) value), stolen_t{}) { + if (!m_ptr) pybind11_fail("Could not allocate float object!"); + } + float_(double value = .0) : object(PyFloat_FromDouble((double) value), stolen_t{}) { + if (!m_ptr) pybind11_fail("Could not allocate float object!"); + } + operator float() const { return (float) PyFloat_AsDouble(m_ptr); } + operator double() const { return (double) PyFloat_AsDouble(m_ptr); } +}; + +class weakref : public object { +public: + PYBIND11_OBJECT_DEFAULT(weakref, object, PyWeakref_Check) + explicit weakref(handle obj, handle callback = {}) + : object(PyWeakref_NewRef(obj.ptr(), callback.ptr()), stolen_t{}) { + if (!m_ptr) pybind11_fail("Could not allocate weak reference!"); + } +}; + +class slice : public object { +public: + PYBIND11_OBJECT_DEFAULT(slice, object, PySlice_Check) + slice(ssize_t start_, ssize_t stop_, ssize_t step_) { + int_ start(start_), stop(stop_), step(step_); + m_ptr = PySlice_New(start.ptr(), stop.ptr(), step.ptr()); + if (!m_ptr) pybind11_fail("Could not allocate slice object!"); + } + bool compute(size_t length, size_t *start, size_t *stop, size_t *step, + size_t *slicelength) const { + return PySlice_GetIndicesEx((PYBIND11_SLICE_OBJECT *) m_ptr, + (ssize_t) length, (ssize_t *) start, + (ssize_t *) stop, (ssize_t *) step, + (ssize_t *) slicelength) == 0; + } +}; + +class capsule : public object { +public: + PYBIND11_OBJECT_DEFAULT(capsule, object, PyCapsule_CheckExact) + PYBIND11_DEPRECATED("Use reinterpret_borrow() or reinterpret_steal()") + capsule(PyObject *ptr, bool is_borrowed) : object(is_borrowed ? object(ptr, borrowed_t{}) : object(ptr, stolen_t{})) { } + + explicit capsule(const void *value, const char *name = nullptr, void (*destructor)(PyObject *) = nullptr) + : object(PyCapsule_New(const_cast(value), name, destructor), stolen_t{}) { + if (!m_ptr) + pybind11_fail("Could not allocate capsule object!"); + } + + PYBIND11_DEPRECATED("Please pass a destructor that takes a void pointer as input") + capsule(const void *value, void (*destruct)(PyObject *)) + : object(PyCapsule_New(const_cast(value), nullptr, destruct), stolen_t{}) { + if (!m_ptr) + pybind11_fail("Could not allocate capsule object!"); + } + + capsule(const void *value, void (*destructor)(void *)) { + m_ptr = PyCapsule_New(const_cast(value), nullptr, [](PyObject *o) { + auto destructor = reinterpret_cast(PyCapsule_GetContext(o)); + void *ptr = PyCapsule_GetPointer(o, nullptr); + destructor(ptr); + }); + + if (!m_ptr) + pybind11_fail("Could not allocate capsule object!"); + + if (PyCapsule_SetContext(m_ptr, (void *) destructor) != 0) + pybind11_fail("Could not set capsule context!"); + } + + capsule(void (*destructor)()) { + m_ptr = PyCapsule_New(reinterpret_cast(destructor), nullptr, [](PyObject *o) { + auto destructor = reinterpret_cast(PyCapsule_GetPointer(o, nullptr)); + destructor(); + }); + + if (!m_ptr) + pybind11_fail("Could not allocate capsule object!"); + } + + template operator T *() const { + auto name = this->name(); + T * result = static_cast(PyCapsule_GetPointer(m_ptr, name)); + if (!result) pybind11_fail("Unable to extract capsule contents!"); + return result; + } + + const char *name() const { return PyCapsule_GetName(m_ptr); } +}; + +class tuple : public object { +public: + PYBIND11_OBJECT_CVT(tuple, object, PyTuple_Check, PySequence_Tuple) + explicit tuple(size_t size = 0) : object(PyTuple_New((ssize_t) size), stolen_t{}) { + if (!m_ptr) pybind11_fail("Could not allocate tuple object!"); + } + size_t size() const { return (size_t) PyTuple_Size(m_ptr); } + detail::tuple_accessor operator[](size_t index) const { return {*this, index}; } + detail::tuple_iterator begin() const { return {*this, 0}; } + detail::tuple_iterator end() const { return {*this, PyTuple_GET_SIZE(m_ptr)}; } +}; + +class dict : public object { +public: + PYBIND11_OBJECT_CVT(dict, object, PyDict_Check, raw_dict) + dict() : object(PyDict_New(), stolen_t{}) { + if (!m_ptr) pybind11_fail("Could not allocate dict object!"); + } + template ...>::value>, + // MSVC workaround: it can't compile an out-of-line definition, so defer the collector + typename collector = detail::deferred_t, Args...>> + explicit dict(Args &&...args) : dict(collector(std::forward(args)...).kwargs()) { } + + size_t size() const { return (size_t) PyDict_Size(m_ptr); } + detail::dict_iterator begin() const { return {*this, 0}; } + detail::dict_iterator end() const { return {}; } + void clear() const { PyDict_Clear(ptr()); } + bool contains(handle key) const { return PyDict_Contains(ptr(), key.ptr()) == 1; } + bool contains(const char *key) const { return PyDict_Contains(ptr(), pybind11::str(key).ptr()) == 1; } + +private: + /// Call the `dict` Python type -- always returns a new reference + static PyObject *raw_dict(PyObject *op) { + if (PyDict_Check(op)) + return handle(op).inc_ref().ptr(); + return PyObject_CallFunctionObjArgs((PyObject *) &PyDict_Type, op, nullptr); + } +}; + +class sequence : public object { +public: + PYBIND11_OBJECT_DEFAULT(sequence, object, PySequence_Check) + size_t size() const { return (size_t) PySequence_Size(m_ptr); } + detail::sequence_accessor operator[](size_t index) const { return {*this, index}; } + detail::sequence_iterator begin() const { return {*this, 0}; } + detail::sequence_iterator end() const { return {*this, PySequence_Size(m_ptr)}; } +}; + +class list : public object { +public: + PYBIND11_OBJECT_CVT(list, object, PyList_Check, PySequence_List) + explicit list(size_t size = 0) : object(PyList_New((ssize_t) size), stolen_t{}) { + if (!m_ptr) pybind11_fail("Could not allocate list object!"); + } + size_t size() const { return (size_t) PyList_Size(m_ptr); } + detail::list_accessor operator[](size_t index) const { return {*this, index}; } + detail::list_iterator begin() const { return {*this, 0}; } + detail::list_iterator end() const { return {*this, PyList_GET_SIZE(m_ptr)}; } + template void append(T &&val) const { + PyList_Append(m_ptr, detail::object_or_cast(std::forward(val)).ptr()); + } +}; + +class args : public tuple { PYBIND11_OBJECT_DEFAULT(args, tuple, PyTuple_Check) }; +class kwargs : public dict { PYBIND11_OBJECT_DEFAULT(kwargs, dict, PyDict_Check) }; + +class set : public object { +public: + PYBIND11_OBJECT_CVT(set, object, PySet_Check, PySet_New) + set() : object(PySet_New(nullptr), stolen_t{}) { + if (!m_ptr) pybind11_fail("Could not allocate set object!"); + } + size_t size() const { return (size_t) PySet_Size(m_ptr); } + template bool add(T &&val) const { + return PySet_Add(m_ptr, detail::object_or_cast(std::forward(val)).ptr()) == 0; + } + void clear() const { PySet_Clear(m_ptr); } +}; + +class function : public object { +public: + PYBIND11_OBJECT_DEFAULT(function, object, PyCallable_Check) + handle cpp_function() const { + handle fun = detail::get_function(m_ptr); + if (fun && PyCFunction_Check(fun.ptr())) + return fun; + return handle(); + } + bool is_cpp_function() const { return (bool) cpp_function(); } +}; + +class buffer : public object { +public: + PYBIND11_OBJECT_DEFAULT(buffer, object, PyObject_CheckBuffer) + + buffer_info request(bool writable = false) { + int flags = PyBUF_STRIDES | PyBUF_FORMAT; + if (writable) flags |= PyBUF_WRITABLE; + Py_buffer *view = new Py_buffer(); + if (PyObject_GetBuffer(m_ptr, view, flags) != 0) { + delete view; + throw error_already_set(); + } + return buffer_info(view); + } +}; + +class memoryview : public object { +public: + explicit memoryview(const buffer_info& info) { + static Py_buffer buf { }; + // Py_buffer uses signed sizes, strides and shape!.. + static std::vector py_strides { }; + static std::vector py_shape { }; + buf.buf = info.ptr; + buf.itemsize = info.itemsize; + buf.format = const_cast(info.format.c_str()); + buf.ndim = (int) info.ndim; + buf.len = info.size; + py_strides.clear(); + py_shape.clear(); + for (size_t i = 0; i < (size_t) info.ndim; ++i) { + py_strides.push_back(info.strides[i]); + py_shape.push_back(info.shape[i]); + } + buf.strides = py_strides.data(); + buf.shape = py_shape.data(); + buf.suboffsets = nullptr; + buf.readonly = false; + buf.internal = nullptr; + + m_ptr = PyMemoryView_FromBuffer(&buf); + if (!m_ptr) + pybind11_fail("Unable to create memoryview from buffer descriptor"); + } + + PYBIND11_OBJECT_CVT(memoryview, object, PyMemoryView_Check, PyMemoryView_FromObject) +}; +/// @} pytypes + +/// \addtogroup python_builtins +/// @{ +inline size_t len(handle h) { + ssize_t result = PyObject_Length(h.ptr()); + if (result < 0) + pybind11_fail("Unable to compute length of object"); + return (size_t) result; +} + +inline str repr(handle h) { + PyObject *str_value = PyObject_Repr(h.ptr()); + if (!str_value) throw error_already_set(); +#if PY_MAJOR_VERSION < 3 + PyObject *unicode = PyUnicode_FromEncodedObject(str_value, "utf-8", nullptr); + Py_XDECREF(str_value); str_value = unicode; + if (!str_value) throw error_already_set(); +#endif + return reinterpret_steal(str_value); +} + +inline iterator iter(handle obj) { + PyObject *result = PyObject_GetIter(obj.ptr()); + if (!result) { throw error_already_set(); } + return reinterpret_steal(result); +} +/// @} python_builtins + +NAMESPACE_BEGIN(detail) +template iterator object_api::begin() const { return iter(derived()); } +template iterator object_api::end() const { return iterator::sentinel(); } +template item_accessor object_api::operator[](handle key) const { + return {derived(), reinterpret_borrow(key)}; +} +template item_accessor object_api::operator[](const char *key) const { + return {derived(), pybind11::str(key)}; +} +template obj_attr_accessor object_api::attr(handle key) const { + return {derived(), reinterpret_borrow(key)}; +} +template str_attr_accessor object_api::attr(const char *key) const { + return {derived(), key}; +} +template args_proxy object_api::operator*() const { + return args_proxy(derived().ptr()); +} +template template bool object_api::contains(T &&item) const { + return attr("__contains__")(std::forward(item)).template cast(); +} + +template +pybind11::str object_api::str() const { return pybind11::str(derived()); } + +template +str_attr_accessor object_api::doc() const { return attr("__doc__"); } + +template +handle object_api::get_type() const { return (PyObject *) Py_TYPE(derived().ptr()); } + +NAMESPACE_END(detail) +NAMESPACE_END(pybind11) diff --git a/ptocr/postprocess/lanms/include/pybind11/stl.h b/ptocr/postprocess/lanms/include/pybind11/stl.h new file mode 100644 index 0000000..d07a81f --- /dev/null +++ b/ptocr/postprocess/lanms/include/pybind11/stl.h @@ -0,0 +1,367 @@ +/* + pybind11/stl.h: Transparent conversion for STL data types + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "pybind11.h" +#include +#include +#include +#include +#include +#include +#include + +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable: 4127) // warning C4127: Conditional expression is constant +#endif + +#ifdef __has_include +// std::optional (but including it in c++14 mode isn't allowed) +# if defined(PYBIND11_CPP17) && __has_include() +# include +# define PYBIND11_HAS_OPTIONAL 1 +# endif +// std::experimental::optional (but not allowed in c++11 mode) +# if defined(PYBIND11_CPP14) && __has_include() +# include +# define PYBIND11_HAS_EXP_OPTIONAL 1 +# endif +// std::variant +# if defined(PYBIND11_CPP17) && __has_include() +# include +# define PYBIND11_HAS_VARIANT 1 +# endif +#elif defined(_MSC_VER) && defined(PYBIND11_CPP17) +# include +# include +# define PYBIND11_HAS_OPTIONAL 1 +# define PYBIND11_HAS_VARIANT 1 +#endif + +NAMESPACE_BEGIN(pybind11) +NAMESPACE_BEGIN(detail) + +/// Extracts an const lvalue reference or rvalue reference for U based on the type of T (e.g. for +/// forwarding a container element). Typically used indirect via forwarded_type(), below. +template +using forwarded_type = conditional_t< + std::is_lvalue_reference::value, remove_reference_t &, remove_reference_t &&>; + +/// Forwards a value U as rvalue or lvalue according to whether T is rvalue or lvalue; typically +/// used for forwarding a container's elements. +template +forwarded_type forward_like(U &&u) { + return std::forward>(std::forward(u)); +} + +template struct set_caster { + using type = Type; + using key_conv = make_caster; + + bool load(handle src, bool convert) { + if (!isinstance(src)) + return false; + auto s = reinterpret_borrow(src); + value.clear(); + for (auto entry : s) { + key_conv conv; + if (!conv.load(entry, convert)) + return false; + value.insert(cast_op(std::move(conv))); + } + return true; + } + + template + static handle cast(T &&src, return_value_policy policy, handle parent) { + pybind11::set s; + for (auto &value: src) { + auto value_ = reinterpret_steal(key_conv::cast(forward_like(value), policy, parent)); + if (!value_ || !s.add(value_)) + return handle(); + } + return s.release(); + } + + PYBIND11_TYPE_CASTER(type, _("Set[") + key_conv::name() + _("]")); +}; + +template struct map_caster { + using key_conv = make_caster; + using value_conv = make_caster; + + bool load(handle src, bool convert) { + if (!isinstance(src)) + return false; + auto d = reinterpret_borrow(src); + value.clear(); + for (auto it : d) { + key_conv kconv; + value_conv vconv; + if (!kconv.load(it.first.ptr(), convert) || + !vconv.load(it.second.ptr(), convert)) + return false; + value.emplace(cast_op(std::move(kconv)), cast_op(std::move(vconv))); + } + return true; + } + + template + static handle cast(T &&src, return_value_policy policy, handle parent) { + dict d; + for (auto &kv: src) { + auto key = reinterpret_steal(key_conv::cast(forward_like(kv.first), policy, parent)); + auto value = reinterpret_steal(value_conv::cast(forward_like(kv.second), policy, parent)); + if (!key || !value) + return handle(); + d[key] = value; + } + return d.release(); + } + + PYBIND11_TYPE_CASTER(Type, _("Dict[") + key_conv::name() + _(", ") + value_conv::name() + _("]")); +}; + +template struct list_caster { + using value_conv = make_caster; + + bool load(handle src, bool convert) { + if (!isinstance(src)) + return false; + auto s = reinterpret_borrow(src); + value.clear(); + reserve_maybe(s, &value); + for (auto it : s) { + value_conv conv; + if (!conv.load(it, convert)) + return false; + value.push_back(cast_op(std::move(conv))); + } + return true; + } + +private: + template ().reserve(0)), void>::value, int> = 0> + void reserve_maybe(sequence s, Type *) { value.reserve(s.size()); } + void reserve_maybe(sequence, void *) { } + +public: + template + static handle cast(T &&src, return_value_policy policy, handle parent) { + list l(src.size()); + size_t index = 0; + for (auto &value: src) { + auto value_ = reinterpret_steal(value_conv::cast(forward_like(value), policy, parent)); + if (!value_) + return handle(); + PyList_SET_ITEM(l.ptr(), (ssize_t) index++, value_.release().ptr()); // steals a reference + } + return l.release(); + } + + PYBIND11_TYPE_CASTER(Type, _("List[") + value_conv::name() + _("]")); +}; + +template struct type_caster> + : list_caster, Type> { }; + +template struct type_caster> + : list_caster, Type> { }; + +template struct array_caster { + using value_conv = make_caster; + +private: + template + bool require_size(enable_if_t size) { + if (value.size() != size) + value.resize(size); + return true; + } + template + bool require_size(enable_if_t size) { + return size == Size; + } + +public: + bool load(handle src, bool convert) { + if (!isinstance(src)) + return false; + auto l = reinterpret_borrow(src); + if (!require_size(l.size())) + return false; + size_t ctr = 0; + for (auto it : l) { + value_conv conv; + if (!conv.load(it, convert)) + return false; + value[ctr++] = cast_op(std::move(conv)); + } + return true; + } + + template + static handle cast(T &&src, return_value_policy policy, handle parent) { + list l(src.size()); + size_t index = 0; + for (auto &value: src) { + auto value_ = reinterpret_steal(value_conv::cast(forward_like(value), policy, parent)); + if (!value_) + return handle(); + PyList_SET_ITEM(l.ptr(), (ssize_t) index++, value_.release().ptr()); // steals a reference + } + return l.release(); + } + + PYBIND11_TYPE_CASTER(ArrayType, _("List[") + value_conv::name() + _(_(""), _("[") + _() + _("]")) + _("]")); +}; + +template struct type_caster> + : array_caster, Type, false, Size> { }; + +template struct type_caster> + : array_caster, Type, true> { }; + +template struct type_caster> + : set_caster, Key> { }; + +template struct type_caster> + : set_caster, Key> { }; + +template struct type_caster> + : map_caster, Key, Value> { }; + +template struct type_caster> + : map_caster, Key, Value> { }; + +// This type caster is intended to be used for std::optional and std::experimental::optional +template struct optional_caster { + using value_conv = make_caster; + + template + static handle cast(T_ &&src, return_value_policy policy, handle parent) { + if (!src) + return none().inc_ref(); + return value_conv::cast(*std::forward(src), policy, parent); + } + + bool load(handle src, bool convert) { + if (!src) { + return false; + } else if (src.is_none()) { + return true; // default-constructed value is already empty + } + value_conv inner_caster; + if (!inner_caster.load(src, convert)) + return false; + + value.emplace(cast_op(std::move(inner_caster))); + return true; + } + + PYBIND11_TYPE_CASTER(T, _("Optional[") + value_conv::name() + _("]")); +}; + +#if PYBIND11_HAS_OPTIONAL +template struct type_caster> + : public optional_caster> {}; + +template<> struct type_caster + : public void_caster {}; +#endif + +#if PYBIND11_HAS_EXP_OPTIONAL +template struct type_caster> + : public optional_caster> {}; + +template<> struct type_caster + : public void_caster {}; +#endif + +/// Visit a variant and cast any found type to Python +struct variant_caster_visitor { + return_value_policy policy; + handle parent; + + template + handle operator()(T &&src) const { + return make_caster::cast(std::forward(src), policy, parent); + } +}; + +/// Helper class which abstracts away variant's `visit` function. `std::variant` and similar +/// `namespace::variant` types which provide a `namespace::visit()` function are handled here +/// automatically using argument-dependent lookup. Users can provide specializations for other +/// variant-like classes, e.g. `boost::variant` and `boost::apply_visitor`. +template class Variant> +struct visit_helper { + template + static auto call(Args &&...args) -> decltype(visit(std::forward(args)...)) { + return visit(std::forward(args)...); + } +}; + +/// Generic variant caster +template struct variant_caster; + +template class V, typename... Ts> +struct variant_caster> { + static_assert(sizeof...(Ts) > 0, "Variant must consist of at least one alternative."); + + template + bool load_alternative(handle src, bool convert, type_list) { + auto caster = make_caster(); + if (caster.load(src, convert)) { + value = cast_op(caster); + return true; + } + return load_alternative(src, convert, type_list{}); + } + + bool load_alternative(handle, bool, type_list<>) { return false; } + + bool load(handle src, bool convert) { + // Do a first pass without conversions to improve constructor resolution. + // E.g. `py::int_(1).cast>()` needs to fill the `int` + // slot of the variant. Without two-pass loading `double` would be filled + // because it appears first and a conversion is possible. + if (convert && load_alternative(src, false, type_list{})) + return true; + return load_alternative(src, convert, type_list{}); + } + + template + static handle cast(Variant &&src, return_value_policy policy, handle parent) { + return visit_helper::call(variant_caster_visitor{policy, parent}, + std::forward(src)); + } + + using Type = V; + PYBIND11_TYPE_CASTER(Type, _("Union[") + detail::concat(make_caster::name()...) + _("]")); +}; + +#if PYBIND11_HAS_VARIANT +template +struct type_caster> : variant_caster> { }; +#endif +NAMESPACE_END(detail) + +inline std::ostream &operator<<(std::ostream &os, const handle &obj) { + os << (std::string) str(obj); + return os; +} + +NAMESPACE_END(pybind11) + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif diff --git a/ptocr/postprocess/lanms/include/pybind11/stl_bind.h b/ptocr/postprocess/lanms/include/pybind11/stl_bind.h new file mode 100644 index 0000000..f16e9d2 --- /dev/null +++ b/ptocr/postprocess/lanms/include/pybind11/stl_bind.h @@ -0,0 +1,585 @@ +/* + pybind11/std_bind.h: Binding generators for STL data types + + Copyright (c) 2016 Sergey Lyskov and Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "common.h" +#include "operators.h" + +#include +#include + +NAMESPACE_BEGIN(pybind11) +NAMESPACE_BEGIN(detail) + +/* SFINAE helper class used by 'is_comparable */ +template struct container_traits { + template static std::true_type test_comparable(decltype(std::declval() == std::declval())*); + template static std::false_type test_comparable(...); + template static std::true_type test_value(typename T2::value_type *); + template static std::false_type test_value(...); + template static std::true_type test_pair(typename T2::first_type *, typename T2::second_type *); + template static std::false_type test_pair(...); + + static constexpr const bool is_comparable = std::is_same(nullptr))>::value; + static constexpr const bool is_pair = std::is_same(nullptr, nullptr))>::value; + static constexpr const bool is_vector = std::is_same(nullptr))>::value; + static constexpr const bool is_element = !is_pair && !is_vector; +}; + +/* Default: is_comparable -> std::false_type */ +template +struct is_comparable : std::false_type { }; + +/* For non-map data structures, check whether operator== can be instantiated */ +template +struct is_comparable< + T, enable_if_t::is_element && + container_traits::is_comparable>> + : std::true_type { }; + +/* For a vector/map data structure, recursively check the value type (which is std::pair for maps) */ +template +struct is_comparable::is_vector>> { + static constexpr const bool value = + is_comparable::value; +}; + +/* For pairs, recursively check the two data types */ +template +struct is_comparable::is_pair>> { + static constexpr const bool value = + is_comparable::value && + is_comparable::value; +}; + +/* Fallback functions */ +template void vector_if_copy_constructible(const Args &...) { } +template void vector_if_equal_operator(const Args &...) { } +template void vector_if_insertion_operator(const Args &...) { } +template void vector_modifiers(const Args &...) { } + +template +void vector_if_copy_constructible(enable_if_t::value, Class_> &cl) { + cl.def(init(), "Copy constructor"); +} + +template +void vector_if_equal_operator(enable_if_t::value, Class_> &cl) { + using T = typename Vector::value_type; + + cl.def(self == self); + cl.def(self != self); + + cl.def("count", + [](const Vector &v, const T &x) { + return std::count(v.begin(), v.end(), x); + }, + arg("x"), + "Return the number of times ``x`` appears in the list" + ); + + cl.def("remove", [](Vector &v, const T &x) { + auto p = std::find(v.begin(), v.end(), x); + if (p != v.end()) + v.erase(p); + else + throw value_error(); + }, + arg("x"), + "Remove the first item from the list whose value is x. " + "It is an error if there is no such item." + ); + + cl.def("__contains__", + [](const Vector &v, const T &x) { + return std::find(v.begin(), v.end(), x) != v.end(); + }, + arg("x"), + "Return true the container contains ``x``" + ); +} + +// Vector modifiers -- requires a copyable vector_type: +// (Technically, some of these (pop and __delitem__) don't actually require copyability, but it seems +// silly to allow deletion but not insertion, so include them here too.) +template +void vector_modifiers(enable_if_t::value, Class_> &cl) { + using T = typename Vector::value_type; + using SizeType = typename Vector::size_type; + using DiffType = typename Vector::difference_type; + + cl.def("append", + [](Vector &v, const T &value) { v.push_back(value); }, + arg("x"), + "Add an item to the end of the list"); + + cl.def("__init__", [](Vector &v, iterable it) { + new (&v) Vector(); + try { + v.reserve(len(it)); + for (handle h : it) + v.push_back(h.cast()); + } catch (...) { + v.~Vector(); + throw; + } + }); + + cl.def("extend", + [](Vector &v, const Vector &src) { + v.insert(v.end(), src.begin(), src.end()); + }, + arg("L"), + "Extend the list by appending all the items in the given list" + ); + + cl.def("insert", + [](Vector &v, SizeType i, const T &x) { + if (i > v.size()) + throw index_error(); + v.insert(v.begin() + (DiffType) i, x); + }, + arg("i") , arg("x"), + "Insert an item at a given position." + ); + + cl.def("pop", + [](Vector &v) { + if (v.empty()) + throw index_error(); + T t = v.back(); + v.pop_back(); + return t; + }, + "Remove and return the last item" + ); + + cl.def("pop", + [](Vector &v, SizeType i) { + if (i >= v.size()) + throw index_error(); + T t = v[i]; + v.erase(v.begin() + (DiffType) i); + return t; + }, + arg("i"), + "Remove and return the item at index ``i``" + ); + + cl.def("__setitem__", + [](Vector &v, SizeType i, const T &t) { + if (i >= v.size()) + throw index_error(); + v[i] = t; + } + ); + + /// Slicing protocol + cl.def("__getitem__", + [](const Vector &v, slice slice) -> Vector * { + size_t start, stop, step, slicelength; + + if (!slice.compute(v.size(), &start, &stop, &step, &slicelength)) + throw error_already_set(); + + Vector *seq = new Vector(); + seq->reserve((size_t) slicelength); + + for (size_t i=0; ipush_back(v[start]); + start += step; + } + return seq; + }, + arg("s"), + "Retrieve list elements using a slice object" + ); + + cl.def("__setitem__", + [](Vector &v, slice slice, const Vector &value) { + size_t start, stop, step, slicelength; + if (!slice.compute(v.size(), &start, &stop, &step, &slicelength)) + throw error_already_set(); + + if (slicelength != value.size()) + throw std::runtime_error("Left and right hand size of slice assignment have different sizes!"); + + for (size_t i=0; i= v.size()) + throw index_error(); + v.erase(v.begin() + DiffType(i)); + }, + "Delete the list elements at index ``i``" + ); + + cl.def("__delitem__", + [](Vector &v, slice slice) { + size_t start, stop, step, slicelength; + + if (!slice.compute(v.size(), &start, &stop, &step, &slicelength)) + throw error_already_set(); + + if (step == 1 && false) { + v.erase(v.begin() + (DiffType) start, v.begin() + DiffType(start + slicelength)); + } else { + for (size_t i = 0; i < slicelength; ++i) { + v.erase(v.begin() + DiffType(start)); + start += step - 1; + } + } + }, + "Delete list elements using a slice object" + ); + +} + +// If the type has an operator[] that doesn't return a reference (most notably std::vector), +// we have to access by copying; otherwise we return by reference. +template using vector_needs_copy = negation< + std::is_same()[typename Vector::size_type()]), typename Vector::value_type &>>; + +// The usual case: access and iterate by reference +template +void vector_accessor(enable_if_t::value, Class_> &cl) { + using T = typename Vector::value_type; + using SizeType = typename Vector::size_type; + using ItType = typename Vector::iterator; + + cl.def("__getitem__", + [](Vector &v, SizeType i) -> T & { + if (i >= v.size()) + throw index_error(); + return v[i]; + }, + return_value_policy::reference_internal // ref + keepalive + ); + + cl.def("__iter__", + [](Vector &v) { + return make_iterator< + return_value_policy::reference_internal, ItType, ItType, T&>( + v.begin(), v.end()); + }, + keep_alive<0, 1>() /* Essential: keep list alive while iterator exists */ + ); +} + +// The case for special objects, like std::vector, that have to be returned-by-copy: +template +void vector_accessor(enable_if_t::value, Class_> &cl) { + using T = typename Vector::value_type; + using SizeType = typename Vector::size_type; + using ItType = typename Vector::iterator; + cl.def("__getitem__", + [](const Vector &v, SizeType i) -> T { + if (i >= v.size()) + throw index_error(); + return v[i]; + } + ); + + cl.def("__iter__", + [](Vector &v) { + return make_iterator< + return_value_policy::copy, ItType, ItType, T>( + v.begin(), v.end()); + }, + keep_alive<0, 1>() /* Essential: keep list alive while iterator exists */ + ); +} + +template auto vector_if_insertion_operator(Class_ &cl, std::string const &name) + -> decltype(std::declval() << std::declval(), void()) { + using size_type = typename Vector::size_type; + + cl.def("__repr__", + [name](Vector &v) { + std::ostringstream s; + s << name << '['; + for (size_type i=0; i < v.size(); ++i) { + s << v[i]; + if (i != v.size() - 1) + s << ", "; + } + s << ']'; + return s.str(); + }, + "Return the canonical string representation of this list." + ); +} + +// Provide the buffer interface for vectors if we have data() and we have a format for it +// GCC seems to have "void std::vector::data()" - doing SFINAE on the existence of data() is insufficient, we need to check it returns an appropriate pointer +template +struct vector_has_data_and_format : std::false_type {}; +template +struct vector_has_data_and_format::format(), std::declval().data()), typename Vector::value_type*>::value>> : std::true_type {}; + +// Add the buffer interface to a vector +template +enable_if_t...>::value> +vector_buffer(Class_& cl) { + using T = typename Vector::value_type; + + static_assert(vector_has_data_and_format::value, "There is not an appropriate format descriptor for this vector"); + + // numpy.h declares this for arbitrary types, but it may raise an exception and crash hard at runtime if PYBIND11_NUMPY_DTYPE hasn't been called, so check here + format_descriptor::format(); + + cl.def_buffer([](Vector& v) -> buffer_info { + return buffer_info(v.data(), static_cast(sizeof(T)), format_descriptor::format(), 1, {v.size()}, {sizeof(T)}); + }); + + cl.def("__init__", [](Vector& vec, buffer buf) { + auto info = buf.request(); + if (info.ndim != 1 || info.strides[0] % static_cast(sizeof(T))) + throw type_error("Only valid 1D buffers can be copied to a vector"); + if (!detail::compare_buffer_info::compare(info) || (ssize_t) sizeof(T) != info.itemsize) + throw type_error("Format mismatch (Python: " + info.format + " C++: " + format_descriptor::format() + ")"); + new (&vec) Vector(); + vec.reserve((size_t) info.shape[0]); + T *p = static_cast(info.ptr); + ssize_t step = info.strides[0] / static_cast(sizeof(T)); + T *end = p + info.shape[0] * step; + for (; p != end; p += step) + vec.push_back(*p); + }); + + return; +} + +template +enable_if_t...>::value> vector_buffer(Class_&) {} + +NAMESPACE_END(detail) + +// +// std::vector +// +template , typename... Args> +class_ bind_vector(module &m, std::string const &name, Args&&... args) { + using Class_ = class_; + + Class_ cl(m, name.c_str(), std::forward(args)...); + + // Declare the buffer interface if a buffer_protocol() is passed in + detail::vector_buffer(cl); + + cl.def(init<>()); + + // Register copy constructor (if possible) + detail::vector_if_copy_constructible(cl); + + // Register comparison-related operators and functions (if possible) + detail::vector_if_equal_operator(cl); + + // Register stream insertion operator (if possible) + detail::vector_if_insertion_operator(cl, name); + + // Modifiers require copyable vector value type + detail::vector_modifiers(cl); + + // Accessor and iterator; return by value if copyable, otherwise we return by ref + keep-alive + detail::vector_accessor(cl); + + cl.def("__bool__", + [](const Vector &v) -> bool { + return !v.empty(); + }, + "Check whether the list is nonempty" + ); + + cl.def("__len__", &Vector::size); + + + + +#if 0 + // C++ style functions deprecated, leaving it here as an example + cl.def(init()); + + cl.def("resize", + (void (Vector::*) (size_type count)) & Vector::resize, + "changes the number of elements stored"); + + cl.def("erase", + [](Vector &v, SizeType i) { + if (i >= v.size()) + throw index_error(); + v.erase(v.begin() + i); + }, "erases element at index ``i``"); + + cl.def("empty", &Vector::empty, "checks whether the container is empty"); + cl.def("size", &Vector::size, "returns the number of elements"); + cl.def("push_back", (void (Vector::*)(const T&)) &Vector::push_back, "adds an element to the end"); + cl.def("pop_back", &Vector::pop_back, "removes the last element"); + + cl.def("max_size", &Vector::max_size, "returns the maximum possible number of elements"); + cl.def("reserve", &Vector::reserve, "reserves storage"); + cl.def("capacity", &Vector::capacity, "returns the number of elements that can be held in currently allocated storage"); + cl.def("shrink_to_fit", &Vector::shrink_to_fit, "reduces memory usage by freeing unused memory"); + + cl.def("clear", &Vector::clear, "clears the contents"); + cl.def("swap", &Vector::swap, "swaps the contents"); + + cl.def("front", [](Vector &v) { + if (v.size()) return v.front(); + else throw index_error(); + }, "access the first element"); + + cl.def("back", [](Vector &v) { + if (v.size()) return v.back(); + else throw index_error(); + }, "access the last element "); + +#endif + + return cl; +} + + + +// +// std::map, std::unordered_map +// + +NAMESPACE_BEGIN(detail) + +/* Fallback functions */ +template void map_if_insertion_operator(const Args &...) { } +template void map_assignment(const Args &...) { } + +// Map assignment when copy-assignable: just copy the value +template +void map_assignment(enable_if_t::value, Class_> &cl) { + using KeyType = typename Map::key_type; + using MappedType = typename Map::mapped_type; + + cl.def("__setitem__", + [](Map &m, const KeyType &k, const MappedType &v) { + auto it = m.find(k); + if (it != m.end()) it->second = v; + else m.emplace(k, v); + } + ); +} + +// Not copy-assignable, but still copy-constructible: we can update the value by erasing and reinserting +template +void map_assignment(enable_if_t< + !std::is_copy_assignable::value && + is_copy_constructible::value, + Class_> &cl) { + using KeyType = typename Map::key_type; + using MappedType = typename Map::mapped_type; + + cl.def("__setitem__", + [](Map &m, const KeyType &k, const MappedType &v) { + // We can't use m[k] = v; because value type might not be default constructable + auto r = m.emplace(k, v); + if (!r.second) { + // value type is not copy assignable so the only way to insert it is to erase it first... + m.erase(r.first); + m.emplace(k, v); + } + } + ); +} + + +template auto map_if_insertion_operator(Class_ &cl, std::string const &name) +-> decltype(std::declval() << std::declval() << std::declval(), void()) { + + cl.def("__repr__", + [name](Map &m) { + std::ostringstream s; + s << name << '{'; + bool f = false; + for (auto const &kv : m) { + if (f) + s << ", "; + s << kv.first << ": " << kv.second; + f = true; + } + s << '}'; + return s.str(); + }, + "Return the canonical string representation of this map." + ); +} + + +NAMESPACE_END(detail) + +template , typename... Args> +class_ bind_map(module &m, const std::string &name, Args&&... args) { + using KeyType = typename Map::key_type; + using MappedType = typename Map::mapped_type; + using Class_ = class_; + + Class_ cl(m, name.c_str(), std::forward(args)...); + + cl.def(init<>()); + + // Register stream insertion operator (if possible) + detail::map_if_insertion_operator(cl, name); + + cl.def("__bool__", + [](const Map &m) -> bool { return !m.empty(); }, + "Check whether the map is nonempty" + ); + + cl.def("__iter__", + [](Map &m) { return make_key_iterator(m.begin(), m.end()); }, + keep_alive<0, 1>() /* Essential: keep list alive while iterator exists */ + ); + + cl.def("items", + [](Map &m) { return make_iterator(m.begin(), m.end()); }, + keep_alive<0, 1>() /* Essential: keep list alive while iterator exists */ + ); + + cl.def("__getitem__", + [](Map &m, const KeyType &k) -> MappedType & { + auto it = m.find(k); + if (it == m.end()) + throw key_error(); + return it->second; + }, + return_value_policy::reference_internal // ref + keepalive + ); + + // Assignment provided only if the type is copyable + detail::map_assignment(cl); + + cl.def("__delitem__", + [](Map &m, const KeyType &k) { + auto it = m.find(k); + if (it == m.end()) + throw key_error(); + return m.erase(it); + } + ); + + cl.def("__len__", &Map::size); + + return cl; +} + +NAMESPACE_END(pybind11) diff --git a/ptocr/postprocess/lanms/include/pybind11/typeid.h b/ptocr/postprocess/lanms/include/pybind11/typeid.h new file mode 100644 index 0000000..c903fb1 --- /dev/null +++ b/ptocr/postprocess/lanms/include/pybind11/typeid.h @@ -0,0 +1,53 @@ +/* + pybind11/typeid.h: Compiler-independent access to type identifiers + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include +#include + +#if defined(__GNUG__) +#include +#endif + +NAMESPACE_BEGIN(pybind11) +NAMESPACE_BEGIN(detail) +/// Erase all occurrences of a substring +inline void erase_all(std::string &string, const std::string &search) { + for (size_t pos = 0;;) { + pos = string.find(search, pos); + if (pos == std::string::npos) break; + string.erase(pos, search.length()); + } +} + +PYBIND11_NOINLINE inline void clean_type_id(std::string &name) { +#if defined(__GNUG__) + int status = 0; + std::unique_ptr res { + abi::__cxa_demangle(name.c_str(), nullptr, nullptr, &status), std::free }; + if (status == 0) + name = res.get(); +#else + detail::erase_all(name, "class "); + detail::erase_all(name, "struct "); + detail::erase_all(name, "enum "); +#endif + detail::erase_all(name, "pybind11::"); +} +NAMESPACE_END(detail) + +/// Return a string representation of a C++ type +template static std::string type_id() { + std::string name(typeid(T).name()); + detail::clean_type_id(name); + return name; +} + +NAMESPACE_END(pybind11) diff --git a/ptocr/postprocess/lanms/lanms.h b/ptocr/postprocess/lanms/lanms.h new file mode 100644 index 0000000..679666c --- /dev/null +++ b/ptocr/postprocess/lanms/lanms.h @@ -0,0 +1,234 @@ +#pragma once + +#include "clipper/clipper.hpp" + +// locality-aware NMS +namespace lanms { + + namespace cl = ClipperLib; + + struct Polygon { + cl::Path poly; + float score; + }; + + float paths_area(const ClipperLib::Paths &ps) { + float area = 0; + for (auto &&p: ps) + area += cl::Area(p); + return area; + } + + float poly_iou(const Polygon &a, const Polygon &b) { + cl::Clipper clpr; + clpr.AddPath(a.poly, cl::ptSubject, true); + clpr.AddPath(b.poly, cl::ptClip, true); + + cl::Paths inter, uni; + clpr.Execute(cl::ctIntersection, inter, cl::pftEvenOdd); + clpr.Execute(cl::ctUnion, uni, cl::pftEvenOdd); + + auto inter_area = paths_area(inter), + uni_area = paths_area(uni); + return std::abs(inter_area) / std::max(std::abs(uni_area), 1.0f); + } + + bool should_merge(const Polygon &a, const Polygon &b, float iou_threshold) { + return poly_iou(a, b) > iou_threshold; + } + + /** + * Incrementally merge polygons + */ + class PolyMerger { + public: + PolyMerger(): score(0), nr_polys(0) { + memset(data, 0, sizeof(data)); + } + + /** + * Add a new polygon to be merged. + */ + void add(const Polygon &p_given) { + Polygon p; + if (nr_polys > 0) { + // vertices of two polygons to merge may not in the same order; + // we match their vertices by choosing the ordering that + // minimizes the total squared distance. + // see function normalize_poly for details. + p = normalize_poly(get(), p_given); + } else { + p = p_given; + } + assert(p.poly.size() == 4); + auto &poly = p.poly; + auto s = p.score; + data[0] += poly[0].X * s; + data[1] += poly[0].Y * s; + + data[2] += poly[1].X * s; + data[3] += poly[1].Y * s; + + data[4] += poly[2].X * s; + data[5] += poly[2].Y * s; + + data[6] += poly[3].X * s; + data[7] += poly[3].Y * s; + + score += p.score; + + nr_polys += 1; + } + + inline std::int64_t sqr(std::int64_t x) { return x * x; } + + Polygon normalize_poly( + const Polygon &ref, + const Polygon &p) { + + std::int64_t min_d = std::numeric_limits::max(); + size_t best_start = 0, best_order = 0; + + for (size_t start = 0; start < 4; start ++) { + size_t j = start; + std::int64_t d = ( + sqr(ref.poly[(j + 0) % 4].X - p.poly[(j + 0) % 4].X) + + sqr(ref.poly[(j + 0) % 4].Y - p.poly[(j + 0) % 4].Y) + + sqr(ref.poly[(j + 1) % 4].X - p.poly[(j + 1) % 4].X) + + sqr(ref.poly[(j + 1) % 4].Y - p.poly[(j + 1) % 4].Y) + + sqr(ref.poly[(j + 2) % 4].X - p.poly[(j + 2) % 4].X) + + sqr(ref.poly[(j + 2) % 4].Y - p.poly[(j + 2) % 4].Y) + + sqr(ref.poly[(j + 3) % 4].X - p.poly[(j + 3) % 4].X) + + sqr(ref.poly[(j + 3) % 4].Y - p.poly[(j + 3) % 4].Y) + ); + if (d < min_d) { + min_d = d; + best_start = start; + best_order = 0; + } + + d = ( + sqr(ref.poly[(j + 0) % 4].X - p.poly[(j + 3) % 4].X) + + sqr(ref.poly[(j + 0) % 4].Y - p.poly[(j + 3) % 4].Y) + + sqr(ref.poly[(j + 1) % 4].X - p.poly[(j + 2) % 4].X) + + sqr(ref.poly[(j + 1) % 4].Y - p.poly[(j + 2) % 4].Y) + + sqr(ref.poly[(j + 2) % 4].X - p.poly[(j + 1) % 4].X) + + sqr(ref.poly[(j + 2) % 4].Y - p.poly[(j + 1) % 4].Y) + + sqr(ref.poly[(j + 3) % 4].X - p.poly[(j + 0) % 4].X) + + sqr(ref.poly[(j + 3) % 4].Y - p.poly[(j + 0) % 4].Y) + ); + if (d < min_d) { + min_d = d; + best_start = start; + best_order = 1; + } + } + + Polygon r; + r.poly.resize(4); + auto j = best_start; + if (best_order == 0) { + for (size_t i = 0; i < 4; i ++) + r.poly[i] = p.poly[(j + i) % 4]; + } else { + for (size_t i = 0; i < 4; i ++) + r.poly[i] = p.poly[(j + 4 - i - 1) % 4]; + } + r.score = p.score; + return r; + } + + Polygon get() const { + Polygon p; + + auto &poly = p.poly; + poly.resize(4); + auto score_inv = 1.0f / std::max(1e-8f, score); + poly[0].X = data[0] * score_inv; + poly[0].Y = data[1] * score_inv; + poly[1].X = data[2] * score_inv; + poly[1].Y = data[3] * score_inv; + poly[2].X = data[4] * score_inv; + poly[2].Y = data[5] * score_inv; + poly[3].X = data[6] * score_inv; + poly[3].Y = data[7] * score_inv; + + assert(score > 0); + p.score = score; + + return p; + } + + private: + std::int64_t data[8]; + float score; + std::int32_t nr_polys; + }; + + + /** + * The standard NMS algorithm. + */ + std::vector standard_nms(std::vector &polys, float iou_threshold) { + size_t n = polys.size(); + if (n == 0) + return {}; + std::vector indices(n); + std::iota(std::begin(indices), std::end(indices), 0); + std::sort(std::begin(indices), std::end(indices), [&](size_t i, size_t j) { return polys[i].score > polys[j].score; }); + + std::vector keep; + while (indices.size()) { + size_t p = 0, cur = indices[0]; + keep.emplace_back(cur); + for (size_t i = 1; i < indices.size(); i ++) { + if (!should_merge(polys[cur], polys[indices[i]], iou_threshold)) { + indices[p ++] = indices[i]; + } + } + indices.resize(p); + } + + std::vector ret; + for (auto &&i: keep) { + ret.emplace_back(polys[i]); + } + return ret; + } + + std::vector + merge_quadrangle_n9(const float *data, size_t n, float iou_threshold) { + using cInt = cl::cInt; + + // first pass + std::vector polys; + for (size_t i = 0; i < n; i ++) { + auto p = data + i * 9; + Polygon poly{ + { + {cInt(p[0]), cInt(p[1])}, + {cInt(p[2]), cInt(p[3])}, + {cInt(p[4]), cInt(p[5])}, + {cInt(p[6]), cInt(p[7])}, + }, + p[8], + }; + + if (polys.size()) { + // merge with the last one + auto &bpoly = polys.back(); + if (should_merge(poly, bpoly, iou_threshold)) { + PolyMerger merger; + merger.add(bpoly); + merger.add(poly); + bpoly = merger.get(); + } else { + polys.emplace_back(poly); + } + } else { + polys.emplace_back(poly); + } + } + return standard_nms(polys, iou_threshold); + } +} diff --git a/ptocr/postprocess/locality_aware_nms.py b/ptocr/postprocess/locality_aware_nms.py new file mode 100644 index 0000000..1220ffa --- /dev/null +++ b/ptocr/postprocess/locality_aware_nms.py @@ -0,0 +1,199 @@ +""" +Locality aware nms. +""" + +import numpy as np +from shapely.geometry import Polygon + + +def intersection(g, p): + """ + Intersection. + """ + g = Polygon(g[:8].reshape((4, 2))) + p = Polygon(p[:8].reshape((4, 2))) + g = g.buffer(0) + p = p.buffer(0) + if not g.is_valid or not p.is_valid: + return 0 + inter = Polygon(g).intersection(Polygon(p)).area + union = g.area + p.area - inter + if union == 0: + return 0 + else: + return inter / union + + +def intersection_iog(g, p): + """ + Intersection_iog. + """ + g = Polygon(g[:8].reshape((4, 2))) + p = Polygon(p[:8].reshape((4, 2))) + if not g.is_valid or not p.is_valid: + return 0 + inter = Polygon(g).intersection(Polygon(p)).area + #union = g.area + p.area - inter + union = p.area + if union == 0: + print("p_area is very small") + return 0 + else: + return inter / union + + +def weighted_merge(g, p): + """ + Weighted merge. + """ + g[:8] = (g[8] * g[:8] + p[8] * p[:8]) / (g[8] + p[8]) + g[8] = (g[8] + p[8]) + return g + + +def standard_nms(S, thres): + """ + Standard nms. + """ + order = np.argsort(S[:, 8])[::-1] + keep = [] + while order.size > 0: + i = order[0] + keep.append(i) + ovr = np.array([intersection(S[i], S[t]) for t in order[1:]]) + + inds = np.where(ovr <= thres)[0] + order = order[inds + 1] + + return S[keep] + + +def standard_nms_inds(S, thres): + """ + Standard nms, retun inds. + """ + order = np.argsort(S[:, 8])[::-1] + keep = [] + while order.size > 0: + i = order[0] + keep.append(i) + ovr = np.array([intersection(S[i], S[t]) for t in order[1:]]) + + inds = np.where(ovr <= thres)[0] + order = order[inds + 1] + + return keep + + +def nms(S, thres): + """ + nms. + """ + order = np.argsort(S[:, 8])[::-1] + keep = [] + while order.size > 0: + i = order[0] + keep.append(i) + ovr = np.array([intersection(S[i], S[t]) for t in order[1:]]) + + inds = np.where(ovr <= thres)[0] + order = order[inds + 1] + + return keep + + +def soft_nms(boxes_in, Nt_thres=0.3, threshold=0.8, sigma=0.5, method=2): + """ + soft_nms + :para boxes_in, N x 9 (coords + score) + :para threshould, eliminate cases min score(0.001) + :para Nt_thres, iou_threshi + :para sigma, gaussian weght + :method, linear or gaussian + """ + boxes = boxes_in.copy() + N = boxes.shape[0] + if N is None or N < 1: + return np.array([]) + pos, maxpos = 0, 0 + weight = 0.0 + inds = np.arange(N) + tbox, sbox = boxes[0].copy(), boxes[0].copy() + for i in range(N): + maxscore = boxes[i, 8] + maxpos = i + tbox = boxes[i].copy() + ti = inds[i] + pos = i + 1 + #get max box + while pos < N: + if maxscore < boxes[pos, 8]: + maxscore = boxes[pos, 8] + maxpos = pos + pos = pos + 1 + #add max box as a detection + boxes[i, :] = boxes[maxpos, :] + inds[i] = inds[maxpos] + #swap + boxes[maxpos, :] = tbox + inds[maxpos] = ti + tbox = boxes[i].copy() + pos = i + 1 + #NMS iteration + while pos < N: + sbox = boxes[pos].copy() + ts_iou_val = intersection(tbox, sbox) + if ts_iou_val > 0: + if method == 1: + if ts_iou_val > Nt_thres: + weight = 1 - ts_iou_val + else: + weight = 1 + elif method == 2: + weight = np.exp(-1.0 * ts_iou_val**2 / sigma) + else: + if ts_iou_val > Nt_thres: + weight = 0 + else: + weight = 1 + boxes[pos, 8] = weight * boxes[pos, 8] + #if box score falls below thresold, discard the box by + #swaping last box update N + if boxes[pos, 8] < threshold: + boxes[pos, :] = boxes[N - 1, :] + inds[pos] = inds[N - 1] + N = N - 1 + pos = pos - 1 + pos = pos + 1 + + return boxes[:N] + + +def nms_locality(polys, thres=0.3): + """ + locality aware nms of EAST + :param polys: a N*9 numpy array. first 8 coordinates, then prob + :return: boxes after nms + """ + S = [] + p = None + for g in polys: + if p is not None and intersection(g, p) > thres: + p = weighted_merge(g, p) + else: + if p is not None: + S.append(p) + p = g + if p is not None: + S.append(p) + + if len(S) == 0: + return np.array([]) + return standard_nms(np.array(S), thres) + + +if __name__ == '__main__': + # 343,350,448,135,474,143,369,359 + print( + Polygon(np.array([[343, 350], [448, 135], [474, 143], [369, 359]])) + .area) diff --git a/ptocr/postprocess/piexlmerge/Makefile b/ptocr/postprocess/piexlmerge/Makefile new file mode 100644 index 0000000..7603d58 --- /dev/null +++ b/ptocr/postprocess/piexlmerge/Makefile @@ -0,0 +1,15 @@ +CXXFLAGS = -I include -std=c++11 -O3 $(shell python3-config --cflags) + +DEPS = lanms.h $(shell find include -xtype f) +CXX_SOURCES = pixelmerge.cpp include/clipper/clipper.cpp +OPENCV = `pkg-config --cflags --libs opencv` + +LIB_SO = pixelmerge.so + +$(LIB_SO): $(CXX_SOURCES) $(DEPS) + $(CXX) -o $@ $(CXXFLAGS) $(LDFLAGS) $(CXX_SOURCES) --shared -fPIC $(OPENCV) + +clean: + rm -rf $(LIB_SO) + + diff --git a/ptocr/postprocess/piexlmerge/__init__.py b/ptocr/postprocess/piexlmerge/__init__.py new file mode 100644 index 0000000..0e3f802 --- /dev/null +++ b/ptocr/postprocess/piexlmerge/__init__.py @@ -0,0 +1,88 @@ + +import os +import cv2 +import torch +import time +import subprocess +import numpy as np + + + +BASE_DIR = os.path.dirname(os.path.realpath(__file__)) + +if subprocess.call(['make', '-C', BASE_DIR]) != 0: # return value + raise RuntimeError('Cannot compile pse: {}'.format(BASE_DIR)) + + +def pse(outputs,config): + + from .pixelmerge import pse_cpp,get_points,get_num + + score = torch.sigmoid(outputs[:, 0, :, :]) + outputs = (torch.sign(outputs - config['postprocess']['binary_th']) + 1) / 2 + + text = outputs[:, 0, :, :] + kernels = outputs[:, 0:config['base']['classes'], :, :] * text + + score = score.data.cpu().numpy()[0].astype(np.float32) + kernels = kernels.data.cpu().numpy()[0].astype(np.uint8) + + pred = pse_cpp(kernels,config['postprocess']['min_kernel_area'] / (config['postprocess']['scale'] * config['postprocess']['scale'])) + pred = np.array(pred).astype(np.uint8) + label_num = np.max(pred) + 1 + label_points = get_points(pred, score, label_num) + + label_values = [] + for label_idx in range(1, label_num): + label_values.append(label_idx) + +# label_values = [] +# label_sum = get_num(pred, label_num) +# for label_idx in range(1, label_num): +# if label_sum[label_idx] < config['postprocess']['min_kernel_area']: +# continue +# label_values.append(label_idx) + + + return pred,label_points,label_values + + +def pan(preds, config): + + from .pixelmerge import pan_cpp, get_points, get_num + + pred = (torch.sign(preds[0, 0:2, :, :]-config['postprocess']['bin_th']) + 1) / 2 + score = torch.sigmoid(preds[0]).cpu().numpy().astype(np.float32) + text = pred[0] # text + kernel = (pred[1] * text).cpu().numpy() # kernel + text = text.cpu().numpy() + +# score = torch.sigmoid(preds[0]).cpu().numpy().astype(np.float32) +# pred_t = torch.sigmoid(preds[0, 0:2, :, :]) +# text = pred_t[0]> 0.8 +# kernel = (pred_t[1]> 0.8)* text +# text = text.cpu().numpy() +# kernel = kernel.cpu().numpy() + + similarity_vectors = preds[0,2:].permute((1, 2, 0)).cpu().numpy().astype(np.float32) + + + label_num, label = cv2.connectedComponents(kernel.astype(np.uint8), connectivity=4) + label_values = [] + label_sum = get_num(label, label_num) + for label_idx in range(1, label_num): + if label_sum[label_idx] < config['postprocess']['min_kernel_area']: + continue + label_values.append(label_idx) + + pred = pan_cpp(text.astype(np.uint8), similarity_vectors, label, label_num, config['postprocess']['dis_thresh']) + pred = pred.reshape(text.shape) + + label_points = get_points(pred, score, label_num) + + return pred,label_points,label_values + + + + + diff --git a/ptocr/postprocess/piexlmerge/include/clipper/clipper.cpp b/ptocr/postprocess/piexlmerge/include/clipper/clipper.cpp new file mode 100644 index 0000000..4993ba9 --- /dev/null +++ b/ptocr/postprocess/piexlmerge/include/clipper/clipper.cpp @@ -0,0 +1,4622 @@ +/******************************************************************************* +* * +* Author : Angus Johnson * +* Version : 6.4.0 * +* Date : 2 July 2015 * +* Website : http://www.angusj.com * +* Copyright : Angus Johnson 2010-2015 * +* * +* License: * +* Use, modification & distribution is subject to Boost Software License Ver 1. * +* http://www.boost.org/LICENSE_1_0.txt * +* * +* Attributions: * +* The code in this library is an extension of Bala Vatti's clipping algorithm: * +* "A generic solution to polygon clipping" * +* Communications of the ACM, Vol 35, Issue 7 (July 1992) pp 56-63. * +* http://portal.acm.org/citation.cfm?id=129906 * +* * +* Computer graphics and geometric modeling: implementation and algorithms * +* By Max K. Agoston * +* Springer; 1 edition (January 4, 2005) * +* http://books.google.com/books?q=vatti+clipping+agoston * +* * +* See also: * +* "Polygon Offsetting by Computing Winding Numbers" * +* Paper no. DETC2005-85513 pp. 565-575 * +* ASME 2005 International Design Engineering Technical Conferences * +* and Computers and Information in Engineering Conference (IDETC/CIE2005) * +* September 24-28, 2005 , Long Beach, California, USA * +* http://www.me.berkeley.edu/~mcmains/pubs/DAC05OffsetPolygon.pdf * +* * +*******************************************************************************/ + +/******************************************************************************* +* * +* This is a translation of the Delphi Clipper library and the naming style * +* used has retained a Delphi flavour. * +* * +*******************************************************************************/ + +#include "clipper.hpp" +#include +#include +#include +#include +#include +#include +#include +#include + +namespace ClipperLib { + +static double const pi = 3.141592653589793238; +static double const two_pi = pi *2; +static double const def_arc_tolerance = 0.25; + +enum Direction { dRightToLeft, dLeftToRight }; + +static int const Unassigned = -1; //edge not currently 'owning' a solution +static int const Skip = -2; //edge that would otherwise close a path + +#define HORIZONTAL (-1.0E+40) +#define TOLERANCE (1.0e-20) +#define NEAR_ZERO(val) (((val) > -TOLERANCE) && ((val) < TOLERANCE)) + +struct TEdge { + IntPoint Bot; + IntPoint Curr; //current (updated for every new scanbeam) + IntPoint Top; + double Dx; + PolyType PolyTyp; + EdgeSide Side; //side only refers to current side of solution poly + int WindDelta; //1 or -1 depending on winding direction + int WindCnt; + int WindCnt2; //winding count of the opposite polytype + int OutIdx; + TEdge *Next; + TEdge *Prev; + TEdge *NextInLML; + TEdge *NextInAEL; + TEdge *PrevInAEL; + TEdge *NextInSEL; + TEdge *PrevInSEL; +}; + +struct IntersectNode { + TEdge *Edge1; + TEdge *Edge2; + IntPoint Pt; +}; + +struct LocalMinimum { + cInt Y; + TEdge *LeftBound; + TEdge *RightBound; +}; + +struct OutPt; + +//OutRec: contains a path in the clipping solution. Edges in the AEL will +//carry a pointer to an OutRec when they are part of the clipping solution. +struct OutRec { + int Idx; + bool IsHole; + bool IsOpen; + OutRec *FirstLeft; //see comments in clipper.pas + PolyNode *PolyNd; + OutPt *Pts; + OutPt *BottomPt; +}; + +struct OutPt { + int Idx; + IntPoint Pt; + OutPt *Next; + OutPt *Prev; +}; + +struct Join { + OutPt *OutPt1; + OutPt *OutPt2; + IntPoint OffPt; +}; + +struct LocMinSorter +{ + inline bool operator()(const LocalMinimum& locMin1, const LocalMinimum& locMin2) + { + return locMin2.Y < locMin1.Y; + } +}; + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + +inline cInt Round(double val) +{ + if ((val < 0)) return static_cast(val - 0.5); + else return static_cast(val + 0.5); +} +//------------------------------------------------------------------------------ + +inline cInt Abs(cInt val) +{ + return val < 0 ? -val : val; +} + +//------------------------------------------------------------------------------ +// PolyTree methods ... +//------------------------------------------------------------------------------ + +void PolyTree::Clear() +{ + for (PolyNodes::size_type i = 0; i < AllNodes.size(); ++i) + delete AllNodes[i]; + AllNodes.resize(0); + Childs.resize(0); +} +//------------------------------------------------------------------------------ + +PolyNode* PolyTree::GetFirst() const +{ + if (!Childs.empty()) + return Childs[0]; + else + return 0; +} +//------------------------------------------------------------------------------ + +int PolyTree::Total() const +{ + int result = (int)AllNodes.size(); + //with negative offsets, ignore the hidden outer polygon ... + if (result > 0 && Childs[0] != AllNodes[0]) result--; + return result; +} + +//------------------------------------------------------------------------------ +// PolyNode methods ... +//------------------------------------------------------------------------------ + +PolyNode::PolyNode(): Childs(), Parent(0), Index(0), m_IsOpen(false) +{ +} +//------------------------------------------------------------------------------ + +int PolyNode::ChildCount() const +{ + return (int)Childs.size(); +} +//------------------------------------------------------------------------------ + +void PolyNode::AddChild(PolyNode& child) +{ + unsigned cnt = (unsigned)Childs.size(); + Childs.push_back(&child); + child.Parent = this; + child.Index = cnt; +} +//------------------------------------------------------------------------------ + +PolyNode* PolyNode::GetNext() const +{ + if (!Childs.empty()) + return Childs[0]; + else + return GetNextSiblingUp(); +} +//------------------------------------------------------------------------------ + +PolyNode* PolyNode::GetNextSiblingUp() const +{ + if (!Parent) //protects against PolyTree.GetNextSiblingUp() + return 0; + else if (Index == Parent->Childs.size() - 1) + return Parent->GetNextSiblingUp(); + else + return Parent->Childs[Index + 1]; +} +//------------------------------------------------------------------------------ + +bool PolyNode::IsHole() const +{ + bool result = true; + PolyNode* node = Parent; + while (node) + { + result = !result; + node = node->Parent; + } + return result; +} +//------------------------------------------------------------------------------ + +bool PolyNode::IsOpen() const +{ + return m_IsOpen; +} +//------------------------------------------------------------------------------ + +#ifndef use_int32 + +//------------------------------------------------------------------------------ +// Int128 class (enables safe math on signed 64bit integers) +// eg Int128 val1((long64)9223372036854775807); //ie 2^63 -1 +// Int128 val2((long64)9223372036854775807); +// Int128 val3 = val1 * val2; +// val3.AsString => "85070591730234615847396907784232501249" (8.5e+37) +//------------------------------------------------------------------------------ + +class Int128 +{ + public: + ulong64 lo; + long64 hi; + + Int128(long64 _lo = 0) + { + lo = (ulong64)_lo; + if (_lo < 0) hi = -1; else hi = 0; + } + + + Int128(const Int128 &val): lo(val.lo), hi(val.hi){} + + Int128(const long64& _hi, const ulong64& _lo): lo(_lo), hi(_hi){} + + Int128& operator = (const long64 &val) + { + lo = (ulong64)val; + if (val < 0) hi = -1; else hi = 0; + return *this; + } + + bool operator == (const Int128 &val) const + {return (hi == val.hi && lo == val.lo);} + + bool operator != (const Int128 &val) const + { return !(*this == val);} + + bool operator > (const Int128 &val) const + { + if (hi != val.hi) + return hi > val.hi; + else + return lo > val.lo; + } + + bool operator < (const Int128 &val) const + { + if (hi != val.hi) + return hi < val.hi; + else + return lo < val.lo; + } + + bool operator >= (const Int128 &val) const + { return !(*this < val);} + + bool operator <= (const Int128 &val) const + { return !(*this > val);} + + Int128& operator += (const Int128 &rhs) + { + hi += rhs.hi; + lo += rhs.lo; + if (lo < rhs.lo) hi++; + return *this; + } + + Int128 operator + (const Int128 &rhs) const + { + Int128 result(*this); + result+= rhs; + return result; + } + + Int128& operator -= (const Int128 &rhs) + { + *this += -rhs; + return *this; + } + + Int128 operator - (const Int128 &rhs) const + { + Int128 result(*this); + result -= rhs; + return result; + } + + Int128 operator-() const //unary negation + { + if (lo == 0) + return Int128(-hi, 0); + else + return Int128(~hi, ~lo + 1); + } + + operator double() const + { + const double shift64 = 18446744073709551616.0; //2^64 + if (hi < 0) + { + if (lo == 0) return (double)hi * shift64; + else return -(double)(~lo + ~hi * shift64); + } + else + return (double)(lo + hi * shift64); + } + +}; +//------------------------------------------------------------------------------ + +Int128 Int128Mul (long64 lhs, long64 rhs) +{ + bool negate = (lhs < 0) != (rhs < 0); + + if (lhs < 0) lhs = -lhs; + ulong64 int1Hi = ulong64(lhs) >> 32; + ulong64 int1Lo = ulong64(lhs & 0xFFFFFFFF); + + if (rhs < 0) rhs = -rhs; + ulong64 int2Hi = ulong64(rhs) >> 32; + ulong64 int2Lo = ulong64(rhs & 0xFFFFFFFF); + + //nb: see comments in clipper.pas + ulong64 a = int1Hi * int2Hi; + ulong64 b = int1Lo * int2Lo; + ulong64 c = int1Hi * int2Lo + int1Lo * int2Hi; + + Int128 tmp; + tmp.hi = long64(a + (c >> 32)); + tmp.lo = long64(c << 32); + tmp.lo += long64(b); + if (tmp.lo < b) tmp.hi++; + if (negate) tmp = -tmp; + return tmp; +}; +#endif + +//------------------------------------------------------------------------------ +// Miscellaneous global functions +//------------------------------------------------------------------------------ + +bool Orientation(const Path &poly) +{ + return Area(poly) >= 0; +} +//------------------------------------------------------------------------------ + +double Area(const Path &poly) +{ + int size = (int)poly.size(); + if (size < 3) return 0; + + double a = 0; + for (int i = 0, j = size -1; i < size; ++i) + { + a += ((double)poly[j].X + poly[i].X) * ((double)poly[j].Y - poly[i].Y); + j = i; + } + return -a * 0.5; +} +//------------------------------------------------------------------------------ + +double Area(const OutPt *op) +{ + const OutPt *startOp = op; + if (!op) return 0; + double a = 0; + do { + a += (double)(op->Prev->Pt.X + op->Pt.X) * (double)(op->Prev->Pt.Y - op->Pt.Y); + op = op->Next; + } while (op != startOp); + return a * 0.5; +} +//------------------------------------------------------------------------------ + +double Area(const OutRec &outRec) +{ + return Area(outRec.Pts); +} +//------------------------------------------------------------------------------ + +bool PointIsVertex(const IntPoint &Pt, OutPt *pp) +{ + OutPt *pp2 = pp; + do + { + if (pp2->Pt == Pt) return true; + pp2 = pp2->Next; + } + while (pp2 != pp); + return false; +} +//------------------------------------------------------------------------------ + +//See "The Point in Polygon Problem for Arbitrary Polygons" by Hormann & Agathos +//http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.88.5498&rep=rep1&type=pdf +int PointInPolygon(const IntPoint &pt, const Path &path) +{ + //returns 0 if false, +1 if true, -1 if pt ON polygon boundary + int result = 0; + size_t cnt = path.size(); + if (cnt < 3) return 0; + IntPoint ip = path[0]; + for(size_t i = 1; i <= cnt; ++i) + { + IntPoint ipNext = (i == cnt ? path[0] : path[i]); + if (ipNext.Y == pt.Y) + { + if ((ipNext.X == pt.X) || (ip.Y == pt.Y && + ((ipNext.X > pt.X) == (ip.X < pt.X)))) return -1; + } + if ((ip.Y < pt.Y) != (ipNext.Y < pt.Y)) + { + if (ip.X >= pt.X) + { + if (ipNext.X > pt.X) result = 1 - result; + else + { + double d = (double)(ip.X - pt.X) * (ipNext.Y - pt.Y) - + (double)(ipNext.X - pt.X) * (ip.Y - pt.Y); + if (!d) return -1; + if ((d > 0) == (ipNext.Y > ip.Y)) result = 1 - result; + } + } else + { + if (ipNext.X > pt.X) + { + double d = (double)(ip.X - pt.X) * (ipNext.Y - pt.Y) - + (double)(ipNext.X - pt.X) * (ip.Y - pt.Y); + if (!d) return -1; + if ((d > 0) == (ipNext.Y > ip.Y)) result = 1 - result; + } + } + } + ip = ipNext; + } + return result; +} +//------------------------------------------------------------------------------ + +int PointInPolygon (const IntPoint &pt, OutPt *op) +{ + //returns 0 if false, +1 if true, -1 if pt ON polygon boundary + int result = 0; + OutPt* startOp = op; + for(;;) + { + if (op->Next->Pt.Y == pt.Y) + { + if ((op->Next->Pt.X == pt.X) || (op->Pt.Y == pt.Y && + ((op->Next->Pt.X > pt.X) == (op->Pt.X < pt.X)))) return -1; + } + if ((op->Pt.Y < pt.Y) != (op->Next->Pt.Y < pt.Y)) + { + if (op->Pt.X >= pt.X) + { + if (op->Next->Pt.X > pt.X) result = 1 - result; + else + { + double d = (double)(op->Pt.X - pt.X) * (op->Next->Pt.Y - pt.Y) - + (double)(op->Next->Pt.X - pt.X) * (op->Pt.Y - pt.Y); + if (!d) return -1; + if ((d > 0) == (op->Next->Pt.Y > op->Pt.Y)) result = 1 - result; + } + } else + { + if (op->Next->Pt.X > pt.X) + { + double d = (double)(op->Pt.X - pt.X) * (op->Next->Pt.Y - pt.Y) - + (double)(op->Next->Pt.X - pt.X) * (op->Pt.Y - pt.Y); + if (!d) return -1; + if ((d > 0) == (op->Next->Pt.Y > op->Pt.Y)) result = 1 - result; + } + } + } + op = op->Next; + if (startOp == op) break; + } + return result; +} +//------------------------------------------------------------------------------ + +bool Poly2ContainsPoly1(OutPt *OutPt1, OutPt *OutPt2) +{ + OutPt* op = OutPt1; + do + { + //nb: PointInPolygon returns 0 if false, +1 if true, -1 if pt on polygon + int res = PointInPolygon(op->Pt, OutPt2); + if (res >= 0) return res > 0; + op = op->Next; + } + while (op != OutPt1); + return true; +} +//---------------------------------------------------------------------- + +bool SlopesEqual(const TEdge &e1, const TEdge &e2, bool UseFullInt64Range) +{ +#ifndef use_int32 + if (UseFullInt64Range) + return Int128Mul(e1.Top.Y - e1.Bot.Y, e2.Top.X - e2.Bot.X) == + Int128Mul(e1.Top.X - e1.Bot.X, e2.Top.Y - e2.Bot.Y); + else +#endif + return (e1.Top.Y - e1.Bot.Y) * (e2.Top.X - e2.Bot.X) == + (e1.Top.X - e1.Bot.X) * (e2.Top.Y - e2.Bot.Y); +} +//------------------------------------------------------------------------------ + +bool SlopesEqual(const IntPoint pt1, const IntPoint pt2, + const IntPoint pt3, bool UseFullInt64Range) +{ +#ifndef use_int32 + if (UseFullInt64Range) + return Int128Mul(pt1.Y-pt2.Y, pt2.X-pt3.X) == Int128Mul(pt1.X-pt2.X, pt2.Y-pt3.Y); + else +#endif + return (pt1.Y-pt2.Y)*(pt2.X-pt3.X) == (pt1.X-pt2.X)*(pt2.Y-pt3.Y); +} +//------------------------------------------------------------------------------ + +bool SlopesEqual(const IntPoint pt1, const IntPoint pt2, + const IntPoint pt3, const IntPoint pt4, bool UseFullInt64Range) +{ +#ifndef use_int32 + if (UseFullInt64Range) + return Int128Mul(pt1.Y-pt2.Y, pt3.X-pt4.X) == Int128Mul(pt1.X-pt2.X, pt3.Y-pt4.Y); + else +#endif + return (pt1.Y-pt2.Y)*(pt3.X-pt4.X) == (pt1.X-pt2.X)*(pt3.Y-pt4.Y); +} +//------------------------------------------------------------------------------ + +inline bool IsHorizontal(TEdge &e) +{ + return e.Dx == HORIZONTAL; +} +//------------------------------------------------------------------------------ + +inline double GetDx(const IntPoint pt1, const IntPoint pt2) +{ + return (pt1.Y == pt2.Y) ? + HORIZONTAL : (double)(pt2.X - pt1.X) / (pt2.Y - pt1.Y); +} +//--------------------------------------------------------------------------- + +inline void SetDx(TEdge &e) +{ + cInt dy = (e.Top.Y - e.Bot.Y); + if (dy == 0) e.Dx = HORIZONTAL; + else e.Dx = (double)(e.Top.X - e.Bot.X) / dy; +} +//--------------------------------------------------------------------------- + +inline void SwapSides(TEdge &Edge1, TEdge &Edge2) +{ + EdgeSide Side = Edge1.Side; + Edge1.Side = Edge2.Side; + Edge2.Side = Side; +} +//------------------------------------------------------------------------------ + +inline void SwapPolyIndexes(TEdge &Edge1, TEdge &Edge2) +{ + int OutIdx = Edge1.OutIdx; + Edge1.OutIdx = Edge2.OutIdx; + Edge2.OutIdx = OutIdx; +} +//------------------------------------------------------------------------------ + +inline cInt TopX(TEdge &edge, const cInt currentY) +{ + return ( currentY == edge.Top.Y ) ? + edge.Top.X : edge.Bot.X + Round(edge.Dx *(currentY - edge.Bot.Y)); +} +//------------------------------------------------------------------------------ + +void IntersectPoint(TEdge &Edge1, TEdge &Edge2, IntPoint &ip) +{ +#ifdef use_xyz + ip.Z = 0; +#endif + + double b1, b2; + if (Edge1.Dx == Edge2.Dx) + { + ip.Y = Edge1.Curr.Y; + ip.X = TopX(Edge1, ip.Y); + return; + } + else if (Edge1.Dx == 0) + { + ip.X = Edge1.Bot.X; + if (IsHorizontal(Edge2)) + ip.Y = Edge2.Bot.Y; + else + { + b2 = Edge2.Bot.Y - (Edge2.Bot.X / Edge2.Dx); + ip.Y = Round(ip.X / Edge2.Dx + b2); + } + } + else if (Edge2.Dx == 0) + { + ip.X = Edge2.Bot.X; + if (IsHorizontal(Edge1)) + ip.Y = Edge1.Bot.Y; + else + { + b1 = Edge1.Bot.Y - (Edge1.Bot.X / Edge1.Dx); + ip.Y = Round(ip.X / Edge1.Dx + b1); + } + } + else + { + b1 = Edge1.Bot.X - Edge1.Bot.Y * Edge1.Dx; + b2 = Edge2.Bot.X - Edge2.Bot.Y * Edge2.Dx; + double q = (b2-b1) / (Edge1.Dx - Edge2.Dx); + ip.Y = Round(q); + if (std::fabs(Edge1.Dx) < std::fabs(Edge2.Dx)) + ip.X = Round(Edge1.Dx * q + b1); + else + ip.X = Round(Edge2.Dx * q + b2); + } + + if (ip.Y < Edge1.Top.Y || ip.Y < Edge2.Top.Y) + { + if (Edge1.Top.Y > Edge2.Top.Y) + ip.Y = Edge1.Top.Y; + else + ip.Y = Edge2.Top.Y; + if (std::fabs(Edge1.Dx) < std::fabs(Edge2.Dx)) + ip.X = TopX(Edge1, ip.Y); + else + ip.X = TopX(Edge2, ip.Y); + } + //finally, don't allow 'ip' to be BELOW curr.Y (ie bottom of scanbeam) ... + if (ip.Y > Edge1.Curr.Y) + { + ip.Y = Edge1.Curr.Y; + //use the more vertical edge to derive X ... + if (std::fabs(Edge1.Dx) > std::fabs(Edge2.Dx)) + ip.X = TopX(Edge2, ip.Y); else + ip.X = TopX(Edge1, ip.Y); + } +} +//------------------------------------------------------------------------------ + +void ReversePolyPtLinks(OutPt *pp) +{ + if (!pp) return; + OutPt *pp1, *pp2; + pp1 = pp; + do { + pp2 = pp1->Next; + pp1->Next = pp1->Prev; + pp1->Prev = pp2; + pp1 = pp2; + } while( pp1 != pp ); +} +//------------------------------------------------------------------------------ + +void DisposeOutPts(OutPt*& pp) +{ + if (pp == 0) return; + pp->Prev->Next = 0; + while( pp ) + { + OutPt *tmpPp = pp; + pp = pp->Next; + delete tmpPp; + } +} +//------------------------------------------------------------------------------ + +inline void InitEdge(TEdge* e, TEdge* eNext, TEdge* ePrev, const IntPoint& Pt) +{ + std::memset(e, 0, sizeof(TEdge)); + e->Next = eNext; + e->Prev = ePrev; + e->Curr = Pt; + e->OutIdx = Unassigned; +} +//------------------------------------------------------------------------------ + +void InitEdge2(TEdge& e, PolyType Pt) +{ + if (e.Curr.Y >= e.Next->Curr.Y) + { + e.Bot = e.Curr; + e.Top = e.Next->Curr; + } else + { + e.Top = e.Curr; + e.Bot = e.Next->Curr; + } + SetDx(e); + e.PolyTyp = Pt; +} +//------------------------------------------------------------------------------ + +TEdge* RemoveEdge(TEdge* e) +{ + //removes e from double_linked_list (but without removing from memory) + e->Prev->Next = e->Next; + e->Next->Prev = e->Prev; + TEdge* result = e->Next; + e->Prev = 0; //flag as removed (see ClipperBase.Clear) + return result; +} +//------------------------------------------------------------------------------ + +inline void ReverseHorizontal(TEdge &e) +{ + //swap horizontal edges' Top and Bottom x's so they follow the natural + //progression of the bounds - ie so their xbots will align with the + //adjoining lower edge. [Helpful in the ProcessHorizontal() method.] + std::swap(e.Top.X, e.Bot.X); +#ifdef use_xyz + std::swap(e.Top.Z, e.Bot.Z); +#endif +} +//------------------------------------------------------------------------------ + +void SwapPoints(IntPoint &pt1, IntPoint &pt2) +{ + IntPoint tmp = pt1; + pt1 = pt2; + pt2 = tmp; +} +//------------------------------------------------------------------------------ + +bool GetOverlapSegment(IntPoint pt1a, IntPoint pt1b, IntPoint pt2a, + IntPoint pt2b, IntPoint &pt1, IntPoint &pt2) +{ + //precondition: segments are Collinear. + if (Abs(pt1a.X - pt1b.X) > Abs(pt1a.Y - pt1b.Y)) + { + if (pt1a.X > pt1b.X) SwapPoints(pt1a, pt1b); + if (pt2a.X > pt2b.X) SwapPoints(pt2a, pt2b); + if (pt1a.X > pt2a.X) pt1 = pt1a; else pt1 = pt2a; + if (pt1b.X < pt2b.X) pt2 = pt1b; else pt2 = pt2b; + return pt1.X < pt2.X; + } else + { + if (pt1a.Y < pt1b.Y) SwapPoints(pt1a, pt1b); + if (pt2a.Y < pt2b.Y) SwapPoints(pt2a, pt2b); + if (pt1a.Y < pt2a.Y) pt1 = pt1a; else pt1 = pt2a; + if (pt1b.Y > pt2b.Y) pt2 = pt1b; else pt2 = pt2b; + return pt1.Y > pt2.Y; + } +} +//------------------------------------------------------------------------------ + +bool FirstIsBottomPt(const OutPt* btmPt1, const OutPt* btmPt2) +{ + OutPt *p = btmPt1->Prev; + while ((p->Pt == btmPt1->Pt) && (p != btmPt1)) p = p->Prev; + double dx1p = std::fabs(GetDx(btmPt1->Pt, p->Pt)); + p = btmPt1->Next; + while ((p->Pt == btmPt1->Pt) && (p != btmPt1)) p = p->Next; + double dx1n = std::fabs(GetDx(btmPt1->Pt, p->Pt)); + + p = btmPt2->Prev; + while ((p->Pt == btmPt2->Pt) && (p != btmPt2)) p = p->Prev; + double dx2p = std::fabs(GetDx(btmPt2->Pt, p->Pt)); + p = btmPt2->Next; + while ((p->Pt == btmPt2->Pt) && (p != btmPt2)) p = p->Next; + double dx2n = std::fabs(GetDx(btmPt2->Pt, p->Pt)); + + if (std::max(dx1p, dx1n) == std::max(dx2p, dx2n) && + std::min(dx1p, dx1n) == std::min(dx2p, dx2n)) + return Area(btmPt1) > 0; //if otherwise identical use orientation + else + return (dx1p >= dx2p && dx1p >= dx2n) || (dx1n >= dx2p && dx1n >= dx2n); +} +//------------------------------------------------------------------------------ + +OutPt* GetBottomPt(OutPt *pp) +{ + OutPt* dups = 0; + OutPt* p = pp->Next; + while (p != pp) + { + if (p->Pt.Y > pp->Pt.Y) + { + pp = p; + dups = 0; + } + else if (p->Pt.Y == pp->Pt.Y && p->Pt.X <= pp->Pt.X) + { + if (p->Pt.X < pp->Pt.X) + { + dups = 0; + pp = p; + } else + { + if (p->Next != pp && p->Prev != pp) dups = p; + } + } + p = p->Next; + } + if (dups) + { + //there appears to be at least 2 vertices at BottomPt so ... + while (dups != p) + { + if (!FirstIsBottomPt(p, dups)) pp = dups; + dups = dups->Next; + while (dups->Pt != pp->Pt) dups = dups->Next; + } + } + return pp; +} +//------------------------------------------------------------------------------ + +bool Pt2IsBetweenPt1AndPt3(const IntPoint pt1, + const IntPoint pt2, const IntPoint pt3) +{ + if ((pt1 == pt3) || (pt1 == pt2) || (pt3 == pt2)) + return false; + else if (pt1.X != pt3.X) + return (pt2.X > pt1.X) == (pt2.X < pt3.X); + else + return (pt2.Y > pt1.Y) == (pt2.Y < pt3.Y); +} +//------------------------------------------------------------------------------ + +bool HorzSegmentsOverlap(cInt seg1a, cInt seg1b, cInt seg2a, cInt seg2b) +{ + if (seg1a > seg1b) std::swap(seg1a, seg1b); + if (seg2a > seg2b) std::swap(seg2a, seg2b); + return (seg1a < seg2b) && (seg2a < seg1b); +} + +//------------------------------------------------------------------------------ +// ClipperBase class methods ... +//------------------------------------------------------------------------------ + +ClipperBase::ClipperBase() //constructor +{ + m_CurrentLM = m_MinimaList.begin(); //begin() == end() here + m_UseFullRange = false; +} +//------------------------------------------------------------------------------ + +ClipperBase::~ClipperBase() //destructor +{ + Clear(); +} +//------------------------------------------------------------------------------ + +void RangeTest(const IntPoint& Pt, bool& useFullRange) +{ + if (useFullRange) + { + if (Pt.X > hiRange || Pt.Y > hiRange || -Pt.X > hiRange || -Pt.Y > hiRange) + throw clipperException("Coordinate outside allowed range"); + } + else if (Pt.X > loRange|| Pt.Y > loRange || -Pt.X > loRange || -Pt.Y > loRange) + { + useFullRange = true; + RangeTest(Pt, useFullRange); + } +} +//------------------------------------------------------------------------------ + +TEdge* FindNextLocMin(TEdge* E) +{ + for (;;) + { + while (E->Bot != E->Prev->Bot || E->Curr == E->Top) E = E->Next; + if (!IsHorizontal(*E) && !IsHorizontal(*E->Prev)) break; + while (IsHorizontal(*E->Prev)) E = E->Prev; + TEdge* E2 = E; + while (IsHorizontal(*E)) E = E->Next; + if (E->Top.Y == E->Prev->Bot.Y) continue; //ie just an intermediate horz. + if (E2->Prev->Bot.X < E->Bot.X) E = E2; + break; + } + return E; +} +//------------------------------------------------------------------------------ + +TEdge* ClipperBase::ProcessBound(TEdge* E, bool NextIsForward) +{ + TEdge *Result = E; + TEdge *Horz = 0; + + if (E->OutIdx == Skip) + { + //if edges still remain in the current bound beyond the skip edge then + //create another LocMin and call ProcessBound once more + if (NextIsForward) + { + while (E->Top.Y == E->Next->Bot.Y) E = E->Next; + //don't include top horizontals when parsing a bound a second time, + //they will be contained in the opposite bound ... + while (E != Result && IsHorizontal(*E)) E = E->Prev; + } + else + { + while (E->Top.Y == E->Prev->Bot.Y) E = E->Prev; + while (E != Result && IsHorizontal(*E)) E = E->Next; + } + + if (E == Result) + { + if (NextIsForward) Result = E->Next; + else Result = E->Prev; + } + else + { + //there are more edges in the bound beyond result starting with E + if (NextIsForward) + E = Result->Next; + else + E = Result->Prev; + MinimaList::value_type locMin; + locMin.Y = E->Bot.Y; + locMin.LeftBound = 0; + locMin.RightBound = E; + E->WindDelta = 0; + Result = ProcessBound(E, NextIsForward); + m_MinimaList.push_back(locMin); + } + return Result; + } + + TEdge *EStart; + + if (IsHorizontal(*E)) + { + //We need to be careful with open paths because this may not be a + //true local minima (ie E may be following a skip edge). + //Also, consecutive horz. edges may start heading left before going right. + if (NextIsForward) + EStart = E->Prev; + else + EStart = E->Next; + if (IsHorizontal(*EStart)) //ie an adjoining horizontal skip edge + { + if (EStart->Bot.X != E->Bot.X && EStart->Top.X != E->Bot.X) + ReverseHorizontal(*E); + } + else if (EStart->Bot.X != E->Bot.X) + ReverseHorizontal(*E); + } + + EStart = E; + if (NextIsForward) + { + while (Result->Top.Y == Result->Next->Bot.Y && Result->Next->OutIdx != Skip) + Result = Result->Next; + if (IsHorizontal(*Result) && Result->Next->OutIdx != Skip) + { + //nb: at the top of a bound, horizontals are added to the bound + //only when the preceding edge attaches to the horizontal's left vertex + //unless a Skip edge is encountered when that becomes the top divide + Horz = Result; + while (IsHorizontal(*Horz->Prev)) Horz = Horz->Prev; + if (Horz->Prev->Top.X > Result->Next->Top.X) Result = Horz->Prev; + } + while (E != Result) + { + E->NextInLML = E->Next; + if (IsHorizontal(*E) && E != EStart && + E->Bot.X != E->Prev->Top.X) ReverseHorizontal(*E); + E = E->Next; + } + if (IsHorizontal(*E) && E != EStart && E->Bot.X != E->Prev->Top.X) + ReverseHorizontal(*E); + Result = Result->Next; //move to the edge just beyond current bound + } else + { + while (Result->Top.Y == Result->Prev->Bot.Y && Result->Prev->OutIdx != Skip) + Result = Result->Prev; + if (IsHorizontal(*Result) && Result->Prev->OutIdx != Skip) + { + Horz = Result; + while (IsHorizontal(*Horz->Next)) Horz = Horz->Next; + if (Horz->Next->Top.X == Result->Prev->Top.X || + Horz->Next->Top.X > Result->Prev->Top.X) Result = Horz->Next; + } + + while (E != Result) + { + E->NextInLML = E->Prev; + if (IsHorizontal(*E) && E != EStart && E->Bot.X != E->Next->Top.X) + ReverseHorizontal(*E); + E = E->Prev; + } + if (IsHorizontal(*E) && E != EStart && E->Bot.X != E->Next->Top.X) + ReverseHorizontal(*E); + Result = Result->Prev; //move to the edge just beyond current bound + } + + return Result; +} +//------------------------------------------------------------------------------ + +bool ClipperBase::AddPath(const Path &pg, PolyType PolyTyp, bool Closed) +{ +#ifdef use_lines + if (!Closed && PolyTyp == ptClip) + throw clipperException("AddPath: Open paths must be subject."); +#else + if (!Closed) + throw clipperException("AddPath: Open paths have been disabled."); +#endif + + int highI = (int)pg.size() -1; + if (Closed) while (highI > 0 && (pg[highI] == pg[0])) --highI; + while (highI > 0 && (pg[highI] == pg[highI -1])) --highI; + if ((Closed && highI < 2) || (!Closed && highI < 1)) return false; + + //create a new edge array ... + TEdge *edges = new TEdge [highI +1]; + + bool IsFlat = true; + //1. Basic (first) edge initialization ... + try + { + edges[1].Curr = pg[1]; + RangeTest(pg[0], m_UseFullRange); + RangeTest(pg[highI], m_UseFullRange); + InitEdge(&edges[0], &edges[1], &edges[highI], pg[0]); + InitEdge(&edges[highI], &edges[0], &edges[highI-1], pg[highI]); + for (int i = highI - 1; i >= 1; --i) + { + RangeTest(pg[i], m_UseFullRange); + InitEdge(&edges[i], &edges[i+1], &edges[i-1], pg[i]); + } + } + catch(...) + { + delete [] edges; + throw; //range test fails + } + TEdge *eStart = &edges[0]; + + //2. Remove duplicate vertices, and (when closed) collinear edges ... + TEdge *E = eStart, *eLoopStop = eStart; + for (;;) + { + //nb: allows matching start and end points when not Closed ... + if (E->Curr == E->Next->Curr && (Closed || E->Next != eStart)) + { + if (E == E->Next) break; + if (E == eStart) eStart = E->Next; + E = RemoveEdge(E); + eLoopStop = E; + continue; + } + if (E->Prev == E->Next) + break; //only two vertices + else if (Closed && + SlopesEqual(E->Prev->Curr, E->Curr, E->Next->Curr, m_UseFullRange) && + (!m_PreserveCollinear || + !Pt2IsBetweenPt1AndPt3(E->Prev->Curr, E->Curr, E->Next->Curr))) + { + //Collinear edges are allowed for open paths but in closed paths + //the default is to merge adjacent collinear edges into a single edge. + //However, if the PreserveCollinear property is enabled, only overlapping + //collinear edges (ie spikes) will be removed from closed paths. + if (E == eStart) eStart = E->Next; + E = RemoveEdge(E); + E = E->Prev; + eLoopStop = E; + continue; + } + E = E->Next; + if ((E == eLoopStop) || (!Closed && E->Next == eStart)) break; + } + + if ((!Closed && (E == E->Next)) || (Closed && (E->Prev == E->Next))) + { + delete [] edges; + return false; + } + + if (!Closed) + { + m_HasOpenPaths = true; + eStart->Prev->OutIdx = Skip; + } + + //3. Do second stage of edge initialization ... + E = eStart; + do + { + InitEdge2(*E, PolyTyp); + E = E->Next; + if (IsFlat && E->Curr.Y != eStart->Curr.Y) IsFlat = false; + } + while (E != eStart); + + //4. Finally, add edge bounds to LocalMinima list ... + + //Totally flat paths must be handled differently when adding them + //to LocalMinima list to avoid endless loops etc ... + if (IsFlat) + { + if (Closed) + { + delete [] edges; + return false; + } + E->Prev->OutIdx = Skip; + MinimaList::value_type locMin; + locMin.Y = E->Bot.Y; + locMin.LeftBound = 0; + locMin.RightBound = E; + locMin.RightBound->Side = esRight; + locMin.RightBound->WindDelta = 0; + for (;;) + { + if (E->Bot.X != E->Prev->Top.X) ReverseHorizontal(*E); + if (E->Next->OutIdx == Skip) break; + E->NextInLML = E->Next; + E = E->Next; + } + m_MinimaList.push_back(locMin); + m_edges.push_back(edges); + return true; + } + + m_edges.push_back(edges); + bool leftBoundIsForward; + TEdge* EMin = 0; + + //workaround to avoid an endless loop in the while loop below when + //open paths have matching start and end points ... + if (E->Prev->Bot == E->Prev->Top) E = E->Next; + + for (;;) + { + E = FindNextLocMin(E); + if (E == EMin) break; + else if (!EMin) EMin = E; + + //E and E.Prev now share a local minima (left aligned if horizontal). + //Compare their slopes to find which starts which bound ... + MinimaList::value_type locMin; + locMin.Y = E->Bot.Y; + if (E->Dx < E->Prev->Dx) + { + locMin.LeftBound = E->Prev; + locMin.RightBound = E; + leftBoundIsForward = false; //Q.nextInLML = Q.prev + } else + { + locMin.LeftBound = E; + locMin.RightBound = E->Prev; + leftBoundIsForward = true; //Q.nextInLML = Q.next + } + + if (!Closed) locMin.LeftBound->WindDelta = 0; + else if (locMin.LeftBound->Next == locMin.RightBound) + locMin.LeftBound->WindDelta = -1; + else locMin.LeftBound->WindDelta = 1; + locMin.RightBound->WindDelta = -locMin.LeftBound->WindDelta; + + E = ProcessBound(locMin.LeftBound, leftBoundIsForward); + if (E->OutIdx == Skip) E = ProcessBound(E, leftBoundIsForward); + + TEdge* E2 = ProcessBound(locMin.RightBound, !leftBoundIsForward); + if (E2->OutIdx == Skip) E2 = ProcessBound(E2, !leftBoundIsForward); + + if (locMin.LeftBound->OutIdx == Skip) + locMin.LeftBound = 0; + else if (locMin.RightBound->OutIdx == Skip) + locMin.RightBound = 0; + m_MinimaList.push_back(locMin); + if (!leftBoundIsForward) E = E2; + } + return true; +} +//------------------------------------------------------------------------------ + +bool ClipperBase::AddPaths(const Paths &ppg, PolyType PolyTyp, bool Closed) +{ + bool result = false; + for (Paths::size_type i = 0; i < ppg.size(); ++i) + if (AddPath(ppg[i], PolyTyp, Closed)) result = true; + return result; +} +//------------------------------------------------------------------------------ + +void ClipperBase::Clear() +{ + DisposeLocalMinimaList(); + for (EdgeList::size_type i = 0; i < m_edges.size(); ++i) + { + TEdge* edges = m_edges[i]; + delete [] edges; + } + m_edges.clear(); + m_UseFullRange = false; + m_HasOpenPaths = false; +} +//------------------------------------------------------------------------------ + +void ClipperBase::Reset() +{ + m_CurrentLM = m_MinimaList.begin(); + if (m_CurrentLM == m_MinimaList.end()) return; //ie nothing to process + std::sort(m_MinimaList.begin(), m_MinimaList.end(), LocMinSorter()); + + m_Scanbeam = ScanbeamList(); //clears/resets priority_queue + //reset all edges ... + for (MinimaList::iterator lm = m_MinimaList.begin(); lm != m_MinimaList.end(); ++lm) + { + InsertScanbeam(lm->Y); + TEdge* e = lm->LeftBound; + if (e) + { + e->Curr = e->Bot; + e->Side = esLeft; + e->OutIdx = Unassigned; + } + + e = lm->RightBound; + if (e) + { + e->Curr = e->Bot; + e->Side = esRight; + e->OutIdx = Unassigned; + } + } + m_ActiveEdges = 0; + m_CurrentLM = m_MinimaList.begin(); +} +//------------------------------------------------------------------------------ + +void ClipperBase::DisposeLocalMinimaList() +{ + m_MinimaList.clear(); + m_CurrentLM = m_MinimaList.begin(); +} +//------------------------------------------------------------------------------ + +bool ClipperBase::PopLocalMinima(cInt Y, const LocalMinimum *&locMin) +{ + if (m_CurrentLM == m_MinimaList.end() || (*m_CurrentLM).Y != Y) return false; + locMin = &(*m_CurrentLM); + ++m_CurrentLM; + return true; +} +//------------------------------------------------------------------------------ + +IntRect ClipperBase::GetBounds() +{ + IntRect result; + MinimaList::iterator lm = m_MinimaList.begin(); + if (lm == m_MinimaList.end()) + { + result.left = result.top = result.right = result.bottom = 0; + return result; + } + result.left = lm->LeftBound->Bot.X; + result.top = lm->LeftBound->Bot.Y; + result.right = lm->LeftBound->Bot.X; + result.bottom = lm->LeftBound->Bot.Y; + while (lm != m_MinimaList.end()) + { + //todo - needs fixing for open paths + result.bottom = std::max(result.bottom, lm->LeftBound->Bot.Y); + TEdge* e = lm->LeftBound; + for (;;) { + TEdge* bottomE = e; + while (e->NextInLML) + { + if (e->Bot.X < result.left) result.left = e->Bot.X; + if (e->Bot.X > result.right) result.right = e->Bot.X; + e = e->NextInLML; + } + result.left = std::min(result.left, e->Bot.X); + result.right = std::max(result.right, e->Bot.X); + result.left = std::min(result.left, e->Top.X); + result.right = std::max(result.right, e->Top.X); + result.top = std::min(result.top, e->Top.Y); + if (bottomE == lm->LeftBound) e = lm->RightBound; + else break; + } + ++lm; + } + return result; +} +//------------------------------------------------------------------------------ + +void ClipperBase::InsertScanbeam(const cInt Y) +{ + m_Scanbeam.push(Y); +} +//------------------------------------------------------------------------------ + +bool ClipperBase::PopScanbeam(cInt &Y) +{ + if (m_Scanbeam.empty()) return false; + Y = m_Scanbeam.top(); + m_Scanbeam.pop(); + while (!m_Scanbeam.empty() && Y == m_Scanbeam.top()) { m_Scanbeam.pop(); } // Pop duplicates. + return true; +} +//------------------------------------------------------------------------------ + +void ClipperBase::DisposeAllOutRecs(){ + for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) + DisposeOutRec(i); + m_PolyOuts.clear(); +} +//------------------------------------------------------------------------------ + +void ClipperBase::DisposeOutRec(PolyOutList::size_type index) +{ + OutRec *outRec = m_PolyOuts[index]; + if (outRec->Pts) DisposeOutPts(outRec->Pts); + delete outRec; + m_PolyOuts[index] = 0; +} +//------------------------------------------------------------------------------ + +void ClipperBase::DeleteFromAEL(TEdge *e) +{ + TEdge* AelPrev = e->PrevInAEL; + TEdge* AelNext = e->NextInAEL; + if (!AelPrev && !AelNext && (e != m_ActiveEdges)) return; //already deleted + if (AelPrev) AelPrev->NextInAEL = AelNext; + else m_ActiveEdges = AelNext; + if (AelNext) AelNext->PrevInAEL = AelPrev; + e->NextInAEL = 0; + e->PrevInAEL = 0; +} +//------------------------------------------------------------------------------ + +OutRec* ClipperBase::CreateOutRec() +{ + OutRec* result = new OutRec; + result->IsHole = false; + result->IsOpen = false; + result->FirstLeft = 0; + result->Pts = 0; + result->BottomPt = 0; + result->PolyNd = 0; + m_PolyOuts.push_back(result); + result->Idx = (int)m_PolyOuts.size() - 1; + return result; +} +//------------------------------------------------------------------------------ + +void ClipperBase::SwapPositionsInAEL(TEdge *Edge1, TEdge *Edge2) +{ + //check that one or other edge hasn't already been removed from AEL ... + if (Edge1->NextInAEL == Edge1->PrevInAEL || + Edge2->NextInAEL == Edge2->PrevInAEL) return; + + if (Edge1->NextInAEL == Edge2) + { + TEdge* Next = Edge2->NextInAEL; + if (Next) Next->PrevInAEL = Edge1; + TEdge* Prev = Edge1->PrevInAEL; + if (Prev) Prev->NextInAEL = Edge2; + Edge2->PrevInAEL = Prev; + Edge2->NextInAEL = Edge1; + Edge1->PrevInAEL = Edge2; + Edge1->NextInAEL = Next; + } + else if (Edge2->NextInAEL == Edge1) + { + TEdge* Next = Edge1->NextInAEL; + if (Next) Next->PrevInAEL = Edge2; + TEdge* Prev = Edge2->PrevInAEL; + if (Prev) Prev->NextInAEL = Edge1; + Edge1->PrevInAEL = Prev; + Edge1->NextInAEL = Edge2; + Edge2->PrevInAEL = Edge1; + Edge2->NextInAEL = Next; + } + else + { + TEdge* Next = Edge1->NextInAEL; + TEdge* Prev = Edge1->PrevInAEL; + Edge1->NextInAEL = Edge2->NextInAEL; + if (Edge1->NextInAEL) Edge1->NextInAEL->PrevInAEL = Edge1; + Edge1->PrevInAEL = Edge2->PrevInAEL; + if (Edge1->PrevInAEL) Edge1->PrevInAEL->NextInAEL = Edge1; + Edge2->NextInAEL = Next; + if (Edge2->NextInAEL) Edge2->NextInAEL->PrevInAEL = Edge2; + Edge2->PrevInAEL = Prev; + if (Edge2->PrevInAEL) Edge2->PrevInAEL->NextInAEL = Edge2; + } + + if (!Edge1->PrevInAEL) m_ActiveEdges = Edge1; + else if (!Edge2->PrevInAEL) m_ActiveEdges = Edge2; +} +//------------------------------------------------------------------------------ + +void ClipperBase::UpdateEdgeIntoAEL(TEdge *&e) +{ + if (!e->NextInLML) + throw clipperException("UpdateEdgeIntoAEL: invalid call"); + + e->NextInLML->OutIdx = e->OutIdx; + TEdge* AelPrev = e->PrevInAEL; + TEdge* AelNext = e->NextInAEL; + if (AelPrev) AelPrev->NextInAEL = e->NextInLML; + else m_ActiveEdges = e->NextInLML; + if (AelNext) AelNext->PrevInAEL = e->NextInLML; + e->NextInLML->Side = e->Side; + e->NextInLML->WindDelta = e->WindDelta; + e->NextInLML->WindCnt = e->WindCnt; + e->NextInLML->WindCnt2 = e->WindCnt2; + e = e->NextInLML; + e->Curr = e->Bot; + e->PrevInAEL = AelPrev; + e->NextInAEL = AelNext; + if (!IsHorizontal(*e)) InsertScanbeam(e->Top.Y); +} +//------------------------------------------------------------------------------ + +bool ClipperBase::LocalMinimaPending() +{ + return (m_CurrentLM != m_MinimaList.end()); +} + +//------------------------------------------------------------------------------ +// TClipper methods ... +//------------------------------------------------------------------------------ + +Clipper::Clipper(int initOptions) : ClipperBase() //constructor +{ + m_ExecuteLocked = false; + m_UseFullRange = false; + m_ReverseOutput = ((initOptions & ioReverseSolution) != 0); + m_StrictSimple = ((initOptions & ioStrictlySimple) != 0); + m_PreserveCollinear = ((initOptions & ioPreserveCollinear) != 0); + m_HasOpenPaths = false; +#ifdef use_xyz + m_ZFill = 0; +#endif +} +//------------------------------------------------------------------------------ + +#ifdef use_xyz +void Clipper::ZFillFunction(ZFillCallback zFillFunc) +{ + m_ZFill = zFillFunc; +} +//------------------------------------------------------------------------------ +#endif + +bool Clipper::Execute(ClipType clipType, Paths &solution, PolyFillType fillType) +{ + return Execute(clipType, solution, fillType, fillType); +} +//------------------------------------------------------------------------------ + +bool Clipper::Execute(ClipType clipType, PolyTree &polytree, PolyFillType fillType) +{ + return Execute(clipType, polytree, fillType, fillType); +} +//------------------------------------------------------------------------------ + +bool Clipper::Execute(ClipType clipType, Paths &solution, + PolyFillType subjFillType, PolyFillType clipFillType) +{ + if( m_ExecuteLocked ) return false; + if (m_HasOpenPaths) + throw clipperException("Error: PolyTree struct is needed for open path clipping."); + m_ExecuteLocked = true; + solution.resize(0); + m_SubjFillType = subjFillType; + m_ClipFillType = clipFillType; + m_ClipType = clipType; + m_UsingPolyTree = false; + bool succeeded = ExecuteInternal(); + if (succeeded) BuildResult(solution); + DisposeAllOutRecs(); + m_ExecuteLocked = false; + return succeeded; +} +//------------------------------------------------------------------------------ + +bool Clipper::Execute(ClipType clipType, PolyTree& polytree, + PolyFillType subjFillType, PolyFillType clipFillType) +{ + if( m_ExecuteLocked ) return false; + m_ExecuteLocked = true; + m_SubjFillType = subjFillType; + m_ClipFillType = clipFillType; + m_ClipType = clipType; + m_UsingPolyTree = true; + bool succeeded = ExecuteInternal(); + if (succeeded) BuildResult2(polytree); + DisposeAllOutRecs(); + m_ExecuteLocked = false; + return succeeded; +} +//------------------------------------------------------------------------------ + +void Clipper::FixHoleLinkage(OutRec &outrec) +{ + //skip OutRecs that (a) contain outermost polygons or + //(b) already have the correct owner/child linkage ... + if (!outrec.FirstLeft || + (outrec.IsHole != outrec.FirstLeft->IsHole && + outrec.FirstLeft->Pts)) return; + + OutRec* orfl = outrec.FirstLeft; + while (orfl && ((orfl->IsHole == outrec.IsHole) || !orfl->Pts)) + orfl = orfl->FirstLeft; + outrec.FirstLeft = orfl; +} +//------------------------------------------------------------------------------ + +bool Clipper::ExecuteInternal() +{ + bool succeeded = true; + try { + Reset(); + m_Maxima = MaximaList(); + m_SortedEdges = 0; + + succeeded = true; + cInt botY, topY; + if (!PopScanbeam(botY)) return false; + InsertLocalMinimaIntoAEL(botY); + while (PopScanbeam(topY) || LocalMinimaPending()) + { + ProcessHorizontals(); + ClearGhostJoins(); + if (!ProcessIntersections(topY)) + { + succeeded = false; + break; + } + ProcessEdgesAtTopOfScanbeam(topY); + botY = topY; + InsertLocalMinimaIntoAEL(botY); + } + } + catch(...) + { + succeeded = false; + } + + if (succeeded) + { + //fix orientations ... + for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) + { + OutRec *outRec = m_PolyOuts[i]; + if (!outRec->Pts || outRec->IsOpen) continue; + if ((outRec->IsHole ^ m_ReverseOutput) == (Area(*outRec) > 0)) + ReversePolyPtLinks(outRec->Pts); + } + + if (!m_Joins.empty()) JoinCommonEdges(); + + //unfortunately FixupOutPolygon() must be done after JoinCommonEdges() + for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) + { + OutRec *outRec = m_PolyOuts[i]; + if (!outRec->Pts) continue; + if (outRec->IsOpen) + FixupOutPolyline(*outRec); + else + FixupOutPolygon(*outRec); + } + + if (m_StrictSimple) DoSimplePolygons(); + } + + ClearJoins(); + ClearGhostJoins(); + return succeeded; +} +//------------------------------------------------------------------------------ + +void Clipper::SetWindingCount(TEdge &edge) +{ + TEdge *e = edge.PrevInAEL; + //find the edge of the same polytype that immediately preceeds 'edge' in AEL + while (e && ((e->PolyTyp != edge.PolyTyp) || (e->WindDelta == 0))) e = e->PrevInAEL; + if (!e) + { + if (edge.WindDelta == 0) + { + PolyFillType pft = (edge.PolyTyp == ptSubject ? m_SubjFillType : m_ClipFillType); + edge.WindCnt = (pft == pftNegative ? -1 : 1); + } + else + edge.WindCnt = edge.WindDelta; + edge.WindCnt2 = 0; + e = m_ActiveEdges; //ie get ready to calc WindCnt2 + } + else if (edge.WindDelta == 0 && m_ClipType != ctUnion) + { + edge.WindCnt = 1; + edge.WindCnt2 = e->WindCnt2; + e = e->NextInAEL; //ie get ready to calc WindCnt2 + } + else if (IsEvenOddFillType(edge)) + { + //EvenOdd filling ... + if (edge.WindDelta == 0) + { + //are we inside a subj polygon ... + bool Inside = true; + TEdge *e2 = e->PrevInAEL; + while (e2) + { + if (e2->PolyTyp == e->PolyTyp && e2->WindDelta != 0) + Inside = !Inside; + e2 = e2->PrevInAEL; + } + edge.WindCnt = (Inside ? 0 : 1); + } + else + { + edge.WindCnt = edge.WindDelta; + } + edge.WindCnt2 = e->WindCnt2; + e = e->NextInAEL; //ie get ready to calc WindCnt2 + } + else + { + //nonZero, Positive or Negative filling ... + if (e->WindCnt * e->WindDelta < 0) + { + //prev edge is 'decreasing' WindCount (WC) toward zero + //so we're outside the previous polygon ... + if (Abs(e->WindCnt) > 1) + { + //outside prev poly but still inside another. + //when reversing direction of prev poly use the same WC + if (e->WindDelta * edge.WindDelta < 0) edge.WindCnt = e->WindCnt; + //otherwise continue to 'decrease' WC ... + else edge.WindCnt = e->WindCnt + edge.WindDelta; + } + else + //now outside all polys of same polytype so set own WC ... + edge.WindCnt = (edge.WindDelta == 0 ? 1 : edge.WindDelta); + } else + { + //prev edge is 'increasing' WindCount (WC) away from zero + //so we're inside the previous polygon ... + if (edge.WindDelta == 0) + edge.WindCnt = (e->WindCnt < 0 ? e->WindCnt - 1 : e->WindCnt + 1); + //if wind direction is reversing prev then use same WC + else if (e->WindDelta * edge.WindDelta < 0) edge.WindCnt = e->WindCnt; + //otherwise add to WC ... + else edge.WindCnt = e->WindCnt + edge.WindDelta; + } + edge.WindCnt2 = e->WindCnt2; + e = e->NextInAEL; //ie get ready to calc WindCnt2 + } + + //update WindCnt2 ... + if (IsEvenOddAltFillType(edge)) + { + //EvenOdd filling ... + while (e != &edge) + { + if (e->WindDelta != 0) + edge.WindCnt2 = (edge.WindCnt2 == 0 ? 1 : 0); + e = e->NextInAEL; + } + } else + { + //nonZero, Positive or Negative filling ... + while ( e != &edge ) + { + edge.WindCnt2 += e->WindDelta; + e = e->NextInAEL; + } + } +} +//------------------------------------------------------------------------------ + +bool Clipper::IsEvenOddFillType(const TEdge& edge) const +{ + if (edge.PolyTyp == ptSubject) + return m_SubjFillType == pftEvenOdd; else + return m_ClipFillType == pftEvenOdd; +} +//------------------------------------------------------------------------------ + +bool Clipper::IsEvenOddAltFillType(const TEdge& edge) const +{ + if (edge.PolyTyp == ptSubject) + return m_ClipFillType == pftEvenOdd; else + return m_SubjFillType == pftEvenOdd; +} +//------------------------------------------------------------------------------ + +bool Clipper::IsContributing(const TEdge& edge) const +{ + PolyFillType pft, pft2; + if (edge.PolyTyp == ptSubject) + { + pft = m_SubjFillType; + pft2 = m_ClipFillType; + } else + { + pft = m_ClipFillType; + pft2 = m_SubjFillType; + } + + switch(pft) + { + case pftEvenOdd: + //return false if a subj line has been flagged as inside a subj polygon + if (edge.WindDelta == 0 && edge.WindCnt != 1) return false; + break; + case pftNonZero: + if (Abs(edge.WindCnt) != 1) return false; + break; + case pftPositive: + if (edge.WindCnt != 1) return false; + break; + default: //pftNegative + if (edge.WindCnt != -1) return false; + } + + switch(m_ClipType) + { + case ctIntersection: + switch(pft2) + { + case pftEvenOdd: + case pftNonZero: + return (edge.WindCnt2 != 0); + case pftPositive: + return (edge.WindCnt2 > 0); + default: + return (edge.WindCnt2 < 0); + } + break; + case ctUnion: + switch(pft2) + { + case pftEvenOdd: + case pftNonZero: + return (edge.WindCnt2 == 0); + case pftPositive: + return (edge.WindCnt2 <= 0); + default: + return (edge.WindCnt2 >= 0); + } + break; + case ctDifference: + if (edge.PolyTyp == ptSubject) + switch(pft2) + { + case pftEvenOdd: + case pftNonZero: + return (edge.WindCnt2 == 0); + case pftPositive: + return (edge.WindCnt2 <= 0); + default: + return (edge.WindCnt2 >= 0); + } + else + switch(pft2) + { + case pftEvenOdd: + case pftNonZero: + return (edge.WindCnt2 != 0); + case pftPositive: + return (edge.WindCnt2 > 0); + default: + return (edge.WindCnt2 < 0); + } + break; + case ctXor: + if (edge.WindDelta == 0) //XOr always contributing unless open + switch(pft2) + { + case pftEvenOdd: + case pftNonZero: + return (edge.WindCnt2 == 0); + case pftPositive: + return (edge.WindCnt2 <= 0); + default: + return (edge.WindCnt2 >= 0); + } + else + return true; + break; + default: + return true; + } +} +//------------------------------------------------------------------------------ + +OutPt* Clipper::AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &Pt) +{ + OutPt* result; + TEdge *e, *prevE; + if (IsHorizontal(*e2) || ( e1->Dx > e2->Dx )) + { + result = AddOutPt(e1, Pt); + e2->OutIdx = e1->OutIdx; + e1->Side = esLeft; + e2->Side = esRight; + e = e1; + if (e->PrevInAEL == e2) + prevE = e2->PrevInAEL; + else + prevE = e->PrevInAEL; + } else + { + result = AddOutPt(e2, Pt); + e1->OutIdx = e2->OutIdx; + e1->Side = esRight; + e2->Side = esLeft; + e = e2; + if (e->PrevInAEL == e1) + prevE = e1->PrevInAEL; + else + prevE = e->PrevInAEL; + } + + if (prevE && prevE->OutIdx >= 0) + { + cInt xPrev = TopX(*prevE, Pt.Y); + cInt xE = TopX(*e, Pt.Y); + if (xPrev == xE && (e->WindDelta != 0) && (prevE->WindDelta != 0) && + SlopesEqual(IntPoint(xPrev, Pt.Y), prevE->Top, IntPoint(xE, Pt.Y), e->Top, m_UseFullRange)) + { + OutPt* outPt = AddOutPt(prevE, Pt); + AddJoin(result, outPt, e->Top); + } + } + return result; +} +//------------------------------------------------------------------------------ + +void Clipper::AddLocalMaxPoly(TEdge *e1, TEdge *e2, const IntPoint &Pt) +{ + AddOutPt( e1, Pt ); + if (e2->WindDelta == 0) AddOutPt(e2, Pt); + if( e1->OutIdx == e2->OutIdx ) + { + e1->OutIdx = Unassigned; + e2->OutIdx = Unassigned; + } + else if (e1->OutIdx < e2->OutIdx) + AppendPolygon(e1, e2); + else + AppendPolygon(e2, e1); +} +//------------------------------------------------------------------------------ + +void Clipper::AddEdgeToSEL(TEdge *edge) +{ + //SEL pointers in PEdge are reused to build a list of horizontal edges. + //However, we don't need to worry about order with horizontal edge processing. + if( !m_SortedEdges ) + { + m_SortedEdges = edge; + edge->PrevInSEL = 0; + edge->NextInSEL = 0; + } + else + { + edge->NextInSEL = m_SortedEdges; + edge->PrevInSEL = 0; + m_SortedEdges->PrevInSEL = edge; + m_SortedEdges = edge; + } +} +//------------------------------------------------------------------------------ + +bool Clipper::PopEdgeFromSEL(TEdge *&edge) +{ + if (!m_SortedEdges) return false; + edge = m_SortedEdges; + DeleteFromSEL(m_SortedEdges); + return true; +} +//------------------------------------------------------------------------------ + +void Clipper::CopyAELToSEL() +{ + TEdge* e = m_ActiveEdges; + m_SortedEdges = e; + while ( e ) + { + e->PrevInSEL = e->PrevInAEL; + e->NextInSEL = e->NextInAEL; + e = e->NextInAEL; + } +} +//------------------------------------------------------------------------------ + +void Clipper::AddJoin(OutPt *op1, OutPt *op2, const IntPoint OffPt) +{ + Join* j = new Join; + j->OutPt1 = op1; + j->OutPt2 = op2; + j->OffPt = OffPt; + m_Joins.push_back(j); +} +//------------------------------------------------------------------------------ + +void Clipper::ClearJoins() +{ + for (JoinList::size_type i = 0; i < m_Joins.size(); i++) + delete m_Joins[i]; + m_Joins.resize(0); +} +//------------------------------------------------------------------------------ + +void Clipper::ClearGhostJoins() +{ + for (JoinList::size_type i = 0; i < m_GhostJoins.size(); i++) + delete m_GhostJoins[i]; + m_GhostJoins.resize(0); +} +//------------------------------------------------------------------------------ + +void Clipper::AddGhostJoin(OutPt *op, const IntPoint OffPt) +{ + Join* j = new Join; + j->OutPt1 = op; + j->OutPt2 = 0; + j->OffPt = OffPt; + m_GhostJoins.push_back(j); +} +//------------------------------------------------------------------------------ + +void Clipper::InsertLocalMinimaIntoAEL(const cInt botY) +{ + const LocalMinimum *lm; + while (PopLocalMinima(botY, lm)) + { + TEdge* lb = lm->LeftBound; + TEdge* rb = lm->RightBound; + + OutPt *Op1 = 0; + if (!lb) + { + //nb: don't insert LB into either AEL or SEL + InsertEdgeIntoAEL(rb, 0); + SetWindingCount(*rb); + if (IsContributing(*rb)) + Op1 = AddOutPt(rb, rb->Bot); + } + else if (!rb) + { + InsertEdgeIntoAEL(lb, 0); + SetWindingCount(*lb); + if (IsContributing(*lb)) + Op1 = AddOutPt(lb, lb->Bot); + InsertScanbeam(lb->Top.Y); + } + else + { + InsertEdgeIntoAEL(lb, 0); + InsertEdgeIntoAEL(rb, lb); + SetWindingCount( *lb ); + rb->WindCnt = lb->WindCnt; + rb->WindCnt2 = lb->WindCnt2; + if (IsContributing(*lb)) + Op1 = AddLocalMinPoly(lb, rb, lb->Bot); + InsertScanbeam(lb->Top.Y); + } + + if (rb) + { + if (IsHorizontal(*rb)) + { + AddEdgeToSEL(rb); + if (rb->NextInLML) + InsertScanbeam(rb->NextInLML->Top.Y); + } + else InsertScanbeam( rb->Top.Y ); + } + + if (!lb || !rb) continue; + + //if any output polygons share an edge, they'll need joining later ... + if (Op1 && IsHorizontal(*rb) && + m_GhostJoins.size() > 0 && (rb->WindDelta != 0)) + { + for (JoinList::size_type i = 0; i < m_GhostJoins.size(); ++i) + { + Join* jr = m_GhostJoins[i]; + //if the horizontal Rb and a 'ghost' horizontal overlap, then convert + //the 'ghost' join to a real join ready for later ... + if (HorzSegmentsOverlap(jr->OutPt1->Pt.X, jr->OffPt.X, rb->Bot.X, rb->Top.X)) + AddJoin(jr->OutPt1, Op1, jr->OffPt); + } + } + + if (lb->OutIdx >= 0 && lb->PrevInAEL && + lb->PrevInAEL->Curr.X == lb->Bot.X && + lb->PrevInAEL->OutIdx >= 0 && + SlopesEqual(lb->PrevInAEL->Bot, lb->PrevInAEL->Top, lb->Curr, lb->Top, m_UseFullRange) && + (lb->WindDelta != 0) && (lb->PrevInAEL->WindDelta != 0)) + { + OutPt *Op2 = AddOutPt(lb->PrevInAEL, lb->Bot); + AddJoin(Op1, Op2, lb->Top); + } + + if(lb->NextInAEL != rb) + { + + if (rb->OutIdx >= 0 && rb->PrevInAEL->OutIdx >= 0 && + SlopesEqual(rb->PrevInAEL->Curr, rb->PrevInAEL->Top, rb->Curr, rb->Top, m_UseFullRange) && + (rb->WindDelta != 0) && (rb->PrevInAEL->WindDelta != 0)) + { + OutPt *Op2 = AddOutPt(rb->PrevInAEL, rb->Bot); + AddJoin(Op1, Op2, rb->Top); + } + + TEdge* e = lb->NextInAEL; + if (e) + { + while( e != rb ) + { + //nb: For calculating winding counts etc, IntersectEdges() assumes + //that param1 will be to the Right of param2 ABOVE the intersection ... + IntersectEdges(rb , e , lb->Curr); //order important here + e = e->NextInAEL; + } + } + } + + } +} +//------------------------------------------------------------------------------ + +void Clipper::DeleteFromSEL(TEdge *e) +{ + TEdge* SelPrev = e->PrevInSEL; + TEdge* SelNext = e->NextInSEL; + if( !SelPrev && !SelNext && (e != m_SortedEdges) ) return; //already deleted + if( SelPrev ) SelPrev->NextInSEL = SelNext; + else m_SortedEdges = SelNext; + if( SelNext ) SelNext->PrevInSEL = SelPrev; + e->NextInSEL = 0; + e->PrevInSEL = 0; +} +//------------------------------------------------------------------------------ + +#ifdef use_xyz +void Clipper::SetZ(IntPoint& pt, TEdge& e1, TEdge& e2) +{ + if (pt.Z != 0 || !m_ZFill) return; + else if (pt == e1.Bot) pt.Z = e1.Bot.Z; + else if (pt == e1.Top) pt.Z = e1.Top.Z; + else if (pt == e2.Bot) pt.Z = e2.Bot.Z; + else if (pt == e2.Top) pt.Z = e2.Top.Z; + else (*m_ZFill)(e1.Bot, e1.Top, e2.Bot, e2.Top, pt); +} +//------------------------------------------------------------------------------ +#endif + +void Clipper::IntersectEdges(TEdge *e1, TEdge *e2, IntPoint &Pt) +{ + bool e1Contributing = ( e1->OutIdx >= 0 ); + bool e2Contributing = ( e2->OutIdx >= 0 ); + +#ifdef use_xyz + SetZ(Pt, *e1, *e2); +#endif + +#ifdef use_lines + //if either edge is on an OPEN path ... + if (e1->WindDelta == 0 || e2->WindDelta == 0) + { + //ignore subject-subject open path intersections UNLESS they + //are both open paths, AND they are both 'contributing maximas' ... + if (e1->WindDelta == 0 && e2->WindDelta == 0) return; + + //if intersecting a subj line with a subj poly ... + else if (e1->PolyTyp == e2->PolyTyp && + e1->WindDelta != e2->WindDelta && m_ClipType == ctUnion) + { + if (e1->WindDelta == 0) + { + if (e2Contributing) + { + AddOutPt(e1, Pt); + if (e1Contributing) e1->OutIdx = Unassigned; + } + } + else + { + if (e1Contributing) + { + AddOutPt(e2, Pt); + if (e2Contributing) e2->OutIdx = Unassigned; + } + } + } + else if (e1->PolyTyp != e2->PolyTyp) + { + //toggle subj open path OutIdx on/off when Abs(clip.WndCnt) == 1 ... + if ((e1->WindDelta == 0) && abs(e2->WindCnt) == 1 && + (m_ClipType != ctUnion || e2->WindCnt2 == 0)) + { + AddOutPt(e1, Pt); + if (e1Contributing) e1->OutIdx = Unassigned; + } + else if ((e2->WindDelta == 0) && (abs(e1->WindCnt) == 1) && + (m_ClipType != ctUnion || e1->WindCnt2 == 0)) + { + AddOutPt(e2, Pt); + if (e2Contributing) e2->OutIdx = Unassigned; + } + } + return; + } +#endif + + //update winding counts... + //assumes that e1 will be to the Right of e2 ABOVE the intersection + if ( e1->PolyTyp == e2->PolyTyp ) + { + if ( IsEvenOddFillType( *e1) ) + { + int oldE1WindCnt = e1->WindCnt; + e1->WindCnt = e2->WindCnt; + e2->WindCnt = oldE1WindCnt; + } else + { + if (e1->WindCnt + e2->WindDelta == 0 ) e1->WindCnt = -e1->WindCnt; + else e1->WindCnt += e2->WindDelta; + if ( e2->WindCnt - e1->WindDelta == 0 ) e2->WindCnt = -e2->WindCnt; + else e2->WindCnt -= e1->WindDelta; + } + } else + { + if (!IsEvenOddFillType(*e2)) e1->WindCnt2 += e2->WindDelta; + else e1->WindCnt2 = ( e1->WindCnt2 == 0 ) ? 1 : 0; + if (!IsEvenOddFillType(*e1)) e2->WindCnt2 -= e1->WindDelta; + else e2->WindCnt2 = ( e2->WindCnt2 == 0 ) ? 1 : 0; + } + + PolyFillType e1FillType, e2FillType, e1FillType2, e2FillType2; + if (e1->PolyTyp == ptSubject) + { + e1FillType = m_SubjFillType; + e1FillType2 = m_ClipFillType; + } else + { + e1FillType = m_ClipFillType; + e1FillType2 = m_SubjFillType; + } + if (e2->PolyTyp == ptSubject) + { + e2FillType = m_SubjFillType; + e2FillType2 = m_ClipFillType; + } else + { + e2FillType = m_ClipFillType; + e2FillType2 = m_SubjFillType; + } + + cInt e1Wc, e2Wc; + switch (e1FillType) + { + case pftPositive: e1Wc = e1->WindCnt; break; + case pftNegative: e1Wc = -e1->WindCnt; break; + default: e1Wc = Abs(e1->WindCnt); + } + switch(e2FillType) + { + case pftPositive: e2Wc = e2->WindCnt; break; + case pftNegative: e2Wc = -e2->WindCnt; break; + default: e2Wc = Abs(e2->WindCnt); + } + + if ( e1Contributing && e2Contributing ) + { + if ((e1Wc != 0 && e1Wc != 1) || (e2Wc != 0 && e2Wc != 1) || + (e1->PolyTyp != e2->PolyTyp && m_ClipType != ctXor) ) + { + AddLocalMaxPoly(e1, e2, Pt); + } + else + { + AddOutPt(e1, Pt); + AddOutPt(e2, Pt); + SwapSides( *e1 , *e2 ); + SwapPolyIndexes( *e1 , *e2 ); + } + } + else if ( e1Contributing ) + { + if (e2Wc == 0 || e2Wc == 1) + { + AddOutPt(e1, Pt); + SwapSides(*e1, *e2); + SwapPolyIndexes(*e1, *e2); + } + } + else if ( e2Contributing ) + { + if (e1Wc == 0 || e1Wc == 1) + { + AddOutPt(e2, Pt); + SwapSides(*e1, *e2); + SwapPolyIndexes(*e1, *e2); + } + } + else if ( (e1Wc == 0 || e1Wc == 1) && (e2Wc == 0 || e2Wc == 1)) + { + //neither edge is currently contributing ... + + cInt e1Wc2, e2Wc2; + switch (e1FillType2) + { + case pftPositive: e1Wc2 = e1->WindCnt2; break; + case pftNegative : e1Wc2 = -e1->WindCnt2; break; + default: e1Wc2 = Abs(e1->WindCnt2); + } + switch (e2FillType2) + { + case pftPositive: e2Wc2 = e2->WindCnt2; break; + case pftNegative: e2Wc2 = -e2->WindCnt2; break; + default: e2Wc2 = Abs(e2->WindCnt2); + } + + if (e1->PolyTyp != e2->PolyTyp) + { + AddLocalMinPoly(e1, e2, Pt); + } + else if (e1Wc == 1 && e2Wc == 1) + switch( m_ClipType ) { + case ctIntersection: + if (e1Wc2 > 0 && e2Wc2 > 0) + AddLocalMinPoly(e1, e2, Pt); + break; + case ctUnion: + if ( e1Wc2 <= 0 && e2Wc2 <= 0 ) + AddLocalMinPoly(e1, e2, Pt); + break; + case ctDifference: + if (((e1->PolyTyp == ptClip) && (e1Wc2 > 0) && (e2Wc2 > 0)) || + ((e1->PolyTyp == ptSubject) && (e1Wc2 <= 0) && (e2Wc2 <= 0))) + AddLocalMinPoly(e1, e2, Pt); + break; + case ctXor: + AddLocalMinPoly(e1, e2, Pt); + } + else + SwapSides( *e1, *e2 ); + } +} +//------------------------------------------------------------------------------ + +void Clipper::SetHoleState(TEdge *e, OutRec *outrec) +{ + TEdge *e2 = e->PrevInAEL; + TEdge *eTmp = 0; + while (e2) + { + if (e2->OutIdx >= 0 && e2->WindDelta != 0) + { + if (!eTmp) eTmp = e2; + else if (eTmp->OutIdx == e2->OutIdx) eTmp = 0; + } + e2 = e2->PrevInAEL; + } + if (!eTmp) + { + outrec->FirstLeft = 0; + outrec->IsHole = false; + } + else + { + outrec->FirstLeft = m_PolyOuts[eTmp->OutIdx]; + outrec->IsHole = !outrec->FirstLeft->IsHole; + } +} +//------------------------------------------------------------------------------ + +OutRec* GetLowermostRec(OutRec *outRec1, OutRec *outRec2) +{ + //work out which polygon fragment has the correct hole state ... + if (!outRec1->BottomPt) + outRec1->BottomPt = GetBottomPt(outRec1->Pts); + if (!outRec2->BottomPt) + outRec2->BottomPt = GetBottomPt(outRec2->Pts); + OutPt *OutPt1 = outRec1->BottomPt; + OutPt *OutPt2 = outRec2->BottomPt; + if (OutPt1->Pt.Y > OutPt2->Pt.Y) return outRec1; + else if (OutPt1->Pt.Y < OutPt2->Pt.Y) return outRec2; + else if (OutPt1->Pt.X < OutPt2->Pt.X) return outRec1; + else if (OutPt1->Pt.X > OutPt2->Pt.X) return outRec2; + else if (OutPt1->Next == OutPt1) return outRec2; + else if (OutPt2->Next == OutPt2) return outRec1; + else if (FirstIsBottomPt(OutPt1, OutPt2)) return outRec1; + else return outRec2; +} +//------------------------------------------------------------------------------ + +bool OutRec1RightOfOutRec2(OutRec* outRec1, OutRec* outRec2) +{ + do + { + outRec1 = outRec1->FirstLeft; + if (outRec1 == outRec2) return true; + } while (outRec1); + return false; +} +//------------------------------------------------------------------------------ + +OutRec* Clipper::GetOutRec(int Idx) +{ + OutRec* outrec = m_PolyOuts[Idx]; + while (outrec != m_PolyOuts[outrec->Idx]) + outrec = m_PolyOuts[outrec->Idx]; + return outrec; +} +//------------------------------------------------------------------------------ + +void Clipper::AppendPolygon(TEdge *e1, TEdge *e2) +{ + //get the start and ends of both output polygons ... + OutRec *outRec1 = m_PolyOuts[e1->OutIdx]; + OutRec *outRec2 = m_PolyOuts[e2->OutIdx]; + + OutRec *holeStateRec; + if (OutRec1RightOfOutRec2(outRec1, outRec2)) + holeStateRec = outRec2; + else if (OutRec1RightOfOutRec2(outRec2, outRec1)) + holeStateRec = outRec1; + else + holeStateRec = GetLowermostRec(outRec1, outRec2); + + //get the start and ends of both output polygons and + //join e2 poly onto e1 poly and delete pointers to e2 ... + + OutPt* p1_lft = outRec1->Pts; + OutPt* p1_rt = p1_lft->Prev; + OutPt* p2_lft = outRec2->Pts; + OutPt* p2_rt = p2_lft->Prev; + + //join e2 poly onto e1 poly and delete pointers to e2 ... + if( e1->Side == esLeft ) + { + if( e2->Side == esLeft ) + { + //z y x a b c + ReversePolyPtLinks(p2_lft); + p2_lft->Next = p1_lft; + p1_lft->Prev = p2_lft; + p1_rt->Next = p2_rt; + p2_rt->Prev = p1_rt; + outRec1->Pts = p2_rt; + } else + { + //x y z a b c + p2_rt->Next = p1_lft; + p1_lft->Prev = p2_rt; + p2_lft->Prev = p1_rt; + p1_rt->Next = p2_lft; + outRec1->Pts = p2_lft; + } + } else + { + if( e2->Side == esRight ) + { + //a b c z y x + ReversePolyPtLinks(p2_lft); + p1_rt->Next = p2_rt; + p2_rt->Prev = p1_rt; + p2_lft->Next = p1_lft; + p1_lft->Prev = p2_lft; + } else + { + //a b c x y z + p1_rt->Next = p2_lft; + p2_lft->Prev = p1_rt; + p1_lft->Prev = p2_rt; + p2_rt->Next = p1_lft; + } + } + + outRec1->BottomPt = 0; + if (holeStateRec == outRec2) + { + if (outRec2->FirstLeft != outRec1) + outRec1->FirstLeft = outRec2->FirstLeft; + outRec1->IsHole = outRec2->IsHole; + } + outRec2->Pts = 0; + outRec2->BottomPt = 0; + outRec2->FirstLeft = outRec1; + + int OKIdx = e1->OutIdx; + int ObsoleteIdx = e2->OutIdx; + + e1->OutIdx = Unassigned; //nb: safe because we only get here via AddLocalMaxPoly + e2->OutIdx = Unassigned; + + TEdge* e = m_ActiveEdges; + while( e ) + { + if( e->OutIdx == ObsoleteIdx ) + { + e->OutIdx = OKIdx; + e->Side = e1->Side; + break; + } + e = e->NextInAEL; + } + + outRec2->Idx = outRec1->Idx; +} +//------------------------------------------------------------------------------ + +OutPt* Clipper::AddOutPt(TEdge *e, const IntPoint &pt) +{ + if( e->OutIdx < 0 ) + { + OutRec *outRec = CreateOutRec(); + outRec->IsOpen = (e->WindDelta == 0); + OutPt* newOp = new OutPt; + outRec->Pts = newOp; + newOp->Idx = outRec->Idx; + newOp->Pt = pt; + newOp->Next = newOp; + newOp->Prev = newOp; + if (!outRec->IsOpen) + SetHoleState(e, outRec); + e->OutIdx = outRec->Idx; + return newOp; + } else + { + OutRec *outRec = m_PolyOuts[e->OutIdx]; + //OutRec.Pts is the 'Left-most' point & OutRec.Pts.Prev is the 'Right-most' + OutPt* op = outRec->Pts; + + bool ToFront = (e->Side == esLeft); + if (ToFront && (pt == op->Pt)) return op; + else if (!ToFront && (pt == op->Prev->Pt)) return op->Prev; + + OutPt* newOp = new OutPt; + newOp->Idx = outRec->Idx; + newOp->Pt = pt; + newOp->Next = op; + newOp->Prev = op->Prev; + newOp->Prev->Next = newOp; + op->Prev = newOp; + if (ToFront) outRec->Pts = newOp; + return newOp; + } +} +//------------------------------------------------------------------------------ + +OutPt* Clipper::GetLastOutPt(TEdge *e) +{ + OutRec *outRec = m_PolyOuts[e->OutIdx]; + if (e->Side == esLeft) + return outRec->Pts; + else + return outRec->Pts->Prev; +} +//------------------------------------------------------------------------------ + +void Clipper::ProcessHorizontals() +{ + TEdge* horzEdge; + while (PopEdgeFromSEL(horzEdge)) + ProcessHorizontal(horzEdge); +} +//------------------------------------------------------------------------------ + +inline bool IsMinima(TEdge *e) +{ + return e && (e->Prev->NextInLML != e) && (e->Next->NextInLML != e); +} +//------------------------------------------------------------------------------ + +inline bool IsMaxima(TEdge *e, const cInt Y) +{ + return e && e->Top.Y == Y && !e->NextInLML; +} +//------------------------------------------------------------------------------ + +inline bool IsIntermediate(TEdge *e, const cInt Y) +{ + return e->Top.Y == Y && e->NextInLML; +} +//------------------------------------------------------------------------------ + +TEdge *GetMaximaPair(TEdge *e) +{ + if ((e->Next->Top == e->Top) && !e->Next->NextInLML) + return e->Next; + else if ((e->Prev->Top == e->Top) && !e->Prev->NextInLML) + return e->Prev; + else return 0; +} +//------------------------------------------------------------------------------ + +TEdge *GetMaximaPairEx(TEdge *e) +{ + //as GetMaximaPair() but returns 0 if MaxPair isn't in AEL (unless it's horizontal) + TEdge* result = GetMaximaPair(e); + if (result && (result->OutIdx == Skip || + (result->NextInAEL == result->PrevInAEL && !IsHorizontal(*result)))) return 0; + return result; +} +//------------------------------------------------------------------------------ + +void Clipper::SwapPositionsInSEL(TEdge *Edge1, TEdge *Edge2) +{ + if( !( Edge1->NextInSEL ) && !( Edge1->PrevInSEL ) ) return; + if( !( Edge2->NextInSEL ) && !( Edge2->PrevInSEL ) ) return; + + if( Edge1->NextInSEL == Edge2 ) + { + TEdge* Next = Edge2->NextInSEL; + if( Next ) Next->PrevInSEL = Edge1; + TEdge* Prev = Edge1->PrevInSEL; + if( Prev ) Prev->NextInSEL = Edge2; + Edge2->PrevInSEL = Prev; + Edge2->NextInSEL = Edge1; + Edge1->PrevInSEL = Edge2; + Edge1->NextInSEL = Next; + } + else if( Edge2->NextInSEL == Edge1 ) + { + TEdge* Next = Edge1->NextInSEL; + if( Next ) Next->PrevInSEL = Edge2; + TEdge* Prev = Edge2->PrevInSEL; + if( Prev ) Prev->NextInSEL = Edge1; + Edge1->PrevInSEL = Prev; + Edge1->NextInSEL = Edge2; + Edge2->PrevInSEL = Edge1; + Edge2->NextInSEL = Next; + } + else + { + TEdge* Next = Edge1->NextInSEL; + TEdge* Prev = Edge1->PrevInSEL; + Edge1->NextInSEL = Edge2->NextInSEL; + if( Edge1->NextInSEL ) Edge1->NextInSEL->PrevInSEL = Edge1; + Edge1->PrevInSEL = Edge2->PrevInSEL; + if( Edge1->PrevInSEL ) Edge1->PrevInSEL->NextInSEL = Edge1; + Edge2->NextInSEL = Next; + if( Edge2->NextInSEL ) Edge2->NextInSEL->PrevInSEL = Edge2; + Edge2->PrevInSEL = Prev; + if( Edge2->PrevInSEL ) Edge2->PrevInSEL->NextInSEL = Edge2; + } + + if( !Edge1->PrevInSEL ) m_SortedEdges = Edge1; + else if( !Edge2->PrevInSEL ) m_SortedEdges = Edge2; +} +//------------------------------------------------------------------------------ + +TEdge* GetNextInAEL(TEdge *e, Direction dir) +{ + return dir == dLeftToRight ? e->NextInAEL : e->PrevInAEL; +} +//------------------------------------------------------------------------------ + +void GetHorzDirection(TEdge& HorzEdge, Direction& Dir, cInt& Left, cInt& Right) +{ + if (HorzEdge.Bot.X < HorzEdge.Top.X) + { + Left = HorzEdge.Bot.X; + Right = HorzEdge.Top.X; + Dir = dLeftToRight; + } else + { + Left = HorzEdge.Top.X; + Right = HorzEdge.Bot.X; + Dir = dRightToLeft; + } +} +//------------------------------------------------------------------------ + +/******************************************************************************* +* Notes: Horizontal edges (HEs) at scanline intersections (ie at the Top or * +* Bottom of a scanbeam) are processed as if layered. The order in which HEs * +* are processed doesn't matter. HEs intersect with other HE Bot.Xs only [#] * +* (or they could intersect with Top.Xs only, ie EITHER Bot.Xs OR Top.Xs), * +* and with other non-horizontal edges [*]. Once these intersections are * +* processed, intermediate HEs then 'promote' the Edge above (NextInLML) into * +* the AEL. These 'promoted' edges may in turn intersect [%] with other HEs. * +*******************************************************************************/ + +void Clipper::ProcessHorizontal(TEdge *horzEdge) +{ + Direction dir; + cInt horzLeft, horzRight; + bool IsOpen = (horzEdge->WindDelta == 0); + + GetHorzDirection(*horzEdge, dir, horzLeft, horzRight); + + TEdge* eLastHorz = horzEdge, *eMaxPair = 0; + while (eLastHorz->NextInLML && IsHorizontal(*eLastHorz->NextInLML)) + eLastHorz = eLastHorz->NextInLML; + if (!eLastHorz->NextInLML) + eMaxPair = GetMaximaPair(eLastHorz); + + MaximaList::const_iterator maxIt; + MaximaList::const_reverse_iterator maxRit; + if (m_Maxima.size() > 0) + { + //get the first maxima in range (X) ... + if (dir == dLeftToRight) + { + maxIt = m_Maxima.begin(); + while (maxIt != m_Maxima.end() && *maxIt <= horzEdge->Bot.X) maxIt++; + if (maxIt != m_Maxima.end() && *maxIt >= eLastHorz->Top.X) + maxIt = m_Maxima.end(); + } + else + { + maxRit = m_Maxima.rbegin(); + while (maxRit != m_Maxima.rend() && *maxRit > horzEdge->Bot.X) maxRit++; + if (maxRit != m_Maxima.rend() && *maxRit <= eLastHorz->Top.X) + maxRit = m_Maxima.rend(); + } + } + + OutPt* op1 = 0; + + for (;;) //loop through consec. horizontal edges + { + + bool IsLastHorz = (horzEdge == eLastHorz); + TEdge* e = GetNextInAEL(horzEdge, dir); + while(e) + { + + //this code block inserts extra coords into horizontal edges (in output + //polygons) whereever maxima touch these horizontal edges. This helps + //'simplifying' polygons (ie if the Simplify property is set). + if (m_Maxima.size() > 0) + { + if (dir == dLeftToRight) + { + while (maxIt != m_Maxima.end() && *maxIt < e->Curr.X) + { + if (horzEdge->OutIdx >= 0 && !IsOpen) + AddOutPt(horzEdge, IntPoint(*maxIt, horzEdge->Bot.Y)); + maxIt++; + } + } + else + { + while (maxRit != m_Maxima.rend() && *maxRit > e->Curr.X) + { + if (horzEdge->OutIdx >= 0 && !IsOpen) + AddOutPt(horzEdge, IntPoint(*maxRit, horzEdge->Bot.Y)); + maxRit++; + } + } + }; + + if ((dir == dLeftToRight && e->Curr.X > horzRight) || + (dir == dRightToLeft && e->Curr.X < horzLeft)) break; + + //Also break if we've got to the end of an intermediate horizontal edge ... + //nb: Smaller Dx's are to the right of larger Dx's ABOVE the horizontal. + if (e->Curr.X == horzEdge->Top.X && horzEdge->NextInLML && + e->Dx < horzEdge->NextInLML->Dx) break; + + if (horzEdge->OutIdx >= 0 && !IsOpen) //note: may be done multiple times + { + op1 = AddOutPt(horzEdge, e->Curr); + TEdge* eNextHorz = m_SortedEdges; + while (eNextHorz) + { + if (eNextHorz->OutIdx >= 0 && + HorzSegmentsOverlap(horzEdge->Bot.X, + horzEdge->Top.X, eNextHorz->Bot.X, eNextHorz->Top.X)) + { + OutPt* op2 = GetLastOutPt(eNextHorz); + AddJoin(op2, op1, eNextHorz->Top); + } + eNextHorz = eNextHorz->NextInSEL; + } + AddGhostJoin(op1, horzEdge->Bot); + } + + //OK, so far we're still in range of the horizontal Edge but make sure + //we're at the last of consec. horizontals when matching with eMaxPair + if(e == eMaxPair && IsLastHorz) + { + if (horzEdge->OutIdx >= 0) + AddLocalMaxPoly(horzEdge, eMaxPair, horzEdge->Top); + DeleteFromAEL(horzEdge); + DeleteFromAEL(eMaxPair); + return; + } + + if(dir == dLeftToRight) + { + IntPoint Pt = IntPoint(e->Curr.X, horzEdge->Curr.Y); + IntersectEdges(horzEdge, e, Pt); + } + else + { + IntPoint Pt = IntPoint(e->Curr.X, horzEdge->Curr.Y); + IntersectEdges( e, horzEdge, Pt); + } + TEdge* eNext = GetNextInAEL(e, dir); + SwapPositionsInAEL( horzEdge, e ); + e = eNext; + } //end while(e) + + //Break out of loop if HorzEdge.NextInLML is not also horizontal ... + if (!horzEdge->NextInLML || !IsHorizontal(*horzEdge->NextInLML)) break; + + UpdateEdgeIntoAEL(horzEdge); + if (horzEdge->OutIdx >= 0) AddOutPt(horzEdge, horzEdge->Bot); + GetHorzDirection(*horzEdge, dir, horzLeft, horzRight); + + } //end for (;;) + + if (horzEdge->OutIdx >= 0 && !op1) + { + op1 = GetLastOutPt(horzEdge); + TEdge* eNextHorz = m_SortedEdges; + while (eNextHorz) + { + if (eNextHorz->OutIdx >= 0 && + HorzSegmentsOverlap(horzEdge->Bot.X, + horzEdge->Top.X, eNextHorz->Bot.X, eNextHorz->Top.X)) + { + OutPt* op2 = GetLastOutPt(eNextHorz); + AddJoin(op2, op1, eNextHorz->Top); + } + eNextHorz = eNextHorz->NextInSEL; + } + AddGhostJoin(op1, horzEdge->Top); + } + + if (horzEdge->NextInLML) + { + if(horzEdge->OutIdx >= 0) + { + op1 = AddOutPt( horzEdge, horzEdge->Top); + UpdateEdgeIntoAEL(horzEdge); + if (horzEdge->WindDelta == 0) return; + //nb: HorzEdge is no longer horizontal here + TEdge* ePrev = horzEdge->PrevInAEL; + TEdge* eNext = horzEdge->NextInAEL; + if (ePrev && ePrev->Curr.X == horzEdge->Bot.X && + ePrev->Curr.Y == horzEdge->Bot.Y && ePrev->WindDelta != 0 && + (ePrev->OutIdx >= 0 && ePrev->Curr.Y > ePrev->Top.Y && + SlopesEqual(*horzEdge, *ePrev, m_UseFullRange))) + { + OutPt* op2 = AddOutPt(ePrev, horzEdge->Bot); + AddJoin(op1, op2, horzEdge->Top); + } + else if (eNext && eNext->Curr.X == horzEdge->Bot.X && + eNext->Curr.Y == horzEdge->Bot.Y && eNext->WindDelta != 0 && + eNext->OutIdx >= 0 && eNext->Curr.Y > eNext->Top.Y && + SlopesEqual(*horzEdge, *eNext, m_UseFullRange)) + { + OutPt* op2 = AddOutPt(eNext, horzEdge->Bot); + AddJoin(op1, op2, horzEdge->Top); + } + } + else + UpdateEdgeIntoAEL(horzEdge); + } + else + { + if (horzEdge->OutIdx >= 0) AddOutPt(horzEdge, horzEdge->Top); + DeleteFromAEL(horzEdge); + } +} +//------------------------------------------------------------------------------ + +bool Clipper::ProcessIntersections(const cInt topY) +{ + if( !m_ActiveEdges ) return true; + try { + BuildIntersectList(topY); + size_t IlSize = m_IntersectList.size(); + if (IlSize == 0) return true; + if (IlSize == 1 || FixupIntersectionOrder()) ProcessIntersectList(); + else return false; + } + catch(...) + { + m_SortedEdges = 0; + DisposeIntersectNodes(); + throw clipperException("ProcessIntersections error"); + } + m_SortedEdges = 0; + return true; +} +//------------------------------------------------------------------------------ + +void Clipper::DisposeIntersectNodes() +{ + for (size_t i = 0; i < m_IntersectList.size(); ++i ) + delete m_IntersectList[i]; + m_IntersectList.clear(); +} +//------------------------------------------------------------------------------ + +void Clipper::BuildIntersectList(const cInt topY) +{ + if ( !m_ActiveEdges ) return; + + //prepare for sorting ... + TEdge* e = m_ActiveEdges; + m_SortedEdges = e; + while( e ) + { + e->PrevInSEL = e->PrevInAEL; + e->NextInSEL = e->NextInAEL; + e->Curr.X = TopX( *e, topY ); + e = e->NextInAEL; + } + + //bubblesort ... + bool isModified; + do + { + isModified = false; + e = m_SortedEdges; + while( e->NextInSEL ) + { + TEdge *eNext = e->NextInSEL; + IntPoint Pt; + if(e->Curr.X > eNext->Curr.X) + { + IntersectPoint(*e, *eNext, Pt); + if (Pt.Y < topY) Pt = IntPoint(TopX(*e, topY), topY); + IntersectNode * newNode = new IntersectNode; + newNode->Edge1 = e; + newNode->Edge2 = eNext; + newNode->Pt = Pt; + m_IntersectList.push_back(newNode); + + SwapPositionsInSEL(e, eNext); + isModified = true; + } + else + e = eNext; + } + if( e->PrevInSEL ) e->PrevInSEL->NextInSEL = 0; + else break; + } + while ( isModified ); + m_SortedEdges = 0; //important +} +//------------------------------------------------------------------------------ + + +void Clipper::ProcessIntersectList() +{ + for (size_t i = 0; i < m_IntersectList.size(); ++i) + { + IntersectNode* iNode = m_IntersectList[i]; + { + IntersectEdges( iNode->Edge1, iNode->Edge2, iNode->Pt); + SwapPositionsInAEL( iNode->Edge1 , iNode->Edge2 ); + } + delete iNode; + } + m_IntersectList.clear(); +} +//------------------------------------------------------------------------------ + +bool IntersectListSort(IntersectNode* node1, IntersectNode* node2) +{ + return node2->Pt.Y < node1->Pt.Y; +} +//------------------------------------------------------------------------------ + +inline bool EdgesAdjacent(const IntersectNode &inode) +{ + return (inode.Edge1->NextInSEL == inode.Edge2) || + (inode.Edge1->PrevInSEL == inode.Edge2); +} +//------------------------------------------------------------------------------ + +bool Clipper::FixupIntersectionOrder() +{ + //pre-condition: intersections are sorted Bottom-most first. + //Now it's crucial that intersections are made only between adjacent edges, + //so to ensure this the order of intersections may need adjusting ... + CopyAELToSEL(); + std::sort(m_IntersectList.begin(), m_IntersectList.end(), IntersectListSort); + size_t cnt = m_IntersectList.size(); + for (size_t i = 0; i < cnt; ++i) + { + if (!EdgesAdjacent(*m_IntersectList[i])) + { + size_t j = i + 1; + while (j < cnt && !EdgesAdjacent(*m_IntersectList[j])) j++; + if (j == cnt) return false; + std::swap(m_IntersectList[i], m_IntersectList[j]); + } + SwapPositionsInSEL(m_IntersectList[i]->Edge1, m_IntersectList[i]->Edge2); + } + return true; +} +//------------------------------------------------------------------------------ + +void Clipper::DoMaxima(TEdge *e) +{ + TEdge* eMaxPair = GetMaximaPairEx(e); + if (!eMaxPair) + { + if (e->OutIdx >= 0) + AddOutPt(e, e->Top); + DeleteFromAEL(e); + return; + } + + TEdge* eNext = e->NextInAEL; + while(eNext && eNext != eMaxPair) + { + IntersectEdges(e, eNext, e->Top); + SwapPositionsInAEL(e, eNext); + eNext = e->NextInAEL; + } + + if(e->OutIdx == Unassigned && eMaxPair->OutIdx == Unassigned) + { + DeleteFromAEL(e); + DeleteFromAEL(eMaxPair); + } + else if( e->OutIdx >= 0 && eMaxPair->OutIdx >= 0 ) + { + if (e->OutIdx >= 0) AddLocalMaxPoly(e, eMaxPair, e->Top); + DeleteFromAEL(e); + DeleteFromAEL(eMaxPair); + } +#ifdef use_lines + else if (e->WindDelta == 0) + { + if (e->OutIdx >= 0) + { + AddOutPt(e, e->Top); + e->OutIdx = Unassigned; + } + DeleteFromAEL(e); + + if (eMaxPair->OutIdx >= 0) + { + AddOutPt(eMaxPair, e->Top); + eMaxPair->OutIdx = Unassigned; + } + DeleteFromAEL(eMaxPair); + } +#endif + else throw clipperException("DoMaxima error"); +} +//------------------------------------------------------------------------------ + +void Clipper::ProcessEdgesAtTopOfScanbeam(const cInt topY) +{ + TEdge* e = m_ActiveEdges; + while( e ) + { + //1. process maxima, treating them as if they're 'bent' horizontal edges, + // but exclude maxima with horizontal edges. nb: e can't be a horizontal. + bool IsMaximaEdge = IsMaxima(e, topY); + + if(IsMaximaEdge) + { + TEdge* eMaxPair = GetMaximaPairEx(e); + IsMaximaEdge = (!eMaxPair || !IsHorizontal(*eMaxPair)); + } + + if(IsMaximaEdge) + { + if (m_StrictSimple) m_Maxima.push_back(e->Top.X); + TEdge* ePrev = e->PrevInAEL; + DoMaxima(e); + if( !ePrev ) e = m_ActiveEdges; + else e = ePrev->NextInAEL; + } + else + { + //2. promote horizontal edges, otherwise update Curr.X and Curr.Y ... + if (IsIntermediate(e, topY) && IsHorizontal(*e->NextInLML)) + { + UpdateEdgeIntoAEL(e); + if (e->OutIdx >= 0) + AddOutPt(e, e->Bot); + AddEdgeToSEL(e); + } + else + { + e->Curr.X = TopX( *e, topY ); + e->Curr.Y = topY; + } + + //When StrictlySimple and 'e' is being touched by another edge, then + //make sure both edges have a vertex here ... + if (m_StrictSimple) + { + TEdge* ePrev = e->PrevInAEL; + if ((e->OutIdx >= 0) && (e->WindDelta != 0) && ePrev && (ePrev->OutIdx >= 0) && + (ePrev->Curr.X == e->Curr.X) && (ePrev->WindDelta != 0)) + { + IntPoint pt = e->Curr; +#ifdef use_xyz + SetZ(pt, *ePrev, *e); +#endif + OutPt* op = AddOutPt(ePrev, pt); + OutPt* op2 = AddOutPt(e, pt); + AddJoin(op, op2, pt); //StrictlySimple (type-3) join + } + } + + e = e->NextInAEL; + } + } + + //3. Process horizontals at the Top of the scanbeam ... + m_Maxima.sort(); + ProcessHorizontals(); + m_Maxima.clear(); + + //4. Promote intermediate vertices ... + e = m_ActiveEdges; + while(e) + { + if(IsIntermediate(e, topY)) + { + OutPt* op = 0; + if( e->OutIdx >= 0 ) + op = AddOutPt(e, e->Top); + UpdateEdgeIntoAEL(e); + + //if output polygons share an edge, they'll need joining later ... + TEdge* ePrev = e->PrevInAEL; + TEdge* eNext = e->NextInAEL; + if (ePrev && ePrev->Curr.X == e->Bot.X && + ePrev->Curr.Y == e->Bot.Y && op && + ePrev->OutIdx >= 0 && ePrev->Curr.Y > ePrev->Top.Y && + SlopesEqual(e->Curr, e->Top, ePrev->Curr, ePrev->Top, m_UseFullRange) && + (e->WindDelta != 0) && (ePrev->WindDelta != 0)) + { + OutPt* op2 = AddOutPt(ePrev, e->Bot); + AddJoin(op, op2, e->Top); + } + else if (eNext && eNext->Curr.X == e->Bot.X && + eNext->Curr.Y == e->Bot.Y && op && + eNext->OutIdx >= 0 && eNext->Curr.Y > eNext->Top.Y && + SlopesEqual(e->Curr, e->Top, eNext->Curr, eNext->Top, m_UseFullRange) && + (e->WindDelta != 0) && (eNext->WindDelta != 0)) + { + OutPt* op2 = AddOutPt(eNext, e->Bot); + AddJoin(op, op2, e->Top); + } + } + e = e->NextInAEL; + } +} +//------------------------------------------------------------------------------ + +void Clipper::FixupOutPolyline(OutRec &outrec) +{ + OutPt *pp = outrec.Pts; + OutPt *lastPP = pp->Prev; + while (pp != lastPP) + { + pp = pp->Next; + if (pp->Pt == pp->Prev->Pt) + { + if (pp == lastPP) lastPP = pp->Prev; + OutPt *tmpPP = pp->Prev; + tmpPP->Next = pp->Next; + pp->Next->Prev = tmpPP; + delete pp; + pp = tmpPP; + } + } + + if (pp == pp->Prev) + { + DisposeOutPts(pp); + outrec.Pts = 0; + return; + } +} +//------------------------------------------------------------------------------ + +void Clipper::FixupOutPolygon(OutRec &outrec) +{ + //FixupOutPolygon() - removes duplicate points and simplifies consecutive + //parallel edges by removing the middle vertex. + OutPt *lastOK = 0; + outrec.BottomPt = 0; + OutPt *pp = outrec.Pts; + bool preserveCol = m_PreserveCollinear || m_StrictSimple; + + for (;;) + { + if (pp->Prev == pp || pp->Prev == pp->Next) + { + DisposeOutPts(pp); + outrec.Pts = 0; + return; + } + + //test for duplicate points and collinear edges ... + if ((pp->Pt == pp->Next->Pt) || (pp->Pt == pp->Prev->Pt) || + (SlopesEqual(pp->Prev->Pt, pp->Pt, pp->Next->Pt, m_UseFullRange) && + (!preserveCol || !Pt2IsBetweenPt1AndPt3(pp->Prev->Pt, pp->Pt, pp->Next->Pt)))) + { + lastOK = 0; + OutPt *tmp = pp; + pp->Prev->Next = pp->Next; + pp->Next->Prev = pp->Prev; + pp = pp->Prev; + delete tmp; + } + else if (pp == lastOK) break; + else + { + if (!lastOK) lastOK = pp; + pp = pp->Next; + } + } + outrec.Pts = pp; +} +//------------------------------------------------------------------------------ + +int PointCount(OutPt *Pts) +{ + if (!Pts) return 0; + int result = 0; + OutPt* p = Pts; + do + { + result++; + p = p->Next; + } + while (p != Pts); + return result; +} +//------------------------------------------------------------------------------ + +void Clipper::BuildResult(Paths &polys) +{ + polys.reserve(m_PolyOuts.size()); + for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) + { + if (!m_PolyOuts[i]->Pts) continue; + Path pg; + OutPt* p = m_PolyOuts[i]->Pts->Prev; + int cnt = PointCount(p); + if (cnt < 2) continue; + pg.reserve(cnt); + for (int i = 0; i < cnt; ++i) + { + pg.push_back(p->Pt); + p = p->Prev; + } + polys.push_back(pg); + } +} +//------------------------------------------------------------------------------ + +void Clipper::BuildResult2(PolyTree& polytree) +{ + polytree.Clear(); + polytree.AllNodes.reserve(m_PolyOuts.size()); + //add each output polygon/contour to polytree ... + for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); i++) + { + OutRec* outRec = m_PolyOuts[i]; + int cnt = PointCount(outRec->Pts); + if ((outRec->IsOpen && cnt < 2) || (!outRec->IsOpen && cnt < 3)) continue; + FixHoleLinkage(*outRec); + PolyNode* pn = new PolyNode(); + //nb: polytree takes ownership of all the PolyNodes + polytree.AllNodes.push_back(pn); + outRec->PolyNd = pn; + pn->Parent = 0; + pn->Index = 0; + pn->Contour.reserve(cnt); + OutPt *op = outRec->Pts->Prev; + for (int j = 0; j < cnt; j++) + { + pn->Contour.push_back(op->Pt); + op = op->Prev; + } + } + + //fixup PolyNode links etc ... + polytree.Childs.reserve(m_PolyOuts.size()); + for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); i++) + { + OutRec* outRec = m_PolyOuts[i]; + if (!outRec->PolyNd) continue; + if (outRec->IsOpen) + { + outRec->PolyNd->m_IsOpen = true; + polytree.AddChild(*outRec->PolyNd); + } + else if (outRec->FirstLeft && outRec->FirstLeft->PolyNd) + outRec->FirstLeft->PolyNd->AddChild(*outRec->PolyNd); + else + polytree.AddChild(*outRec->PolyNd); + } +} +//------------------------------------------------------------------------------ + +void SwapIntersectNodes(IntersectNode &int1, IntersectNode &int2) +{ + //just swap the contents (because fIntersectNodes is a single-linked-list) + IntersectNode inode = int1; //gets a copy of Int1 + int1.Edge1 = int2.Edge1; + int1.Edge2 = int2.Edge2; + int1.Pt = int2.Pt; + int2.Edge1 = inode.Edge1; + int2.Edge2 = inode.Edge2; + int2.Pt = inode.Pt; +} +//------------------------------------------------------------------------------ + +inline bool E2InsertsBeforeE1(TEdge &e1, TEdge &e2) +{ + if (e2.Curr.X == e1.Curr.X) + { + if (e2.Top.Y > e1.Top.Y) + return e2.Top.X < TopX(e1, e2.Top.Y); + else return e1.Top.X > TopX(e2, e1.Top.Y); + } + else return e2.Curr.X < e1.Curr.X; +} +//------------------------------------------------------------------------------ + +bool GetOverlap(const cInt a1, const cInt a2, const cInt b1, const cInt b2, + cInt& Left, cInt& Right) +{ + if (a1 < a2) + { + if (b1 < b2) {Left = std::max(a1,b1); Right = std::min(a2,b2);} + else {Left = std::max(a1,b2); Right = std::min(a2,b1);} + } + else + { + if (b1 < b2) {Left = std::max(a2,b1); Right = std::min(a1,b2);} + else {Left = std::max(a2,b2); Right = std::min(a1,b1);} + } + return Left < Right; +} +//------------------------------------------------------------------------------ + +inline void UpdateOutPtIdxs(OutRec& outrec) +{ + OutPt* op = outrec.Pts; + do + { + op->Idx = outrec.Idx; + op = op->Prev; + } + while(op != outrec.Pts); +} +//------------------------------------------------------------------------------ + +void Clipper::InsertEdgeIntoAEL(TEdge *edge, TEdge* startEdge) +{ + if(!m_ActiveEdges) + { + edge->PrevInAEL = 0; + edge->NextInAEL = 0; + m_ActiveEdges = edge; + } + else if(!startEdge && E2InsertsBeforeE1(*m_ActiveEdges, *edge)) + { + edge->PrevInAEL = 0; + edge->NextInAEL = m_ActiveEdges; + m_ActiveEdges->PrevInAEL = edge; + m_ActiveEdges = edge; + } + else + { + if(!startEdge) startEdge = m_ActiveEdges; + while(startEdge->NextInAEL && + !E2InsertsBeforeE1(*startEdge->NextInAEL , *edge)) + startEdge = startEdge->NextInAEL; + edge->NextInAEL = startEdge->NextInAEL; + if(startEdge->NextInAEL) startEdge->NextInAEL->PrevInAEL = edge; + edge->PrevInAEL = startEdge; + startEdge->NextInAEL = edge; + } +} +//---------------------------------------------------------------------- + +OutPt* DupOutPt(OutPt* outPt, bool InsertAfter) +{ + OutPt* result = new OutPt; + result->Pt = outPt->Pt; + result->Idx = outPt->Idx; + if (InsertAfter) + { + result->Next = outPt->Next; + result->Prev = outPt; + outPt->Next->Prev = result; + outPt->Next = result; + } + else + { + result->Prev = outPt->Prev; + result->Next = outPt; + outPt->Prev->Next = result; + outPt->Prev = result; + } + return result; +} +//------------------------------------------------------------------------------ + +bool JoinHorz(OutPt* op1, OutPt* op1b, OutPt* op2, OutPt* op2b, + const IntPoint Pt, bool DiscardLeft) +{ + Direction Dir1 = (op1->Pt.X > op1b->Pt.X ? dRightToLeft : dLeftToRight); + Direction Dir2 = (op2->Pt.X > op2b->Pt.X ? dRightToLeft : dLeftToRight); + if (Dir1 == Dir2) return false; + + //When DiscardLeft, we want Op1b to be on the Left of Op1, otherwise we + //want Op1b to be on the Right. (And likewise with Op2 and Op2b.) + //So, to facilitate this while inserting Op1b and Op2b ... + //when DiscardLeft, make sure we're AT or RIGHT of Pt before adding Op1b, + //otherwise make sure we're AT or LEFT of Pt. (Likewise with Op2b.) + if (Dir1 == dLeftToRight) + { + while (op1->Next->Pt.X <= Pt.X && + op1->Next->Pt.X >= op1->Pt.X && op1->Next->Pt.Y == Pt.Y) + op1 = op1->Next; + if (DiscardLeft && (op1->Pt.X != Pt.X)) op1 = op1->Next; + op1b = DupOutPt(op1, !DiscardLeft); + if (op1b->Pt != Pt) + { + op1 = op1b; + op1->Pt = Pt; + op1b = DupOutPt(op1, !DiscardLeft); + } + } + else + { + while (op1->Next->Pt.X >= Pt.X && + op1->Next->Pt.X <= op1->Pt.X && op1->Next->Pt.Y == Pt.Y) + op1 = op1->Next; + if (!DiscardLeft && (op1->Pt.X != Pt.X)) op1 = op1->Next; + op1b = DupOutPt(op1, DiscardLeft); + if (op1b->Pt != Pt) + { + op1 = op1b; + op1->Pt = Pt; + op1b = DupOutPt(op1, DiscardLeft); + } + } + + if (Dir2 == dLeftToRight) + { + while (op2->Next->Pt.X <= Pt.X && + op2->Next->Pt.X >= op2->Pt.X && op2->Next->Pt.Y == Pt.Y) + op2 = op2->Next; + if (DiscardLeft && (op2->Pt.X != Pt.X)) op2 = op2->Next; + op2b = DupOutPt(op2, !DiscardLeft); + if (op2b->Pt != Pt) + { + op2 = op2b; + op2->Pt = Pt; + op2b = DupOutPt(op2, !DiscardLeft); + }; + } else + { + while (op2->Next->Pt.X >= Pt.X && + op2->Next->Pt.X <= op2->Pt.X && op2->Next->Pt.Y == Pt.Y) + op2 = op2->Next; + if (!DiscardLeft && (op2->Pt.X != Pt.X)) op2 = op2->Next; + op2b = DupOutPt(op2, DiscardLeft); + if (op2b->Pt != Pt) + { + op2 = op2b; + op2->Pt = Pt; + op2b = DupOutPt(op2, DiscardLeft); + }; + }; + + if ((Dir1 == dLeftToRight) == DiscardLeft) + { + op1->Prev = op2; + op2->Next = op1; + op1b->Next = op2b; + op2b->Prev = op1b; + } + else + { + op1->Next = op2; + op2->Prev = op1; + op1b->Prev = op2b; + op2b->Next = op1b; + } + return true; +} +//------------------------------------------------------------------------------ + +bool Clipper::JoinPoints(Join *j, OutRec* outRec1, OutRec* outRec2) +{ + OutPt *op1 = j->OutPt1, *op1b; + OutPt *op2 = j->OutPt2, *op2b; + + //There are 3 kinds of joins for output polygons ... + //1. Horizontal joins where Join.OutPt1 & Join.OutPt2 are vertices anywhere + //along (horizontal) collinear edges (& Join.OffPt is on the same horizontal). + //2. Non-horizontal joins where Join.OutPt1 & Join.OutPt2 are at the same + //location at the Bottom of the overlapping segment (& Join.OffPt is above). + //3. StrictSimple joins where edges touch but are not collinear and where + //Join.OutPt1, Join.OutPt2 & Join.OffPt all share the same point. + bool isHorizontal = (j->OutPt1->Pt.Y == j->OffPt.Y); + + if (isHorizontal && (j->OffPt == j->OutPt1->Pt) && + (j->OffPt == j->OutPt2->Pt)) + { + //Strictly Simple join ... + if (outRec1 != outRec2) return false; + op1b = j->OutPt1->Next; + while (op1b != op1 && (op1b->Pt == j->OffPt)) + op1b = op1b->Next; + bool reverse1 = (op1b->Pt.Y > j->OffPt.Y); + op2b = j->OutPt2->Next; + while (op2b != op2 && (op2b->Pt == j->OffPt)) + op2b = op2b->Next; + bool reverse2 = (op2b->Pt.Y > j->OffPt.Y); + if (reverse1 == reverse2) return false; + if (reverse1) + { + op1b = DupOutPt(op1, false); + op2b = DupOutPt(op2, true); + op1->Prev = op2; + op2->Next = op1; + op1b->Next = op2b; + op2b->Prev = op1b; + j->OutPt1 = op1; + j->OutPt2 = op1b; + return true; + } else + { + op1b = DupOutPt(op1, true); + op2b = DupOutPt(op2, false); + op1->Next = op2; + op2->Prev = op1; + op1b->Prev = op2b; + op2b->Next = op1b; + j->OutPt1 = op1; + j->OutPt2 = op1b; + return true; + } + } + else if (isHorizontal) + { + //treat horizontal joins differently to non-horizontal joins since with + //them we're not yet sure where the overlapping is. OutPt1.Pt & OutPt2.Pt + //may be anywhere along the horizontal edge. + op1b = op1; + while (op1->Prev->Pt.Y == op1->Pt.Y && op1->Prev != op1b && op1->Prev != op2) + op1 = op1->Prev; + while (op1b->Next->Pt.Y == op1b->Pt.Y && op1b->Next != op1 && op1b->Next != op2) + op1b = op1b->Next; + if (op1b->Next == op1 || op1b->Next == op2) return false; //a flat 'polygon' + + op2b = op2; + while (op2->Prev->Pt.Y == op2->Pt.Y && op2->Prev != op2b && op2->Prev != op1b) + op2 = op2->Prev; + while (op2b->Next->Pt.Y == op2b->Pt.Y && op2b->Next != op2 && op2b->Next != op1) + op2b = op2b->Next; + if (op2b->Next == op2 || op2b->Next == op1) return false; //a flat 'polygon' + + cInt Left, Right; + //Op1 --> Op1b & Op2 --> Op2b are the extremites of the horizontal edges + if (!GetOverlap(op1->Pt.X, op1b->Pt.X, op2->Pt.X, op2b->Pt.X, Left, Right)) + return false; + + //DiscardLeftSide: when overlapping edges are joined, a spike will created + //which needs to be cleaned up. However, we don't want Op1 or Op2 caught up + //on the discard Side as either may still be needed for other joins ... + IntPoint Pt; + bool DiscardLeftSide; + if (op1->Pt.X >= Left && op1->Pt.X <= Right) + { + Pt = op1->Pt; DiscardLeftSide = (op1->Pt.X > op1b->Pt.X); + } + else if (op2->Pt.X >= Left&& op2->Pt.X <= Right) + { + Pt = op2->Pt; DiscardLeftSide = (op2->Pt.X > op2b->Pt.X); + } + else if (op1b->Pt.X >= Left && op1b->Pt.X <= Right) + { + Pt = op1b->Pt; DiscardLeftSide = op1b->Pt.X > op1->Pt.X; + } + else + { + Pt = op2b->Pt; DiscardLeftSide = (op2b->Pt.X > op2->Pt.X); + } + j->OutPt1 = op1; j->OutPt2 = op2; + return JoinHorz(op1, op1b, op2, op2b, Pt, DiscardLeftSide); + } else + { + //nb: For non-horizontal joins ... + // 1. Jr.OutPt1.Pt.Y == Jr.OutPt2.Pt.Y + // 2. Jr.OutPt1.Pt > Jr.OffPt.Y + + //make sure the polygons are correctly oriented ... + op1b = op1->Next; + while ((op1b->Pt == op1->Pt) && (op1b != op1)) op1b = op1b->Next; + bool Reverse1 = ((op1b->Pt.Y > op1->Pt.Y) || + !SlopesEqual(op1->Pt, op1b->Pt, j->OffPt, m_UseFullRange)); + if (Reverse1) + { + op1b = op1->Prev; + while ((op1b->Pt == op1->Pt) && (op1b != op1)) op1b = op1b->Prev; + if ((op1b->Pt.Y > op1->Pt.Y) || + !SlopesEqual(op1->Pt, op1b->Pt, j->OffPt, m_UseFullRange)) return false; + }; + op2b = op2->Next; + while ((op2b->Pt == op2->Pt) && (op2b != op2))op2b = op2b->Next; + bool Reverse2 = ((op2b->Pt.Y > op2->Pt.Y) || + !SlopesEqual(op2->Pt, op2b->Pt, j->OffPt, m_UseFullRange)); + if (Reverse2) + { + op2b = op2->Prev; + while ((op2b->Pt == op2->Pt) && (op2b != op2)) op2b = op2b->Prev; + if ((op2b->Pt.Y > op2->Pt.Y) || + !SlopesEqual(op2->Pt, op2b->Pt, j->OffPt, m_UseFullRange)) return false; + } + + if ((op1b == op1) || (op2b == op2) || (op1b == op2b) || + ((outRec1 == outRec2) && (Reverse1 == Reverse2))) return false; + + if (Reverse1) + { + op1b = DupOutPt(op1, false); + op2b = DupOutPt(op2, true); + op1->Prev = op2; + op2->Next = op1; + op1b->Next = op2b; + op2b->Prev = op1b; + j->OutPt1 = op1; + j->OutPt2 = op1b; + return true; + } else + { + op1b = DupOutPt(op1, true); + op2b = DupOutPt(op2, false); + op1->Next = op2; + op2->Prev = op1; + op1b->Prev = op2b; + op2b->Next = op1b; + j->OutPt1 = op1; + j->OutPt2 = op1b; + return true; + } + } +} +//---------------------------------------------------------------------- + +static OutRec* ParseFirstLeft(OutRec* FirstLeft) +{ + while (FirstLeft && !FirstLeft->Pts) + FirstLeft = FirstLeft->FirstLeft; + return FirstLeft; +} +//------------------------------------------------------------------------------ + +void Clipper::FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec) +{ + //tests if NewOutRec contains the polygon before reassigning FirstLeft + for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) + { + OutRec* outRec = m_PolyOuts[i]; + OutRec* firstLeft = ParseFirstLeft(outRec->FirstLeft); + if (outRec->Pts && firstLeft == OldOutRec) + { + if (Poly2ContainsPoly1(outRec->Pts, NewOutRec->Pts)) + outRec->FirstLeft = NewOutRec; + } + } +} +//---------------------------------------------------------------------- + +void Clipper::FixupFirstLefts2(OutRec* InnerOutRec, OutRec* OuterOutRec) +{ + //A polygon has split into two such that one is now the inner of the other. + //It's possible that these polygons now wrap around other polygons, so check + //every polygon that's also contained by OuterOutRec's FirstLeft container + //(including 0) to see if they've become inner to the new inner polygon ... + OutRec* orfl = OuterOutRec->FirstLeft; + for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) + { + OutRec* outRec = m_PolyOuts[i]; + + if (!outRec->Pts || outRec == OuterOutRec || outRec == InnerOutRec) + continue; + OutRec* firstLeft = ParseFirstLeft(outRec->FirstLeft); + if (firstLeft != orfl && firstLeft != InnerOutRec && firstLeft != OuterOutRec) + continue; + if (Poly2ContainsPoly1(outRec->Pts, InnerOutRec->Pts)) + outRec->FirstLeft = InnerOutRec; + else if (Poly2ContainsPoly1(outRec->Pts, OuterOutRec->Pts)) + outRec->FirstLeft = OuterOutRec; + else if (outRec->FirstLeft == InnerOutRec || outRec->FirstLeft == OuterOutRec) + outRec->FirstLeft = orfl; + } +} +//---------------------------------------------------------------------- +void Clipper::FixupFirstLefts3(OutRec* OldOutRec, OutRec* NewOutRec) +{ + //reassigns FirstLeft WITHOUT testing if NewOutRec contains the polygon + for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) + { + OutRec* outRec = m_PolyOuts[i]; + OutRec* firstLeft = ParseFirstLeft(outRec->FirstLeft); + if (outRec->Pts && outRec->FirstLeft == OldOutRec) + outRec->FirstLeft = NewOutRec; + } +} +//---------------------------------------------------------------------- + +void Clipper::JoinCommonEdges() +{ + for (JoinList::size_type i = 0; i < m_Joins.size(); i++) + { + Join* join = m_Joins[i]; + + OutRec *outRec1 = GetOutRec(join->OutPt1->Idx); + OutRec *outRec2 = GetOutRec(join->OutPt2->Idx); + + if (!outRec1->Pts || !outRec2->Pts) continue; + if (outRec1->IsOpen || outRec2->IsOpen) continue; + + //get the polygon fragment with the correct hole state (FirstLeft) + //before calling JoinPoints() ... + OutRec *holeStateRec; + if (outRec1 == outRec2) holeStateRec = outRec1; + else if (OutRec1RightOfOutRec2(outRec1, outRec2)) holeStateRec = outRec2; + else if (OutRec1RightOfOutRec2(outRec2, outRec1)) holeStateRec = outRec1; + else holeStateRec = GetLowermostRec(outRec1, outRec2); + + if (!JoinPoints(join, outRec1, outRec2)) continue; + + if (outRec1 == outRec2) + { + //instead of joining two polygons, we've just created a new one by + //splitting one polygon into two. + outRec1->Pts = join->OutPt1; + outRec1->BottomPt = 0; + outRec2 = CreateOutRec(); + outRec2->Pts = join->OutPt2; + + //update all OutRec2.Pts Idx's ... + UpdateOutPtIdxs(*outRec2); + + if (Poly2ContainsPoly1(outRec2->Pts, outRec1->Pts)) + { + //outRec1 contains outRec2 ... + outRec2->IsHole = !outRec1->IsHole; + outRec2->FirstLeft = outRec1; + + if (m_UsingPolyTree) FixupFirstLefts2(outRec2, outRec1); + + if ((outRec2->IsHole ^ m_ReverseOutput) == (Area(*outRec2) > 0)) + ReversePolyPtLinks(outRec2->Pts); + + } else if (Poly2ContainsPoly1(outRec1->Pts, outRec2->Pts)) + { + //outRec2 contains outRec1 ... + outRec2->IsHole = outRec1->IsHole; + outRec1->IsHole = !outRec2->IsHole; + outRec2->FirstLeft = outRec1->FirstLeft; + outRec1->FirstLeft = outRec2; + + if (m_UsingPolyTree) FixupFirstLefts2(outRec1, outRec2); + + if ((outRec1->IsHole ^ m_ReverseOutput) == (Area(*outRec1) > 0)) + ReversePolyPtLinks(outRec1->Pts); + } + else + { + //the 2 polygons are completely separate ... + outRec2->IsHole = outRec1->IsHole; + outRec2->FirstLeft = outRec1->FirstLeft; + + //fixup FirstLeft pointers that may need reassigning to OutRec2 + if (m_UsingPolyTree) FixupFirstLefts1(outRec1, outRec2); + } + + } else + { + //joined 2 polygons together ... + + outRec2->Pts = 0; + outRec2->BottomPt = 0; + outRec2->Idx = outRec1->Idx; + + outRec1->IsHole = holeStateRec->IsHole; + if (holeStateRec == outRec2) + outRec1->FirstLeft = outRec2->FirstLeft; + outRec2->FirstLeft = outRec1; + + if (m_UsingPolyTree) FixupFirstLefts3(outRec2, outRec1); + } + } +} + +//------------------------------------------------------------------------------ +// ClipperOffset support functions ... +//------------------------------------------------------------------------------ + +DoublePoint GetUnitNormal(const IntPoint &pt1, const IntPoint &pt2) +{ + if(pt2.X == pt1.X && pt2.Y == pt1.Y) + return DoublePoint(0, 0); + + double Dx = (double)(pt2.X - pt1.X); + double dy = (double)(pt2.Y - pt1.Y); + double f = 1 *1.0/ std::sqrt( Dx*Dx + dy*dy ); + Dx *= f; + dy *= f; + return DoublePoint(dy, -Dx); +} + +//------------------------------------------------------------------------------ +// ClipperOffset class +//------------------------------------------------------------------------------ + +ClipperOffset::ClipperOffset(double miterLimit, double arcTolerance) +{ + this->MiterLimit = miterLimit; + this->ArcTolerance = arcTolerance; + m_lowest.X = -1; +} +//------------------------------------------------------------------------------ + +ClipperOffset::~ClipperOffset() +{ + Clear(); +} +//------------------------------------------------------------------------------ + +void ClipperOffset::Clear() +{ + for (int i = 0; i < m_polyNodes.ChildCount(); ++i) + delete m_polyNodes.Childs[i]; + m_polyNodes.Childs.clear(); + m_lowest.X = -1; +} +//------------------------------------------------------------------------------ + +void ClipperOffset::AddPath(const Path& path, JoinType joinType, EndType endType) +{ + int highI = (int)path.size() - 1; + if (highI < 0) return; + PolyNode* newNode = new PolyNode(); + newNode->m_jointype = joinType; + newNode->m_endtype = endType; + + //strip duplicate points from path and also get index to the lowest point ... + if (endType == etClosedLine || endType == etClosedPolygon) + while (highI > 0 && path[0] == path[highI]) highI--; + newNode->Contour.reserve(highI + 1); + newNode->Contour.push_back(path[0]); + int j = 0, k = 0; + for (int i = 1; i <= highI; i++) + if (newNode->Contour[j] != path[i]) + { + j++; + newNode->Contour.push_back(path[i]); + if (path[i].Y > newNode->Contour[k].Y || + (path[i].Y == newNode->Contour[k].Y && + path[i].X < newNode->Contour[k].X)) k = j; + } + if (endType == etClosedPolygon && j < 2) + { + delete newNode; + return; + } + m_polyNodes.AddChild(*newNode); + + //if this path's lowest pt is lower than all the others then update m_lowest + if (endType != etClosedPolygon) return; + if (m_lowest.X < 0) + m_lowest = IntPoint(m_polyNodes.ChildCount() - 1, k); + else + { + IntPoint ip = m_polyNodes.Childs[(int)m_lowest.X]->Contour[(int)m_lowest.Y]; + if (newNode->Contour[k].Y > ip.Y || + (newNode->Contour[k].Y == ip.Y && + newNode->Contour[k].X < ip.X)) + m_lowest = IntPoint(m_polyNodes.ChildCount() - 1, k); + } +} +//------------------------------------------------------------------------------ + +void ClipperOffset::AddPaths(const Paths& paths, JoinType joinType, EndType endType) +{ + for (Paths::size_type i = 0; i < paths.size(); ++i) + AddPath(paths[i], joinType, endType); +} +//------------------------------------------------------------------------------ + +void ClipperOffset::FixOrientations() +{ + //fixup orientations of all closed paths if the orientation of the + //closed path with the lowermost vertex is wrong ... + if (m_lowest.X >= 0 && + !Orientation(m_polyNodes.Childs[(int)m_lowest.X]->Contour)) + { + for (int i = 0; i < m_polyNodes.ChildCount(); ++i) + { + PolyNode& node = *m_polyNodes.Childs[i]; + if (node.m_endtype == etClosedPolygon || + (node.m_endtype == etClosedLine && Orientation(node.Contour))) + ReversePath(node.Contour); + } + } else + { + for (int i = 0; i < m_polyNodes.ChildCount(); ++i) + { + PolyNode& node = *m_polyNodes.Childs[i]; + if (node.m_endtype == etClosedLine && !Orientation(node.Contour)) + ReversePath(node.Contour); + } + } +} +//------------------------------------------------------------------------------ + +void ClipperOffset::Execute(Paths& solution, double delta) +{ + solution.clear(); + FixOrientations(); + DoOffset(delta); + + //now clean up 'corners' ... + Clipper clpr; + clpr.AddPaths(m_destPolys, ptSubject, true); + if (delta > 0) + { + clpr.Execute(ctUnion, solution, pftPositive, pftPositive); + } + else + { + IntRect r = clpr.GetBounds(); + Path outer(4); + outer[0] = IntPoint(r.left - 10, r.bottom + 10); + outer[1] = IntPoint(r.right + 10, r.bottom + 10); + outer[2] = IntPoint(r.right + 10, r.top - 10); + outer[3] = IntPoint(r.left - 10, r.top - 10); + + clpr.AddPath(outer, ptSubject, true); + clpr.ReverseSolution(true); + clpr.Execute(ctUnion, solution, pftNegative, pftNegative); + if (solution.size() > 0) solution.erase(solution.begin()); + } +} +//------------------------------------------------------------------------------ + +void ClipperOffset::Execute(PolyTree& solution, double delta) +{ + solution.Clear(); + FixOrientations(); + DoOffset(delta); + + //now clean up 'corners' ... + Clipper clpr; + clpr.AddPaths(m_destPolys, ptSubject, true); + if (delta > 0) + { + clpr.Execute(ctUnion, solution, pftPositive, pftPositive); + } + else + { + IntRect r = clpr.GetBounds(); + Path outer(4); + outer[0] = IntPoint(r.left - 10, r.bottom + 10); + outer[1] = IntPoint(r.right + 10, r.bottom + 10); + outer[2] = IntPoint(r.right + 10, r.top - 10); + outer[3] = IntPoint(r.left - 10, r.top - 10); + + clpr.AddPath(outer, ptSubject, true); + clpr.ReverseSolution(true); + clpr.Execute(ctUnion, solution, pftNegative, pftNegative); + //remove the outer PolyNode rectangle ... + if (solution.ChildCount() == 1 && solution.Childs[0]->ChildCount() > 0) + { + PolyNode* outerNode = solution.Childs[0]; + solution.Childs.reserve(outerNode->ChildCount()); + solution.Childs[0] = outerNode->Childs[0]; + solution.Childs[0]->Parent = outerNode->Parent; + for (int i = 1; i < outerNode->ChildCount(); ++i) + solution.AddChild(*outerNode->Childs[i]); + } + else + solution.Clear(); + } +} +//------------------------------------------------------------------------------ + +void ClipperOffset::DoOffset(double delta) +{ + m_destPolys.clear(); + m_delta = delta; + + //if Zero offset, just copy any CLOSED polygons to m_p and return ... + if (NEAR_ZERO(delta)) + { + m_destPolys.reserve(m_polyNodes.ChildCount()); + for (int i = 0; i < m_polyNodes.ChildCount(); i++) + { + PolyNode& node = *m_polyNodes.Childs[i]; + if (node.m_endtype == etClosedPolygon) + m_destPolys.push_back(node.Contour); + } + return; + } + + //see offset_triginometry3.svg in the documentation folder ... + if (MiterLimit > 2) m_miterLim = 2/(MiterLimit * MiterLimit); + else m_miterLim = 0.5; + + double y; + if (ArcTolerance <= 0.0) y = def_arc_tolerance; + else if (ArcTolerance > std::fabs(delta) * def_arc_tolerance) + y = std::fabs(delta) * def_arc_tolerance; + else y = ArcTolerance; + //see offset_triginometry2.svg in the documentation folder ... + double steps = pi / std::acos(1 - y / std::fabs(delta)); + if (steps > std::fabs(delta) * pi) + steps = std::fabs(delta) * pi; //ie excessive precision check + m_sin = std::sin(two_pi / steps); + m_cos = std::cos(two_pi / steps); + m_StepsPerRad = steps / two_pi; + if (delta < 0.0) m_sin = -m_sin; + + m_destPolys.reserve(m_polyNodes.ChildCount() * 2); + for (int i = 0; i < m_polyNodes.ChildCount(); i++) + { + PolyNode& node = *m_polyNodes.Childs[i]; + m_srcPoly = node.Contour; + + int len = (int)m_srcPoly.size(); + if (len == 0 || (delta <= 0 && (len < 3 || node.m_endtype != etClosedPolygon))) + continue; + + m_destPoly.clear(); + if (len == 1) + { + if (node.m_jointype == jtRound) + { + double X = 1.0, Y = 0.0; + for (cInt j = 1; j <= steps; j++) + { + m_destPoly.push_back(IntPoint( + Round(m_srcPoly[0].X + X * delta), + Round(m_srcPoly[0].Y + Y * delta))); + double X2 = X; + X = X * m_cos - m_sin * Y; + Y = X2 * m_sin + Y * m_cos; + } + } + else + { + double X = -1.0, Y = -1.0; + for (int j = 0; j < 4; ++j) + { + m_destPoly.push_back(IntPoint( + Round(m_srcPoly[0].X + X * delta), + Round(m_srcPoly[0].Y + Y * delta))); + if (X < 0) X = 1; + else if (Y < 0) Y = 1; + else X = -1; + } + } + m_destPolys.push_back(m_destPoly); + continue; + } + //build m_normals ... + m_normals.clear(); + m_normals.reserve(len); + for (int j = 0; j < len - 1; ++j) + m_normals.push_back(GetUnitNormal(m_srcPoly[j], m_srcPoly[j + 1])); + if (node.m_endtype == etClosedLine || node.m_endtype == etClosedPolygon) + m_normals.push_back(GetUnitNormal(m_srcPoly[len - 1], m_srcPoly[0])); + else + m_normals.push_back(DoublePoint(m_normals[len - 2])); + + if (node.m_endtype == etClosedPolygon) + { + int k = len - 1; + for (int j = 0; j < len; ++j) + OffsetPoint(j, k, node.m_jointype); + m_destPolys.push_back(m_destPoly); + } + else if (node.m_endtype == etClosedLine) + { + int k = len - 1; + for (int j = 0; j < len; ++j) + OffsetPoint(j, k, node.m_jointype); + m_destPolys.push_back(m_destPoly); + m_destPoly.clear(); + //re-build m_normals ... + DoublePoint n = m_normals[len -1]; + for (int j = len - 1; j > 0; j--) + m_normals[j] = DoublePoint(-m_normals[j - 1].X, -m_normals[j - 1].Y); + m_normals[0] = DoublePoint(-n.X, -n.Y); + k = 0; + for (int j = len - 1; j >= 0; j--) + OffsetPoint(j, k, node.m_jointype); + m_destPolys.push_back(m_destPoly); + } + else + { + int k = 0; + for (int j = 1; j < len - 1; ++j) + OffsetPoint(j, k, node.m_jointype); + + IntPoint pt1; + if (node.m_endtype == etOpenButt) + { + int j = len - 1; + pt1 = IntPoint((cInt)Round(m_srcPoly[j].X + m_normals[j].X * + delta), (cInt)Round(m_srcPoly[j].Y + m_normals[j].Y * delta)); + m_destPoly.push_back(pt1); + pt1 = IntPoint((cInt)Round(m_srcPoly[j].X - m_normals[j].X * + delta), (cInt)Round(m_srcPoly[j].Y - m_normals[j].Y * delta)); + m_destPoly.push_back(pt1); + } + else + { + int j = len - 1; + k = len - 2; + m_sinA = 0; + m_normals[j] = DoublePoint(-m_normals[j].X, -m_normals[j].Y); + if (node.m_endtype == etOpenSquare) + DoSquare(j, k); + else + DoRound(j, k); + } + + //re-build m_normals ... + for (int j = len - 1; j > 0; j--) + m_normals[j] = DoublePoint(-m_normals[j - 1].X, -m_normals[j - 1].Y); + m_normals[0] = DoublePoint(-m_normals[1].X, -m_normals[1].Y); + + k = len - 1; + for (int j = k - 1; j > 0; --j) OffsetPoint(j, k, node.m_jointype); + + if (node.m_endtype == etOpenButt) + { + pt1 = IntPoint((cInt)Round(m_srcPoly[0].X - m_normals[0].X * delta), + (cInt)Round(m_srcPoly[0].Y - m_normals[0].Y * delta)); + m_destPoly.push_back(pt1); + pt1 = IntPoint((cInt)Round(m_srcPoly[0].X + m_normals[0].X * delta), + (cInt)Round(m_srcPoly[0].Y + m_normals[0].Y * delta)); + m_destPoly.push_back(pt1); + } + else + { + k = 1; + m_sinA = 0; + if (node.m_endtype == etOpenSquare) + DoSquare(0, 1); + else + DoRound(0, 1); + } + m_destPolys.push_back(m_destPoly); + } + } +} +//------------------------------------------------------------------------------ + +void ClipperOffset::OffsetPoint(int j, int& k, JoinType jointype) +{ + //cross product ... + m_sinA = (m_normals[k].X * m_normals[j].Y - m_normals[j].X * m_normals[k].Y); + if (std::fabs(m_sinA * m_delta) < 1.0) + { + //dot product ... + double cosA = (m_normals[k].X * m_normals[j].X + m_normals[j].Y * m_normals[k].Y ); + if (cosA > 0) // angle => 0 degrees + { + m_destPoly.push_back(IntPoint(Round(m_srcPoly[j].X + m_normals[k].X * m_delta), + Round(m_srcPoly[j].Y + m_normals[k].Y * m_delta))); + return; + } + //else angle => 180 degrees + } + else if (m_sinA > 1.0) m_sinA = 1.0; + else if (m_sinA < -1.0) m_sinA = -1.0; + + if (m_sinA * m_delta < 0) + { + m_destPoly.push_back(IntPoint(Round(m_srcPoly[j].X + m_normals[k].X * m_delta), + Round(m_srcPoly[j].Y + m_normals[k].Y * m_delta))); + m_destPoly.push_back(m_srcPoly[j]); + m_destPoly.push_back(IntPoint(Round(m_srcPoly[j].X + m_normals[j].X * m_delta), + Round(m_srcPoly[j].Y + m_normals[j].Y * m_delta))); + } + else + switch (jointype) + { + case jtMiter: + { + double r = 1 + (m_normals[j].X * m_normals[k].X + + m_normals[j].Y * m_normals[k].Y); + if (r >= m_miterLim) DoMiter(j, k, r); else DoSquare(j, k); + break; + } + case jtSquare: DoSquare(j, k); break; + case jtRound: DoRound(j, k); break; + } + k = j; +} +//------------------------------------------------------------------------------ + +void ClipperOffset::DoSquare(int j, int k) +{ + double dx = std::tan(std::atan2(m_sinA, + m_normals[k].X * m_normals[j].X + m_normals[k].Y * m_normals[j].Y) / 4); + m_destPoly.push_back(IntPoint( + Round(m_srcPoly[j].X + m_delta * (m_normals[k].X - m_normals[k].Y * dx)), + Round(m_srcPoly[j].Y + m_delta * (m_normals[k].Y + m_normals[k].X * dx)))); + m_destPoly.push_back(IntPoint( + Round(m_srcPoly[j].X + m_delta * (m_normals[j].X + m_normals[j].Y * dx)), + Round(m_srcPoly[j].Y + m_delta * (m_normals[j].Y - m_normals[j].X * dx)))); +} +//------------------------------------------------------------------------------ + +void ClipperOffset::DoMiter(int j, int k, double r) +{ + double q = m_delta / r; + m_destPoly.push_back(IntPoint(Round(m_srcPoly[j].X + (m_normals[k].X + m_normals[j].X) * q), + Round(m_srcPoly[j].Y + (m_normals[k].Y + m_normals[j].Y) * q))); +} +//------------------------------------------------------------------------------ + +void ClipperOffset::DoRound(int j, int k) +{ + double a = std::atan2(m_sinA, + m_normals[k].X * m_normals[j].X + m_normals[k].Y * m_normals[j].Y); + int steps = std::max((int)Round(m_StepsPerRad * std::fabs(a)), 1); + + double X = m_normals[k].X, Y = m_normals[k].Y, X2; + for (int i = 0; i < steps; ++i) + { + m_destPoly.push_back(IntPoint( + Round(m_srcPoly[j].X + X * m_delta), + Round(m_srcPoly[j].Y + Y * m_delta))); + X2 = X; + X = X * m_cos - m_sin * Y; + Y = X2 * m_sin + Y * m_cos; + } + m_destPoly.push_back(IntPoint( + Round(m_srcPoly[j].X + m_normals[j].X * m_delta), + Round(m_srcPoly[j].Y + m_normals[j].Y * m_delta))); +} + +//------------------------------------------------------------------------------ +// Miscellaneous public functions +//------------------------------------------------------------------------------ + +void Clipper::DoSimplePolygons() +{ + PolyOutList::size_type i = 0; + while (i < m_PolyOuts.size()) + { + OutRec* outrec = m_PolyOuts[i++]; + OutPt* op = outrec->Pts; + if (!op || outrec->IsOpen) continue; + do //for each Pt in Polygon until duplicate found do ... + { + OutPt* op2 = op->Next; + while (op2 != outrec->Pts) + { + if ((op->Pt == op2->Pt) && op2->Next != op && op2->Prev != op) + { + //split the polygon into two ... + OutPt* op3 = op->Prev; + OutPt* op4 = op2->Prev; + op->Prev = op4; + op4->Next = op; + op2->Prev = op3; + op3->Next = op2; + + outrec->Pts = op; + OutRec* outrec2 = CreateOutRec(); + outrec2->Pts = op2; + UpdateOutPtIdxs(*outrec2); + if (Poly2ContainsPoly1(outrec2->Pts, outrec->Pts)) + { + //OutRec2 is contained by OutRec1 ... + outrec2->IsHole = !outrec->IsHole; + outrec2->FirstLeft = outrec; + if (m_UsingPolyTree) FixupFirstLefts2(outrec2, outrec); + } + else + if (Poly2ContainsPoly1(outrec->Pts, outrec2->Pts)) + { + //OutRec1 is contained by OutRec2 ... + outrec2->IsHole = outrec->IsHole; + outrec->IsHole = !outrec2->IsHole; + outrec2->FirstLeft = outrec->FirstLeft; + outrec->FirstLeft = outrec2; + if (m_UsingPolyTree) FixupFirstLefts2(outrec, outrec2); + } + else + { + //the 2 polygons are separate ... + outrec2->IsHole = outrec->IsHole; + outrec2->FirstLeft = outrec->FirstLeft; + if (m_UsingPolyTree) FixupFirstLefts1(outrec, outrec2); + } + op2 = op; //ie get ready for the Next iteration + } + op2 = op2->Next; + } + op = op->Next; + } + while (op != outrec->Pts); + } +} +//------------------------------------------------------------------------------ + +void ReversePath(Path& p) +{ + std::reverse(p.begin(), p.end()); +} +//------------------------------------------------------------------------------ + +void ReversePaths(Paths& p) +{ + for (Paths::size_type i = 0; i < p.size(); ++i) + ReversePath(p[i]); +} +//------------------------------------------------------------------------------ + +void SimplifyPolygon(const Path &in_poly, Paths &out_polys, PolyFillType fillType) +{ + Clipper c; + c.StrictlySimple(true); + c.AddPath(in_poly, ptSubject, true); + c.Execute(ctUnion, out_polys, fillType, fillType); +} +//------------------------------------------------------------------------------ + +void SimplifyPolygons(const Paths &in_polys, Paths &out_polys, PolyFillType fillType) +{ + Clipper c; + c.StrictlySimple(true); + c.AddPaths(in_polys, ptSubject, true); + c.Execute(ctUnion, out_polys, fillType, fillType); +} +//------------------------------------------------------------------------------ + +void SimplifyPolygons(Paths &polys, PolyFillType fillType) +{ + SimplifyPolygons(polys, polys, fillType); +} +//------------------------------------------------------------------------------ + +inline double DistanceSqrd(const IntPoint& pt1, const IntPoint& pt2) +{ + double Dx = ((double)pt1.X - pt2.X); + double dy = ((double)pt1.Y - pt2.Y); + return (Dx*Dx + dy*dy); +} +//------------------------------------------------------------------------------ + +double DistanceFromLineSqrd( + const IntPoint& pt, const IntPoint& ln1, const IntPoint& ln2) +{ + //The equation of a line in general form (Ax + By + C = 0) + //given 2 points (x,y) & (x,y) is ... + //(y - y)x + (x - x)y + (y - y)x - (x - x)y = 0 + //A = (y - y); B = (x - x); C = (y - y)x - (x - x)y + //perpendicular distance of point (x,y) = (Ax + By + C)/Sqrt(A + B) + //see http://en.wikipedia.org/wiki/Perpendicular_distance + double A = double(ln1.Y - ln2.Y); + double B = double(ln2.X - ln1.X); + double C = A * ln1.X + B * ln1.Y; + C = A * pt.X + B * pt.Y - C; + return (C * C) / (A * A + B * B); +} +//--------------------------------------------------------------------------- + +bool SlopesNearCollinear(const IntPoint& pt1, + const IntPoint& pt2, const IntPoint& pt3, double distSqrd) +{ + //this function is more accurate when the point that's geometrically + //between the other 2 points is the one that's tested for distance. + //ie makes it more likely to pick up 'spikes' ... + if (Abs(pt1.X - pt2.X) > Abs(pt1.Y - pt2.Y)) + { + if ((pt1.X > pt2.X) == (pt1.X < pt3.X)) + return DistanceFromLineSqrd(pt1, pt2, pt3) < distSqrd; + else if ((pt2.X > pt1.X) == (pt2.X < pt3.X)) + return DistanceFromLineSqrd(pt2, pt1, pt3) < distSqrd; + else + return DistanceFromLineSqrd(pt3, pt1, pt2) < distSqrd; + } + else + { + if ((pt1.Y > pt2.Y) == (pt1.Y < pt3.Y)) + return DistanceFromLineSqrd(pt1, pt2, pt3) < distSqrd; + else if ((pt2.Y > pt1.Y) == (pt2.Y < pt3.Y)) + return DistanceFromLineSqrd(pt2, pt1, pt3) < distSqrd; + else + return DistanceFromLineSqrd(pt3, pt1, pt2) < distSqrd; + } +} +//------------------------------------------------------------------------------ + +bool PointsAreClose(IntPoint pt1, IntPoint pt2, double distSqrd) +{ + double Dx = (double)pt1.X - pt2.X; + double dy = (double)pt1.Y - pt2.Y; + return ((Dx * Dx) + (dy * dy) <= distSqrd); +} +//------------------------------------------------------------------------------ + +OutPt* ExcludeOp(OutPt* op) +{ + OutPt* result = op->Prev; + result->Next = op->Next; + op->Next->Prev = result; + result->Idx = 0; + return result; +} +//------------------------------------------------------------------------------ + +void CleanPolygon(const Path& in_poly, Path& out_poly, double distance) +{ + //distance = proximity in units/pixels below which vertices + //will be stripped. Default ~= sqrt(2). + + size_t size = in_poly.size(); + + if (size == 0) + { + out_poly.clear(); + return; + } + + OutPt* outPts = new OutPt[size]; + for (size_t i = 0; i < size; ++i) + { + outPts[i].Pt = in_poly[i]; + outPts[i].Next = &outPts[(i + 1) % size]; + outPts[i].Next->Prev = &outPts[i]; + outPts[i].Idx = 0; + } + + double distSqrd = distance * distance; + OutPt* op = &outPts[0]; + while (op->Idx == 0 && op->Next != op->Prev) + { + if (PointsAreClose(op->Pt, op->Prev->Pt, distSqrd)) + { + op = ExcludeOp(op); + size--; + } + else if (PointsAreClose(op->Prev->Pt, op->Next->Pt, distSqrd)) + { + ExcludeOp(op->Next); + op = ExcludeOp(op); + size -= 2; + } + else if (SlopesNearCollinear(op->Prev->Pt, op->Pt, op->Next->Pt, distSqrd)) + { + op = ExcludeOp(op); + size--; + } + else + { + op->Idx = 1; + op = op->Next; + } + } + + if (size < 3) size = 0; + out_poly.resize(size); + for (size_t i = 0; i < size; ++i) + { + out_poly[i] = op->Pt; + op = op->Next; + } + delete [] outPts; +} +//------------------------------------------------------------------------------ + +void CleanPolygon(Path& poly, double distance) +{ + CleanPolygon(poly, poly, distance); +} +//------------------------------------------------------------------------------ + +void CleanPolygons(const Paths& in_polys, Paths& out_polys, double distance) +{ + out_polys.resize(in_polys.size()); + for (Paths::size_type i = 0; i < in_polys.size(); ++i) + CleanPolygon(in_polys[i], out_polys[i], distance); +} +//------------------------------------------------------------------------------ + +void CleanPolygons(Paths& polys, double distance) +{ + CleanPolygons(polys, polys, distance); +} +//------------------------------------------------------------------------------ + +void Minkowski(const Path& poly, const Path& path, + Paths& solution, bool isSum, bool isClosed) +{ + int delta = (isClosed ? 1 : 0); + size_t polyCnt = poly.size(); + size_t pathCnt = path.size(); + Paths pp; + pp.reserve(pathCnt); + if (isSum) + for (size_t i = 0; i < pathCnt; ++i) + { + Path p; + p.reserve(polyCnt); + for (size_t j = 0; j < poly.size(); ++j) + p.push_back(IntPoint(path[i].X + poly[j].X, path[i].Y + poly[j].Y)); + pp.push_back(p); + } + else + for (size_t i = 0; i < pathCnt; ++i) + { + Path p; + p.reserve(polyCnt); + for (size_t j = 0; j < poly.size(); ++j) + p.push_back(IntPoint(path[i].X - poly[j].X, path[i].Y - poly[j].Y)); + pp.push_back(p); + } + + solution.clear(); + solution.reserve((pathCnt + delta) * (polyCnt + 1)); + for (size_t i = 0; i < pathCnt - 1 + delta; ++i) + for (size_t j = 0; j < polyCnt; ++j) + { + Path quad; + quad.reserve(4); + quad.push_back(pp[i % pathCnt][j % polyCnt]); + quad.push_back(pp[(i + 1) % pathCnt][j % polyCnt]); + quad.push_back(pp[(i + 1) % pathCnt][(j + 1) % polyCnt]); + quad.push_back(pp[i % pathCnt][(j + 1) % polyCnt]); + if (!Orientation(quad)) ReversePath(quad); + solution.push_back(quad); + } +} +//------------------------------------------------------------------------------ + +void MinkowskiSum(const Path& pattern, const Path& path, Paths& solution, bool pathIsClosed) +{ + Minkowski(pattern, path, solution, true, pathIsClosed); + Clipper c; + c.AddPaths(solution, ptSubject, true); + c.Execute(ctUnion, solution, pftNonZero, pftNonZero); +} +//------------------------------------------------------------------------------ + +void TranslatePath(const Path& input, Path& output, const IntPoint delta) +{ + //precondition: input != output + output.resize(input.size()); + for (size_t i = 0; i < input.size(); ++i) + output[i] = IntPoint(input[i].X + delta.X, input[i].Y + delta.Y); +} +//------------------------------------------------------------------------------ + +void MinkowskiSum(const Path& pattern, const Paths& paths, Paths& solution, bool pathIsClosed) +{ + Clipper c; + for (size_t i = 0; i < paths.size(); ++i) + { + Paths tmp; + Minkowski(pattern, paths[i], tmp, true, pathIsClosed); + c.AddPaths(tmp, ptSubject, true); + if (pathIsClosed) + { + Path tmp2; + TranslatePath(paths[i], tmp2, pattern[0]); + c.AddPath(tmp2, ptClip, true); + } + } + c.Execute(ctUnion, solution, pftNonZero, pftNonZero); +} +//------------------------------------------------------------------------------ + +void MinkowskiDiff(const Path& poly1, const Path& poly2, Paths& solution) +{ + Minkowski(poly1, poly2, solution, false, true); + Clipper c; + c.AddPaths(solution, ptSubject, true); + c.Execute(ctUnion, solution, pftNonZero, pftNonZero); +} +//------------------------------------------------------------------------------ + +enum NodeType {ntAny, ntOpen, ntClosed}; + +void AddPolyNodeToPaths(const PolyNode& polynode, NodeType nodetype, Paths& paths) +{ + bool match = true; + if (nodetype == ntClosed) match = !polynode.IsOpen(); + else if (nodetype == ntOpen) return; + + if (!polynode.Contour.empty() && match) + paths.push_back(polynode.Contour); + for (int i = 0; i < polynode.ChildCount(); ++i) + AddPolyNodeToPaths(*polynode.Childs[i], nodetype, paths); +} +//------------------------------------------------------------------------------ + +void PolyTreeToPaths(const PolyTree& polytree, Paths& paths) +{ + paths.resize(0); + paths.reserve(polytree.Total()); + AddPolyNodeToPaths(polytree, ntAny, paths); +} +//------------------------------------------------------------------------------ + +void ClosedPathsFromPolyTree(const PolyTree& polytree, Paths& paths) +{ + paths.resize(0); + paths.reserve(polytree.Total()); + AddPolyNodeToPaths(polytree, ntClosed, paths); +} +//------------------------------------------------------------------------------ + +void OpenPathsFromPolyTree(PolyTree& polytree, Paths& paths) +{ + paths.resize(0); + paths.reserve(polytree.Total()); + //Open paths are top level only, so ... + for (int i = 0; i < polytree.ChildCount(); ++i) + if (polytree.Childs[i]->IsOpen()) + paths.push_back(polytree.Childs[i]->Contour); +} +//------------------------------------------------------------------------------ + +std::ostream& operator <<(std::ostream &s, const IntPoint &p) +{ + s << "(" << p.X << "," << p.Y << ")"; + return s; +} +//------------------------------------------------------------------------------ + +std::ostream& operator <<(std::ostream &s, const Path &p) +{ + if (p.empty()) return s; + Path::size_type last = p.size() -1; + for (Path::size_type i = 0; i < last; i++) + s << "(" << p[i].X << "," << p[i].Y << "), "; + s << "(" << p[last].X << "," << p[last].Y << ")\n"; + return s; +} +//------------------------------------------------------------------------------ + +std::ostream& operator <<(std::ostream &s, const Paths &p) +{ + for (Paths::size_type i = 0; i < p.size(); i++) + s << p[i]; + s << "\n"; + return s; +} +//------------------------------------------------------------------------------ + +} //ClipperLib namespace diff --git a/ptocr/postprocess/piexlmerge/include/clipper/clipper.hpp b/ptocr/postprocess/piexlmerge/include/clipper/clipper.hpp new file mode 100644 index 0000000..1c38371 --- /dev/null +++ b/ptocr/postprocess/piexlmerge/include/clipper/clipper.hpp @@ -0,0 +1,404 @@ +/******************************************************************************* +* * +* Author : Angus Johnson * +* Version : 6.4.0 * +* Date : 2 July 2015 * +* Website : http://www.angusj.com * +* Copyright : Angus Johnson 2010-2015 * +* * +* License: * +* Use, modification & distribution is subject to Boost Software License Ver 1. * +* http://www.boost.org/LICENSE_1_0.txt * +* * +* Attributions: * +* The code in this library is an extension of Bala Vatti's clipping algorithm: * +* "A generic solution to polygon clipping" * +* Communications of the ACM, Vol 35, Issue 7 (July 1992) pp 56-63. * +* http://portal.acm.org/citation.cfm?id=129906 * +* * +* Computer graphics and geometric modeling: implementation and algorithms * +* By Max K. Agoston * +* Springer; 1 edition (January 4, 2005) * +* http://books.google.com/books?q=vatti+clipping+agoston * +* * +* See also: * +* "Polygon Offsetting by Computing Winding Numbers" * +* Paper no. DETC2005-85513 pp. 565-575 * +* ASME 2005 International Design Engineering Technical Conferences * +* and Computers and Information in Engineering Conference (IDETC/CIE2005) * +* September 24-28, 2005 , Long Beach, California, USA * +* http://www.me.berkeley.edu/~mcmains/pubs/DAC05OffsetPolygon.pdf * +* * +*******************************************************************************/ + +#ifndef clipper_hpp +#define clipper_hpp + +#define CLIPPER_VERSION "6.2.6" + +//use_int32: When enabled 32bit ints are used instead of 64bit ints. This +//improve performance but coordinate values are limited to the range +/- 46340 +//#define use_int32 + +//use_xyz: adds a Z member to IntPoint. Adds a minor cost to perfomance. +//#define use_xyz + +//use_lines: Enables line clipping. Adds a very minor cost to performance. +#define use_lines + +//use_deprecated: Enables temporary support for the obsolete functions +//#define use_deprecated + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace ClipperLib { + +enum ClipType { ctIntersection, ctUnion, ctDifference, ctXor }; +enum PolyType { ptSubject, ptClip }; +//By far the most widely used winding rules for polygon filling are +//EvenOdd & NonZero (GDI, GDI+, XLib, OpenGL, Cairo, AGG, Quartz, SVG, Gr32) +//Others rules include Positive, Negative and ABS_GTR_EQ_TWO (only in OpenGL) +//see http://glprogramming.com/red/chapter11.html +enum PolyFillType { pftEvenOdd, pftNonZero, pftPositive, pftNegative }; + +#ifdef use_int32 + typedef int cInt; + static cInt const loRange = 0x7FFF; + static cInt const hiRange = 0x7FFF; +#else + typedef signed long long cInt; + static cInt const loRange = 0x3FFFFFFF; + static cInt const hiRange = 0x3FFFFFFFFFFFFFFFLL; + typedef signed long long long64; //used by Int128 class + typedef unsigned long long ulong64; + +#endif + +struct IntPoint { + cInt X; + cInt Y; +#ifdef use_xyz + cInt Z; + IntPoint(cInt x = 0, cInt y = 0, cInt z = 0): X(x), Y(y), Z(z) {}; +#else + IntPoint(cInt x = 0, cInt y = 0): X(x), Y(y) {}; +#endif + + friend inline bool operator== (const IntPoint& a, const IntPoint& b) + { + return a.X == b.X && a.Y == b.Y; + } + friend inline bool operator!= (const IntPoint& a, const IntPoint& b) + { + return a.X != b.X || a.Y != b.Y; + } +}; +//------------------------------------------------------------------------------ + +typedef std::vector< IntPoint > Path; +typedef std::vector< Path > Paths; + +inline Path& operator <<(Path& poly, const IntPoint& p) {poly.push_back(p); return poly;} +inline Paths& operator <<(Paths& polys, const Path& p) {polys.push_back(p); return polys;} + +std::ostream& operator <<(std::ostream &s, const IntPoint &p); +std::ostream& operator <<(std::ostream &s, const Path &p); +std::ostream& operator <<(std::ostream &s, const Paths &p); + +struct DoublePoint +{ + double X; + double Y; + DoublePoint(double x = 0, double y = 0) : X(x), Y(y) {} + DoublePoint(IntPoint ip) : X((double)ip.X), Y((double)ip.Y) {} +}; +//------------------------------------------------------------------------------ + +#ifdef use_xyz +typedef void (*ZFillCallback)(IntPoint& e1bot, IntPoint& e1top, IntPoint& e2bot, IntPoint& e2top, IntPoint& pt); +#endif + +enum InitOptions {ioReverseSolution = 1, ioStrictlySimple = 2, ioPreserveCollinear = 4}; +enum JoinType {jtSquare, jtRound, jtMiter}; +enum EndType {etClosedPolygon, etClosedLine, etOpenButt, etOpenSquare, etOpenRound}; + +class PolyNode; +typedef std::vector< PolyNode* > PolyNodes; + +class PolyNode +{ +public: + PolyNode(); + virtual ~PolyNode(){}; + Path Contour; + PolyNodes Childs; + PolyNode* Parent; + PolyNode* GetNext() const; + bool IsHole() const; + bool IsOpen() const; + int ChildCount() const; +private: + unsigned Index; //node index in Parent.Childs + bool m_IsOpen; + JoinType m_jointype; + EndType m_endtype; + PolyNode* GetNextSiblingUp() const; + void AddChild(PolyNode& child); + friend class Clipper; //to access Index + friend class ClipperOffset; +}; + +class PolyTree: public PolyNode +{ +public: + ~PolyTree(){Clear();}; + PolyNode* GetFirst() const; + void Clear(); + int Total() const; +private: + PolyNodes AllNodes; + friend class Clipper; //to access AllNodes +}; + +bool Orientation(const Path &poly); +double Area(const Path &poly); +int PointInPolygon(const IntPoint &pt, const Path &path); + +void SimplifyPolygon(const Path &in_poly, Paths &out_polys, PolyFillType fillType = pftEvenOdd); +void SimplifyPolygons(const Paths &in_polys, Paths &out_polys, PolyFillType fillType = pftEvenOdd); +void SimplifyPolygons(Paths &polys, PolyFillType fillType = pftEvenOdd); + +void CleanPolygon(const Path& in_poly, Path& out_poly, double distance = 1.415); +void CleanPolygon(Path& poly, double distance = 1.415); +void CleanPolygons(const Paths& in_polys, Paths& out_polys, double distance = 1.415); +void CleanPolygons(Paths& polys, double distance = 1.415); + +void MinkowskiSum(const Path& pattern, const Path& path, Paths& solution, bool pathIsClosed); +void MinkowskiSum(const Path& pattern, const Paths& paths, Paths& solution, bool pathIsClosed); +void MinkowskiDiff(const Path& poly1, const Path& poly2, Paths& solution); + +void PolyTreeToPaths(const PolyTree& polytree, Paths& paths); +void ClosedPathsFromPolyTree(const PolyTree& polytree, Paths& paths); +void OpenPathsFromPolyTree(PolyTree& polytree, Paths& paths); + +void ReversePath(Path& p); +void ReversePaths(Paths& p); + +struct IntRect { cInt left; cInt top; cInt right; cInt bottom; }; + +//enums that are used internally ... +enum EdgeSide { esLeft = 1, esRight = 2}; + +//forward declarations (for stuff used internally) ... +struct TEdge; +struct IntersectNode; +struct LocalMinimum; +struct OutPt; +struct OutRec; +struct Join; + +typedef std::vector < OutRec* > PolyOutList; +typedef std::vector < TEdge* > EdgeList; +typedef std::vector < Join* > JoinList; +typedef std::vector < IntersectNode* > IntersectList; + +//------------------------------------------------------------------------------ + +//ClipperBase is the ancestor to the Clipper class. It should not be +//instantiated directly. This class simply abstracts the conversion of sets of +//polygon coordinates into edge objects that are stored in a LocalMinima list. +class ClipperBase +{ +public: + ClipperBase(); + virtual ~ClipperBase(); + virtual bool AddPath(const Path &pg, PolyType PolyTyp, bool Closed); + bool AddPaths(const Paths &ppg, PolyType PolyTyp, bool Closed); + virtual void Clear(); + IntRect GetBounds(); + bool PreserveCollinear() {return m_PreserveCollinear;}; + void PreserveCollinear(bool value) {m_PreserveCollinear = value;}; +protected: + void DisposeLocalMinimaList(); + TEdge* AddBoundsToLML(TEdge *e, bool IsClosed); + virtual void Reset(); + TEdge* ProcessBound(TEdge* E, bool IsClockwise); + void InsertScanbeam(const cInt Y); + bool PopScanbeam(cInt &Y); + bool LocalMinimaPending(); + bool PopLocalMinima(cInt Y, const LocalMinimum *&locMin); + OutRec* CreateOutRec(); + void DisposeAllOutRecs(); + void DisposeOutRec(PolyOutList::size_type index); + void SwapPositionsInAEL(TEdge *edge1, TEdge *edge2); + void DeleteFromAEL(TEdge *e); + void UpdateEdgeIntoAEL(TEdge *&e); + + typedef std::vector MinimaList; + MinimaList::iterator m_CurrentLM; + MinimaList m_MinimaList; + + bool m_UseFullRange; + EdgeList m_edges; + bool m_PreserveCollinear; + bool m_HasOpenPaths; + PolyOutList m_PolyOuts; + TEdge *m_ActiveEdges; + + typedef std::priority_queue ScanbeamList; + ScanbeamList m_Scanbeam; +}; +//------------------------------------------------------------------------------ + +class Clipper : public virtual ClipperBase +{ +public: + Clipper(int initOptions = 0); + bool Execute(ClipType clipType, + Paths &solution, + PolyFillType fillType = pftEvenOdd); + bool Execute(ClipType clipType, + Paths &solution, + PolyFillType subjFillType, + PolyFillType clipFillType); + bool Execute(ClipType clipType, + PolyTree &polytree, + PolyFillType fillType = pftEvenOdd); + bool Execute(ClipType clipType, + PolyTree &polytree, + PolyFillType subjFillType, + PolyFillType clipFillType); + bool ReverseSolution() { return m_ReverseOutput; }; + void ReverseSolution(bool value) {m_ReverseOutput = value;}; + bool StrictlySimple() {return m_StrictSimple;}; + void StrictlySimple(bool value) {m_StrictSimple = value;}; + //set the callback function for z value filling on intersections (otherwise Z is 0) +#ifdef use_xyz + void ZFillFunction(ZFillCallback zFillFunc); +#endif +protected: + virtual bool ExecuteInternal(); +private: + JoinList m_Joins; + JoinList m_GhostJoins; + IntersectList m_IntersectList; + ClipType m_ClipType; + typedef std::list MaximaList; + MaximaList m_Maxima; + TEdge *m_SortedEdges; + bool m_ExecuteLocked; + PolyFillType m_ClipFillType; + PolyFillType m_SubjFillType; + bool m_ReverseOutput; + bool m_UsingPolyTree; + bool m_StrictSimple; +#ifdef use_xyz + ZFillCallback m_ZFill; //custom callback +#endif + void SetWindingCount(TEdge& edge); + bool IsEvenOddFillType(const TEdge& edge) const; + bool IsEvenOddAltFillType(const TEdge& edge) const; + void InsertLocalMinimaIntoAEL(const cInt botY); + void InsertEdgeIntoAEL(TEdge *edge, TEdge* startEdge); + void AddEdgeToSEL(TEdge *edge); + bool PopEdgeFromSEL(TEdge *&edge); + void CopyAELToSEL(); + void DeleteFromSEL(TEdge *e); + void SwapPositionsInSEL(TEdge *edge1, TEdge *edge2); + bool IsContributing(const TEdge& edge) const; + bool IsTopHorz(const cInt XPos); + void DoMaxima(TEdge *e); + void ProcessHorizontals(); + void ProcessHorizontal(TEdge *horzEdge); + void AddLocalMaxPoly(TEdge *e1, TEdge *e2, const IntPoint &pt); + OutPt* AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &pt); + OutRec* GetOutRec(int idx); + void AppendPolygon(TEdge *e1, TEdge *e2); + void IntersectEdges(TEdge *e1, TEdge *e2, IntPoint &pt); + OutPt* AddOutPt(TEdge *e, const IntPoint &pt); + OutPt* GetLastOutPt(TEdge *e); + bool ProcessIntersections(const cInt topY); + void BuildIntersectList(const cInt topY); + void ProcessIntersectList(); + void ProcessEdgesAtTopOfScanbeam(const cInt topY); + void BuildResult(Paths& polys); + void BuildResult2(PolyTree& polytree); + void SetHoleState(TEdge *e, OutRec *outrec); + void DisposeIntersectNodes(); + bool FixupIntersectionOrder(); + void FixupOutPolygon(OutRec &outrec); + void FixupOutPolyline(OutRec &outrec); + bool IsHole(TEdge *e); + bool FindOwnerFromSplitRecs(OutRec &outRec, OutRec *&currOrfl); + void FixHoleLinkage(OutRec &outrec); + void AddJoin(OutPt *op1, OutPt *op2, const IntPoint offPt); + void ClearJoins(); + void ClearGhostJoins(); + void AddGhostJoin(OutPt *op, const IntPoint offPt); + bool JoinPoints(Join *j, OutRec* outRec1, OutRec* outRec2); + void JoinCommonEdges(); + void DoSimplePolygons(); + void FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec); + void FixupFirstLefts2(OutRec* InnerOutRec, OutRec* OuterOutRec); + void FixupFirstLefts3(OutRec* OldOutRec, OutRec* NewOutRec); +#ifdef use_xyz + void SetZ(IntPoint& pt, TEdge& e1, TEdge& e2); +#endif +}; +//------------------------------------------------------------------------------ + +class ClipperOffset +{ +public: + ClipperOffset(double miterLimit = 2.0, double roundPrecision = 0.25); + ~ClipperOffset(); + void AddPath(const Path& path, JoinType joinType, EndType endType); + void AddPaths(const Paths& paths, JoinType joinType, EndType endType); + void Execute(Paths& solution, double delta); + void Execute(PolyTree& solution, double delta); + void Clear(); + double MiterLimit; + double ArcTolerance; +private: + Paths m_destPolys; + Path m_srcPoly; + Path m_destPoly; + std::vector m_normals; + double m_delta, m_sinA, m_sin, m_cos; + double m_miterLim, m_StepsPerRad; + IntPoint m_lowest; + PolyNode m_polyNodes; + + void FixOrientations(); + void DoOffset(double delta); + void OffsetPoint(int j, int& k, JoinType jointype); + void DoSquare(int j, int k); + void DoMiter(int j, int k, double r); + void DoRound(int j, int k); +}; +//------------------------------------------------------------------------------ + +class clipperException : public std::exception +{ + public: + clipperException(const char* description): m_descr(description) {} + virtual ~clipperException() throw() {} + virtual const char* what() const throw() {return m_descr.c_str();} + private: + std::string m_descr; +}; +//------------------------------------------------------------------------------ + +} //ClipperLib namespace + +#endif //clipper_hpp + + diff --git a/ptocr/postprocess/piexlmerge/include/pybind11/attr.h b/ptocr/postprocess/piexlmerge/include/pybind11/attr.h new file mode 100644 index 0000000..8732cfe --- /dev/null +++ b/ptocr/postprocess/piexlmerge/include/pybind11/attr.h @@ -0,0 +1,492 @@ +/* + pybind11/attr.h: Infrastructure for processing custom + type and function attributes + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "cast.h" + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) + +/// \addtogroup annotations +/// @{ + +/// Annotation for methods +struct is_method { handle class_; is_method(const handle &c) : class_(c) { } }; + +/// Annotation for operators +struct is_operator { }; + +/// Annotation for parent scope +struct scope { handle value; scope(const handle &s) : value(s) { } }; + +/// Annotation for documentation +struct doc { const char *value; doc(const char *value) : value(value) { } }; + +/// Annotation for function names +struct name { const char *value; name(const char *value) : value(value) { } }; + +/// Annotation indicating that a function is an overload associated with a given "sibling" +struct sibling { handle value; sibling(const handle &value) : value(value.ptr()) { } }; + +/// Annotation indicating that a class derives from another given type +template struct base { + PYBIND11_DEPRECATED("base() was deprecated in favor of specifying 'T' as a template argument to class_") + base() { } +}; + +/// Keep patient alive while nurse lives +template struct keep_alive { }; + +/// Annotation indicating that a class is involved in a multiple inheritance relationship +struct multiple_inheritance { }; + +/// Annotation which enables dynamic attributes, i.e. adds `__dict__` to a class +struct dynamic_attr { }; + +/// Annotation which enables the buffer protocol for a type +struct buffer_protocol { }; + +/// Annotation which requests that a special metaclass is created for a type +struct metaclass { + handle value; + + PYBIND11_DEPRECATED("py::metaclass() is no longer required. It's turned on by default now.") + metaclass() {} + + /// Override pybind11's default metaclass + explicit metaclass(handle value) : value(value) { } +}; + +/// Annotation that marks a class as local to the module: +struct module_local { const bool value; constexpr module_local(bool v = true) : value(v) { } }; + +/// Annotation to mark enums as an arithmetic type +struct arithmetic { }; + +/** \rst + A call policy which places one or more guard variables (``Ts...``) around the function call. + + For example, this definition: + + .. code-block:: cpp + + m.def("foo", foo, py::call_guard()); + + is equivalent to the following pseudocode: + + .. code-block:: cpp + + m.def("foo", [](args...) { + T scope_guard; + return foo(args...); // forwarded arguments + }); + \endrst */ +template struct call_guard; + +template <> struct call_guard<> { using type = detail::void_type; }; + +template +struct call_guard { + static_assert(std::is_default_constructible::value, + "The guard type must be default constructible"); + + using type = T; +}; + +template +struct call_guard { + struct type { + T guard{}; // Compose multiple guard types with left-to-right default-constructor order + typename call_guard::type next{}; + }; +}; + +/// @} annotations + +NAMESPACE_BEGIN(detail) +/* Forward declarations */ +enum op_id : int; +enum op_type : int; +struct undefined_t; +template struct op_; +inline void keep_alive_impl(size_t Nurse, size_t Patient, function_call &call, handle ret); + +/// Internal data structure which holds metadata about a keyword argument +struct argument_record { + const char *name; ///< Argument name + const char *descr; ///< Human-readable version of the argument value + handle value; ///< Associated Python object + bool convert : 1; ///< True if the argument is allowed to convert when loading + bool none : 1; ///< True if None is allowed when loading + + argument_record(const char *name, const char *descr, handle value, bool convert, bool none) + : name(name), descr(descr), value(value), convert(convert), none(none) { } +}; + +/// Internal data structure which holds metadata about a bound function (signature, overloads, etc.) +struct function_record { + function_record() + : is_constructor(false), is_new_style_constructor(false), is_stateless(false), + is_operator(false), has_args(false), has_kwargs(false), is_method(false) { } + + /// Function name + char *name = nullptr; /* why no C++ strings? They generate heavier code.. */ + + // User-specified documentation string + char *doc = nullptr; + + /// Human-readable version of the function signature + char *signature = nullptr; + + /// List of registered keyword arguments + std::vector args; + + /// Pointer to lambda function which converts arguments and performs the actual call + handle (*impl) (function_call &) = nullptr; + + /// Storage for the wrapped function pointer and captured data, if any + void *data[3] = { }; + + /// Pointer to custom destructor for 'data' (if needed) + void (*free_data) (function_record *ptr) = nullptr; + + /// Return value policy associated with this function + return_value_policy policy = return_value_policy::automatic; + + /// True if name == '__init__' + bool is_constructor : 1; + + /// True if this is a new-style `__init__` defined in `detail/init.h` + bool is_new_style_constructor : 1; + + /// True if this is a stateless function pointer + bool is_stateless : 1; + + /// True if this is an operator (__add__), etc. + bool is_operator : 1; + + /// True if the function has a '*args' argument + bool has_args : 1; + + /// True if the function has a '**kwargs' argument + bool has_kwargs : 1; + + /// True if this is a method + bool is_method : 1; + + /// Number of arguments (including py::args and/or py::kwargs, if present) + std::uint16_t nargs; + + /// Python method object + PyMethodDef *def = nullptr; + + /// Python handle to the parent scope (a class or a module) + handle scope; + + /// Python handle to the sibling function representing an overload chain + handle sibling; + + /// Pointer to next overload + function_record *next = nullptr; +}; + +/// Special data structure which (temporarily) holds metadata about a bound class +struct type_record { + PYBIND11_NOINLINE type_record() + : multiple_inheritance(false), dynamic_attr(false), buffer_protocol(false), module_local(false) { } + + /// Handle to the parent scope + handle scope; + + /// Name of the class + const char *name = nullptr; + + // Pointer to RTTI type_info data structure + const std::type_info *type = nullptr; + + /// How large is the underlying C++ type? + size_t type_size = 0; + + /// What is the alignment of the underlying C++ type? + size_t type_align = 0; + + /// How large is the type's holder? + size_t holder_size = 0; + + /// The global operator new can be overridden with a class-specific variant + void *(*operator_new)(size_t) = nullptr; + + /// Function pointer to class_<..>::init_instance + void (*init_instance)(instance *, const void *) = nullptr; + + /// Function pointer to class_<..>::dealloc + void (*dealloc)(detail::value_and_holder &) = nullptr; + + /// List of base classes of the newly created type + list bases; + + /// Optional docstring + const char *doc = nullptr; + + /// Custom metaclass (optional) + handle metaclass; + + /// Multiple inheritance marker + bool multiple_inheritance : 1; + + /// Does the class manage a __dict__? + bool dynamic_attr : 1; + + /// Does the class implement the buffer protocol? + bool buffer_protocol : 1; + + /// Is the default (unique_ptr) holder type used? + bool default_holder : 1; + + /// Is the class definition local to the module shared object? + bool module_local : 1; + + PYBIND11_NOINLINE void add_base(const std::type_info &base, void *(*caster)(void *)) { + auto base_info = detail::get_type_info(base, false); + if (!base_info) { + std::string tname(base.name()); + detail::clean_type_id(tname); + pybind11_fail("generic_type: type \"" + std::string(name) + + "\" referenced unknown base type \"" + tname + "\""); + } + + if (default_holder != base_info->default_holder) { + std::string tname(base.name()); + detail::clean_type_id(tname); + pybind11_fail("generic_type: type \"" + std::string(name) + "\" " + + (default_holder ? "does not have" : "has") + + " a non-default holder type while its base \"" + tname + "\" " + + (base_info->default_holder ? "does not" : "does")); + } + + bases.append((PyObject *) base_info->type); + + if (base_info->type->tp_dictoffset != 0) + dynamic_attr = true; + + if (caster) + base_info->implicit_casts.emplace_back(type, caster); + } +}; + +inline function_call::function_call(const function_record &f, handle p) : + func(f), parent(p) { + args.reserve(f.nargs); + args_convert.reserve(f.nargs); +} + +/// Tag for a new-style `__init__` defined in `detail/init.h` +struct is_new_style_constructor { }; + +/** + * Partial template specializations to process custom attributes provided to + * cpp_function_ and class_. These are either used to initialize the respective + * fields in the type_record and function_record data structures or executed at + * runtime to deal with custom call policies (e.g. keep_alive). + */ +template struct process_attribute; + +template struct process_attribute_default { + /// Default implementation: do nothing + static void init(const T &, function_record *) { } + static void init(const T &, type_record *) { } + static void precall(function_call &) { } + static void postcall(function_call &, handle) { } +}; + +/// Process an attribute specifying the function's name +template <> struct process_attribute : process_attribute_default { + static void init(const name &n, function_record *r) { r->name = const_cast(n.value); } +}; + +/// Process an attribute specifying the function's docstring +template <> struct process_attribute : process_attribute_default { + static void init(const doc &n, function_record *r) { r->doc = const_cast(n.value); } +}; + +/// Process an attribute specifying the function's docstring (provided as a C-style string) +template <> struct process_attribute : process_attribute_default { + static void init(const char *d, function_record *r) { r->doc = const_cast(d); } + static void init(const char *d, type_record *r) { r->doc = const_cast(d); } +}; +template <> struct process_attribute : process_attribute { }; + +/// Process an attribute indicating the function's return value policy +template <> struct process_attribute : process_attribute_default { + static void init(const return_value_policy &p, function_record *r) { r->policy = p; } +}; + +/// Process an attribute which indicates that this is an overloaded function associated with a given sibling +template <> struct process_attribute : process_attribute_default { + static void init(const sibling &s, function_record *r) { r->sibling = s.value; } +}; + +/// Process an attribute which indicates that this function is a method +template <> struct process_attribute : process_attribute_default { + static void init(const is_method &s, function_record *r) { r->is_method = true; r->scope = s.class_; } +}; + +/// Process an attribute which indicates the parent scope of a method +template <> struct process_attribute : process_attribute_default { + static void init(const scope &s, function_record *r) { r->scope = s.value; } +}; + +/// Process an attribute which indicates that this function is an operator +template <> struct process_attribute : process_attribute_default { + static void init(const is_operator &, function_record *r) { r->is_operator = true; } +}; + +template <> struct process_attribute : process_attribute_default { + static void init(const is_new_style_constructor &, function_record *r) { r->is_new_style_constructor = true; } +}; + +/// Process a keyword argument attribute (*without* a default value) +template <> struct process_attribute : process_attribute_default { + static void init(const arg &a, function_record *r) { + if (r->is_method && r->args.empty()) + r->args.emplace_back("self", nullptr, handle(), true /*convert*/, false /*none not allowed*/); + r->args.emplace_back(a.name, nullptr, handle(), !a.flag_noconvert, a.flag_none); + } +}; + +/// Process a keyword argument attribute (*with* a default value) +template <> struct process_attribute : process_attribute_default { + static void init(const arg_v &a, function_record *r) { + if (r->is_method && r->args.empty()) + r->args.emplace_back("self", nullptr /*descr*/, handle() /*parent*/, true /*convert*/, false /*none not allowed*/); + + if (!a.value) { +#if !defined(NDEBUG) + std::string descr("'"); + if (a.name) descr += std::string(a.name) + ": "; + descr += a.type + "'"; + if (r->is_method) { + if (r->name) + descr += " in method '" + (std::string) str(r->scope) + "." + (std::string) r->name + "'"; + else + descr += " in method of '" + (std::string) str(r->scope) + "'"; + } else if (r->name) { + descr += " in function '" + (std::string) r->name + "'"; + } + pybind11_fail("arg(): could not convert default argument " + + descr + " into a Python object (type not registered yet?)"); +#else + pybind11_fail("arg(): could not convert default argument " + "into a Python object (type not registered yet?). " + "Compile in debug mode for more information."); +#endif + } + r->args.emplace_back(a.name, a.descr, a.value.inc_ref(), !a.flag_noconvert, a.flag_none); + } +}; + +/// Process a parent class attribute. Single inheritance only (class_ itself already guarantees that) +template +struct process_attribute::value>> : process_attribute_default { + static void init(const handle &h, type_record *r) { r->bases.append(h); } +}; + +/// Process a parent class attribute (deprecated, does not support multiple inheritance) +template +struct process_attribute> : process_attribute_default> { + static void init(const base &, type_record *r) { r->add_base(typeid(T), nullptr); } +}; + +/// Process a multiple inheritance attribute +template <> +struct process_attribute : process_attribute_default { + static void init(const multiple_inheritance &, type_record *r) { r->multiple_inheritance = true; } +}; + +template <> +struct process_attribute : process_attribute_default { + static void init(const dynamic_attr &, type_record *r) { r->dynamic_attr = true; } +}; + +template <> +struct process_attribute : process_attribute_default { + static void init(const buffer_protocol &, type_record *r) { r->buffer_protocol = true; } +}; + +template <> +struct process_attribute : process_attribute_default { + static void init(const metaclass &m, type_record *r) { r->metaclass = m.value; } +}; + +template <> +struct process_attribute : process_attribute_default { + static void init(const module_local &l, type_record *r) { r->module_local = l.value; } +}; + +/// Process an 'arithmetic' attribute for enums (does nothing here) +template <> +struct process_attribute : process_attribute_default {}; + +template +struct process_attribute> : process_attribute_default> { }; + +/** + * Process a keep_alive call policy -- invokes keep_alive_impl during the + * pre-call handler if both Nurse, Patient != 0 and use the post-call handler + * otherwise + */ +template struct process_attribute> : public process_attribute_default> { + template = 0> + static void precall(function_call &call) { keep_alive_impl(Nurse, Patient, call, handle()); } + template = 0> + static void postcall(function_call &, handle) { } + template = 0> + static void precall(function_call &) { } + template = 0> + static void postcall(function_call &call, handle ret) { keep_alive_impl(Nurse, Patient, call, ret); } +}; + +/// Recursively iterate over variadic template arguments +template struct process_attributes { + static void init(const Args&... args, function_record *r) { + int unused[] = { 0, (process_attribute::type>::init(args, r), 0) ... }; + ignore_unused(unused); + } + static void init(const Args&... args, type_record *r) { + int unused[] = { 0, (process_attribute::type>::init(args, r), 0) ... }; + ignore_unused(unused); + } + static void precall(function_call &call) { + int unused[] = { 0, (process_attribute::type>::precall(call), 0) ... }; + ignore_unused(unused); + } + static void postcall(function_call &call, handle fn_ret) { + int unused[] = { 0, (process_attribute::type>::postcall(call, fn_ret), 0) ... }; + ignore_unused(unused); + } +}; + +template +using is_call_guard = is_instantiation; + +/// Extract the ``type`` from the first `call_guard` in `Extras...` (or `void_type` if none found) +template +using extract_guard_t = typename exactly_one_t, Extra...>::type; + +/// Check the number of named arguments at compile time +template ::value...), + size_t self = constexpr_sum(std::is_same::value...)> +constexpr bool expected_num_args(size_t nargs, bool has_args, bool has_kwargs) { + return named == 0 || (self + named + has_args + has_kwargs) == nargs; +} + +NAMESPACE_END(detail) +NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/ptocr/postprocess/piexlmerge/include/pybind11/buffer_info.h b/ptocr/postprocess/piexlmerge/include/pybind11/buffer_info.h new file mode 100644 index 0000000..9f072fa --- /dev/null +++ b/ptocr/postprocess/piexlmerge/include/pybind11/buffer_info.h @@ -0,0 +1,108 @@ +/* + pybind11/buffer_info.h: Python buffer object interface + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "detail/common.h" + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) + +/// Information record describing a Python buffer object +struct buffer_info { + void *ptr = nullptr; // Pointer to the underlying storage + ssize_t itemsize = 0; // Size of individual items in bytes + ssize_t size = 0; // Total number of entries + std::string format; // For homogeneous buffers, this should be set to format_descriptor::format() + ssize_t ndim = 0; // Number of dimensions + std::vector shape; // Shape of the tensor (1 entry per dimension) + std::vector strides; // Number of entries between adjacent entries (for each per dimension) + + buffer_info() { } + + buffer_info(void *ptr, ssize_t itemsize, const std::string &format, ssize_t ndim, + detail::any_container shape_in, detail::any_container strides_in) + : ptr(ptr), itemsize(itemsize), size(1), format(format), ndim(ndim), + shape(std::move(shape_in)), strides(std::move(strides_in)) { + if (ndim != (ssize_t) shape.size() || ndim != (ssize_t) strides.size()) + pybind11_fail("buffer_info: ndim doesn't match shape and/or strides length"); + for (size_t i = 0; i < (size_t) ndim; ++i) + size *= shape[i]; + } + + template + buffer_info(T *ptr, detail::any_container shape_in, detail::any_container strides_in) + : buffer_info(private_ctr_tag(), ptr, sizeof(T), format_descriptor::format(), static_cast(shape_in->size()), std::move(shape_in), std::move(strides_in)) { } + + buffer_info(void *ptr, ssize_t itemsize, const std::string &format, ssize_t size) + : buffer_info(ptr, itemsize, format, 1, {size}, {itemsize}) { } + + template + buffer_info(T *ptr, ssize_t size) + : buffer_info(ptr, sizeof(T), format_descriptor::format(), size) { } + + explicit buffer_info(Py_buffer *view, bool ownview = true) + : buffer_info(view->buf, view->itemsize, view->format, view->ndim, + {view->shape, view->shape + view->ndim}, {view->strides, view->strides + view->ndim}) { + this->view = view; + this->ownview = ownview; + } + + buffer_info(const buffer_info &) = delete; + buffer_info& operator=(const buffer_info &) = delete; + + buffer_info(buffer_info &&other) { + (*this) = std::move(other); + } + + buffer_info& operator=(buffer_info &&rhs) { + ptr = rhs.ptr; + itemsize = rhs.itemsize; + size = rhs.size; + format = std::move(rhs.format); + ndim = rhs.ndim; + shape = std::move(rhs.shape); + strides = std::move(rhs.strides); + std::swap(view, rhs.view); + std::swap(ownview, rhs.ownview); + return *this; + } + + ~buffer_info() { + if (view && ownview) { PyBuffer_Release(view); delete view; } + } + +private: + struct private_ctr_tag { }; + + buffer_info(private_ctr_tag, void *ptr, ssize_t itemsize, const std::string &format, ssize_t ndim, + detail::any_container &&shape_in, detail::any_container &&strides_in) + : buffer_info(ptr, itemsize, format, ndim, std::move(shape_in), std::move(strides_in)) { } + + Py_buffer *view = nullptr; + bool ownview = false; +}; + +NAMESPACE_BEGIN(detail) + +template struct compare_buffer_info { + static bool compare(const buffer_info& b) { + return b.format == format_descriptor::format() && b.itemsize == (ssize_t) sizeof(T); + } +}; + +template struct compare_buffer_info::value>> { + static bool compare(const buffer_info& b) { + return (size_t) b.itemsize == sizeof(T) && (b.format == format_descriptor::value || + ((sizeof(T) == sizeof(long)) && b.format == (std::is_unsigned::value ? "L" : "l")) || + ((sizeof(T) == sizeof(size_t)) && b.format == (std::is_unsigned::value ? "N" : "n"))); + } +}; + +NAMESPACE_END(detail) +NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/ptocr/postprocess/piexlmerge/include/pybind11/cast.h b/ptocr/postprocess/piexlmerge/include/pybind11/cast.h new file mode 100644 index 0000000..80abb2b --- /dev/null +++ b/ptocr/postprocess/piexlmerge/include/pybind11/cast.h @@ -0,0 +1,2128 @@ +/* + pybind11/cast.h: Partial template specializations to cast between + C++ and Python types + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "pytypes.h" +#include "detail/typeid.h" +#include "detail/descr.h" +#include "detail/internals.h" +#include +#include +#include +#include + +#if defined(PYBIND11_CPP17) +# if defined(__has_include) +# if __has_include() +# define PYBIND11_HAS_STRING_VIEW +# endif +# elif defined(_MSC_VER) +# define PYBIND11_HAS_STRING_VIEW +# endif +#endif +#ifdef PYBIND11_HAS_STRING_VIEW +#include +#endif + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +NAMESPACE_BEGIN(detail) + +/// A life support system for temporary objects created by `type_caster::load()`. +/// Adding a patient will keep it alive up until the enclosing function returns. +class loader_life_support { +public: + /// A new patient frame is created when a function is entered + loader_life_support() { + get_internals().loader_patient_stack.push_back(nullptr); + } + + /// ... and destroyed after it returns + ~loader_life_support() { + auto &stack = get_internals().loader_patient_stack; + if (stack.empty()) + pybind11_fail("loader_life_support: internal error"); + + auto ptr = stack.back(); + stack.pop_back(); + Py_CLEAR(ptr); + + // A heuristic to reduce the stack's capacity (e.g. after long recursive calls) + if (stack.capacity() > 16 && stack.size() != 0 && stack.capacity() / stack.size() > 2) + stack.shrink_to_fit(); + } + + /// This can only be used inside a pybind11-bound function, either by `argument_loader` + /// at argument preparation time or by `py::cast()` at execution time. + PYBIND11_NOINLINE static void add_patient(handle h) { + auto &stack = get_internals().loader_patient_stack; + if (stack.empty()) + throw cast_error("When called outside a bound function, py::cast() cannot " + "do Python -> C++ conversions which require the creation " + "of temporary values"); + + auto &list_ptr = stack.back(); + if (list_ptr == nullptr) { + list_ptr = PyList_New(1); + if (!list_ptr) + pybind11_fail("loader_life_support: error allocating list"); + PyList_SET_ITEM(list_ptr, 0, h.inc_ref().ptr()); + } else { + auto result = PyList_Append(list_ptr, h.ptr()); + if (result == -1) + pybind11_fail("loader_life_support: error adding patient"); + } + } +}; + +// Gets the cache entry for the given type, creating it if necessary. The return value is the pair +// returned by emplace, i.e. an iterator for the entry and a bool set to `true` if the entry was +// just created. +inline std::pair all_type_info_get_cache(PyTypeObject *type); + +// Populates a just-created cache entry. +PYBIND11_NOINLINE inline void all_type_info_populate(PyTypeObject *t, std::vector &bases) { + std::vector check; + for (handle parent : reinterpret_borrow(t->tp_bases)) + check.push_back((PyTypeObject *) parent.ptr()); + + auto const &type_dict = get_internals().registered_types_py; + for (size_t i = 0; i < check.size(); i++) { + auto type = check[i]; + // Ignore Python2 old-style class super type: + if (!PyType_Check((PyObject *) type)) continue; + + // Check `type` in the current set of registered python types: + auto it = type_dict.find(type); + if (it != type_dict.end()) { + // We found a cache entry for it, so it's either pybind-registered or has pre-computed + // pybind bases, but we have to make sure we haven't already seen the type(s) before: we + // want to follow Python/virtual C++ rules that there should only be one instance of a + // common base. + for (auto *tinfo : it->second) { + // NB: Could use a second set here, rather than doing a linear search, but since + // having a large number of immediate pybind11-registered types seems fairly + // unlikely, that probably isn't worthwhile. + bool found = false; + for (auto *known : bases) { + if (known == tinfo) { found = true; break; } + } + if (!found) bases.push_back(tinfo); + } + } + else if (type->tp_bases) { + // It's some python type, so keep follow its bases classes to look for one or more + // registered types + if (i + 1 == check.size()) { + // When we're at the end, we can pop off the current element to avoid growing + // `check` when adding just one base (which is typical--i.e. when there is no + // multiple inheritance) + check.pop_back(); + i--; + } + for (handle parent : reinterpret_borrow(type->tp_bases)) + check.push_back((PyTypeObject *) parent.ptr()); + } + } +} + +/** + * Extracts vector of type_info pointers of pybind-registered roots of the given Python type. Will + * be just 1 pybind type for the Python type of a pybind-registered class, or for any Python-side + * derived class that uses single inheritance. Will contain as many types as required for a Python + * class that uses multiple inheritance to inherit (directly or indirectly) from multiple + * pybind-registered classes. Will be empty if neither the type nor any base classes are + * pybind-registered. + * + * The value is cached for the lifetime of the Python type. + */ +inline const std::vector &all_type_info(PyTypeObject *type) { + auto ins = all_type_info_get_cache(type); + if (ins.second) + // New cache entry: populate it + all_type_info_populate(type, ins.first->second); + + return ins.first->second; +} + +/** + * Gets a single pybind11 type info for a python type. Returns nullptr if neither the type nor any + * ancestors are pybind11-registered. Throws an exception if there are multiple bases--use + * `all_type_info` instead if you want to support multiple bases. + */ +PYBIND11_NOINLINE inline detail::type_info* get_type_info(PyTypeObject *type) { + auto &bases = all_type_info(type); + if (bases.size() == 0) + return nullptr; + if (bases.size() > 1) + pybind11_fail("pybind11::detail::get_type_info: type has multiple pybind11-registered bases"); + return bases.front(); +} + +inline detail::type_info *get_local_type_info(const std::type_index &tp) { + auto &locals = registered_local_types_cpp(); + auto it = locals.find(tp); + if (it != locals.end()) + return it->second; + return nullptr; +} + +inline detail::type_info *get_global_type_info(const std::type_index &tp) { + auto &types = get_internals().registered_types_cpp; + auto it = types.find(tp); + if (it != types.end()) + return it->second; + return nullptr; +} + +/// Return the type info for a given C++ type; on lookup failure can either throw or return nullptr. +PYBIND11_NOINLINE inline detail::type_info *get_type_info(const std::type_index &tp, + bool throw_if_missing = false) { + if (auto ltype = get_local_type_info(tp)) + return ltype; + if (auto gtype = get_global_type_info(tp)) + return gtype; + + if (throw_if_missing) { + std::string tname = tp.name(); + detail::clean_type_id(tname); + pybind11_fail("pybind11::detail::get_type_info: unable to find type info for \"" + tname + "\""); + } + return nullptr; +} + +PYBIND11_NOINLINE inline handle get_type_handle(const std::type_info &tp, bool throw_if_missing) { + detail::type_info *type_info = get_type_info(tp, throw_if_missing); + return handle(type_info ? ((PyObject *) type_info->type) : nullptr); +} + +struct value_and_holder { + instance *inst; + size_t index; + const detail::type_info *type; + void **vh; + + // Main constructor for a found value/holder: + value_and_holder(instance *i, const detail::type_info *type, size_t vpos, size_t index) : + inst{i}, index{index}, type{type}, + vh{inst->simple_layout ? inst->simple_value_holder : &inst->nonsimple.values_and_holders[vpos]} + {} + + // Default constructor (used to signal a value-and-holder not found by get_value_and_holder()) + value_and_holder() : inst{nullptr} {} + + // Used for past-the-end iterator + value_and_holder(size_t index) : index{index} {} + + template V *&value_ptr() const { + return reinterpret_cast(vh[0]); + } + // True if this `value_and_holder` has a non-null value pointer + explicit operator bool() const { return value_ptr(); } + + template H &holder() const { + return reinterpret_cast(vh[1]); + } + bool holder_constructed() const { + return inst->simple_layout + ? inst->simple_holder_constructed + : inst->nonsimple.status[index] & instance::status_holder_constructed; + } + void set_holder_constructed(bool v = true) { + if (inst->simple_layout) + inst->simple_holder_constructed = v; + else if (v) + inst->nonsimple.status[index] |= instance::status_holder_constructed; + else + inst->nonsimple.status[index] &= (uint8_t) ~instance::status_holder_constructed; + } + bool instance_registered() const { + return inst->simple_layout + ? inst->simple_instance_registered + : inst->nonsimple.status[index] & instance::status_instance_registered; + } + void set_instance_registered(bool v = true) { + if (inst->simple_layout) + inst->simple_instance_registered = v; + else if (v) + inst->nonsimple.status[index] |= instance::status_instance_registered; + else + inst->nonsimple.status[index] &= (uint8_t) ~instance::status_instance_registered; + } +}; + +// Container for accessing and iterating over an instance's values/holders +struct values_and_holders { +private: + instance *inst; + using type_vec = std::vector; + const type_vec &tinfo; + +public: + values_and_holders(instance *inst) : inst{inst}, tinfo(all_type_info(Py_TYPE(inst))) {} + + struct iterator { + private: + instance *inst; + const type_vec *types; + value_and_holder curr; + friend struct values_and_holders; + iterator(instance *inst, const type_vec *tinfo) + : inst{inst}, types{tinfo}, + curr(inst /* instance */, + types->empty() ? nullptr : (*types)[0] /* type info */, + 0, /* vpos: (non-simple types only): the first vptr comes first */ + 0 /* index */) + {} + // Past-the-end iterator: + iterator(size_t end) : curr(end) {} + public: + bool operator==(const iterator &other) { return curr.index == other.curr.index; } + bool operator!=(const iterator &other) { return curr.index != other.curr.index; } + iterator &operator++() { + if (!inst->simple_layout) + curr.vh += 1 + (*types)[curr.index]->holder_size_in_ptrs; + ++curr.index; + curr.type = curr.index < types->size() ? (*types)[curr.index] : nullptr; + return *this; + } + value_and_holder &operator*() { return curr; } + value_and_holder *operator->() { return &curr; } + }; + + iterator begin() { return iterator(inst, &tinfo); } + iterator end() { return iterator(tinfo.size()); } + + iterator find(const type_info *find_type) { + auto it = begin(), endit = end(); + while (it != endit && it->type != find_type) ++it; + return it; + } + + size_t size() { return tinfo.size(); } +}; + +/** + * Extracts C++ value and holder pointer references from an instance (which may contain multiple + * values/holders for python-side multiple inheritance) that match the given type. Throws an error + * if the given type (or ValueType, if omitted) is not a pybind11 base of the given instance. If + * `find_type` is omitted (or explicitly specified as nullptr) the first value/holder are returned, + * regardless of type (and the resulting .type will be nullptr). + * + * The returned object should be short-lived: in particular, it must not outlive the called-upon + * instance. + */ +PYBIND11_NOINLINE inline value_and_holder instance::get_value_and_holder(const type_info *find_type /*= nullptr default in common.h*/, bool throw_if_missing /*= true in common.h*/) { + // Optimize common case: + if (!find_type || Py_TYPE(this) == find_type->type) + return value_and_holder(this, find_type, 0, 0); + + detail::values_and_holders vhs(this); + auto it = vhs.find(find_type); + if (it != vhs.end()) + return *it; + + if (!throw_if_missing) + return value_and_holder(); + +#if defined(NDEBUG) + pybind11_fail("pybind11::detail::instance::get_value_and_holder: " + "type is not a pybind11 base of the given instance " + "(compile in debug mode for type details)"); +#else + pybind11_fail("pybind11::detail::instance::get_value_and_holder: `" + + std::string(find_type->type->tp_name) + "' is not a pybind11 base of the given `" + + std::string(Py_TYPE(this)->tp_name) + "' instance"); +#endif +} + +PYBIND11_NOINLINE inline void instance::allocate_layout() { + auto &tinfo = all_type_info(Py_TYPE(this)); + + const size_t n_types = tinfo.size(); + + if (n_types == 0) + pybind11_fail("instance allocation failed: new instance has no pybind11-registered base types"); + + simple_layout = + n_types == 1 && tinfo.front()->holder_size_in_ptrs <= instance_simple_holder_in_ptrs(); + + // Simple path: no python-side multiple inheritance, and a small-enough holder + if (simple_layout) { + simple_value_holder[0] = nullptr; + simple_holder_constructed = false; + simple_instance_registered = false; + } + else { // multiple base types or a too-large holder + // Allocate space to hold: [v1*][h1][v2*][h2]...[bb...] where [vN*] is a value pointer, + // [hN] is the (uninitialized) holder instance for value N, and [bb...] is a set of bool + // values that tracks whether each associated holder has been initialized. Each [block] is + // padded, if necessary, to an integer multiple of sizeof(void *). + size_t space = 0; + for (auto t : tinfo) { + space += 1; // value pointer + space += t->holder_size_in_ptrs; // holder instance + } + size_t flags_at = space; + space += size_in_ptrs(n_types); // status bytes (holder_constructed and instance_registered) + + // Allocate space for flags, values, and holders, and initialize it to 0 (flags and values, + // in particular, need to be 0). Use Python's memory allocation functions: in Python 3.6 + // they default to using pymalloc, which is designed to be efficient for small allocations + // like the one we're doing here; in earlier versions (and for larger allocations) they are + // just wrappers around malloc. +#if PY_VERSION_HEX >= 0x03050000 + nonsimple.values_and_holders = (void **) PyMem_Calloc(space, sizeof(void *)); + if (!nonsimple.values_and_holders) throw std::bad_alloc(); +#else + nonsimple.values_and_holders = (void **) PyMem_New(void *, space); + if (!nonsimple.values_and_holders) throw std::bad_alloc(); + std::memset(nonsimple.values_and_holders, 0, space * sizeof(void *)); +#endif + nonsimple.status = reinterpret_cast(&nonsimple.values_and_holders[flags_at]); + } + owned = true; +} + +PYBIND11_NOINLINE inline void instance::deallocate_layout() { + if (!simple_layout) + PyMem_Free(nonsimple.values_and_holders); +} + +PYBIND11_NOINLINE inline bool isinstance_generic(handle obj, const std::type_info &tp) { + handle type = detail::get_type_handle(tp, false); + if (!type) + return false; + return isinstance(obj, type); +} + +PYBIND11_NOINLINE inline std::string error_string() { + if (!PyErr_Occurred()) { + PyErr_SetString(PyExc_RuntimeError, "Unknown internal error occurred"); + return "Unknown internal error occurred"; + } + + error_scope scope; // Preserve error state + + std::string errorString; + if (scope.type) { + errorString += handle(scope.type).attr("__name__").cast(); + errorString += ": "; + } + if (scope.value) + errorString += (std::string) str(scope.value); + + PyErr_NormalizeException(&scope.type, &scope.value, &scope.trace); + +#if PY_MAJOR_VERSION >= 3 + if (scope.trace != nullptr) + PyException_SetTraceback(scope.value, scope.trace); +#endif + +#if !defined(PYPY_VERSION) + if (scope.trace) { + PyTracebackObject *trace = (PyTracebackObject *) scope.trace; + + /* Get the deepest trace possible */ + while (trace->tb_next) + trace = trace->tb_next; + + PyFrameObject *frame = trace->tb_frame; + errorString += "\n\nAt:\n"; + while (frame) { + int lineno = PyFrame_GetLineNumber(frame); + errorString += + " " + handle(frame->f_code->co_filename).cast() + + "(" + std::to_string(lineno) + "): " + + handle(frame->f_code->co_name).cast() + "\n"; + frame = frame->f_back; + } + } +#endif + + return errorString; +} + +PYBIND11_NOINLINE inline handle get_object_handle(const void *ptr, const detail::type_info *type ) { + auto &instances = get_internals().registered_instances; + auto range = instances.equal_range(ptr); + for (auto it = range.first; it != range.second; ++it) { + for (auto vh : values_and_holders(it->second)) { + if (vh.type == type) + return handle((PyObject *) it->second); + } + } + return handle(); +} + +inline PyThreadState *get_thread_state_unchecked() { +#if defined(PYPY_VERSION) + return PyThreadState_GET(); +#elif PY_VERSION_HEX < 0x03000000 + return _PyThreadState_Current; +#elif PY_VERSION_HEX < 0x03050000 + return (PyThreadState*) _Py_atomic_load_relaxed(&_PyThreadState_Current); +#elif PY_VERSION_HEX < 0x03050200 + return (PyThreadState*) _PyThreadState_Current.value; +#else + return _PyThreadState_UncheckedGet(); +#endif +} + +// Forward declarations +inline void keep_alive_impl(handle nurse, handle patient); +inline PyObject *make_new_instance(PyTypeObject *type); + +class type_caster_generic { +public: + PYBIND11_NOINLINE type_caster_generic(const std::type_info &type_info) + : typeinfo(get_type_info(type_info)), cpptype(&type_info) { } + + type_caster_generic(const type_info *typeinfo) + : typeinfo(typeinfo), cpptype(typeinfo ? typeinfo->cpptype : nullptr) { } + + bool load(handle src, bool convert) { + return load_impl(src, convert); + } + + PYBIND11_NOINLINE static handle cast(const void *_src, return_value_policy policy, handle parent, + const detail::type_info *tinfo, + void *(*copy_constructor)(const void *), + void *(*move_constructor)(const void *), + const void *existing_holder = nullptr) { + if (!tinfo) // no type info: error will be set already + return handle(); + + void *src = const_cast(_src); + if (src == nullptr) + return none().release(); + + auto it_instances = get_internals().registered_instances.equal_range(src); + for (auto it_i = it_instances.first; it_i != it_instances.second; ++it_i) { + for (auto instance_type : detail::all_type_info(Py_TYPE(it_i->second))) { + if (instance_type && same_type(*instance_type->cpptype, *tinfo->cpptype)) + return handle((PyObject *) it_i->second).inc_ref(); + } + } + + auto inst = reinterpret_steal(make_new_instance(tinfo->type)); + auto wrapper = reinterpret_cast(inst.ptr()); + wrapper->owned = false; + void *&valueptr = values_and_holders(wrapper).begin()->value_ptr(); + + switch (policy) { + case return_value_policy::automatic: + case return_value_policy::take_ownership: + valueptr = src; + wrapper->owned = true; + break; + + case return_value_policy::automatic_reference: + case return_value_policy::reference: + valueptr = src; + wrapper->owned = false; + break; + + case return_value_policy::copy: + if (copy_constructor) + valueptr = copy_constructor(src); + else + throw cast_error("return_value_policy = copy, but the " + "object is non-copyable!"); + wrapper->owned = true; + break; + + case return_value_policy::move: + if (move_constructor) + valueptr = move_constructor(src); + else if (copy_constructor) + valueptr = copy_constructor(src); + else + throw cast_error("return_value_policy = move, but the " + "object is neither movable nor copyable!"); + wrapper->owned = true; + break; + + case return_value_policy::reference_internal: + valueptr = src; + wrapper->owned = false; + keep_alive_impl(inst, parent); + break; + + default: + throw cast_error("unhandled return_value_policy: should not happen!"); + } + + tinfo->init_instance(wrapper, existing_holder); + + return inst.release(); + } + + // Base methods for generic caster; there are overridden in copyable_holder_caster + void load_value(value_and_holder &&v_h) { + auto *&vptr = v_h.value_ptr(); + // Lazy allocation for unallocated values: + if (vptr == nullptr) { + auto *type = v_h.type ? v_h.type : typeinfo; + if (type->operator_new) { + vptr = type->operator_new(type->type_size); + } else { + #if defined(PYBIND11_CPP17) + if (type->type_align > __STDCPP_DEFAULT_NEW_ALIGNMENT__) + vptr = ::operator new(type->type_size, + (std::align_val_t) type->type_align); + else + #endif + vptr = ::operator new(type->type_size); + } + } + value = vptr; + } + bool try_implicit_casts(handle src, bool convert) { + for (auto &cast : typeinfo->implicit_casts) { + type_caster_generic sub_caster(*cast.first); + if (sub_caster.load(src, convert)) { + value = cast.second(sub_caster.value); + return true; + } + } + return false; + } + bool try_direct_conversions(handle src) { + for (auto &converter : *typeinfo->direct_conversions) { + if (converter(src.ptr(), value)) + return true; + } + return false; + } + void check_holder_compat() {} + + PYBIND11_NOINLINE static void *local_load(PyObject *src, const type_info *ti) { + auto caster = type_caster_generic(ti); + if (caster.load(src, false)) + return caster.value; + return nullptr; + } + + /// Try to load with foreign typeinfo, if available. Used when there is no + /// native typeinfo, or when the native one wasn't able to produce a value. + PYBIND11_NOINLINE bool try_load_foreign_module_local(handle src) { + constexpr auto *local_key = PYBIND11_MODULE_LOCAL_ID; + const auto pytype = src.get_type(); + if (!hasattr(pytype, local_key)) + return false; + + type_info *foreign_typeinfo = reinterpret_borrow(getattr(pytype, local_key)); + // Only consider this foreign loader if actually foreign and is a loader of the correct cpp type + if (foreign_typeinfo->module_local_load == &local_load + || (cpptype && !same_type(*cpptype, *foreign_typeinfo->cpptype))) + return false; + + if (auto result = foreign_typeinfo->module_local_load(src.ptr(), foreign_typeinfo)) { + value = result; + return true; + } + return false; + } + + // Implementation of `load`; this takes the type of `this` so that it can dispatch the relevant + // bits of code between here and copyable_holder_caster where the two classes need different + // logic (without having to resort to virtual inheritance). + template + PYBIND11_NOINLINE bool load_impl(handle src, bool convert) { + if (!src) return false; + if (!typeinfo) return try_load_foreign_module_local(src); + if (src.is_none()) { + // Defer accepting None to other overloads (if we aren't in convert mode): + if (!convert) return false; + value = nullptr; + return true; + } + + auto &this_ = static_cast(*this); + this_.check_holder_compat(); + + PyTypeObject *srctype = Py_TYPE(src.ptr()); + + // Case 1: If src is an exact type match for the target type then we can reinterpret_cast + // the instance's value pointer to the target type: + if (srctype == typeinfo->type) { + this_.load_value(reinterpret_cast(src.ptr())->get_value_and_holder()); + return true; + } + // Case 2: We have a derived class + else if (PyType_IsSubtype(srctype, typeinfo->type)) { + auto &bases = all_type_info(srctype); + bool no_cpp_mi = typeinfo->simple_type; + + // Case 2a: the python type is a Python-inherited derived class that inherits from just + // one simple (no MI) pybind11 class, or is an exact match, so the C++ instance is of + // the right type and we can use reinterpret_cast. + // (This is essentially the same as case 2b, but because not using multiple inheritance + // is extremely common, we handle it specially to avoid the loop iterator and type + // pointer lookup overhead) + if (bases.size() == 1 && (no_cpp_mi || bases.front()->type == typeinfo->type)) { + this_.load_value(reinterpret_cast(src.ptr())->get_value_and_holder()); + return true; + } + // Case 2b: the python type inherits from multiple C++ bases. Check the bases to see if + // we can find an exact match (or, for a simple C++ type, an inherited match); if so, we + // can safely reinterpret_cast to the relevant pointer. + else if (bases.size() > 1) { + for (auto base : bases) { + if (no_cpp_mi ? PyType_IsSubtype(base->type, typeinfo->type) : base->type == typeinfo->type) { + this_.load_value(reinterpret_cast(src.ptr())->get_value_and_holder(base)); + return true; + } + } + } + + // Case 2c: C++ multiple inheritance is involved and we couldn't find an exact type match + // in the registered bases, above, so try implicit casting (needed for proper C++ casting + // when MI is involved). + if (this_.try_implicit_casts(src, convert)) + return true; + } + + // Perform an implicit conversion + if (convert) { + for (auto &converter : typeinfo->implicit_conversions) { + auto temp = reinterpret_steal(converter(src.ptr(), typeinfo->type)); + if (load_impl(temp, false)) { + loader_life_support::add_patient(temp); + return true; + } + } + if (this_.try_direct_conversions(src)) + return true; + } + + // Failed to match local typeinfo. Try again with global. + if (typeinfo->module_local) { + if (auto gtype = get_global_type_info(*typeinfo->cpptype)) { + typeinfo = gtype; + return load(src, false); + } + } + + // Global typeinfo has precedence over foreign module_local + return try_load_foreign_module_local(src); + } + + + // Called to do type lookup and wrap the pointer and type in a pair when a dynamic_cast + // isn't needed or can't be used. If the type is unknown, sets the error and returns a pair + // with .second = nullptr. (p.first = nullptr is not an error: it becomes None). + PYBIND11_NOINLINE static std::pair src_and_type( + const void *src, const std::type_info &cast_type, const std::type_info *rtti_type = nullptr) { + if (auto *tpi = get_type_info(cast_type)) + return {src, const_cast(tpi)}; + + // Not found, set error: + std::string tname = rtti_type ? rtti_type->name() : cast_type.name(); + detail::clean_type_id(tname); + std::string msg = "Unregistered type : " + tname; + PyErr_SetString(PyExc_TypeError, msg.c_str()); + return {nullptr, nullptr}; + } + + const type_info *typeinfo = nullptr; + const std::type_info *cpptype = nullptr; + void *value = nullptr; +}; + +/** + * Determine suitable casting operator for pointer-or-lvalue-casting type casters. The type caster + * needs to provide `operator T*()` and `operator T&()` operators. + * + * If the type supports moving the value away via an `operator T&&() &&` method, it should use + * `movable_cast_op_type` instead. + */ +template +using cast_op_type = + conditional_t>::value, + typename std::add_pointer>::type, + typename std::add_lvalue_reference>::type>; + +/** + * Determine suitable casting operator for a type caster with a movable value. Such a type caster + * needs to provide `operator T*()`, `operator T&()`, and `operator T&&() &&`. The latter will be + * called in appropriate contexts where the value can be moved rather than copied. + * + * These operator are automatically provided when using the PYBIND11_TYPE_CASTER macro. + */ +template +using movable_cast_op_type = + conditional_t::type>::value, + typename std::add_pointer>::type, + conditional_t::value, + typename std::add_rvalue_reference>::type, + typename std::add_lvalue_reference>::type>>; + +// std::is_copy_constructible isn't quite enough: it lets std::vector (and similar) through when +// T is non-copyable, but code containing such a copy constructor fails to actually compile. +template struct is_copy_constructible : std::is_copy_constructible {}; + +// Specialization for types that appear to be copy constructible but also look like stl containers +// (we specifically check for: has `value_type` and `reference` with `reference = value_type&`): if +// so, copy constructability depends on whether the value_type is copy constructible. +template struct is_copy_constructible, + std::is_same + >::value>> : is_copy_constructible {}; + +#if !defined(PYBIND11_CPP17) +// Likewise for std::pair before C++17 (which mandates that the copy constructor not exist when the +// two types aren't themselves copy constructible). +template struct is_copy_constructible> + : all_of, is_copy_constructible> {}; +#endif + +NAMESPACE_END(detail) + +// polymorphic_type_hook::get(src, tinfo) determines whether the object pointed +// to by `src` actually is an instance of some class derived from `itype`. +// If so, it sets `tinfo` to point to the std::type_info representing that derived +// type, and returns a pointer to the start of the most-derived object of that type +// (in which `src` is a subobject; this will be the same address as `src` in most +// single inheritance cases). If not, or if `src` is nullptr, it simply returns `src` +// and leaves `tinfo` at its default value of nullptr. +// +// The default polymorphic_type_hook just returns src. A specialization for polymorphic +// types determines the runtime type of the passed object and adjusts the this-pointer +// appropriately via dynamic_cast. This is what enables a C++ Animal* to appear +// to Python as a Dog (if Dog inherits from Animal, Animal is polymorphic, Dog is +// registered with pybind11, and this Animal is in fact a Dog). +// +// You may specialize polymorphic_type_hook yourself for types that want to appear +// polymorphic to Python but do not use C++ RTTI. (This is a not uncommon pattern +// in performance-sensitive applications, used most notably in LLVM.) +template +struct polymorphic_type_hook +{ + static const void *get(const itype *src, const std::type_info*&) { return src; } +}; +template +struct polymorphic_type_hook::value>> +{ + static const void *get(const itype *src, const std::type_info*& type) { + type = src ? &typeid(*src) : nullptr; + return dynamic_cast(src); + } +}; + +NAMESPACE_BEGIN(detail) + +/// Generic type caster for objects stored on the heap +template class type_caster_base : public type_caster_generic { + using itype = intrinsic_t; + +public: + static constexpr auto name = _(); + + type_caster_base() : type_caster_base(typeid(type)) { } + explicit type_caster_base(const std::type_info &info) : type_caster_generic(info) { } + + static handle cast(const itype &src, return_value_policy policy, handle parent) { + if (policy == return_value_policy::automatic || policy == return_value_policy::automatic_reference) + policy = return_value_policy::copy; + return cast(&src, policy, parent); + } + + static handle cast(itype &&src, return_value_policy, handle parent) { + return cast(&src, return_value_policy::move, parent); + } + + // Returns a (pointer, type_info) pair taking care of necessary type lookup for a + // polymorphic type (using RTTI by default, but can be overridden by specializing + // polymorphic_type_hook). If the instance isn't derived, returns the base version. + static std::pair src_and_type(const itype *src) { + auto &cast_type = typeid(itype); + const std::type_info *instance_type = nullptr; + const void *vsrc = polymorphic_type_hook::get(src, instance_type); + if (instance_type && !same_type(cast_type, *instance_type)) { + // This is a base pointer to a derived type. If the derived type is registered + // with pybind11, we want to make the full derived object available. + // In the typical case where itype is polymorphic, we get the correct + // derived pointer (which may be != base pointer) by a dynamic_cast to + // most derived type. If itype is not polymorphic, we won't get here + // except via a user-provided specialization of polymorphic_type_hook, + // and the user has promised that no this-pointer adjustment is + // required in that case, so it's OK to use static_cast. + if (const auto *tpi = get_type_info(*instance_type)) + return {vsrc, tpi}; + } + // Otherwise we have either a nullptr, an `itype` pointer, or an unknown derived pointer, so + // don't do a cast + return type_caster_generic::src_and_type(src, cast_type, instance_type); + } + + static handle cast(const itype *src, return_value_policy policy, handle parent) { + auto st = src_and_type(src); + return type_caster_generic::cast( + st.first, policy, parent, st.second, + make_copy_constructor(src), make_move_constructor(src)); + } + + static handle cast_holder(const itype *src, const void *holder) { + auto st = src_and_type(src); + return type_caster_generic::cast( + st.first, return_value_policy::take_ownership, {}, st.second, + nullptr, nullptr, holder); + } + + template using cast_op_type = detail::cast_op_type; + + operator itype*() { return (type *) value; } + operator itype&() { if (!value) throw reference_cast_error(); return *((itype *) value); } + +protected: + using Constructor = void *(*)(const void *); + + /* Only enabled when the types are {copy,move}-constructible *and* when the type + does not have a private operator new implementation. */ + template ::value>> + static auto make_copy_constructor(const T *x) -> decltype(new T(*x), Constructor{}) { + return [](const void *arg) -> void * { + return new T(*reinterpret_cast(arg)); + }; + } + + template ::value>> + static auto make_move_constructor(const T *x) -> decltype(new T(std::move(*const_cast(x))), Constructor{}) { + return [](const void *arg) -> void * { + return new T(std::move(*const_cast(reinterpret_cast(arg)))); + }; + } + + static Constructor make_copy_constructor(...) { return nullptr; } + static Constructor make_move_constructor(...) { return nullptr; } +}; + +template class type_caster : public type_caster_base { }; +template using make_caster = type_caster>; + +// Shortcut for calling a caster's `cast_op_type` cast operator for casting a type_caster to a T +template typename make_caster::template cast_op_type cast_op(make_caster &caster) { + return caster.operator typename make_caster::template cast_op_type(); +} +template typename make_caster::template cast_op_type::type> +cast_op(make_caster &&caster) { + return std::move(caster).operator + typename make_caster::template cast_op_type::type>(); +} + +template class type_caster> { +private: + using caster_t = make_caster; + caster_t subcaster; + using subcaster_cast_op_type = typename caster_t::template cast_op_type; + static_assert(std::is_same::type &, subcaster_cast_op_type>::value, + "std::reference_wrapper caster requires T to have a caster with an `T &` operator"); +public: + bool load(handle src, bool convert) { return subcaster.load(src, convert); } + static constexpr auto name = caster_t::name; + static handle cast(const std::reference_wrapper &src, return_value_policy policy, handle parent) { + // It is definitely wrong to take ownership of this pointer, so mask that rvp + if (policy == return_value_policy::take_ownership || policy == return_value_policy::automatic) + policy = return_value_policy::automatic_reference; + return caster_t::cast(&src.get(), policy, parent); + } + template using cast_op_type = std::reference_wrapper; + operator std::reference_wrapper() { return subcaster.operator subcaster_cast_op_type&(); } +}; + +#define PYBIND11_TYPE_CASTER(type, py_name) \ + protected: \ + type value; \ + public: \ + static constexpr auto name = py_name; \ + template >::value, int> = 0> \ + static handle cast(T_ *src, return_value_policy policy, handle parent) { \ + if (!src) return none().release(); \ + if (policy == return_value_policy::take_ownership) { \ + auto h = cast(std::move(*src), policy, parent); delete src; return h; \ + } else { \ + return cast(*src, policy, parent); \ + } \ + } \ + operator type*() { return &value; } \ + operator type&() { return value; } \ + operator type&&() && { return std::move(value); } \ + template using cast_op_type = pybind11::detail::movable_cast_op_type + + +template using is_std_char_type = any_of< + std::is_same, /* std::string */ + std::is_same, /* std::u16string */ + std::is_same, /* std::u32string */ + std::is_same /* std::wstring */ +>; + +template +struct type_caster::value && !is_std_char_type::value>> { + using _py_type_0 = conditional_t; + using _py_type_1 = conditional_t::value, _py_type_0, typename std::make_unsigned<_py_type_0>::type>; + using py_type = conditional_t::value, double, _py_type_1>; +public: + + bool load(handle src, bool convert) { + py_type py_value; + + if (!src) + return false; + + if (std::is_floating_point::value) { + if (convert || PyFloat_Check(src.ptr())) + py_value = (py_type) PyFloat_AsDouble(src.ptr()); + else + return false; + } else if (PyFloat_Check(src.ptr())) { + return false; + } else if (std::is_unsigned::value) { + py_value = as_unsigned(src.ptr()); + } else { // signed integer: + py_value = sizeof(T) <= sizeof(long) + ? (py_type) PyLong_AsLong(src.ptr()) + : (py_type) PYBIND11_LONG_AS_LONGLONG(src.ptr()); + } + + bool py_err = py_value == (py_type) -1 && PyErr_Occurred(); + if (py_err || (std::is_integral::value && sizeof(py_type) != sizeof(T) && + (py_value < (py_type) std::numeric_limits::min() || + py_value > (py_type) std::numeric_limits::max()))) { + bool type_error = py_err && PyErr_ExceptionMatches( +#if PY_VERSION_HEX < 0x03000000 && !defined(PYPY_VERSION) + PyExc_SystemError +#else + PyExc_TypeError +#endif + ); + PyErr_Clear(); + if (type_error && convert && PyNumber_Check(src.ptr())) { + auto tmp = reinterpret_steal(std::is_floating_point::value + ? PyNumber_Float(src.ptr()) + : PyNumber_Long(src.ptr())); + PyErr_Clear(); + return load(tmp, false); + } + return false; + } + + value = (T) py_value; + return true; + } + + template + static typename std::enable_if::value, handle>::type + cast(U src, return_value_policy /* policy */, handle /* parent */) { + return PyFloat_FromDouble((double) src); + } + + template + static typename std::enable_if::value && std::is_signed::value && (sizeof(U) <= sizeof(long)), handle>::type + cast(U src, return_value_policy /* policy */, handle /* parent */) { + return PYBIND11_LONG_FROM_SIGNED((long) src); + } + + template + static typename std::enable_if::value && std::is_unsigned::value && (sizeof(U) <= sizeof(unsigned long)), handle>::type + cast(U src, return_value_policy /* policy */, handle /* parent */) { + return PYBIND11_LONG_FROM_UNSIGNED((unsigned long) src); + } + + template + static typename std::enable_if::value && std::is_signed::value && (sizeof(U) > sizeof(long)), handle>::type + cast(U src, return_value_policy /* policy */, handle /* parent */) { + return PyLong_FromLongLong((long long) src); + } + + template + static typename std::enable_if::value && std::is_unsigned::value && (sizeof(U) > sizeof(unsigned long)), handle>::type + cast(U src, return_value_policy /* policy */, handle /* parent */) { + return PyLong_FromUnsignedLongLong((unsigned long long) src); + } + + PYBIND11_TYPE_CASTER(T, _::value>("int", "float")); +}; + +template struct void_caster { +public: + bool load(handle src, bool) { + if (src && src.is_none()) + return true; + return false; + } + static handle cast(T, return_value_policy /* policy */, handle /* parent */) { + return none().inc_ref(); + } + PYBIND11_TYPE_CASTER(T, _("None")); +}; + +template <> class type_caster : public void_caster {}; + +template <> class type_caster : public type_caster { +public: + using type_caster::cast; + + bool load(handle h, bool) { + if (!h) { + return false; + } else if (h.is_none()) { + value = nullptr; + return true; + } + + /* Check if this is a capsule */ + if (isinstance(h)) { + value = reinterpret_borrow(h); + return true; + } + + /* Check if this is a C++ type */ + auto &bases = all_type_info((PyTypeObject *) h.get_type().ptr()); + if (bases.size() == 1) { // Only allowing loading from a single-value type + value = values_and_holders(reinterpret_cast(h.ptr())).begin()->value_ptr(); + return true; + } + + /* Fail */ + return false; + } + + static handle cast(const void *ptr, return_value_policy /* policy */, handle /* parent */) { + if (ptr) + return capsule(ptr).release(); + else + return none().inc_ref(); + } + + template using cast_op_type = void*&; + operator void *&() { return value; } + static constexpr auto name = _("capsule"); +private: + void *value = nullptr; +}; + +template <> class type_caster : public void_caster { }; + +template <> class type_caster { +public: + bool load(handle src, bool convert) { + if (!src) return false; + else if (src.ptr() == Py_True) { value = true; return true; } + else if (src.ptr() == Py_False) { value = false; return true; } + else if (convert || !strcmp("numpy.bool_", Py_TYPE(src.ptr())->tp_name)) { + // (allow non-implicit conversion for numpy booleans) + + Py_ssize_t res = -1; + if (src.is_none()) { + res = 0; // None is implicitly converted to False + } + #if defined(PYPY_VERSION) + // On PyPy, check that "__bool__" (or "__nonzero__" on Python 2.7) attr exists + else if (hasattr(src, PYBIND11_BOOL_ATTR)) { + res = PyObject_IsTrue(src.ptr()); + } + #else + // Alternate approach for CPython: this does the same as the above, but optimized + // using the CPython API so as to avoid an unneeded attribute lookup. + else if (auto tp_as_number = src.ptr()->ob_type->tp_as_number) { + if (PYBIND11_NB_BOOL(tp_as_number)) { + res = (*PYBIND11_NB_BOOL(tp_as_number))(src.ptr()); + } + } + #endif + if (res == 0 || res == 1) { + value = (bool) res; + return true; + } + } + return false; + } + static handle cast(bool src, return_value_policy /* policy */, handle /* parent */) { + return handle(src ? Py_True : Py_False).inc_ref(); + } + PYBIND11_TYPE_CASTER(bool, _("bool")); +}; + +// Helper class for UTF-{8,16,32} C++ stl strings: +template struct string_caster { + using CharT = typename StringType::value_type; + + // Simplify life by being able to assume standard char sizes (the standard only guarantees + // minimums, but Python requires exact sizes) + static_assert(!std::is_same::value || sizeof(CharT) == 1, "Unsupported char size != 1"); + static_assert(!std::is_same::value || sizeof(CharT) == 2, "Unsupported char16_t size != 2"); + static_assert(!std::is_same::value || sizeof(CharT) == 4, "Unsupported char32_t size != 4"); + // wchar_t can be either 16 bits (Windows) or 32 (everywhere else) + static_assert(!std::is_same::value || sizeof(CharT) == 2 || sizeof(CharT) == 4, + "Unsupported wchar_t size != 2/4"); + static constexpr size_t UTF_N = 8 * sizeof(CharT); + + bool load(handle src, bool) { +#if PY_MAJOR_VERSION < 3 + object temp; +#endif + handle load_src = src; + if (!src) { + return false; + } else if (!PyUnicode_Check(load_src.ptr())) { +#if PY_MAJOR_VERSION >= 3 + return load_bytes(load_src); +#else + if (sizeof(CharT) == 1) { + return load_bytes(load_src); + } + + // The below is a guaranteed failure in Python 3 when PyUnicode_Check returns false + if (!PYBIND11_BYTES_CHECK(load_src.ptr())) + return false; + + temp = reinterpret_steal(PyUnicode_FromObject(load_src.ptr())); + if (!temp) { PyErr_Clear(); return false; } + load_src = temp; +#endif + } + + object utfNbytes = reinterpret_steal(PyUnicode_AsEncodedString( + load_src.ptr(), UTF_N == 8 ? "utf-8" : UTF_N == 16 ? "utf-16" : "utf-32", nullptr)); + if (!utfNbytes) { PyErr_Clear(); return false; } + + const CharT *buffer = reinterpret_cast(PYBIND11_BYTES_AS_STRING(utfNbytes.ptr())); + size_t length = (size_t) PYBIND11_BYTES_SIZE(utfNbytes.ptr()) / sizeof(CharT); + if (UTF_N > 8) { buffer++; length--; } // Skip BOM for UTF-16/32 + value = StringType(buffer, length); + + // If we're loading a string_view we need to keep the encoded Python object alive: + if (IsView) + loader_life_support::add_patient(utfNbytes); + + return true; + } + + static handle cast(const StringType &src, return_value_policy /* policy */, handle /* parent */) { + const char *buffer = reinterpret_cast(src.data()); + ssize_t nbytes = ssize_t(src.size() * sizeof(CharT)); + handle s = decode_utfN(buffer, nbytes); + if (!s) throw error_already_set(); + return s; + } + + PYBIND11_TYPE_CASTER(StringType, _(PYBIND11_STRING_NAME)); + +private: + static handle decode_utfN(const char *buffer, ssize_t nbytes) { +#if !defined(PYPY_VERSION) + return + UTF_N == 8 ? PyUnicode_DecodeUTF8(buffer, nbytes, nullptr) : + UTF_N == 16 ? PyUnicode_DecodeUTF16(buffer, nbytes, nullptr, nullptr) : + PyUnicode_DecodeUTF32(buffer, nbytes, nullptr, nullptr); +#else + // PyPy seems to have multiple problems related to PyUnicode_UTF*: the UTF8 version + // sometimes segfaults for unknown reasons, while the UTF16 and 32 versions require a + // non-const char * arguments, which is also a nuisance, so bypass the whole thing by just + // passing the encoding as a string value, which works properly: + return PyUnicode_Decode(buffer, nbytes, UTF_N == 8 ? "utf-8" : UTF_N == 16 ? "utf-16" : "utf-32", nullptr); +#endif + } + + // When loading into a std::string or char*, accept a bytes object as-is (i.e. + // without any encoding/decoding attempt). For other C++ char sizes this is a no-op. + // which supports loading a unicode from a str, doesn't take this path. + template + bool load_bytes(enable_if_t src) { + if (PYBIND11_BYTES_CHECK(src.ptr())) { + // We were passed a Python 3 raw bytes; accept it into a std::string or char* + // without any encoding attempt. + const char *bytes = PYBIND11_BYTES_AS_STRING(src.ptr()); + if (bytes) { + value = StringType(bytes, (size_t) PYBIND11_BYTES_SIZE(src.ptr())); + return true; + } + } + + return false; + } + + template + bool load_bytes(enable_if_t) { return false; } +}; + +template +struct type_caster, enable_if_t::value>> + : string_caster> {}; + +#ifdef PYBIND11_HAS_STRING_VIEW +template +struct type_caster, enable_if_t::value>> + : string_caster, true> {}; +#endif + +// Type caster for C-style strings. We basically use a std::string type caster, but also add the +// ability to use None as a nullptr char* (which the string caster doesn't allow). +template struct type_caster::value>> { + using StringType = std::basic_string; + using StringCaster = type_caster; + StringCaster str_caster; + bool none = false; + CharT one_char = 0; +public: + bool load(handle src, bool convert) { + if (!src) return false; + if (src.is_none()) { + // Defer accepting None to other overloads (if we aren't in convert mode): + if (!convert) return false; + none = true; + return true; + } + return str_caster.load(src, convert); + } + + static handle cast(const CharT *src, return_value_policy policy, handle parent) { + if (src == nullptr) return pybind11::none().inc_ref(); + return StringCaster::cast(StringType(src), policy, parent); + } + + static handle cast(CharT src, return_value_policy policy, handle parent) { + if (std::is_same::value) { + handle s = PyUnicode_DecodeLatin1((const char *) &src, 1, nullptr); + if (!s) throw error_already_set(); + return s; + } + return StringCaster::cast(StringType(1, src), policy, parent); + } + + operator CharT*() { return none ? nullptr : const_cast(static_cast(str_caster).c_str()); } + operator CharT&() { + if (none) + throw value_error("Cannot convert None to a character"); + + auto &value = static_cast(str_caster); + size_t str_len = value.size(); + if (str_len == 0) + throw value_error("Cannot convert empty string to a character"); + + // If we're in UTF-8 mode, we have two possible failures: one for a unicode character that + // is too high, and one for multiple unicode characters (caught later), so we need to figure + // out how long the first encoded character is in bytes to distinguish between these two + // errors. We also allow want to allow unicode characters U+0080 through U+00FF, as those + // can fit into a single char value. + if (StringCaster::UTF_N == 8 && str_len > 1 && str_len <= 4) { + unsigned char v0 = static_cast(value[0]); + size_t char0_bytes = !(v0 & 0x80) ? 1 : // low bits only: 0-127 + (v0 & 0xE0) == 0xC0 ? 2 : // 0b110xxxxx - start of 2-byte sequence + (v0 & 0xF0) == 0xE0 ? 3 : // 0b1110xxxx - start of 3-byte sequence + 4; // 0b11110xxx - start of 4-byte sequence + + if (char0_bytes == str_len) { + // If we have a 128-255 value, we can decode it into a single char: + if (char0_bytes == 2 && (v0 & 0xFC) == 0xC0) { // 0x110000xx 0x10xxxxxx + one_char = static_cast(((v0 & 3) << 6) + (static_cast(value[1]) & 0x3F)); + return one_char; + } + // Otherwise we have a single character, but it's > U+00FF + throw value_error("Character code point not in range(0x100)"); + } + } + + // UTF-16 is much easier: we can only have a surrogate pair for values above U+FFFF, thus a + // surrogate pair with total length 2 instantly indicates a range error (but not a "your + // string was too long" error). + else if (StringCaster::UTF_N == 16 && str_len == 2) { + one_char = static_cast(value[0]); + if (one_char >= 0xD800 && one_char < 0xE000) + throw value_error("Character code point not in range(0x10000)"); + } + + if (str_len != 1) + throw value_error("Expected a character, but multi-character string found"); + + one_char = value[0]; + return one_char; + } + + static constexpr auto name = _(PYBIND11_STRING_NAME); + template using cast_op_type = pybind11::detail::cast_op_type<_T>; +}; + +// Base implementation for std::tuple and std::pair +template class Tuple, typename... Ts> class tuple_caster { + using type = Tuple; + static constexpr auto size = sizeof...(Ts); + using indices = make_index_sequence; +public: + + bool load(handle src, bool convert) { + if (!isinstance(src)) + return false; + const auto seq = reinterpret_borrow(src); + if (seq.size() != size) + return false; + return load_impl(seq, convert, indices{}); + } + + template + static handle cast(T &&src, return_value_policy policy, handle parent) { + return cast_impl(std::forward(src), policy, parent, indices{}); + } + + static constexpr auto name = _("Tuple[") + concat(make_caster::name...) + _("]"); + + template using cast_op_type = type; + + operator type() & { return implicit_cast(indices{}); } + operator type() && { return std::move(*this).implicit_cast(indices{}); } + +protected: + template + type implicit_cast(index_sequence) & { return type(cast_op(std::get(subcasters))...); } + template + type implicit_cast(index_sequence) && { return type(cast_op(std::move(std::get(subcasters)))...); } + + static constexpr bool load_impl(const sequence &, bool, index_sequence<>) { return true; } + + template + bool load_impl(const sequence &seq, bool convert, index_sequence) { + for (bool r : {std::get(subcasters).load(seq[Is], convert)...}) + if (!r) + return false; + return true; + } + + /* Implementation: Convert a C++ tuple into a Python tuple */ + template + static handle cast_impl(T &&src, return_value_policy policy, handle parent, index_sequence) { + std::array entries{{ + reinterpret_steal(make_caster::cast(std::get(std::forward(src)), policy, parent))... + }}; + for (const auto &entry: entries) + if (!entry) + return handle(); + tuple result(size); + int counter = 0; + for (auto & entry: entries) + PyTuple_SET_ITEM(result.ptr(), counter++, entry.release().ptr()); + return result.release(); + } + + Tuple...> subcasters; +}; + +template class type_caster> + : public tuple_caster {}; + +template class type_caster> + : public tuple_caster {}; + +/// Helper class which abstracts away certain actions. Users can provide specializations for +/// custom holders, but it's only necessary if the type has a non-standard interface. +template +struct holder_helper { + static auto get(const T &p) -> decltype(p.get()) { return p.get(); } +}; + +/// Type caster for holder types like std::shared_ptr, etc. +template +struct copyable_holder_caster : public type_caster_base { +public: + using base = type_caster_base; + static_assert(std::is_base_of>::value, + "Holder classes are only supported for custom types"); + using base::base; + using base::cast; + using base::typeinfo; + using base::value; + + bool load(handle src, bool convert) { + return base::template load_impl>(src, convert); + } + + explicit operator type*() { return this->value; } + explicit operator type&() { return *(this->value); } + explicit operator holder_type*() { return std::addressof(holder); } + + // Workaround for Intel compiler bug + // see pybind11 issue 94 + #if defined(__ICC) || defined(__INTEL_COMPILER) + operator holder_type&() { return holder; } + #else + explicit operator holder_type&() { return holder; } + #endif + + static handle cast(const holder_type &src, return_value_policy, handle) { + const auto *ptr = holder_helper::get(src); + return type_caster_base::cast_holder(ptr, &src); + } + +protected: + friend class type_caster_generic; + void check_holder_compat() { + if (typeinfo->default_holder) + throw cast_error("Unable to load a custom holder type from a default-holder instance"); + } + + bool load_value(value_and_holder &&v_h) { + if (v_h.holder_constructed()) { + value = v_h.value_ptr(); + holder = v_h.template holder(); + return true; + } else { + throw cast_error("Unable to cast from non-held to held instance (T& to Holder) " +#if defined(NDEBUG) + "(compile in debug mode for type information)"); +#else + "of type '" + type_id() + "''"); +#endif + } + } + + template ::value, int> = 0> + bool try_implicit_casts(handle, bool) { return false; } + + template ::value, int> = 0> + bool try_implicit_casts(handle src, bool convert) { + for (auto &cast : typeinfo->implicit_casts) { + copyable_holder_caster sub_caster(*cast.first); + if (sub_caster.load(src, convert)) { + value = cast.second(sub_caster.value); + holder = holder_type(sub_caster.holder, (type *) value); + return true; + } + } + return false; + } + + static bool try_direct_conversions(handle) { return false; } + + + holder_type holder; +}; + +/// Specialize for the common std::shared_ptr, so users don't need to +template +class type_caster> : public copyable_holder_caster> { }; + +template +struct move_only_holder_caster { + static_assert(std::is_base_of, type_caster>::value, + "Holder classes are only supported for custom types"); + + static handle cast(holder_type &&src, return_value_policy, handle) { + auto *ptr = holder_helper::get(src); + return type_caster_base::cast_holder(ptr, std::addressof(src)); + } + static constexpr auto name = type_caster_base::name; +}; + +template +class type_caster> + : public move_only_holder_caster> { }; + +template +using type_caster_holder = conditional_t::value, + copyable_holder_caster, + move_only_holder_caster>; + +template struct always_construct_holder { static constexpr bool value = Value; }; + +/// Create a specialization for custom holder types (silently ignores std::shared_ptr) +#define PYBIND11_DECLARE_HOLDER_TYPE(type, holder_type, ...) \ + namespace pybind11 { namespace detail { \ + template \ + struct always_construct_holder : always_construct_holder { }; \ + template \ + class type_caster::value>> \ + : public type_caster_holder { }; \ + }} + +// PYBIND11_DECLARE_HOLDER_TYPE holder types: +template struct is_holder_type : + std::is_base_of, detail::type_caster> {}; +// Specialization for always-supported unique_ptr holders: +template struct is_holder_type> : + std::true_type {}; + +template struct handle_type_name { static constexpr auto name = _(); }; +template <> struct handle_type_name { static constexpr auto name = _(PYBIND11_BYTES_NAME); }; +template <> struct handle_type_name { static constexpr auto name = _("*args"); }; +template <> struct handle_type_name { static constexpr auto name = _("**kwargs"); }; + +template +struct pyobject_caster { + template ::value, int> = 0> + bool load(handle src, bool /* convert */) { value = src; return static_cast(value); } + + template ::value, int> = 0> + bool load(handle src, bool /* convert */) { + if (!isinstance(src)) + return false; + value = reinterpret_borrow(src); + return true; + } + + static handle cast(const handle &src, return_value_policy /* policy */, handle /* parent */) { + return src.inc_ref(); + } + PYBIND11_TYPE_CASTER(type, handle_type_name::name); +}; + +template +class type_caster::value>> : public pyobject_caster { }; + +// Our conditions for enabling moving are quite restrictive: +// At compile time: +// - T needs to be a non-const, non-pointer, non-reference type +// - type_caster::operator T&() must exist +// - the type must be move constructible (obviously) +// At run-time: +// - if the type is non-copy-constructible, the object must be the sole owner of the type (i.e. it +// must have ref_count() == 1)h +// If any of the above are not satisfied, we fall back to copying. +template using move_is_plain_type = satisfies_none_of; +template struct move_always : std::false_type {}; +template struct move_always, + negation>, + std::is_move_constructible, + std::is_same>().operator T&()), T&> +>::value>> : std::true_type {}; +template struct move_if_unreferenced : std::false_type {}; +template struct move_if_unreferenced, + negation>, + std::is_move_constructible, + std::is_same>().operator T&()), T&> +>::value>> : std::true_type {}; +template using move_never = none_of, move_if_unreferenced>; + +// Detect whether returning a `type` from a cast on type's type_caster is going to result in a +// reference or pointer to a local variable of the type_caster. Basically, only +// non-reference/pointer `type`s and reference/pointers from a type_caster_generic are safe; +// everything else returns a reference/pointer to a local variable. +template using cast_is_temporary_value_reference = bool_constant< + (std::is_reference::value || std::is_pointer::value) && + !std::is_base_of>::value && + !std::is_same, void>::value +>; + +// When a value returned from a C++ function is being cast back to Python, we almost always want to +// force `policy = move`, regardless of the return value policy the function/method was declared +// with. +template struct return_value_policy_override { + static return_value_policy policy(return_value_policy p) { return p; } +}; + +template struct return_value_policy_override>::value, void>> { + static return_value_policy policy(return_value_policy p) { + return !std::is_lvalue_reference::value && + !std::is_pointer::value + ? return_value_policy::move : p; + } +}; + +// Basic python -> C++ casting; throws if casting fails +template type_caster &load_type(type_caster &conv, const handle &handle) { + if (!conv.load(handle, true)) { +#if defined(NDEBUG) + throw cast_error("Unable to cast Python instance to C++ type (compile in debug mode for details)"); +#else + throw cast_error("Unable to cast Python instance of type " + + (std::string) str(handle.get_type()) + " to C++ type '" + type_id() + "'"); +#endif + } + return conv; +} +// Wrapper around the above that also constructs and returns a type_caster +template make_caster load_type(const handle &handle) { + make_caster conv; + load_type(conv, handle); + return conv; +} + +NAMESPACE_END(detail) + +// pytype -> C++ type +template ::value, int> = 0> +T cast(const handle &handle) { + using namespace detail; + static_assert(!cast_is_temporary_value_reference::value, + "Unable to cast type to reference: value is local to type caster"); + return cast_op(load_type(handle)); +} + +// pytype -> pytype (calls converting constructor) +template ::value, int> = 0> +T cast(const handle &handle) { return T(reinterpret_borrow(handle)); } + +// C++ type -> py::object +template ::value, int> = 0> +object cast(const T &value, return_value_policy policy = return_value_policy::automatic_reference, + handle parent = handle()) { + if (policy == return_value_policy::automatic) + policy = std::is_pointer::value ? return_value_policy::take_ownership : return_value_policy::copy; + else if (policy == return_value_policy::automatic_reference) + policy = std::is_pointer::value ? return_value_policy::reference : return_value_policy::copy; + return reinterpret_steal(detail::make_caster::cast(value, policy, parent)); +} + +template T handle::cast() const { return pybind11::cast(*this); } +template <> inline void handle::cast() const { return; } + +template +detail::enable_if_t::value, T> move(object &&obj) { + if (obj.ref_count() > 1) +#if defined(NDEBUG) + throw cast_error("Unable to cast Python instance to C++ rvalue: instance has multiple references" + " (compile in debug mode for details)"); +#else + throw cast_error("Unable to move from Python " + (std::string) str(obj.get_type()) + + " instance to C++ " + type_id() + " instance: instance has multiple references"); +#endif + + // Move into a temporary and return that, because the reference may be a local value of `conv` + T ret = std::move(detail::load_type(obj).operator T&()); + return ret; +} + +// Calling cast() on an rvalue calls pybind::cast with the object rvalue, which does: +// - If we have to move (because T has no copy constructor), do it. This will fail if the moved +// object has multiple references, but trying to copy will fail to compile. +// - If both movable and copyable, check ref count: if 1, move; otherwise copy +// - Otherwise (not movable), copy. +template detail::enable_if_t::value, T> cast(object &&object) { + return move(std::move(object)); +} +template detail::enable_if_t::value, T> cast(object &&object) { + if (object.ref_count() > 1) + return cast(object); + else + return move(std::move(object)); +} +template detail::enable_if_t::value, T> cast(object &&object) { + return cast(object); +} + +template T object::cast() const & { return pybind11::cast(*this); } +template T object::cast() && { return pybind11::cast(std::move(*this)); } +template <> inline void object::cast() const & { return; } +template <> inline void object::cast() && { return; } + +NAMESPACE_BEGIN(detail) + +// Declared in pytypes.h: +template ::value, int>> +object object_or_cast(T &&o) { return pybind11::cast(std::forward(o)); } + +struct overload_unused {}; // Placeholder type for the unneeded (and dead code) static variable in the OVERLOAD_INT macro +template using overload_caster_t = conditional_t< + cast_is_temporary_value_reference::value, make_caster, overload_unused>; + +// Trampoline use: for reference/pointer types to value-converted values, we do a value cast, then +// store the result in the given variable. For other types, this is a no-op. +template enable_if_t::value, T> cast_ref(object &&o, make_caster &caster) { + return cast_op(load_type(caster, o)); +} +template enable_if_t::value, T> cast_ref(object &&, overload_unused &) { + pybind11_fail("Internal error: cast_ref fallback invoked"); } + +// Trampoline use: Having a pybind11::cast with an invalid reference type is going to static_assert, even +// though if it's in dead code, so we provide a "trampoline" to pybind11::cast that only does anything in +// cases where pybind11::cast is valid. +template enable_if_t::value, T> cast_safe(object &&o) { + return pybind11::cast(std::move(o)); } +template enable_if_t::value, T> cast_safe(object &&) { + pybind11_fail("Internal error: cast_safe fallback invoked"); } +template <> inline void cast_safe(object &&) {} + +NAMESPACE_END(detail) + +template +tuple make_tuple() { return tuple(0); } + +template tuple make_tuple(Args&&... args_) { + constexpr size_t size = sizeof...(Args); + std::array args { + { reinterpret_steal(detail::make_caster::cast( + std::forward(args_), policy, nullptr))... } + }; + for (size_t i = 0; i < args.size(); i++) { + if (!args[i]) { +#if defined(NDEBUG) + throw cast_error("make_tuple(): unable to convert arguments to Python object (compile in debug mode for details)"); +#else + std::array argtypes { {type_id()...} }; + throw cast_error("make_tuple(): unable to convert argument of type '" + + argtypes[i] + "' to Python object"); +#endif + } + } + tuple result(size); + int counter = 0; + for (auto &arg_value : args) + PyTuple_SET_ITEM(result.ptr(), counter++, arg_value.release().ptr()); + return result; +} + +/// \ingroup annotations +/// Annotation for arguments +struct arg { + /// Constructs an argument with the name of the argument; if null or omitted, this is a positional argument. + constexpr explicit arg(const char *name = nullptr) : name(name), flag_noconvert(false), flag_none(true) { } + /// Assign a value to this argument + template arg_v operator=(T &&value) const; + /// Indicate that the type should not be converted in the type caster + arg &noconvert(bool flag = true) { flag_noconvert = flag; return *this; } + /// Indicates that the argument should/shouldn't allow None (e.g. for nullable pointer args) + arg &none(bool flag = true) { flag_none = flag; return *this; } + + const char *name; ///< If non-null, this is a named kwargs argument + bool flag_noconvert : 1; ///< If set, do not allow conversion (requires a supporting type caster!) + bool flag_none : 1; ///< If set (the default), allow None to be passed to this argument +}; + +/// \ingroup annotations +/// Annotation for arguments with values +struct arg_v : arg { +private: + template + arg_v(arg &&base, T &&x, const char *descr = nullptr) + : arg(base), + value(reinterpret_steal( + detail::make_caster::cast(x, return_value_policy::automatic, {}) + )), + descr(descr) +#if !defined(NDEBUG) + , type(type_id()) +#endif + { } + +public: + /// Direct construction with name, default, and description + template + arg_v(const char *name, T &&x, const char *descr = nullptr) + : arg_v(arg(name), std::forward(x), descr) { } + + /// Called internally when invoking `py::arg("a") = value` + template + arg_v(const arg &base, T &&x, const char *descr = nullptr) + : arg_v(arg(base), std::forward(x), descr) { } + + /// Same as `arg::noconvert()`, but returns *this as arg_v&, not arg& + arg_v &noconvert(bool flag = true) { arg::noconvert(flag); return *this; } + + /// Same as `arg::nonone()`, but returns *this as arg_v&, not arg& + arg_v &none(bool flag = true) { arg::none(flag); return *this; } + + /// The default value + object value; + /// The (optional) description of the default value + const char *descr; +#if !defined(NDEBUG) + /// The C++ type name of the default value (only available when compiled in debug mode) + std::string type; +#endif +}; + +template +arg_v arg::operator=(T &&value) const { return {std::move(*this), std::forward(value)}; } + +/// Alias for backward compatibility -- to be removed in version 2.0 +template using arg_t = arg_v; + +inline namespace literals { +/** \rst + String literal version of `arg` + \endrst */ +constexpr arg operator"" _a(const char *name, size_t) { return arg(name); } +} + +NAMESPACE_BEGIN(detail) + +// forward declaration (definition in attr.h) +struct function_record; + +/// Internal data associated with a single function call +struct function_call { + function_call(const function_record &f, handle p); // Implementation in attr.h + + /// The function data: + const function_record &func; + + /// Arguments passed to the function: + std::vector args; + + /// The `convert` value the arguments should be loaded with + std::vector args_convert; + + /// Extra references for the optional `py::args` and/or `py::kwargs` arguments (which, if + /// present, are also in `args` but without a reference). + object args_ref, kwargs_ref; + + /// The parent, if any + handle parent; + + /// If this is a call to an initializer, this argument contains `self` + handle init_self; +}; + + +/// Helper class which loads arguments for C++ functions called from Python +template +class argument_loader { + using indices = make_index_sequence; + + template using argument_is_args = std::is_same, args>; + template using argument_is_kwargs = std::is_same, kwargs>; + // Get args/kwargs argument positions relative to the end of the argument list: + static constexpr auto args_pos = constexpr_first() - (int) sizeof...(Args), + kwargs_pos = constexpr_first() - (int) sizeof...(Args); + + static constexpr bool args_kwargs_are_last = kwargs_pos >= - 1 && args_pos >= kwargs_pos - 1; + + static_assert(args_kwargs_are_last, "py::args/py::kwargs are only permitted as the last argument(s) of a function"); + +public: + static constexpr bool has_kwargs = kwargs_pos < 0; + static constexpr bool has_args = args_pos < 0; + + static constexpr auto arg_names = concat(type_descr(make_caster::name)...); + + bool load_args(function_call &call) { + return load_impl_sequence(call, indices{}); + } + + template + enable_if_t::value, Return> call(Func &&f) && { + return std::move(*this).template call_impl(std::forward(f), indices{}, Guard{}); + } + + template + enable_if_t::value, void_type> call(Func &&f) && { + std::move(*this).template call_impl(std::forward(f), indices{}, Guard{}); + return void_type(); + } + +private: + + static bool load_impl_sequence(function_call &, index_sequence<>) { return true; } + + template + bool load_impl_sequence(function_call &call, index_sequence) { + for (bool r : {std::get(argcasters).load(call.args[Is], call.args_convert[Is])...}) + if (!r) + return false; + return true; + } + + template + Return call_impl(Func &&f, index_sequence, Guard &&) { + return std::forward(f)(cast_op(std::move(std::get(argcasters)))...); + } + + std::tuple...> argcasters; +}; + +/// Helper class which collects only positional arguments for a Python function call. +/// A fancier version below can collect any argument, but this one is optimal for simple calls. +template +class simple_collector { +public: + template + explicit simple_collector(Ts &&...values) + : m_args(pybind11::make_tuple(std::forward(values)...)) { } + + const tuple &args() const & { return m_args; } + dict kwargs() const { return {}; } + + tuple args() && { return std::move(m_args); } + + /// Call a Python function and pass the collected arguments + object call(PyObject *ptr) const { + PyObject *result = PyObject_CallObject(ptr, m_args.ptr()); + if (!result) + throw error_already_set(); + return reinterpret_steal(result); + } + +private: + tuple m_args; +}; + +/// Helper class which collects positional, keyword, * and ** arguments for a Python function call +template +class unpacking_collector { +public: + template + explicit unpacking_collector(Ts &&...values) { + // Tuples aren't (easily) resizable so a list is needed for collection, + // but the actual function call strictly requires a tuple. + auto args_list = list(); + int _[] = { 0, (process(args_list, std::forward(values)), 0)... }; + ignore_unused(_); + + m_args = std::move(args_list); + } + + const tuple &args() const & { return m_args; } + const dict &kwargs() const & { return m_kwargs; } + + tuple args() && { return std::move(m_args); } + dict kwargs() && { return std::move(m_kwargs); } + + /// Call a Python function and pass the collected arguments + object call(PyObject *ptr) const { + PyObject *result = PyObject_Call(ptr, m_args.ptr(), m_kwargs.ptr()); + if (!result) + throw error_already_set(); + return reinterpret_steal(result); + } + +private: + template + void process(list &args_list, T &&x) { + auto o = reinterpret_steal(detail::make_caster::cast(std::forward(x), policy, {})); + if (!o) { +#if defined(NDEBUG) + argument_cast_error(); +#else + argument_cast_error(std::to_string(args_list.size()), type_id()); +#endif + } + args_list.append(o); + } + + void process(list &args_list, detail::args_proxy ap) { + for (const auto &a : ap) + args_list.append(a); + } + + void process(list &/*args_list*/, arg_v a) { + if (!a.name) +#if defined(NDEBUG) + nameless_argument_error(); +#else + nameless_argument_error(a.type); +#endif + + if (m_kwargs.contains(a.name)) { +#if defined(NDEBUG) + multiple_values_error(); +#else + multiple_values_error(a.name); +#endif + } + if (!a.value) { +#if defined(NDEBUG) + argument_cast_error(); +#else + argument_cast_error(a.name, a.type); +#endif + } + m_kwargs[a.name] = a.value; + } + + void process(list &/*args_list*/, detail::kwargs_proxy kp) { + if (!kp) + return; + for (const auto &k : reinterpret_borrow(kp)) { + if (m_kwargs.contains(k.first)) { +#if defined(NDEBUG) + multiple_values_error(); +#else + multiple_values_error(str(k.first)); +#endif + } + m_kwargs[k.first] = k.second; + } + } + + [[noreturn]] static void nameless_argument_error() { + throw type_error("Got kwargs without a name; only named arguments " + "may be passed via py::arg() to a python function call. " + "(compile in debug mode for details)"); + } + [[noreturn]] static void nameless_argument_error(std::string type) { + throw type_error("Got kwargs without a name of type '" + type + "'; only named " + "arguments may be passed via py::arg() to a python function call. "); + } + [[noreturn]] static void multiple_values_error() { + throw type_error("Got multiple values for keyword argument " + "(compile in debug mode for details)"); + } + + [[noreturn]] static void multiple_values_error(std::string name) { + throw type_error("Got multiple values for keyword argument '" + name + "'"); + } + + [[noreturn]] static void argument_cast_error() { + throw cast_error("Unable to convert call argument to Python object " + "(compile in debug mode for details)"); + } + + [[noreturn]] static void argument_cast_error(std::string name, std::string type) { + throw cast_error("Unable to convert call argument '" + name + + "' of type '" + type + "' to Python object"); + } + +private: + tuple m_args; + dict m_kwargs; +}; + +/// Collect only positional arguments for a Python function call +template ...>::value>> +simple_collector collect_arguments(Args &&...args) { + return simple_collector(std::forward(args)...); +} + +/// Collect all arguments, including keywords and unpacking (only instantiated when needed) +template ...>::value>> +unpacking_collector collect_arguments(Args &&...args) { + // Following argument order rules for generalized unpacking according to PEP 448 + static_assert( + constexpr_last() < constexpr_first() + && constexpr_last() < constexpr_first(), + "Invalid function call: positional args must precede keywords and ** unpacking; " + "* unpacking must precede ** unpacking" + ); + return unpacking_collector(std::forward(args)...); +} + +template +template +object object_api::operator()(Args &&...args) const { + return detail::collect_arguments(std::forward(args)...).call(derived().ptr()); +} + +template +template +object object_api::call(Args &&...args) const { + return operator()(std::forward(args)...); +} + +NAMESPACE_END(detail) + +#define PYBIND11_MAKE_OPAQUE(...) \ + namespace pybind11 { namespace detail { \ + template<> class type_caster<__VA_ARGS__> : public type_caster_base<__VA_ARGS__> { }; \ + }} + +/// Lets you pass a type containing a `,` through a macro parameter without needing a separate +/// typedef, e.g.: `PYBIND11_OVERLOAD(PYBIND11_TYPE(ReturnType), PYBIND11_TYPE(Parent), f, arg)` +#define PYBIND11_TYPE(...) __VA_ARGS__ + +NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/ptocr/postprocess/piexlmerge/include/pybind11/chrono.h b/ptocr/postprocess/piexlmerge/include/pybind11/chrono.h new file mode 100644 index 0000000..95ada76 --- /dev/null +++ b/ptocr/postprocess/piexlmerge/include/pybind11/chrono.h @@ -0,0 +1,162 @@ +/* + pybind11/chrono.h: Transparent conversion between std::chrono and python's datetime + + Copyright (c) 2016 Trent Houliston and + Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "pybind11.h" +#include +#include +#include +#include + +// Backport the PyDateTime_DELTA functions from Python3.3 if required +#ifndef PyDateTime_DELTA_GET_DAYS +#define PyDateTime_DELTA_GET_DAYS(o) (((PyDateTime_Delta*)o)->days) +#endif +#ifndef PyDateTime_DELTA_GET_SECONDS +#define PyDateTime_DELTA_GET_SECONDS(o) (((PyDateTime_Delta*)o)->seconds) +#endif +#ifndef PyDateTime_DELTA_GET_MICROSECONDS +#define PyDateTime_DELTA_GET_MICROSECONDS(o) (((PyDateTime_Delta*)o)->microseconds) +#endif + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +NAMESPACE_BEGIN(detail) + +template class duration_caster { +public: + typedef typename type::rep rep; + typedef typename type::period period; + + typedef std::chrono::duration> days; + + bool load(handle src, bool) { + using namespace std::chrono; + + // Lazy initialise the PyDateTime import + if (!PyDateTimeAPI) { PyDateTime_IMPORT; } + + if (!src) return false; + // If invoked with datetime.delta object + if (PyDelta_Check(src.ptr())) { + value = type(duration_cast>( + days(PyDateTime_DELTA_GET_DAYS(src.ptr())) + + seconds(PyDateTime_DELTA_GET_SECONDS(src.ptr())) + + microseconds(PyDateTime_DELTA_GET_MICROSECONDS(src.ptr())))); + return true; + } + // If invoked with a float we assume it is seconds and convert + else if (PyFloat_Check(src.ptr())) { + value = type(duration_cast>(duration(PyFloat_AsDouble(src.ptr())))); + return true; + } + else return false; + } + + // If this is a duration just return it back + static const std::chrono::duration& get_duration(const std::chrono::duration &src) { + return src; + } + + // If this is a time_point get the time_since_epoch + template static std::chrono::duration get_duration(const std::chrono::time_point> &src) { + return src.time_since_epoch(); + } + + static handle cast(const type &src, return_value_policy /* policy */, handle /* parent */) { + using namespace std::chrono; + + // Use overloaded function to get our duration from our source + // Works out if it is a duration or time_point and get the duration + auto d = get_duration(src); + + // Lazy initialise the PyDateTime import + if (!PyDateTimeAPI) { PyDateTime_IMPORT; } + + // Declare these special duration types so the conversions happen with the correct primitive types (int) + using dd_t = duration>; + using ss_t = duration>; + using us_t = duration; + + auto dd = duration_cast(d); + auto subd = d - dd; + auto ss = duration_cast(subd); + auto us = duration_cast(subd - ss); + return PyDelta_FromDSU(dd.count(), ss.count(), us.count()); + } + + PYBIND11_TYPE_CASTER(type, _("datetime.timedelta")); +}; + +// This is for casting times on the system clock into datetime.datetime instances +template class type_caster> { +public: + typedef std::chrono::time_point type; + bool load(handle src, bool) { + using namespace std::chrono; + + // Lazy initialise the PyDateTime import + if (!PyDateTimeAPI) { PyDateTime_IMPORT; } + + if (!src) return false; + if (PyDateTime_Check(src.ptr())) { + std::tm cal; + cal.tm_sec = PyDateTime_DATE_GET_SECOND(src.ptr()); + cal.tm_min = PyDateTime_DATE_GET_MINUTE(src.ptr()); + cal.tm_hour = PyDateTime_DATE_GET_HOUR(src.ptr()); + cal.tm_mday = PyDateTime_GET_DAY(src.ptr()); + cal.tm_mon = PyDateTime_GET_MONTH(src.ptr()) - 1; + cal.tm_year = PyDateTime_GET_YEAR(src.ptr()) - 1900; + cal.tm_isdst = -1; + + value = system_clock::from_time_t(std::mktime(&cal)) + microseconds(PyDateTime_DATE_GET_MICROSECOND(src.ptr())); + return true; + } + else return false; + } + + static handle cast(const std::chrono::time_point &src, return_value_policy /* policy */, handle /* parent */) { + using namespace std::chrono; + + // Lazy initialise the PyDateTime import + if (!PyDateTimeAPI) { PyDateTime_IMPORT; } + + std::time_t tt = system_clock::to_time_t(src); + // this function uses static memory so it's best to copy it out asap just in case + // otherwise other code that is using localtime may break this (not just python code) + std::tm localtime = *std::localtime(&tt); + + // Declare these special duration types so the conversions happen with the correct primitive types (int) + using us_t = duration; + + return PyDateTime_FromDateAndTime(localtime.tm_year + 1900, + localtime.tm_mon + 1, + localtime.tm_mday, + localtime.tm_hour, + localtime.tm_min, + localtime.tm_sec, + (duration_cast(src.time_since_epoch() % seconds(1))).count()); + } + PYBIND11_TYPE_CASTER(type, _("datetime.datetime")); +}; + +// Other clocks that are not the system clock are not measured as datetime.datetime objects +// since they are not measured on calendar time. So instead we just make them timedeltas +// Or if they have passed us a time as a float we convert that +template class type_caster> +: public duration_caster> { +}; + +template class type_caster> +: public duration_caster> { +}; + +NAMESPACE_END(detail) +NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/ptocr/postprocess/piexlmerge/include/pybind11/class_support.h b/ptocr/postprocess/piexlmerge/include/pybind11/class_support.h new file mode 100644 index 0000000..8e18c4c --- /dev/null +++ b/ptocr/postprocess/piexlmerge/include/pybind11/class_support.h @@ -0,0 +1,603 @@ +/* + pybind11/class_support.h: Python C API implementation details for py::class_ + + Copyright (c) 2017 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "attr.h" + +NAMESPACE_BEGIN(pybind11) +NAMESPACE_BEGIN(detail) + +inline PyTypeObject *type_incref(PyTypeObject *type) { + Py_INCREF(type); + return type; +} + +#if !defined(PYPY_VERSION) + +/// `pybind11_static_property.__get__()`: Always pass the class instead of the instance. +extern "C" inline PyObject *pybind11_static_get(PyObject *self, PyObject * /*ob*/, PyObject *cls) { + return PyProperty_Type.tp_descr_get(self, cls, cls); +} + +/// `pybind11_static_property.__set__()`: Just like the above `__get__()`. +extern "C" inline int pybind11_static_set(PyObject *self, PyObject *obj, PyObject *value) { + PyObject *cls = PyType_Check(obj) ? obj : (PyObject *) Py_TYPE(obj); + return PyProperty_Type.tp_descr_set(self, cls, value); +} + +/** A `static_property` is the same as a `property` but the `__get__()` and `__set__()` + methods are modified to always use the object type instead of a concrete instance. + Return value: New reference. */ +inline PyTypeObject *make_static_property_type() { + constexpr auto *name = "pybind11_static_property"; + auto name_obj = reinterpret_steal(PYBIND11_FROM_STRING(name)); + + /* Danger zone: from now (and until PyType_Ready), make sure to + issue no Python C API calls which could potentially invoke the + garbage collector (the GC will call type_traverse(), which will in + turn find the newly constructed type in an invalid state) */ + auto heap_type = (PyHeapTypeObject *) PyType_Type.tp_alloc(&PyType_Type, 0); + if (!heap_type) + pybind11_fail("make_static_property_type(): error allocating type!"); + + heap_type->ht_name = name_obj.inc_ref().ptr(); +#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 3 + heap_type->ht_qualname = name_obj.inc_ref().ptr(); +#endif + + auto type = &heap_type->ht_type; + type->tp_name = name; + type->tp_base = type_incref(&PyProperty_Type); + type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE; + type->tp_descr_get = pybind11_static_get; + type->tp_descr_set = pybind11_static_set; + + if (PyType_Ready(type) < 0) + pybind11_fail("make_static_property_type(): failure in PyType_Ready()!"); + + setattr((PyObject *) type, "__module__", str("pybind11_builtins")); + + return type; +} + +#else // PYPY + +/** PyPy has some issues with the above C API, so we evaluate Python code instead. + This function will only be called once so performance isn't really a concern. + Return value: New reference. */ +inline PyTypeObject *make_static_property_type() { + auto d = dict(); + PyObject *result = PyRun_String(R"(\ + class pybind11_static_property(property): + def __get__(self, obj, cls): + return property.__get__(self, cls, cls) + + def __set__(self, obj, value): + cls = obj if isinstance(obj, type) else type(obj) + property.__set__(self, cls, value) + )", Py_file_input, d.ptr(), d.ptr() + ); + if (result == nullptr) + throw error_already_set(); + Py_DECREF(result); + return (PyTypeObject *) d["pybind11_static_property"].cast().release().ptr(); +} + +#endif // PYPY + +/** Types with static properties need to handle `Type.static_prop = x` in a specific way. + By default, Python replaces the `static_property` itself, but for wrapped C++ types + we need to call `static_property.__set__()` in order to propagate the new value to + the underlying C++ data structure. */ +extern "C" inline int pybind11_meta_setattro(PyObject* obj, PyObject* name, PyObject* value) { + // Use `_PyType_Lookup()` instead of `PyObject_GetAttr()` in order to get the raw + // descriptor (`property`) instead of calling `tp_descr_get` (`property.__get__()`). + PyObject *descr = _PyType_Lookup((PyTypeObject *) obj, name); + + // The following assignment combinations are possible: + // 1. `Type.static_prop = value` --> descr_set: `Type.static_prop.__set__(value)` + // 2. `Type.static_prop = other_static_prop` --> setattro: replace existing `static_prop` + // 3. `Type.regular_attribute = value` --> setattro: regular attribute assignment + const auto static_prop = (PyObject *) get_internals().static_property_type; + const auto call_descr_set = descr && PyObject_IsInstance(descr, static_prop) + && !PyObject_IsInstance(value, static_prop); + if (call_descr_set) { + // Call `static_property.__set__()` instead of replacing the `static_property`. +#if !defined(PYPY_VERSION) + return Py_TYPE(descr)->tp_descr_set(descr, obj, value); +#else + if (PyObject *result = PyObject_CallMethod(descr, "__set__", "OO", obj, value)) { + Py_DECREF(result); + return 0; + } else { + return -1; + } +#endif + } else { + // Replace existing attribute. + return PyType_Type.tp_setattro(obj, name, value); + } +} + +#if PY_MAJOR_VERSION >= 3 +/** + * Python 3's PyInstanceMethod_Type hides itself via its tp_descr_get, which prevents aliasing + * methods via cls.attr("m2") = cls.attr("m1"): instead the tp_descr_get returns a plain function, + * when called on a class, or a PyMethod, when called on an instance. Override that behaviour here + * to do a special case bypass for PyInstanceMethod_Types. + */ +extern "C" inline PyObject *pybind11_meta_getattro(PyObject *obj, PyObject *name) { + PyObject *descr = _PyType_Lookup((PyTypeObject *) obj, name); + if (descr && PyInstanceMethod_Check(descr)) { + Py_INCREF(descr); + return descr; + } + else { + return PyType_Type.tp_getattro(obj, name); + } +} +#endif + +/** This metaclass is assigned by default to all pybind11 types and is required in order + for static properties to function correctly. Users may override this using `py::metaclass`. + Return value: New reference. */ +inline PyTypeObject* make_default_metaclass() { + constexpr auto *name = "pybind11_type"; + auto name_obj = reinterpret_steal(PYBIND11_FROM_STRING(name)); + + /* Danger zone: from now (and until PyType_Ready), make sure to + issue no Python C API calls which could potentially invoke the + garbage collector (the GC will call type_traverse(), which will in + turn find the newly constructed type in an invalid state) */ + auto heap_type = (PyHeapTypeObject *) PyType_Type.tp_alloc(&PyType_Type, 0); + if (!heap_type) + pybind11_fail("make_default_metaclass(): error allocating metaclass!"); + + heap_type->ht_name = name_obj.inc_ref().ptr(); +#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 3 + heap_type->ht_qualname = name_obj.inc_ref().ptr(); +#endif + + auto type = &heap_type->ht_type; + type->tp_name = name; + type->tp_base = type_incref(&PyType_Type); + type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE; + + type->tp_setattro = pybind11_meta_setattro; +#if PY_MAJOR_VERSION >= 3 + type->tp_getattro = pybind11_meta_getattro; +#endif + + if (PyType_Ready(type) < 0) + pybind11_fail("make_default_metaclass(): failure in PyType_Ready()!"); + + setattr((PyObject *) type, "__module__", str("pybind11_builtins")); + + return type; +} + +/// For multiple inheritance types we need to recursively register/deregister base pointers for any +/// base classes with pointers that are difference from the instance value pointer so that we can +/// correctly recognize an offset base class pointer. This calls a function with any offset base ptrs. +inline void traverse_offset_bases(void *valueptr, const detail::type_info *tinfo, instance *self, + bool (*f)(void * /*parentptr*/, instance * /*self*/)) { + for (handle h : reinterpret_borrow(tinfo->type->tp_bases)) { + if (auto parent_tinfo = get_type_info((PyTypeObject *) h.ptr())) { + for (auto &c : parent_tinfo->implicit_casts) { + if (c.first == tinfo->cpptype) { + auto *parentptr = c.second(valueptr); + if (parentptr != valueptr) + f(parentptr, self); + traverse_offset_bases(parentptr, parent_tinfo, self, f); + break; + } + } + } + } +} + +inline bool register_instance_impl(void *ptr, instance *self) { + get_internals().registered_instances.emplace(ptr, self); + return true; // unused, but gives the same signature as the deregister func +} +inline bool deregister_instance_impl(void *ptr, instance *self) { + auto ®istered_instances = get_internals().registered_instances; + auto range = registered_instances.equal_range(ptr); + for (auto it = range.first; it != range.second; ++it) { + if (Py_TYPE(self) == Py_TYPE(it->second)) { + registered_instances.erase(it); + return true; + } + } + return false; +} + +inline void register_instance(instance *self, void *valptr, const type_info *tinfo) { + register_instance_impl(valptr, self); + if (!tinfo->simple_ancestors) + traverse_offset_bases(valptr, tinfo, self, register_instance_impl); +} + +inline bool deregister_instance(instance *self, void *valptr, const type_info *tinfo) { + bool ret = deregister_instance_impl(valptr, self); + if (!tinfo->simple_ancestors) + traverse_offset_bases(valptr, tinfo, self, deregister_instance_impl); + return ret; +} + +/// Instance creation function for all pybind11 types. It only allocates space for the C++ object +/// (or multiple objects, for Python-side inheritance from multiple pybind11 types), but doesn't +/// call the constructor -- an `__init__` function must do that (followed by an `init_instance` +/// to set up the holder and register the instance). +inline PyObject *make_new_instance(PyTypeObject *type, bool allocate_value /*= true (in cast.h)*/) { +#if defined(PYPY_VERSION) + // PyPy gets tp_basicsize wrong (issue 2482) under multiple inheritance when the first inherited + // object is a a plain Python type (i.e. not derived from an extension type). Fix it. + ssize_t instance_size = static_cast(sizeof(instance)); + if (type->tp_basicsize < instance_size) { + type->tp_basicsize = instance_size; + } +#endif + PyObject *self = type->tp_alloc(type, 0); + auto inst = reinterpret_cast(self); + // Allocate the value/holder internals: + inst->allocate_layout(); + + inst->owned = true; + // Allocate (if requested) the value pointers; otherwise leave them as nullptr + if (allocate_value) { + for (auto &v_h : values_and_holders(inst)) { + void *&vptr = v_h.value_ptr(); + vptr = v_h.type->operator_new(v_h.type->type_size); + } + } + + return self; +} + +/// Instance creation function for all pybind11 types. It only allocates space for the +/// C++ object, but doesn't call the constructor -- an `__init__` function must do that. +extern "C" inline PyObject *pybind11_object_new(PyTypeObject *type, PyObject *, PyObject *) { + return make_new_instance(type); +} + +/// An `__init__` function constructs the C++ object. Users should provide at least one +/// of these using `py::init` or directly with `.def(__init__, ...)`. Otherwise, the +/// following default function will be used which simply throws an exception. +extern "C" inline int pybind11_object_init(PyObject *self, PyObject *, PyObject *) { + PyTypeObject *type = Py_TYPE(self); + std::string msg; +#if defined(PYPY_VERSION) + msg += handle((PyObject *) type).attr("__module__").cast() + "."; +#endif + msg += type->tp_name; + msg += ": No constructor defined!"; + PyErr_SetString(PyExc_TypeError, msg.c_str()); + return -1; +} + +inline void add_patient(PyObject *nurse, PyObject *patient) { + auto &internals = get_internals(); + auto instance = reinterpret_cast(nurse); + instance->has_patients = true; + Py_INCREF(patient); + internals.patients[nurse].push_back(patient); +} + +inline void clear_patients(PyObject *self) { + auto instance = reinterpret_cast(self); + auto &internals = get_internals(); + auto pos = internals.patients.find(self); + assert(pos != internals.patients.end()); + // Clearing the patients can cause more Python code to run, which + // can invalidate the iterator. Extract the vector of patients + // from the unordered_map first. + auto patients = std::move(pos->second); + internals.patients.erase(pos); + instance->has_patients = false; + for (PyObject *&patient : patients) + Py_CLEAR(patient); +} + +/// Clears all internal data from the instance and removes it from registered instances in +/// preparation for deallocation. +inline void clear_instance(PyObject *self) { + auto instance = reinterpret_cast(self); + + // Deallocate any values/holders, if present: + for (auto &v_h : values_and_holders(instance)) { + if (v_h) { + + // We have to deregister before we call dealloc because, for virtual MI types, we still + // need to be able to get the parent pointers. + if (v_h.instance_registered() && !deregister_instance(instance, v_h.value_ptr(), v_h.type)) + pybind11_fail("pybind11_object_dealloc(): Tried to deallocate unregistered instance!"); + + if (instance->owned || v_h.holder_constructed()) + v_h.type->dealloc(v_h); + } + } + // Deallocate the value/holder layout internals: + instance->deallocate_layout(); + + if (instance->weakrefs) + PyObject_ClearWeakRefs(self); + + PyObject **dict_ptr = _PyObject_GetDictPtr(self); + if (dict_ptr) + Py_CLEAR(*dict_ptr); + + if (instance->has_patients) + clear_patients(self); +} + +/// Instance destructor function for all pybind11 types. It calls `type_info.dealloc` +/// to destroy the C++ object itself, while the rest is Python bookkeeping. +extern "C" inline void pybind11_object_dealloc(PyObject *self) { + clear_instance(self); + Py_TYPE(self)->tp_free(self); +} + +/** Create the type which can be used as a common base for all classes. This is + needed in order to satisfy Python's requirements for multiple inheritance. + Return value: New reference. */ +inline PyObject *make_object_base_type(PyTypeObject *metaclass) { + constexpr auto *name = "pybind11_object"; + auto name_obj = reinterpret_steal(PYBIND11_FROM_STRING(name)); + + /* Danger zone: from now (and until PyType_Ready), make sure to + issue no Python C API calls which could potentially invoke the + garbage collector (the GC will call type_traverse(), which will in + turn find the newly constructed type in an invalid state) */ + auto heap_type = (PyHeapTypeObject *) metaclass->tp_alloc(metaclass, 0); + if (!heap_type) + pybind11_fail("make_object_base_type(): error allocating type!"); + + heap_type->ht_name = name_obj.inc_ref().ptr(); +#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 3 + heap_type->ht_qualname = name_obj.inc_ref().ptr(); +#endif + + auto type = &heap_type->ht_type; + type->tp_name = name; + type->tp_base = type_incref(&PyBaseObject_Type); + type->tp_basicsize = static_cast(sizeof(instance)); + type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE; + + type->tp_new = pybind11_object_new; + type->tp_init = pybind11_object_init; + type->tp_dealloc = pybind11_object_dealloc; + + /* Support weak references (needed for the keep_alive feature) */ + type->tp_weaklistoffset = offsetof(instance, weakrefs); + + if (PyType_Ready(type) < 0) + pybind11_fail("PyType_Ready failed in make_object_base_type():" + error_string()); + + setattr((PyObject *) type, "__module__", str("pybind11_builtins")); + + assert(!PyType_HasFeature(type, Py_TPFLAGS_HAVE_GC)); + return (PyObject *) heap_type; +} + +/// dynamic_attr: Support for `d = instance.__dict__`. +extern "C" inline PyObject *pybind11_get_dict(PyObject *self, void *) { + PyObject *&dict = *_PyObject_GetDictPtr(self); + if (!dict) + dict = PyDict_New(); + Py_XINCREF(dict); + return dict; +} + +/// dynamic_attr: Support for `instance.__dict__ = dict()`. +extern "C" inline int pybind11_set_dict(PyObject *self, PyObject *new_dict, void *) { + if (!PyDict_Check(new_dict)) { + PyErr_Format(PyExc_TypeError, "__dict__ must be set to a dictionary, not a '%.200s'", + Py_TYPE(new_dict)->tp_name); + return -1; + } + PyObject *&dict = *_PyObject_GetDictPtr(self); + Py_INCREF(new_dict); + Py_CLEAR(dict); + dict = new_dict; + return 0; +} + +/// dynamic_attr: Allow the garbage collector to traverse the internal instance `__dict__`. +extern "C" inline int pybind11_traverse(PyObject *self, visitproc visit, void *arg) { + PyObject *&dict = *_PyObject_GetDictPtr(self); + Py_VISIT(dict); + return 0; +} + +/// dynamic_attr: Allow the GC to clear the dictionary. +extern "C" inline int pybind11_clear(PyObject *self) { + PyObject *&dict = *_PyObject_GetDictPtr(self); + Py_CLEAR(dict); + return 0; +} + +/// Give instances of this type a `__dict__` and opt into garbage collection. +inline void enable_dynamic_attributes(PyHeapTypeObject *heap_type) { + auto type = &heap_type->ht_type; +#if defined(PYPY_VERSION) + pybind11_fail(std::string(type->tp_name) + ": dynamic attributes are " + "currently not supported in " + "conjunction with PyPy!"); +#endif + type->tp_flags |= Py_TPFLAGS_HAVE_GC; + type->tp_dictoffset = type->tp_basicsize; // place dict at the end + type->tp_basicsize += (ssize_t)sizeof(PyObject *); // and allocate enough space for it + type->tp_traverse = pybind11_traverse; + type->tp_clear = pybind11_clear; + + static PyGetSetDef getset[] = { + {const_cast("__dict__"), pybind11_get_dict, pybind11_set_dict, nullptr, nullptr}, + {nullptr, nullptr, nullptr, nullptr, nullptr} + }; + type->tp_getset = getset; +} + +/// buffer_protocol: Fill in the view as specified by flags. +extern "C" inline int pybind11_getbuffer(PyObject *obj, Py_buffer *view, int flags) { + // Look for a `get_buffer` implementation in this type's info or any bases (following MRO). + type_info *tinfo = nullptr; + for (auto type : reinterpret_borrow(Py_TYPE(obj)->tp_mro)) { + tinfo = get_type_info((PyTypeObject *) type.ptr()); + if (tinfo && tinfo->get_buffer) + break; + } + if (view == nullptr || obj == nullptr || !tinfo || !tinfo->get_buffer) { + if (view) + view->obj = nullptr; + PyErr_SetString(PyExc_BufferError, "pybind11_getbuffer(): Internal error"); + return -1; + } + std::memset(view, 0, sizeof(Py_buffer)); + buffer_info *info = tinfo->get_buffer(obj, tinfo->get_buffer_data); + view->obj = obj; + view->ndim = 1; + view->internal = info; + view->buf = info->ptr; + view->itemsize = info->itemsize; + view->len = view->itemsize; + for (auto s : info->shape) + view->len *= s; + if ((flags & PyBUF_FORMAT) == PyBUF_FORMAT) + view->format = const_cast(info->format.c_str()); + if ((flags & PyBUF_STRIDES) == PyBUF_STRIDES) { + view->ndim = (int) info->ndim; + view->strides = &info->strides[0]; + view->shape = &info->shape[0]; + } + Py_INCREF(view->obj); + return 0; +} + +/// buffer_protocol: Release the resources of the buffer. +extern "C" inline void pybind11_releasebuffer(PyObject *, Py_buffer *view) { + delete (buffer_info *) view->internal; +} + +/// Give this type a buffer interface. +inline void enable_buffer_protocol(PyHeapTypeObject *heap_type) { + heap_type->ht_type.tp_as_buffer = &heap_type->as_buffer; +#if PY_MAJOR_VERSION < 3 + heap_type->ht_type.tp_flags |= Py_TPFLAGS_HAVE_NEWBUFFER; +#endif + + heap_type->as_buffer.bf_getbuffer = pybind11_getbuffer; + heap_type->as_buffer.bf_releasebuffer = pybind11_releasebuffer; +} + +/** Create a brand new Python type according to the `type_record` specification. + Return value: New reference. */ +inline PyObject* make_new_python_type(const type_record &rec) { + auto name = reinterpret_steal(PYBIND11_FROM_STRING(rec.name)); + +#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 3 + auto ht_qualname = name; + if (rec.scope && hasattr(rec.scope, "__qualname__")) { + ht_qualname = reinterpret_steal( + PyUnicode_FromFormat("%U.%U", rec.scope.attr("__qualname__").ptr(), name.ptr())); + } +#endif + + object module; + if (rec.scope) { + if (hasattr(rec.scope, "__module__")) + module = rec.scope.attr("__module__"); + else if (hasattr(rec.scope, "__name__")) + module = rec.scope.attr("__name__"); + } + +#if !defined(PYPY_VERSION) + const auto full_name = module ? str(module).cast() + "." + rec.name + : std::string(rec.name); +#else + const auto full_name = std::string(rec.name); +#endif + + char *tp_doc = nullptr; + if (rec.doc && options::show_user_defined_docstrings()) { + /* Allocate memory for docstring (using PyObject_MALLOC, since + Python will free this later on) */ + size_t size = strlen(rec.doc) + 1; + tp_doc = (char *) PyObject_MALLOC(size); + memcpy((void *) tp_doc, rec.doc, size); + } + + auto &internals = get_internals(); + auto bases = tuple(rec.bases); + auto base = (bases.size() == 0) ? internals.instance_base + : bases[0].ptr(); + + /* Danger zone: from now (and until PyType_Ready), make sure to + issue no Python C API calls which could potentially invoke the + garbage collector (the GC will call type_traverse(), which will in + turn find the newly constructed type in an invalid state) */ + auto metaclass = rec.metaclass.ptr() ? (PyTypeObject *) rec.metaclass.ptr() + : internals.default_metaclass; + + auto heap_type = (PyHeapTypeObject *) metaclass->tp_alloc(metaclass, 0); + if (!heap_type) + pybind11_fail(std::string(rec.name) + ": Unable to create type object!"); + + heap_type->ht_name = name.release().ptr(); +#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 3 + heap_type->ht_qualname = ht_qualname.release().ptr(); +#endif + + auto type = &heap_type->ht_type; + type->tp_name = strdup(full_name.c_str()); + type->tp_doc = tp_doc; + type->tp_base = type_incref((PyTypeObject *)base); + type->tp_basicsize = static_cast(sizeof(instance)); + if (bases.size() > 0) + type->tp_bases = bases.release().ptr(); + + /* Don't inherit base __init__ */ + type->tp_init = pybind11_object_init; + + /* Supported protocols */ + type->tp_as_number = &heap_type->as_number; + type->tp_as_sequence = &heap_type->as_sequence; + type->tp_as_mapping = &heap_type->as_mapping; + + /* Flags */ + type->tp_flags |= Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE; +#if PY_MAJOR_VERSION < 3 + type->tp_flags |= Py_TPFLAGS_CHECKTYPES; +#endif + + if (rec.dynamic_attr) + enable_dynamic_attributes(heap_type); + + if (rec.buffer_protocol) + enable_buffer_protocol(heap_type); + + if (PyType_Ready(type) < 0) + pybind11_fail(std::string(rec.name) + ": PyType_Ready failed (" + error_string() + ")!"); + + assert(rec.dynamic_attr ? PyType_HasFeature(type, Py_TPFLAGS_HAVE_GC) + : !PyType_HasFeature(type, Py_TPFLAGS_HAVE_GC)); + + /* Register type with the parent scope */ + if (rec.scope) + setattr(rec.scope, rec.name, (PyObject *) type); + + if (module) // Needed by pydoc + setattr((PyObject *) type, "__module__", module); + + return (PyObject *) type; +} + +NAMESPACE_END(detail) +NAMESPACE_END(pybind11) diff --git a/ptocr/postprocess/piexlmerge/include/pybind11/common.h b/ptocr/postprocess/piexlmerge/include/pybind11/common.h new file mode 100644 index 0000000..6c8a4f1 --- /dev/null +++ b/ptocr/postprocess/piexlmerge/include/pybind11/common.h @@ -0,0 +1,2 @@ +#include "detail/common.h" +#warning "Including 'common.h' is deprecated. It will be removed in v3.0. Use 'pybind11.h'." diff --git a/ptocr/postprocess/piexlmerge/include/pybind11/complex.h b/ptocr/postprocess/piexlmerge/include/pybind11/complex.h new file mode 100644 index 0000000..3f89638 --- /dev/null +++ b/ptocr/postprocess/piexlmerge/include/pybind11/complex.h @@ -0,0 +1,65 @@ +/* + pybind11/complex.h: Complex number support + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "pybind11.h" +#include + +/// glibc defines I as a macro which breaks things, e.g., boost template names +#ifdef I +# undef I +#endif + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) + +template struct format_descriptor, detail::enable_if_t::value>> { + static constexpr const char c = format_descriptor::c; + static constexpr const char value[3] = { 'Z', c, '\0' }; + static std::string format() { return std::string(value); } +}; + +#ifndef PYBIND11_CPP17 + +template constexpr const char format_descriptor< + std::complex, detail::enable_if_t::value>>::value[3]; + +#endif + +NAMESPACE_BEGIN(detail) + +template struct is_fmt_numeric, detail::enable_if_t::value>> { + static constexpr bool value = true; + static constexpr int index = is_fmt_numeric::index + 3; +}; + +template class type_caster> { +public: + bool load(handle src, bool convert) { + if (!src) + return false; + if (!convert && !PyComplex_Check(src.ptr())) + return false; + Py_complex result = PyComplex_AsCComplex(src.ptr()); + if (result.real == -1.0 && PyErr_Occurred()) { + PyErr_Clear(); + return false; + } + value = std::complex((T) result.real, (T) result.imag); + return true; + } + + static handle cast(const std::complex &src, return_value_policy /* policy */, handle /* parent */) { + return PyComplex_FromDoubles((double) src.real(), (double) src.imag()); + } + + PYBIND11_TYPE_CASTER(std::complex, _("complex")); +}; +NAMESPACE_END(detail) +NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/ptocr/postprocess/piexlmerge/include/pybind11/descr.h b/ptocr/postprocess/piexlmerge/include/pybind11/descr.h new file mode 100644 index 0000000..23a099c --- /dev/null +++ b/ptocr/postprocess/piexlmerge/include/pybind11/descr.h @@ -0,0 +1,185 @@ +/* + pybind11/descr.h: Helper type for concatenating type signatures + either at runtime (C++11) or compile time (C++14) + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "common.h" + +NAMESPACE_BEGIN(pybind11) +NAMESPACE_BEGIN(detail) + +/* Concatenate type signatures at compile time using C++14 */ +#if defined(PYBIND11_CPP14) && !defined(_MSC_VER) +#define PYBIND11_CONSTEXPR_DESCR + +template class descr { + template friend class descr; +public: + constexpr descr(char const (&text) [Size1+1], const std::type_info * const (&types)[Size2+1]) + : descr(text, types, + make_index_sequence(), + make_index_sequence()) { } + + constexpr const char *text() const { return m_text; } + constexpr const std::type_info * const * types() const { return m_types; } + + template + constexpr descr operator+(const descr &other) const { + return concat(other, + make_index_sequence(), + make_index_sequence(), + make_index_sequence(), + make_index_sequence()); + } + +protected: + template + constexpr descr( + char const (&text) [Size1+1], + const std::type_info * const (&types) [Size2+1], + index_sequence, index_sequence) + : m_text{text[Indices1]..., '\0'}, + m_types{types[Indices2]..., nullptr } {} + + template + constexpr descr + concat(const descr &other, + index_sequence, index_sequence, + index_sequence, index_sequence) const { + return descr( + { m_text[Indices1]..., other.m_text[OtherIndices1]..., '\0' }, + { m_types[Indices2]..., other.m_types[OtherIndices2]..., nullptr } + ); + } + +protected: + char m_text[Size1 + 1]; + const std::type_info * m_types[Size2 + 1]; +}; + +template constexpr descr _(char const(&text)[Size]) { + return descr(text, { nullptr }); +} + +template struct int_to_str : int_to_str { }; +template struct int_to_str<0, Digits...> { + static constexpr auto digits = descr({ ('0' + Digits)..., '\0' }, { nullptr }); +}; + +// Ternary description (like std::conditional) +template +constexpr enable_if_t> _(char const(&text1)[Size1], char const(&)[Size2]) { + return _(text1); +} +template +constexpr enable_if_t> _(char const(&)[Size1], char const(&text2)[Size2]) { + return _(text2); +} +template +constexpr enable_if_t> _(descr d, descr) { return d; } +template +constexpr enable_if_t> _(descr, descr d) { return d; } + +template auto constexpr _() -> decltype(int_to_str::digits) { + return int_to_str::digits; +} + +template constexpr descr<1, 1> _() { + return descr<1, 1>({ '%', '\0' }, { &typeid(Type), nullptr }); +} + +inline constexpr descr<0, 0> concat() { return _(""); } +template auto constexpr concat(descr descr) { return descr; } +template auto constexpr concat(descr descr, Args&&... args) { return descr + _(", ") + concat(args...); } +template auto constexpr type_descr(descr descr) { return _("{") + descr + _("}"); } + +#define PYBIND11_DESCR constexpr auto + +#else /* Simpler C++11 implementation based on run-time memory allocation and copying */ + +class descr { +public: + PYBIND11_NOINLINE descr(const char *text, const std::type_info * const * types) { + size_t nChars = len(text), nTypes = len(types); + m_text = new char[nChars]; + m_types = new const std::type_info *[nTypes]; + memcpy(m_text, text, nChars * sizeof(char)); + memcpy(m_types, types, nTypes * sizeof(const std::type_info *)); + } + + PYBIND11_NOINLINE descr operator+(descr &&d2) && { + descr r; + + size_t nChars1 = len(m_text), nTypes1 = len(m_types); + size_t nChars2 = len(d2.m_text), nTypes2 = len(d2.m_types); + + r.m_text = new char[nChars1 + nChars2 - 1]; + r.m_types = new const std::type_info *[nTypes1 + nTypes2 - 1]; + memcpy(r.m_text, m_text, (nChars1-1) * sizeof(char)); + memcpy(r.m_text + nChars1 - 1, d2.m_text, nChars2 * sizeof(char)); + memcpy(r.m_types, m_types, (nTypes1-1) * sizeof(std::type_info *)); + memcpy(r.m_types + nTypes1 - 1, d2.m_types, nTypes2 * sizeof(std::type_info *)); + + delete[] m_text; delete[] m_types; + delete[] d2.m_text; delete[] d2.m_types; + + return r; + } + + char *text() { return m_text; } + const std::type_info * * types() { return m_types; } + +protected: + PYBIND11_NOINLINE descr() { } + + template static size_t len(const T *ptr) { // return length including null termination + const T *it = ptr; + while (*it++ != (T) 0) + ; + return static_cast(it - ptr); + } + + const std::type_info **m_types = nullptr; + char *m_text = nullptr; +}; + +/* The 'PYBIND11_NOINLINE inline' combinations below are intentional to get the desired linkage while producing as little object code as possible */ + +PYBIND11_NOINLINE inline descr _(const char *text) { + const std::type_info *types[1] = { nullptr }; + return descr(text, types); +} + +template PYBIND11_NOINLINE enable_if_t _(const char *text1, const char *) { return _(text1); } +template PYBIND11_NOINLINE enable_if_t _(char const *, const char *text2) { return _(text2); } +template PYBIND11_NOINLINE enable_if_t _(descr d, descr) { return d; } +template PYBIND11_NOINLINE enable_if_t _(descr, descr d) { return d; } + +template PYBIND11_NOINLINE descr _() { + const std::type_info *types[2] = { &typeid(Type), nullptr }; + return descr("%", types); +} + +template PYBIND11_NOINLINE descr _() { + const std::type_info *types[1] = { nullptr }; + return descr(std::to_string(Size).c_str(), types); +} + +PYBIND11_NOINLINE inline descr concat() { return _(""); } +PYBIND11_NOINLINE inline descr concat(descr &&d) { return d; } +template PYBIND11_NOINLINE descr concat(descr &&d, Args&&... args) { return std::move(d) + _(", ") + concat(std::forward(args)...); } +PYBIND11_NOINLINE inline descr type_descr(descr&& d) { return _("{") + std::move(d) + _("}"); } + +#define PYBIND11_DESCR ::pybind11::detail::descr +#endif + +NAMESPACE_END(detail) +NAMESPACE_END(pybind11) diff --git a/ptocr/postprocess/piexlmerge/include/pybind11/detail/class.h b/ptocr/postprocess/piexlmerge/include/pybind11/detail/class.h new file mode 100644 index 0000000..7a5dd01 --- /dev/null +++ b/ptocr/postprocess/piexlmerge/include/pybind11/detail/class.h @@ -0,0 +1,622 @@ +/* + pybind11/detail/class.h: Python C API implementation details for py::class_ + + Copyright (c) 2017 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "../attr.h" + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +NAMESPACE_BEGIN(detail) + +#if PY_VERSION_HEX >= 0x03030000 +# define PYBIND11_BUILTIN_QUALNAME +# define PYBIND11_SET_OLDPY_QUALNAME(obj, nameobj) +#else +// In pre-3.3 Python, we still set __qualname__ so that we can produce reliable function type +// signatures; in 3.3+ this macro expands to nothing: +# define PYBIND11_SET_OLDPY_QUALNAME(obj, nameobj) setattr((PyObject *) obj, "__qualname__", nameobj) +#endif + +inline PyTypeObject *type_incref(PyTypeObject *type) { + Py_INCREF(type); + return type; +} + +#if !defined(PYPY_VERSION) + +/// `pybind11_static_property.__get__()`: Always pass the class instead of the instance. +extern "C" inline PyObject *pybind11_static_get(PyObject *self, PyObject * /*ob*/, PyObject *cls) { + return PyProperty_Type.tp_descr_get(self, cls, cls); +} + +/// `pybind11_static_property.__set__()`: Just like the above `__get__()`. +extern "C" inline int pybind11_static_set(PyObject *self, PyObject *obj, PyObject *value) { + PyObject *cls = PyType_Check(obj) ? obj : (PyObject *) Py_TYPE(obj); + return PyProperty_Type.tp_descr_set(self, cls, value); +} + +/** A `static_property` is the same as a `property` but the `__get__()` and `__set__()` + methods are modified to always use the object type instead of a concrete instance. + Return value: New reference. */ +inline PyTypeObject *make_static_property_type() { + constexpr auto *name = "pybind11_static_property"; + auto name_obj = reinterpret_steal(PYBIND11_FROM_STRING(name)); + + /* Danger zone: from now (and until PyType_Ready), make sure to + issue no Python C API calls which could potentially invoke the + garbage collector (the GC will call type_traverse(), which will in + turn find the newly constructed type in an invalid state) */ + auto heap_type = (PyHeapTypeObject *) PyType_Type.tp_alloc(&PyType_Type, 0); + if (!heap_type) + pybind11_fail("make_static_property_type(): error allocating type!"); + + heap_type->ht_name = name_obj.inc_ref().ptr(); +#ifdef PYBIND11_BUILTIN_QUALNAME + heap_type->ht_qualname = name_obj.inc_ref().ptr(); +#endif + + auto type = &heap_type->ht_type; + type->tp_name = name; + type->tp_base = type_incref(&PyProperty_Type); + type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE; + type->tp_descr_get = pybind11_static_get; + type->tp_descr_set = pybind11_static_set; + + if (PyType_Ready(type) < 0) + pybind11_fail("make_static_property_type(): failure in PyType_Ready()!"); + + setattr((PyObject *) type, "__module__", str("pybind11_builtins")); + PYBIND11_SET_OLDPY_QUALNAME(type, name_obj); + + return type; +} + +#else // PYPY + +/** PyPy has some issues with the above C API, so we evaluate Python code instead. + This function will only be called once so performance isn't really a concern. + Return value: New reference. */ +inline PyTypeObject *make_static_property_type() { + auto d = dict(); + PyObject *result = PyRun_String(R"(\ + class pybind11_static_property(property): + def __get__(self, obj, cls): + return property.__get__(self, cls, cls) + + def __set__(self, obj, value): + cls = obj if isinstance(obj, type) else type(obj) + property.__set__(self, cls, value) + )", Py_file_input, d.ptr(), d.ptr() + ); + if (result == nullptr) + throw error_already_set(); + Py_DECREF(result); + return (PyTypeObject *) d["pybind11_static_property"].cast().release().ptr(); +} + +#endif // PYPY + +/** Types with static properties need to handle `Type.static_prop = x` in a specific way. + By default, Python replaces the `static_property` itself, but for wrapped C++ types + we need to call `static_property.__set__()` in order to propagate the new value to + the underlying C++ data structure. */ +extern "C" inline int pybind11_meta_setattro(PyObject* obj, PyObject* name, PyObject* value) { + // Use `_PyType_Lookup()` instead of `PyObject_GetAttr()` in order to get the raw + // descriptor (`property`) instead of calling `tp_descr_get` (`property.__get__()`). + PyObject *descr = _PyType_Lookup((PyTypeObject *) obj, name); + + // The following assignment combinations are possible: + // 1. `Type.static_prop = value` --> descr_set: `Type.static_prop.__set__(value)` + // 2. `Type.static_prop = other_static_prop` --> setattro: replace existing `static_prop` + // 3. `Type.regular_attribute = value` --> setattro: regular attribute assignment + const auto static_prop = (PyObject *) get_internals().static_property_type; + const auto call_descr_set = descr && PyObject_IsInstance(descr, static_prop) + && !PyObject_IsInstance(value, static_prop); + if (call_descr_set) { + // Call `static_property.__set__()` instead of replacing the `static_property`. +#if !defined(PYPY_VERSION) + return Py_TYPE(descr)->tp_descr_set(descr, obj, value); +#else + if (PyObject *result = PyObject_CallMethod(descr, "__set__", "OO", obj, value)) { + Py_DECREF(result); + return 0; + } else { + return -1; + } +#endif + } else { + // Replace existing attribute. + return PyType_Type.tp_setattro(obj, name, value); + } +} + +#if PY_MAJOR_VERSION >= 3 +/** + * Python 3's PyInstanceMethod_Type hides itself via its tp_descr_get, which prevents aliasing + * methods via cls.attr("m2") = cls.attr("m1"): instead the tp_descr_get returns a plain function, + * when called on a class, or a PyMethod, when called on an instance. Override that behaviour here + * to do a special case bypass for PyInstanceMethod_Types. + */ +extern "C" inline PyObject *pybind11_meta_getattro(PyObject *obj, PyObject *name) { + PyObject *descr = _PyType_Lookup((PyTypeObject *) obj, name); + if (descr && PyInstanceMethod_Check(descr)) { + Py_INCREF(descr); + return descr; + } + else { + return PyType_Type.tp_getattro(obj, name); + } +} +#endif + +/** This metaclass is assigned by default to all pybind11 types and is required in order + for static properties to function correctly. Users may override this using `py::metaclass`. + Return value: New reference. */ +inline PyTypeObject* make_default_metaclass() { + constexpr auto *name = "pybind11_type"; + auto name_obj = reinterpret_steal(PYBIND11_FROM_STRING(name)); + + /* Danger zone: from now (and until PyType_Ready), make sure to + issue no Python C API calls which could potentially invoke the + garbage collector (the GC will call type_traverse(), which will in + turn find the newly constructed type in an invalid state) */ + auto heap_type = (PyHeapTypeObject *) PyType_Type.tp_alloc(&PyType_Type, 0); + if (!heap_type) + pybind11_fail("make_default_metaclass(): error allocating metaclass!"); + + heap_type->ht_name = name_obj.inc_ref().ptr(); +#ifdef PYBIND11_BUILTIN_QUALNAME + heap_type->ht_qualname = name_obj.inc_ref().ptr(); +#endif + + auto type = &heap_type->ht_type; + type->tp_name = name; + type->tp_base = type_incref(&PyType_Type); + type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE; + + type->tp_setattro = pybind11_meta_setattro; +#if PY_MAJOR_VERSION >= 3 + type->tp_getattro = pybind11_meta_getattro; +#endif + + if (PyType_Ready(type) < 0) + pybind11_fail("make_default_metaclass(): failure in PyType_Ready()!"); + + setattr((PyObject *) type, "__module__", str("pybind11_builtins")); + PYBIND11_SET_OLDPY_QUALNAME(type, name_obj); + + return type; +} + +/// For multiple inheritance types we need to recursively register/deregister base pointers for any +/// base classes with pointers that are difference from the instance value pointer so that we can +/// correctly recognize an offset base class pointer. This calls a function with any offset base ptrs. +inline void traverse_offset_bases(void *valueptr, const detail::type_info *tinfo, instance *self, + bool (*f)(void * /*parentptr*/, instance * /*self*/)) { + for (handle h : reinterpret_borrow(tinfo->type->tp_bases)) { + if (auto parent_tinfo = get_type_info((PyTypeObject *) h.ptr())) { + for (auto &c : parent_tinfo->implicit_casts) { + if (c.first == tinfo->cpptype) { + auto *parentptr = c.second(valueptr); + if (parentptr != valueptr) + f(parentptr, self); + traverse_offset_bases(parentptr, parent_tinfo, self, f); + break; + } + } + } + } +} + +inline bool register_instance_impl(void *ptr, instance *self) { + get_internals().registered_instances.emplace(ptr, self); + return true; // unused, but gives the same signature as the deregister func +} +inline bool deregister_instance_impl(void *ptr, instance *self) { + auto ®istered_instances = get_internals().registered_instances; + auto range = registered_instances.equal_range(ptr); + for (auto it = range.first; it != range.second; ++it) { + if (Py_TYPE(self) == Py_TYPE(it->second)) { + registered_instances.erase(it); + return true; + } + } + return false; +} + +inline void register_instance(instance *self, void *valptr, const type_info *tinfo) { + register_instance_impl(valptr, self); + if (!tinfo->simple_ancestors) + traverse_offset_bases(valptr, tinfo, self, register_instance_impl); +} + +inline bool deregister_instance(instance *self, void *valptr, const type_info *tinfo) { + bool ret = deregister_instance_impl(valptr, self); + if (!tinfo->simple_ancestors) + traverse_offset_bases(valptr, tinfo, self, deregister_instance_impl); + return ret; +} + +/// Instance creation function for all pybind11 types. It allocates the internal instance layout for +/// holding C++ objects and holders. Allocation is done lazily (the first time the instance is cast +/// to a reference or pointer), and initialization is done by an `__init__` function. +inline PyObject *make_new_instance(PyTypeObject *type) { +#if defined(PYPY_VERSION) + // PyPy gets tp_basicsize wrong (issue 2482) under multiple inheritance when the first inherited + // object is a a plain Python type (i.e. not derived from an extension type). Fix it. + ssize_t instance_size = static_cast(sizeof(instance)); + if (type->tp_basicsize < instance_size) { + type->tp_basicsize = instance_size; + } +#endif + PyObject *self = type->tp_alloc(type, 0); + auto inst = reinterpret_cast(self); + // Allocate the value/holder internals: + inst->allocate_layout(); + + inst->owned = true; + + return self; +} + +/// Instance creation function for all pybind11 types. It only allocates space for the +/// C++ object, but doesn't call the constructor -- an `__init__` function must do that. +extern "C" inline PyObject *pybind11_object_new(PyTypeObject *type, PyObject *, PyObject *) { + return make_new_instance(type); +} + +/// An `__init__` function constructs the C++ object. Users should provide at least one +/// of these using `py::init` or directly with `.def(__init__, ...)`. Otherwise, the +/// following default function will be used which simply throws an exception. +extern "C" inline int pybind11_object_init(PyObject *self, PyObject *, PyObject *) { + PyTypeObject *type = Py_TYPE(self); + std::string msg; +#if defined(PYPY_VERSION) + msg += handle((PyObject *) type).attr("__module__").cast() + "."; +#endif + msg += type->tp_name; + msg += ": No constructor defined!"; + PyErr_SetString(PyExc_TypeError, msg.c_str()); + return -1; +} + +inline void add_patient(PyObject *nurse, PyObject *patient) { + auto &internals = get_internals(); + auto instance = reinterpret_cast(nurse); + instance->has_patients = true; + Py_INCREF(patient); + internals.patients[nurse].push_back(patient); +} + +inline void clear_patients(PyObject *self) { + auto instance = reinterpret_cast(self); + auto &internals = get_internals(); + auto pos = internals.patients.find(self); + assert(pos != internals.patients.end()); + // Clearing the patients can cause more Python code to run, which + // can invalidate the iterator. Extract the vector of patients + // from the unordered_map first. + auto patients = std::move(pos->second); + internals.patients.erase(pos); + instance->has_patients = false; + for (PyObject *&patient : patients) + Py_CLEAR(patient); +} + +/// Clears all internal data from the instance and removes it from registered instances in +/// preparation for deallocation. +inline void clear_instance(PyObject *self) { + auto instance = reinterpret_cast(self); + + // Deallocate any values/holders, if present: + for (auto &v_h : values_and_holders(instance)) { + if (v_h) { + + // We have to deregister before we call dealloc because, for virtual MI types, we still + // need to be able to get the parent pointers. + if (v_h.instance_registered() && !deregister_instance(instance, v_h.value_ptr(), v_h.type)) + pybind11_fail("pybind11_object_dealloc(): Tried to deallocate unregistered instance!"); + + if (instance->owned || v_h.holder_constructed()) + v_h.type->dealloc(v_h); + } + } + // Deallocate the value/holder layout internals: + instance->deallocate_layout(); + + if (instance->weakrefs) + PyObject_ClearWeakRefs(self); + + PyObject **dict_ptr = _PyObject_GetDictPtr(self); + if (dict_ptr) + Py_CLEAR(*dict_ptr); + + if (instance->has_patients) + clear_patients(self); +} + +/// Instance destructor function for all pybind11 types. It calls `type_info.dealloc` +/// to destroy the C++ object itself, while the rest is Python bookkeeping. +extern "C" inline void pybind11_object_dealloc(PyObject *self) { + clear_instance(self); + + auto type = Py_TYPE(self); + type->tp_free(self); + + // `type->tp_dealloc != pybind11_object_dealloc` means that we're being called + // as part of a derived type's dealloc, in which case we're not allowed to decref + // the type here. For cross-module compatibility, we shouldn't compare directly + // with `pybind11_object_dealloc`, but with the common one stashed in internals. + auto pybind11_object_type = (PyTypeObject *) get_internals().instance_base; + if (type->tp_dealloc == pybind11_object_type->tp_dealloc) + Py_DECREF(type); +} + +/** Create the type which can be used as a common base for all classes. This is + needed in order to satisfy Python's requirements for multiple inheritance. + Return value: New reference. */ +inline PyObject *make_object_base_type(PyTypeObject *metaclass) { + constexpr auto *name = "pybind11_object"; + auto name_obj = reinterpret_steal(PYBIND11_FROM_STRING(name)); + + /* Danger zone: from now (and until PyType_Ready), make sure to + issue no Python C API calls which could potentially invoke the + garbage collector (the GC will call type_traverse(), which will in + turn find the newly constructed type in an invalid state) */ + auto heap_type = (PyHeapTypeObject *) metaclass->tp_alloc(metaclass, 0); + if (!heap_type) + pybind11_fail("make_object_base_type(): error allocating type!"); + + heap_type->ht_name = name_obj.inc_ref().ptr(); +#ifdef PYBIND11_BUILTIN_QUALNAME + heap_type->ht_qualname = name_obj.inc_ref().ptr(); +#endif + + auto type = &heap_type->ht_type; + type->tp_name = name; + type->tp_base = type_incref(&PyBaseObject_Type); + type->tp_basicsize = static_cast(sizeof(instance)); + type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE; + + type->tp_new = pybind11_object_new; + type->tp_init = pybind11_object_init; + type->tp_dealloc = pybind11_object_dealloc; + + /* Support weak references (needed for the keep_alive feature) */ + type->tp_weaklistoffset = offsetof(instance, weakrefs); + + if (PyType_Ready(type) < 0) + pybind11_fail("PyType_Ready failed in make_object_base_type():" + error_string()); + + setattr((PyObject *) type, "__module__", str("pybind11_builtins")); + PYBIND11_SET_OLDPY_QUALNAME(type, name_obj); + + assert(!PyType_HasFeature(type, Py_TPFLAGS_HAVE_GC)); + return (PyObject *) heap_type; +} + +/// dynamic_attr: Support for `d = instance.__dict__`. +extern "C" inline PyObject *pybind11_get_dict(PyObject *self, void *) { + PyObject *&dict = *_PyObject_GetDictPtr(self); + if (!dict) + dict = PyDict_New(); + Py_XINCREF(dict); + return dict; +} + +/// dynamic_attr: Support for `instance.__dict__ = dict()`. +extern "C" inline int pybind11_set_dict(PyObject *self, PyObject *new_dict, void *) { + if (!PyDict_Check(new_dict)) { + PyErr_Format(PyExc_TypeError, "__dict__ must be set to a dictionary, not a '%.200s'", + Py_TYPE(new_dict)->tp_name); + return -1; + } + PyObject *&dict = *_PyObject_GetDictPtr(self); + Py_INCREF(new_dict); + Py_CLEAR(dict); + dict = new_dict; + return 0; +} + +/// dynamic_attr: Allow the garbage collector to traverse the internal instance `__dict__`. +extern "C" inline int pybind11_traverse(PyObject *self, visitproc visit, void *arg) { + PyObject *&dict = *_PyObject_GetDictPtr(self); + Py_VISIT(dict); + return 0; +} + +/// dynamic_attr: Allow the GC to clear the dictionary. +extern "C" inline int pybind11_clear(PyObject *self) { + PyObject *&dict = *_PyObject_GetDictPtr(self); + Py_CLEAR(dict); + return 0; +} + +/// Give instances of this type a `__dict__` and opt into garbage collection. +inline void enable_dynamic_attributes(PyHeapTypeObject *heap_type) { + auto type = &heap_type->ht_type; +#if defined(PYPY_VERSION) + pybind11_fail(std::string(type->tp_name) + ": dynamic attributes are " + "currently not supported in " + "conjunction with PyPy!"); +#endif + type->tp_flags |= Py_TPFLAGS_HAVE_GC; + type->tp_dictoffset = type->tp_basicsize; // place dict at the end + type->tp_basicsize += (ssize_t)sizeof(PyObject *); // and allocate enough space for it + type->tp_traverse = pybind11_traverse; + type->tp_clear = pybind11_clear; + + static PyGetSetDef getset[] = { + {const_cast("__dict__"), pybind11_get_dict, pybind11_set_dict, nullptr, nullptr}, + {nullptr, nullptr, nullptr, nullptr, nullptr} + }; + type->tp_getset = getset; +} + +/// buffer_protocol: Fill in the view as specified by flags. +extern "C" inline int pybind11_getbuffer(PyObject *obj, Py_buffer *view, int flags) { + // Look for a `get_buffer` implementation in this type's info or any bases (following MRO). + type_info *tinfo = nullptr; + for (auto type : reinterpret_borrow(Py_TYPE(obj)->tp_mro)) { + tinfo = get_type_info((PyTypeObject *) type.ptr()); + if (tinfo && tinfo->get_buffer) + break; + } + if (view == nullptr || obj == nullptr || !tinfo || !tinfo->get_buffer) { + if (view) + view->obj = nullptr; + PyErr_SetString(PyExc_BufferError, "pybind11_getbuffer(): Internal error"); + return -1; + } + std::memset(view, 0, sizeof(Py_buffer)); + buffer_info *info = tinfo->get_buffer(obj, tinfo->get_buffer_data); + view->obj = obj; + view->ndim = 1; + view->internal = info; + view->buf = info->ptr; + view->itemsize = info->itemsize; + view->len = view->itemsize; + for (auto s : info->shape) + view->len *= s; + if ((flags & PyBUF_FORMAT) == PyBUF_FORMAT) + view->format = const_cast(info->format.c_str()); + if ((flags & PyBUF_STRIDES) == PyBUF_STRIDES) { + view->ndim = (int) info->ndim; + view->strides = &info->strides[0]; + view->shape = &info->shape[0]; + } + Py_INCREF(view->obj); + return 0; +} + +/// buffer_protocol: Release the resources of the buffer. +extern "C" inline void pybind11_releasebuffer(PyObject *, Py_buffer *view) { + delete (buffer_info *) view->internal; +} + +/// Give this type a buffer interface. +inline void enable_buffer_protocol(PyHeapTypeObject *heap_type) { + heap_type->ht_type.tp_as_buffer = &heap_type->as_buffer; +#if PY_MAJOR_VERSION < 3 + heap_type->ht_type.tp_flags |= Py_TPFLAGS_HAVE_NEWBUFFER; +#endif + + heap_type->as_buffer.bf_getbuffer = pybind11_getbuffer; + heap_type->as_buffer.bf_releasebuffer = pybind11_releasebuffer; +} + +/** Create a brand new Python type according to the `type_record` specification. + Return value: New reference. */ +inline PyObject* make_new_python_type(const type_record &rec) { + auto name = reinterpret_steal(PYBIND11_FROM_STRING(rec.name)); + + auto qualname = name; + if (rec.scope && !PyModule_Check(rec.scope.ptr()) && hasattr(rec.scope, "__qualname__")) { +#if PY_MAJOR_VERSION >= 3 + qualname = reinterpret_steal( + PyUnicode_FromFormat("%U.%U", rec.scope.attr("__qualname__").ptr(), name.ptr())); +#else + qualname = str(rec.scope.attr("__qualname__").cast() + "." + rec.name); +#endif + } + + object module; + if (rec.scope) { + if (hasattr(rec.scope, "__module__")) + module = rec.scope.attr("__module__"); + else if (hasattr(rec.scope, "__name__")) + module = rec.scope.attr("__name__"); + } + + auto full_name = c_str( +#if !defined(PYPY_VERSION) + module ? str(module).cast() + "." + rec.name : +#endif + rec.name); + + char *tp_doc = nullptr; + if (rec.doc && options::show_user_defined_docstrings()) { + /* Allocate memory for docstring (using PyObject_MALLOC, since + Python will free this later on) */ + size_t size = strlen(rec.doc) + 1; + tp_doc = (char *) PyObject_MALLOC(size); + memcpy((void *) tp_doc, rec.doc, size); + } + + auto &internals = get_internals(); + auto bases = tuple(rec.bases); + auto base = (bases.size() == 0) ? internals.instance_base + : bases[0].ptr(); + + /* Danger zone: from now (and until PyType_Ready), make sure to + issue no Python C API calls which could potentially invoke the + garbage collector (the GC will call type_traverse(), which will in + turn find the newly constructed type in an invalid state) */ + auto metaclass = rec.metaclass.ptr() ? (PyTypeObject *) rec.metaclass.ptr() + : internals.default_metaclass; + + auto heap_type = (PyHeapTypeObject *) metaclass->tp_alloc(metaclass, 0); + if (!heap_type) + pybind11_fail(std::string(rec.name) + ": Unable to create type object!"); + + heap_type->ht_name = name.release().ptr(); +#ifdef PYBIND11_BUILTIN_QUALNAME + heap_type->ht_qualname = qualname.inc_ref().ptr(); +#endif + + auto type = &heap_type->ht_type; + type->tp_name = full_name; + type->tp_doc = tp_doc; + type->tp_base = type_incref((PyTypeObject *)base); + type->tp_basicsize = static_cast(sizeof(instance)); + if (bases.size() > 0) + type->tp_bases = bases.release().ptr(); + + /* Don't inherit base __init__ */ + type->tp_init = pybind11_object_init; + + /* Supported protocols */ + type->tp_as_number = &heap_type->as_number; + type->tp_as_sequence = &heap_type->as_sequence; + type->tp_as_mapping = &heap_type->as_mapping; + + /* Flags */ + type->tp_flags |= Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE; +#if PY_MAJOR_VERSION < 3 + type->tp_flags |= Py_TPFLAGS_CHECKTYPES; +#endif + + if (rec.dynamic_attr) + enable_dynamic_attributes(heap_type); + + if (rec.buffer_protocol) + enable_buffer_protocol(heap_type); + + if (PyType_Ready(type) < 0) + pybind11_fail(std::string(rec.name) + ": PyType_Ready failed (" + error_string() + ")!"); + + assert(rec.dynamic_attr ? PyType_HasFeature(type, Py_TPFLAGS_HAVE_GC) + : !PyType_HasFeature(type, Py_TPFLAGS_HAVE_GC)); + + /* Register type with the parent scope */ + if (rec.scope) + setattr(rec.scope, rec.name, (PyObject *) type); + else + Py_INCREF(type); // Keep it alive forever (reference leak) + + if (module) // Needed by pydoc + setattr((PyObject *) type, "__module__", module); + + PYBIND11_SET_OLDPY_QUALNAME(type, qualname); + + return (PyObject *) type; +} + +NAMESPACE_END(detail) +NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/ptocr/postprocess/piexlmerge/include/pybind11/detail/common.h b/ptocr/postprocess/piexlmerge/include/pybind11/detail/common.h new file mode 100644 index 0000000..5ff7485 --- /dev/null +++ b/ptocr/postprocess/piexlmerge/include/pybind11/detail/common.h @@ -0,0 +1,807 @@ +/* + pybind11/detail/common.h -- Basic macros + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#if !defined(NAMESPACE_BEGIN) +# define NAMESPACE_BEGIN(name) namespace name { +#endif +#if !defined(NAMESPACE_END) +# define NAMESPACE_END(name) } +#endif + +// Robust support for some features and loading modules compiled against different pybind versions +// requires forcing hidden visibility on pybind code, so we enforce this by setting the attribute on +// the main `pybind11` namespace. +#if !defined(PYBIND11_NAMESPACE) +# ifdef __GNUG__ +# define PYBIND11_NAMESPACE pybind11 __attribute__((visibility("hidden"))) +# else +# define PYBIND11_NAMESPACE pybind11 +# endif +#endif + +#if !(defined(_MSC_VER) && __cplusplus == 199711L) && !defined(__INTEL_COMPILER) +# if __cplusplus >= 201402L +# define PYBIND11_CPP14 +# if __cplusplus >= 201703L +# define PYBIND11_CPP17 +# endif +# endif +#elif defined(_MSC_VER) && __cplusplus == 199711L +// MSVC sets _MSVC_LANG rather than __cplusplus (supposedly until the standard is fully implemented) +// Unless you use the /Zc:__cplusplus flag on Visual Studio 2017 15.7 Preview 3 or newer +# if _MSVC_LANG >= 201402L +# define PYBIND11_CPP14 +# if _MSVC_LANG > 201402L && _MSC_VER >= 1910 +# define PYBIND11_CPP17 +# endif +# endif +#endif + +// Compiler version assertions +#if defined(__INTEL_COMPILER) +# if __INTEL_COMPILER < 1700 +# error pybind11 requires Intel C++ compiler v17 or newer +# endif +#elif defined(__clang__) && !defined(__apple_build_version__) +# if __clang_major__ < 3 || (__clang_major__ == 3 && __clang_minor__ < 3) +# error pybind11 requires clang 3.3 or newer +# endif +#elif defined(__clang__) +// Apple changes clang version macros to its Xcode version; the first Xcode release based on +// (upstream) clang 3.3 was Xcode 5: +# if __clang_major__ < 5 +# error pybind11 requires Xcode/clang 5.0 or newer +# endif +#elif defined(__GNUG__) +# if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 8) +# error pybind11 requires gcc 4.8 or newer +# endif +#elif defined(_MSC_VER) +// Pybind hits various compiler bugs in 2015u2 and earlier, and also makes use of some stl features +// (e.g. std::negation) added in 2015u3: +# if _MSC_FULL_VER < 190024210 +# error pybind11 requires MSVC 2015 update 3 or newer +# endif +#endif + +#if !defined(PYBIND11_EXPORT) +# if defined(WIN32) || defined(_WIN32) +# define PYBIND11_EXPORT __declspec(dllexport) +# else +# define PYBIND11_EXPORT __attribute__ ((visibility("default"))) +# endif +#endif + +#if defined(_MSC_VER) +# define PYBIND11_NOINLINE __declspec(noinline) +#else +# define PYBIND11_NOINLINE __attribute__ ((noinline)) +#endif + +#if defined(PYBIND11_CPP14) +# define PYBIND11_DEPRECATED(reason) [[deprecated(reason)]] +#else +# define PYBIND11_DEPRECATED(reason) __attribute__((deprecated(reason))) +#endif + +#define PYBIND11_VERSION_MAJOR 2 +#define PYBIND11_VERSION_MINOR 3 +#define PYBIND11_VERSION_PATCH dev0 + +/// Include Python header, disable linking to pythonX_d.lib on Windows in debug mode +#if defined(_MSC_VER) +# if (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION < 4) +# define HAVE_ROUND 1 +# endif +# pragma warning(push) +# pragma warning(disable: 4510 4610 4512 4005) +# if defined(_DEBUG) +# define PYBIND11_DEBUG_MARKER +# undef _DEBUG +# endif +#endif + +#include +#include +#include + +#if defined(_WIN32) && (defined(min) || defined(max)) +# error Macro clash with min and max -- define NOMINMAX when compiling your program on Windows +#endif + +#if defined(isalnum) +# undef isalnum +# undef isalpha +# undef islower +# undef isspace +# undef isupper +# undef tolower +# undef toupper +#endif + +#if defined(_MSC_VER) +# if defined(PYBIND11_DEBUG_MARKER) +# define _DEBUG +# undef PYBIND11_DEBUG_MARKER +# endif +# pragma warning(pop) +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if PY_MAJOR_VERSION >= 3 /// Compatibility macros for various Python versions +#define PYBIND11_INSTANCE_METHOD_NEW(ptr, class_) PyInstanceMethod_New(ptr) +#define PYBIND11_INSTANCE_METHOD_CHECK PyInstanceMethod_Check +#define PYBIND11_INSTANCE_METHOD_GET_FUNCTION PyInstanceMethod_GET_FUNCTION +#define PYBIND11_BYTES_CHECK PyBytes_Check +#define PYBIND11_BYTES_FROM_STRING PyBytes_FromString +#define PYBIND11_BYTES_FROM_STRING_AND_SIZE PyBytes_FromStringAndSize +#define PYBIND11_BYTES_AS_STRING_AND_SIZE PyBytes_AsStringAndSize +#define PYBIND11_BYTES_AS_STRING PyBytes_AsString +#define PYBIND11_BYTES_SIZE PyBytes_Size +#define PYBIND11_LONG_CHECK(o) PyLong_Check(o) +#define PYBIND11_LONG_AS_LONGLONG(o) PyLong_AsLongLong(o) +#define PYBIND11_LONG_FROM_SIGNED(o) PyLong_FromSsize_t((ssize_t) o) +#define PYBIND11_LONG_FROM_UNSIGNED(o) PyLong_FromSize_t((size_t) o) +#define PYBIND11_BYTES_NAME "bytes" +#define PYBIND11_STRING_NAME "str" +#define PYBIND11_SLICE_OBJECT PyObject +#define PYBIND11_FROM_STRING PyUnicode_FromString +#define PYBIND11_STR_TYPE ::pybind11::str +#define PYBIND11_BOOL_ATTR "__bool__" +#define PYBIND11_NB_BOOL(ptr) ((ptr)->nb_bool) +#define PYBIND11_PLUGIN_IMPL(name) \ + extern "C" PYBIND11_EXPORT PyObject *PyInit_##name() + +#else +#define PYBIND11_INSTANCE_METHOD_NEW(ptr, class_) PyMethod_New(ptr, nullptr, class_) +#define PYBIND11_INSTANCE_METHOD_CHECK PyMethod_Check +#define PYBIND11_INSTANCE_METHOD_GET_FUNCTION PyMethod_GET_FUNCTION +#define PYBIND11_BYTES_CHECK PyString_Check +#define PYBIND11_BYTES_FROM_STRING PyString_FromString +#define PYBIND11_BYTES_FROM_STRING_AND_SIZE PyString_FromStringAndSize +#define PYBIND11_BYTES_AS_STRING_AND_SIZE PyString_AsStringAndSize +#define PYBIND11_BYTES_AS_STRING PyString_AsString +#define PYBIND11_BYTES_SIZE PyString_Size +#define PYBIND11_LONG_CHECK(o) (PyInt_Check(o) || PyLong_Check(o)) +#define PYBIND11_LONG_AS_LONGLONG(o) (PyInt_Check(o) ? (long long) PyLong_AsLong(o) : PyLong_AsLongLong(o)) +#define PYBIND11_LONG_FROM_SIGNED(o) PyInt_FromSsize_t((ssize_t) o) // Returns long if needed. +#define PYBIND11_LONG_FROM_UNSIGNED(o) PyInt_FromSize_t((size_t) o) // Returns long if needed. +#define PYBIND11_BYTES_NAME "str" +#define PYBIND11_STRING_NAME "unicode" +#define PYBIND11_SLICE_OBJECT PySliceObject +#define PYBIND11_FROM_STRING PyString_FromString +#define PYBIND11_STR_TYPE ::pybind11::bytes +#define PYBIND11_BOOL_ATTR "__nonzero__" +#define PYBIND11_NB_BOOL(ptr) ((ptr)->nb_nonzero) +#define PYBIND11_PLUGIN_IMPL(name) \ + static PyObject *pybind11_init_wrapper(); \ + extern "C" PYBIND11_EXPORT void init##name() { \ + (void)pybind11_init_wrapper(); \ + } \ + PyObject *pybind11_init_wrapper() +#endif + +#if PY_VERSION_HEX >= 0x03050000 && PY_VERSION_HEX < 0x03050200 +extern "C" { + struct _Py_atomic_address { void *value; }; + PyAPI_DATA(_Py_atomic_address) _PyThreadState_Current; +} +#endif + +#define PYBIND11_TRY_NEXT_OVERLOAD ((PyObject *) 1) // special failure return code +#define PYBIND11_STRINGIFY(x) #x +#define PYBIND11_TOSTRING(x) PYBIND11_STRINGIFY(x) +#define PYBIND11_CONCAT(first, second) first##second + +#define PYBIND11_CHECK_PYTHON_VERSION \ + { \ + const char *compiled_ver = PYBIND11_TOSTRING(PY_MAJOR_VERSION) \ + "." PYBIND11_TOSTRING(PY_MINOR_VERSION); \ + const char *runtime_ver = Py_GetVersion(); \ + size_t len = std::strlen(compiled_ver); \ + if (std::strncmp(runtime_ver, compiled_ver, len) != 0 \ + || (runtime_ver[len] >= '0' && runtime_ver[len] <= '9')) { \ + PyErr_Format(PyExc_ImportError, \ + "Python version mismatch: module was compiled for Python %s, " \ + "but the interpreter version is incompatible: %s.", \ + compiled_ver, runtime_ver); \ + return nullptr; \ + } \ + } + +#define PYBIND11_CATCH_INIT_EXCEPTIONS \ + catch (pybind11::error_already_set &e) { \ + PyErr_SetString(PyExc_ImportError, e.what()); \ + return nullptr; \ + } catch (const std::exception &e) { \ + PyErr_SetString(PyExc_ImportError, e.what()); \ + return nullptr; \ + } \ + +/** \rst + ***Deprecated in favor of PYBIND11_MODULE*** + + This macro creates the entry point that will be invoked when the Python interpreter + imports a plugin library. Please create a `module` in the function body and return + the pointer to its underlying Python object at the end. + + .. code-block:: cpp + + PYBIND11_PLUGIN(example) { + pybind11::module m("example", "pybind11 example plugin"); + /// Set up bindings here + return m.ptr(); + } +\endrst */ +#define PYBIND11_PLUGIN(name) \ + PYBIND11_DEPRECATED("PYBIND11_PLUGIN is deprecated, use PYBIND11_MODULE") \ + static PyObject *pybind11_init(); \ + PYBIND11_PLUGIN_IMPL(name) { \ + PYBIND11_CHECK_PYTHON_VERSION \ + try { \ + return pybind11_init(); \ + } PYBIND11_CATCH_INIT_EXCEPTIONS \ + } \ + PyObject *pybind11_init() + +/** \rst + This macro creates the entry point that will be invoked when the Python interpreter + imports an extension module. The module name is given as the fist argument and it + should not be in quotes. The second macro argument defines a variable of type + `py::module` which can be used to initialize the module. + + .. code-block:: cpp + + PYBIND11_MODULE(example, m) { + m.doc() = "pybind11 example module"; + + // Add bindings here + m.def("foo", []() { + return "Hello, World!"; + }); + } +\endrst */ +#define PYBIND11_MODULE(name, variable) \ + static void PYBIND11_CONCAT(pybind11_init_, name)(pybind11::module &); \ + PYBIND11_PLUGIN_IMPL(name) { \ + PYBIND11_CHECK_PYTHON_VERSION \ + auto m = pybind11::module(PYBIND11_TOSTRING(name)); \ + try { \ + PYBIND11_CONCAT(pybind11_init_, name)(m); \ + return m.ptr(); \ + } PYBIND11_CATCH_INIT_EXCEPTIONS \ + } \ + void PYBIND11_CONCAT(pybind11_init_, name)(pybind11::module &variable) + + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) + +using ssize_t = Py_ssize_t; +using size_t = std::size_t; + +/// Approach used to cast a previously unknown C++ instance into a Python object +enum class return_value_policy : uint8_t { + /** This is the default return value policy, which falls back to the policy + return_value_policy::take_ownership when the return value is a pointer. + Otherwise, it uses return_value::move or return_value::copy for rvalue + and lvalue references, respectively. See below for a description of what + all of these different policies do. */ + automatic = 0, + + /** As above, but use policy return_value_policy::reference when the return + value is a pointer. This is the default conversion policy for function + arguments when calling Python functions manually from C++ code (i.e. via + handle::operator()). You probably won't need to use this. */ + automatic_reference, + + /** Reference an existing object (i.e. do not create a new copy) and take + ownership. Python will call the destructor and delete operator when the + object’s reference count reaches zero. Undefined behavior ensues when + the C++ side does the same.. */ + take_ownership, + + /** Create a new copy of the returned object, which will be owned by + Python. This policy is comparably safe because the lifetimes of the two + instances are decoupled. */ + copy, + + /** Use std::move to move the return value contents into a new instance + that will be owned by Python. This policy is comparably safe because the + lifetimes of the two instances (move source and destination) are + decoupled. */ + move, + + /** Reference an existing object, but do not take ownership. The C++ side + is responsible for managing the object’s lifetime and deallocating it + when it is no longer used. Warning: undefined behavior will ensue when + the C++ side deletes an object that is still referenced and used by + Python. */ + reference, + + /** This policy only applies to methods and properties. It references the + object without taking ownership similar to the above + return_value_policy::reference policy. In contrast to that policy, the + function or property’s implicit this argument (called the parent) is + considered to be the the owner of the return value (the child). + pybind11 then couples the lifetime of the parent to the child via a + reference relationship that ensures that the parent cannot be garbage + collected while Python is still using the child. More advanced + variations of this scheme are also possible using combinations of + return_value_policy::reference and the keep_alive call policy */ + reference_internal +}; + +NAMESPACE_BEGIN(detail) + +inline static constexpr int log2(size_t n, int k = 0) { return (n <= 1) ? k : log2(n >> 1, k + 1); } + +// Returns the size as a multiple of sizeof(void *), rounded up. +inline static constexpr size_t size_in_ptrs(size_t s) { return 1 + ((s - 1) >> log2(sizeof(void *))); } + +/** + * The space to allocate for simple layout instance holders (see below) in multiple of the size of + * a pointer (e.g. 2 means 16 bytes on 64-bit architectures). The default is the minimum required + * to holder either a std::unique_ptr or std::shared_ptr (which is almost always + * sizeof(std::shared_ptr)). + */ +constexpr size_t instance_simple_holder_in_ptrs() { + static_assert(sizeof(std::shared_ptr) >= sizeof(std::unique_ptr), + "pybind assumes std::shared_ptrs are at least as big as std::unique_ptrs"); + return size_in_ptrs(sizeof(std::shared_ptr)); +} + +// Forward declarations +struct type_info; +struct value_and_holder; + +struct nonsimple_values_and_holders { + void **values_and_holders; + uint8_t *status; +}; + +/// The 'instance' type which needs to be standard layout (need to be able to use 'offsetof') +struct instance { + PyObject_HEAD + /// Storage for pointers and holder; see simple_layout, below, for a description + union { + void *simple_value_holder[1 + instance_simple_holder_in_ptrs()]; + nonsimple_values_and_holders nonsimple; + }; + /// Weak references + PyObject *weakrefs; + /// If true, the pointer is owned which means we're free to manage it with a holder. + bool owned : 1; + /** + * An instance has two possible value/holder layouts. + * + * Simple layout (when this flag is true), means the `simple_value_holder` is set with a pointer + * and the holder object governing that pointer, i.e. [val1*][holder]. This layout is applied + * whenever there is no python-side multiple inheritance of bound C++ types *and* the type's + * holder will fit in the default space (which is large enough to hold either a std::unique_ptr + * or std::shared_ptr). + * + * Non-simple layout applies when using custom holders that require more space than `shared_ptr` + * (which is typically the size of two pointers), or when multiple inheritance is used on the + * python side. Non-simple layout allocates the required amount of memory to have multiple + * bound C++ classes as parents. Under this layout, `nonsimple.values_and_holders` is set to a + * pointer to allocated space of the required space to hold a sequence of value pointers and + * holders followed `status`, a set of bit flags (1 byte each), i.e. + * [val1*][holder1][val2*][holder2]...[bb...] where each [block] is rounded up to a multiple of + * `sizeof(void *)`. `nonsimple.status` is, for convenience, a pointer to the + * beginning of the [bb...] block (but not independently allocated). + * + * Status bits indicate whether the associated holder is constructed (& + * status_holder_constructed) and whether the value pointer is registered (& + * status_instance_registered) in `registered_instances`. + */ + bool simple_layout : 1; + /// For simple layout, tracks whether the holder has been constructed + bool simple_holder_constructed : 1; + /// For simple layout, tracks whether the instance is registered in `registered_instances` + bool simple_instance_registered : 1; + /// If true, get_internals().patients has an entry for this object + bool has_patients : 1; + + /// Initializes all of the above type/values/holders data (but not the instance values themselves) + void allocate_layout(); + + /// Destroys/deallocates all of the above + void deallocate_layout(); + + /// Returns the value_and_holder wrapper for the given type (or the first, if `find_type` + /// omitted). Returns a default-constructed (with `.inst = nullptr`) object on failure if + /// `throw_if_missing` is false. + value_and_holder get_value_and_holder(const type_info *find_type = nullptr, bool throw_if_missing = true); + + /// Bit values for the non-simple status flags + static constexpr uint8_t status_holder_constructed = 1; + static constexpr uint8_t status_instance_registered = 2; +}; + +static_assert(std::is_standard_layout::value, "Internal error: `pybind11::detail::instance` is not standard layout!"); + +/// from __cpp_future__ import (convenient aliases from C++14/17) +#if defined(PYBIND11_CPP14) && (!defined(_MSC_VER) || _MSC_VER >= 1910) +using std::enable_if_t; +using std::conditional_t; +using std::remove_cv_t; +using std::remove_reference_t; +#else +template using enable_if_t = typename std::enable_if::type; +template using conditional_t = typename std::conditional::type; +template using remove_cv_t = typename std::remove_cv::type; +template using remove_reference_t = typename std::remove_reference::type; +#endif + +/// Index sequences +#if defined(PYBIND11_CPP14) +using std::index_sequence; +using std::make_index_sequence; +#else +template struct index_sequence { }; +template struct make_index_sequence_impl : make_index_sequence_impl { }; +template struct make_index_sequence_impl <0, S...> { typedef index_sequence type; }; +template using make_index_sequence = typename make_index_sequence_impl::type; +#endif + +/// Make an index sequence of the indices of true arguments +template struct select_indices_impl { using type = ISeq; }; +template struct select_indices_impl, I, B, Bs...> + : select_indices_impl, index_sequence>, I + 1, Bs...> {}; +template using select_indices = typename select_indices_impl, 0, Bs...>::type; + +/// Backports of std::bool_constant and std::negation to accommodate older compilers +template using bool_constant = std::integral_constant; +template struct negation : bool_constant { }; + +template struct void_t_impl { using type = void; }; +template using void_t = typename void_t_impl::type; + +/// Compile-time all/any/none of that check the boolean value of all template types +#if defined(__cpp_fold_expressions) && !(defined(_MSC_VER) && (_MSC_VER < 1916)) +template using all_of = bool_constant<(Ts::value && ...)>; +template using any_of = bool_constant<(Ts::value || ...)>; +#elif !defined(_MSC_VER) +template struct bools {}; +template using all_of = std::is_same< + bools, + bools>; +template using any_of = negation...>>; +#else +// MSVC has trouble with the above, but supports std::conjunction, which we can use instead (albeit +// at a slight loss of compilation efficiency). +template using all_of = std::conjunction; +template using any_of = std::disjunction; +#endif +template using none_of = negation>; + +template class... Predicates> using satisfies_all_of = all_of...>; +template class... Predicates> using satisfies_any_of = any_of...>; +template class... Predicates> using satisfies_none_of = none_of...>; + +/// Strip the class from a method type +template struct remove_class { }; +template struct remove_class { typedef R type(A...); }; +template struct remove_class { typedef R type(A...); }; + +/// Helper template to strip away type modifiers +template struct intrinsic_type { typedef T type; }; +template struct intrinsic_type { typedef typename intrinsic_type::type type; }; +template struct intrinsic_type { typedef typename intrinsic_type::type type; }; +template struct intrinsic_type { typedef typename intrinsic_type::type type; }; +template struct intrinsic_type { typedef typename intrinsic_type::type type; }; +template struct intrinsic_type { typedef typename intrinsic_type::type type; }; +template struct intrinsic_type { typedef typename intrinsic_type::type type; }; +template using intrinsic_t = typename intrinsic_type::type; + +/// Helper type to replace 'void' in some expressions +struct void_type { }; + +/// Helper template which holds a list of types +template struct type_list { }; + +/// Compile-time integer sum +#ifdef __cpp_fold_expressions +template constexpr size_t constexpr_sum(Ts... ns) { return (0 + ... + size_t{ns}); } +#else +constexpr size_t constexpr_sum() { return 0; } +template +constexpr size_t constexpr_sum(T n, Ts... ns) { return size_t{n} + constexpr_sum(ns...); } +#endif + +NAMESPACE_BEGIN(constexpr_impl) +/// Implementation details for constexpr functions +constexpr int first(int i) { return i; } +template +constexpr int first(int i, T v, Ts... vs) { return v ? i : first(i + 1, vs...); } + +constexpr int last(int /*i*/, int result) { return result; } +template +constexpr int last(int i, int result, T v, Ts... vs) { return last(i + 1, v ? i : result, vs...); } +NAMESPACE_END(constexpr_impl) + +/// Return the index of the first type in Ts which satisfies Predicate. Returns sizeof...(Ts) if +/// none match. +template class Predicate, typename... Ts> +constexpr int constexpr_first() { return constexpr_impl::first(0, Predicate::value...); } + +/// Return the index of the last type in Ts which satisfies Predicate, or -1 if none match. +template class Predicate, typename... Ts> +constexpr int constexpr_last() { return constexpr_impl::last(0, -1, Predicate::value...); } + +/// Return the Nth element from the parameter pack +template +struct pack_element { using type = typename pack_element::type; }; +template +struct pack_element<0, T, Ts...> { using type = T; }; + +/// Return the one and only type which matches the predicate, or Default if none match. +/// If more than one type matches the predicate, fail at compile-time. +template class Predicate, typename Default, typename... Ts> +struct exactly_one { + static constexpr auto found = constexpr_sum(Predicate::value...); + static_assert(found <= 1, "Found more than one type matching the predicate"); + + static constexpr auto index = found ? constexpr_first() : 0; + using type = conditional_t::type, Default>; +}; +template class P, typename Default> +struct exactly_one { using type = Default; }; + +template class Predicate, typename Default, typename... Ts> +using exactly_one_t = typename exactly_one::type; + +/// Defer the evaluation of type T until types Us are instantiated +template struct deferred_type { using type = T; }; +template using deferred_t = typename deferred_type::type; + +/// Like is_base_of, but requires a strict base (i.e. `is_strict_base_of::value == false`, +/// unlike `std::is_base_of`) +template using is_strict_base_of = bool_constant< + std::is_base_of::value && !std::is_same::value>; + +/// Like is_base_of, but also requires that the base type is accessible (i.e. that a Derived pointer +/// can be converted to a Base pointer) +template using is_accessible_base_of = bool_constant< + std::is_base_of::value && std::is_convertible::value>; + +template class Base> +struct is_template_base_of_impl { + template static std::true_type check(Base *); + static std::false_type check(...); +}; + +/// Check if a template is the base of a type. For example: +/// `is_template_base_of` is true if `struct T : Base {}` where U can be anything +template class Base, typename T> +#if !defined(_MSC_VER) +using is_template_base_of = decltype(is_template_base_of_impl::check((intrinsic_t*)nullptr)); +#else // MSVC2015 has trouble with decltype in template aliases +struct is_template_base_of : decltype(is_template_base_of_impl::check((intrinsic_t*)nullptr)) { }; +#endif + +/// Check if T is an instantiation of the template `Class`. For example: +/// `is_instantiation` is true if `T == shared_ptr` where U can be anything. +template class Class, typename T> +struct is_instantiation : std::false_type { }; +template class Class, typename... Us> +struct is_instantiation> : std::true_type { }; + +/// Check if T is std::shared_ptr where U can be anything +template using is_shared_ptr = is_instantiation; + +/// Check if T looks like an input iterator +template struct is_input_iterator : std::false_type {}; +template +struct is_input_iterator()), decltype(++std::declval())>> + : std::true_type {}; + +template using is_function_pointer = bool_constant< + std::is_pointer::value && std::is_function::type>::value>; + +template struct strip_function_object { + using type = typename remove_class::type; +}; + +// Extracts the function signature from a function, function pointer or lambda. +template > +using function_signature_t = conditional_t< + std::is_function::value, + F, + typename conditional_t< + std::is_pointer::value || std::is_member_pointer::value, + std::remove_pointer, + strip_function_object + >::type +>; + +/// Returns true if the type looks like a lambda: that is, isn't a function, pointer or member +/// pointer. Note that this can catch all sorts of other things, too; this is intended to be used +/// in a place where passing a lambda makes sense. +template using is_lambda = satisfies_none_of, + std::is_function, std::is_pointer, std::is_member_pointer>; + +/// Ignore that a variable is unused in compiler warnings +inline void ignore_unused(const int *) { } + +/// Apply a function over each element of a parameter pack +#ifdef __cpp_fold_expressions +#define PYBIND11_EXPAND_SIDE_EFFECTS(PATTERN) (((PATTERN), void()), ...) +#else +using expand_side_effects = bool[]; +#define PYBIND11_EXPAND_SIDE_EFFECTS(PATTERN) pybind11::detail::expand_side_effects{ ((PATTERN), void(), false)..., false } +#endif + +NAMESPACE_END(detail) + +/// C++ bindings of builtin Python exceptions +class builtin_exception : public std::runtime_error { +public: + using std::runtime_error::runtime_error; + /// Set the error using the Python C API + virtual void set_error() const = 0; +}; + +#define PYBIND11_RUNTIME_EXCEPTION(name, type) \ + class name : public builtin_exception { public: \ + using builtin_exception::builtin_exception; \ + name() : name("") { } \ + void set_error() const override { PyErr_SetString(type, what()); } \ + }; + +PYBIND11_RUNTIME_EXCEPTION(stop_iteration, PyExc_StopIteration) +PYBIND11_RUNTIME_EXCEPTION(index_error, PyExc_IndexError) +PYBIND11_RUNTIME_EXCEPTION(key_error, PyExc_KeyError) +PYBIND11_RUNTIME_EXCEPTION(value_error, PyExc_ValueError) +PYBIND11_RUNTIME_EXCEPTION(type_error, PyExc_TypeError) +PYBIND11_RUNTIME_EXCEPTION(cast_error, PyExc_RuntimeError) /// Thrown when pybind11::cast or handle::call fail due to a type casting error +PYBIND11_RUNTIME_EXCEPTION(reference_cast_error, PyExc_RuntimeError) /// Used internally + +[[noreturn]] PYBIND11_NOINLINE inline void pybind11_fail(const char *reason) { throw std::runtime_error(reason); } +[[noreturn]] PYBIND11_NOINLINE inline void pybind11_fail(const std::string &reason) { throw std::runtime_error(reason); } + +template struct format_descriptor { }; + +NAMESPACE_BEGIN(detail) +// Returns the index of the given type in the type char array below, and in the list in numpy.h +// The order here is: bool; 8 ints ((signed,unsigned)x(8,16,32,64)bits); float,double,long double; +// complex float,double,long double. Note that the long double types only participate when long +// double is actually longer than double (it isn't under MSVC). +// NB: not only the string below but also complex.h and numpy.h rely on this order. +template struct is_fmt_numeric { static constexpr bool value = false; }; +template struct is_fmt_numeric::value>> { + static constexpr bool value = true; + static constexpr int index = std::is_same::value ? 0 : 1 + ( + std::is_integral::value ? detail::log2(sizeof(T))*2 + std::is_unsigned::value : 8 + ( + std::is_same::value ? 1 : std::is_same::value ? 2 : 0)); +}; +NAMESPACE_END(detail) + +template struct format_descriptor::value>> { + static constexpr const char c = "?bBhHiIqQfdg"[detail::is_fmt_numeric::index]; + static constexpr const char value[2] = { c, '\0' }; + static std::string format() { return std::string(1, c); } +}; + +#if !defined(PYBIND11_CPP17) + +template constexpr const char format_descriptor< + T, detail::enable_if_t::value>>::value[2]; + +#endif + +/// RAII wrapper that temporarily clears any Python error state +struct error_scope { + PyObject *type, *value, *trace; + error_scope() { PyErr_Fetch(&type, &value, &trace); } + ~error_scope() { PyErr_Restore(type, value, trace); } +}; + +/// Dummy destructor wrapper that can be used to expose classes with a private destructor +struct nodelete { template void operator()(T*) { } }; + +// overload_cast requires variable templates: C++14 +#if defined(PYBIND11_CPP14) +#define PYBIND11_OVERLOAD_CAST 1 + +NAMESPACE_BEGIN(detail) +template +struct overload_cast_impl { + constexpr overload_cast_impl() {} // MSVC 2015 needs this + + template + constexpr auto operator()(Return (*pf)(Args...)) const noexcept + -> decltype(pf) { return pf; } + + template + constexpr auto operator()(Return (Class::*pmf)(Args...), std::false_type = {}) const noexcept + -> decltype(pmf) { return pmf; } + + template + constexpr auto operator()(Return (Class::*pmf)(Args...) const, std::true_type) const noexcept + -> decltype(pmf) { return pmf; } +}; +NAMESPACE_END(detail) + +/// Syntax sugar for resolving overloaded function pointers: +/// - regular: static_cast(&Class::func) +/// - sweet: overload_cast(&Class::func) +template +static constexpr detail::overload_cast_impl overload_cast = {}; +// MSVC 2015 only accepts this particular initialization syntax for this variable template. + +/// Const member function selector for overload_cast +/// - regular: static_cast(&Class::func) +/// - sweet: overload_cast(&Class::func, const_) +static constexpr auto const_ = std::true_type{}; + +#else // no overload_cast: providing something that static_assert-fails: +template struct overload_cast { + static_assert(detail::deferred_t::value, + "pybind11::overload_cast<...> requires compiling in C++14 mode"); +}; +#endif // overload_cast + +NAMESPACE_BEGIN(detail) + +// Adaptor for converting arbitrary container arguments into a vector; implicitly convertible from +// any standard container (or C-style array) supporting std::begin/std::end, any singleton +// arithmetic type (if T is arithmetic), or explicitly constructible from an iterator pair. +template +class any_container { + std::vector v; +public: + any_container() = default; + + // Can construct from a pair of iterators + template ::value>> + any_container(It first, It last) : v(first, last) { } + + // Implicit conversion constructor from any arbitrary container type with values convertible to T + template ())), T>::value>> + any_container(const Container &c) : any_container(std::begin(c), std::end(c)) { } + + // initializer_list's aren't deducible, so don't get matched by the above template; we need this + // to explicitly allow implicit conversion from one: + template ::value>> + any_container(const std::initializer_list &c) : any_container(c.begin(), c.end()) { } + + // Avoid copying if given an rvalue vector of the correct type. + any_container(std::vector &&v) : v(std::move(v)) { } + + // Moves the vector out of an rvalue any_container + operator std::vector &&() && { return std::move(v); } + + // Dereferencing obtains a reference to the underlying vector + std::vector &operator*() { return v; } + const std::vector &operator*() const { return v; } + + // -> lets you call methods on the underlying vector + std::vector *operator->() { return &v; } + const std::vector *operator->() const { return &v; } +}; + +NAMESPACE_END(detail) + + + +NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/ptocr/postprocess/piexlmerge/include/pybind11/detail/descr.h b/ptocr/postprocess/piexlmerge/include/pybind11/detail/descr.h new file mode 100644 index 0000000..8d404e5 --- /dev/null +++ b/ptocr/postprocess/piexlmerge/include/pybind11/detail/descr.h @@ -0,0 +1,100 @@ +/* + pybind11/detail/descr.h: Helper type for concatenating type signatures at compile time + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "common.h" + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +NAMESPACE_BEGIN(detail) + +#if !defined(_MSC_VER) +# define PYBIND11_DESCR_CONSTEXPR static constexpr +#else +# define PYBIND11_DESCR_CONSTEXPR const +#endif + +/* Concatenate type signatures at compile time */ +template +struct descr { + char text[N + 1]; + + constexpr descr() : text{'\0'} { } + constexpr descr(char const (&s)[N+1]) : descr(s, make_index_sequence()) { } + + template + constexpr descr(char const (&s)[N+1], index_sequence) : text{s[Is]..., '\0'} { } + + template + constexpr descr(char c, Chars... cs) : text{c, static_cast(cs)..., '\0'} { } + + static constexpr std::array types() { + return {{&typeid(Ts)..., nullptr}}; + } +}; + +template +constexpr descr plus_impl(const descr &a, const descr &b, + index_sequence, index_sequence) { + return {a.text[Is1]..., b.text[Is2]...}; +} + +template +constexpr descr operator+(const descr &a, const descr &b) { + return plus_impl(a, b, make_index_sequence(), make_index_sequence()); +} + +template +constexpr descr _(char const(&text)[N]) { return descr(text); } +constexpr descr<0> _(char const(&)[1]) { return {}; } + +template struct int_to_str : int_to_str { }; +template struct int_to_str<0, Digits...> { + static constexpr auto digits = descr(('0' + Digits)...); +}; + +// Ternary description (like std::conditional) +template +constexpr enable_if_t> _(char const(&text1)[N1], char const(&)[N2]) { + return _(text1); +} +template +constexpr enable_if_t> _(char const(&)[N1], char const(&text2)[N2]) { + return _(text2); +} + +template +constexpr enable_if_t _(const T1 &d, const T2 &) { return d; } +template +constexpr enable_if_t _(const T1 &, const T2 &d) { return d; } + +template auto constexpr _() -> decltype(int_to_str::digits) { + return int_to_str::digits; +} + +template constexpr descr<1, Type> _() { return {'%'}; } + +constexpr descr<0> concat() { return {}; } + +template +constexpr descr concat(const descr &descr) { return descr; } + +template +constexpr auto concat(const descr &d, const Args &...args) + -> decltype(std::declval>() + concat(args...)) { + return d + _(", ") + concat(args...); +} + +template +constexpr descr type_descr(const descr &descr) { + return _("{") + descr + _("}"); +} + +NAMESPACE_END(detail) +NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/ptocr/postprocess/piexlmerge/include/pybind11/detail/init.h b/ptocr/postprocess/piexlmerge/include/pybind11/detail/init.h new file mode 100644 index 0000000..acfe00b --- /dev/null +++ b/ptocr/postprocess/piexlmerge/include/pybind11/detail/init.h @@ -0,0 +1,335 @@ +/* + pybind11/detail/init.h: init factory function implementation and support code. + + Copyright (c) 2017 Jason Rhinelander + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "class.h" + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +NAMESPACE_BEGIN(detail) + +template <> +class type_caster { +public: + bool load(handle h, bool) { + value = reinterpret_cast(h.ptr()); + return true; + } + + template using cast_op_type = value_and_holder &; + operator value_and_holder &() { return *value; } + static constexpr auto name = _(); + +private: + value_and_holder *value = nullptr; +}; + +NAMESPACE_BEGIN(initimpl) + +inline void no_nullptr(void *ptr) { + if (!ptr) throw type_error("pybind11::init(): factory function returned nullptr"); +} + +// Implementing functions for all forms of py::init<...> and py::init(...) +template using Cpp = typename Class::type; +template using Alias = typename Class::type_alias; +template using Holder = typename Class::holder_type; + +template using is_alias_constructible = std::is_constructible, Cpp &&>; + +// Takes a Cpp pointer and returns true if it actually is a polymorphic Alias instance. +template = 0> +bool is_alias(Cpp *ptr) { + return dynamic_cast *>(ptr) != nullptr; +} +// Failing fallback version of the above for a no-alias class (always returns false) +template +constexpr bool is_alias(void *) { return false; } + +// Constructs and returns a new object; if the given arguments don't map to a constructor, we fall +// back to brace aggregate initiailization so that for aggregate initialization can be used with +// py::init, e.g. `py::init` to initialize a `struct T { int a; int b; }`. For +// non-aggregate types, we need to use an ordinary T(...) constructor (invoking as `T{...}` usually +// works, but will not do the expected thing when `T` has an `initializer_list` constructor). +template ::value, int> = 0> +inline Class *construct_or_initialize(Args &&...args) { return new Class(std::forward(args)...); } +template ::value, int> = 0> +inline Class *construct_or_initialize(Args &&...args) { return new Class{std::forward(args)...}; } + +// Attempts to constructs an alias using a `Alias(Cpp &&)` constructor. This allows types with +// an alias to provide only a single Cpp factory function as long as the Alias can be +// constructed from an rvalue reference of the base Cpp type. This means that Alias classes +// can, when appropriate, simply define a `Alias(Cpp &&)` constructor rather than needing to +// inherit all the base class constructors. +template +void construct_alias_from_cpp(std::true_type /*is_alias_constructible*/, + value_and_holder &v_h, Cpp &&base) { + v_h.value_ptr() = new Alias(std::move(base)); +} +template +[[noreturn]] void construct_alias_from_cpp(std::false_type /*!is_alias_constructible*/, + value_and_holder &, Cpp &&) { + throw type_error("pybind11::init(): unable to convert returned instance to required " + "alias class: no `Alias(Class &&)` constructor available"); +} + +// Error-generating fallback for factories that don't match one of the below construction +// mechanisms. +template +void construct(...) { + static_assert(!std::is_same::value /* always false */, + "pybind11::init(): init function must return a compatible pointer, " + "holder, or value"); +} + +// Pointer return v1: the factory function returns a class pointer for a registered class. +// If we don't need an alias (because this class doesn't have one, or because the final type is +// inherited on the Python side) we can simply take over ownership. Otherwise we need to try to +// construct an Alias from the returned base instance. +template +void construct(value_and_holder &v_h, Cpp *ptr, bool need_alias) { + no_nullptr(ptr); + if (Class::has_alias && need_alias && !is_alias(ptr)) { + // We're going to try to construct an alias by moving the cpp type. Whether or not + // that succeeds, we still need to destroy the original cpp pointer (either the + // moved away leftover, if the alias construction works, or the value itself if we + // throw an error), but we can't just call `delete ptr`: it might have a special + // deleter, or might be shared_from_this. So we construct a holder around it as if + // it was a normal instance, then steal the holder away into a local variable; thus + // the holder and destruction happens when we leave the C++ scope, and the holder + // class gets to handle the destruction however it likes. + v_h.value_ptr() = ptr; + v_h.set_instance_registered(true); // To prevent init_instance from registering it + v_h.type->init_instance(v_h.inst, nullptr); // Set up the holder + Holder temp_holder(std::move(v_h.holder>())); // Steal the holder + v_h.type->dealloc(v_h); // Destroys the moved-out holder remains, resets value ptr to null + v_h.set_instance_registered(false); + + construct_alias_from_cpp(is_alias_constructible{}, v_h, std::move(*ptr)); + } else { + // Otherwise the type isn't inherited, so we don't need an Alias + v_h.value_ptr() = ptr; + } +} + +// Pointer return v2: a factory that always returns an alias instance ptr. We simply take over +// ownership of the pointer. +template = 0> +void construct(value_and_holder &v_h, Alias *alias_ptr, bool) { + no_nullptr(alias_ptr); + v_h.value_ptr() = static_cast *>(alias_ptr); +} + +// Holder return: copy its pointer, and move or copy the returned holder into the new instance's +// holder. This also handles types like std::shared_ptr and std::unique_ptr where T is a +// derived type (through those holder's implicit conversion from derived class holder constructors). +template +void construct(value_and_holder &v_h, Holder holder, bool need_alias) { + auto *ptr = holder_helper>::get(holder); + // If we need an alias, check that the held pointer is actually an alias instance + if (Class::has_alias && need_alias && !is_alias(ptr)) + throw type_error("pybind11::init(): construction failed: returned holder-wrapped instance " + "is not an alias instance"); + + v_h.value_ptr() = ptr; + v_h.type->init_instance(v_h.inst, &holder); +} + +// return-by-value version 1: returning a cpp class by value. If the class has an alias and an +// alias is required the alias must have an `Alias(Cpp &&)` constructor so that we can construct +// the alias from the base when needed (i.e. because of Python-side inheritance). When we don't +// need it, we simply move-construct the cpp value into a new instance. +template +void construct(value_and_holder &v_h, Cpp &&result, bool need_alias) { + static_assert(std::is_move_constructible>::value, + "pybind11::init() return-by-value factory function requires a movable class"); + if (Class::has_alias && need_alias) + construct_alias_from_cpp(is_alias_constructible{}, v_h, std::move(result)); + else + v_h.value_ptr() = new Cpp(std::move(result)); +} + +// return-by-value version 2: returning a value of the alias type itself. We move-construct an +// Alias instance (even if no the python-side inheritance is involved). The is intended for +// cases where Alias initialization is always desired. +template +void construct(value_and_holder &v_h, Alias &&result, bool) { + static_assert(std::is_move_constructible>::value, + "pybind11::init() return-by-alias-value factory function requires a movable alias class"); + v_h.value_ptr() = new Alias(std::move(result)); +} + +// Implementing class for py::init<...>() +template +struct constructor { + template = 0> + static void execute(Class &cl, const Extra&... extra) { + cl.def("__init__", [](value_and_holder &v_h, Args... args) { + v_h.value_ptr() = construct_or_initialize>(std::forward(args)...); + }, is_new_style_constructor(), extra...); + } + + template , Args...>::value, int> = 0> + static void execute(Class &cl, const Extra&... extra) { + cl.def("__init__", [](value_and_holder &v_h, Args... args) { + if (Py_TYPE(v_h.inst) == v_h.type->type) + v_h.value_ptr() = construct_or_initialize>(std::forward(args)...); + else + v_h.value_ptr() = construct_or_initialize>(std::forward(args)...); + }, is_new_style_constructor(), extra...); + } + + template , Args...>::value, int> = 0> + static void execute(Class &cl, const Extra&... extra) { + cl.def("__init__", [](value_and_holder &v_h, Args... args) { + v_h.value_ptr() = construct_or_initialize>(std::forward(args)...); + }, is_new_style_constructor(), extra...); + } +}; + +// Implementing class for py::init_alias<...>() +template struct alias_constructor { + template , Args...>::value, int> = 0> + static void execute(Class &cl, const Extra&... extra) { + cl.def("__init__", [](value_and_holder &v_h, Args... args) { + v_h.value_ptr() = construct_or_initialize>(std::forward(args)...); + }, is_new_style_constructor(), extra...); + } +}; + +// Implementation class for py::init(Func) and py::init(Func, AliasFunc) +template , typename = function_signature_t> +struct factory; + +// Specialization for py::init(Func) +template +struct factory { + remove_reference_t class_factory; + + factory(Func &&f) : class_factory(std::forward(f)) { } + + // The given class either has no alias or has no separate alias factory; + // this always constructs the class itself. If the class is registered with an alias + // type and an alias instance is needed (i.e. because the final type is a Python class + // inheriting from the C++ type) the returned value needs to either already be an alias + // instance, or the alias needs to be constructible from a `Class &&` argument. + template + void execute(Class &cl, const Extra &...extra) && { + #if defined(PYBIND11_CPP14) + cl.def("__init__", [func = std::move(class_factory)] + #else + auto &func = class_factory; + cl.def("__init__", [func] + #endif + (value_and_holder &v_h, Args... args) { + construct(v_h, func(std::forward(args)...), + Py_TYPE(v_h.inst) != v_h.type->type); + }, is_new_style_constructor(), extra...); + } +}; + +// Specialization for py::init(Func, AliasFunc) +template +struct factory { + static_assert(sizeof...(CArgs) == sizeof...(AArgs), + "pybind11::init(class_factory, alias_factory): class and alias factories " + "must have identical argument signatures"); + static_assert(all_of...>::value, + "pybind11::init(class_factory, alias_factory): class and alias factories " + "must have identical argument signatures"); + + remove_reference_t class_factory; + remove_reference_t alias_factory; + + factory(CFunc &&c, AFunc &&a) + : class_factory(std::forward(c)), alias_factory(std::forward(a)) { } + + // The class factory is called when the `self` type passed to `__init__` is the direct + // class (i.e. not inherited), the alias factory when `self` is a Python-side subtype. + template + void execute(Class &cl, const Extra&... extra) && { + static_assert(Class::has_alias, "The two-argument version of `py::init()` can " + "only be used if the class has an alias"); + #if defined(PYBIND11_CPP14) + cl.def("__init__", [class_func = std::move(class_factory), alias_func = std::move(alias_factory)] + #else + auto &class_func = class_factory; + auto &alias_func = alias_factory; + cl.def("__init__", [class_func, alias_func] + #endif + (value_and_holder &v_h, CArgs... args) { + if (Py_TYPE(v_h.inst) == v_h.type->type) + // If the instance type equals the registered type we don't have inheritance, so + // don't need the alias and can construct using the class function: + construct(v_h, class_func(std::forward(args)...), false); + else + construct(v_h, alias_func(std::forward(args)...), true); + }, is_new_style_constructor(), extra...); + } +}; + +/// Set just the C++ state. Same as `__init__`. +template +void setstate(value_and_holder &v_h, T &&result, bool need_alias) { + construct(v_h, std::forward(result), need_alias); +} + +/// Set both the C++ and Python states +template ::value, int> = 0> +void setstate(value_and_holder &v_h, std::pair &&result, bool need_alias) { + construct(v_h, std::move(result.first), need_alias); + setattr((PyObject *) v_h.inst, "__dict__", result.second); +} + +/// Implementation for py::pickle(GetState, SetState) +template , typename = function_signature_t> +struct pickle_factory; + +template +struct pickle_factory { + static_assert(std::is_same, intrinsic_t>::value, + "The type returned by `__getstate__` must be the same " + "as the argument accepted by `__setstate__`"); + + remove_reference_t get; + remove_reference_t set; + + pickle_factory(Get get, Set set) + : get(std::forward(get)), set(std::forward(set)) { } + + template + void execute(Class &cl, const Extra &...extra) && { + cl.def("__getstate__", std::move(get)); + +#if defined(PYBIND11_CPP14) + cl.def("__setstate__", [func = std::move(set)] +#else + auto &func = set; + cl.def("__setstate__", [func] +#endif + (value_and_holder &v_h, ArgState state) { + setstate(v_h, func(std::forward(state)), + Py_TYPE(v_h.inst) != v_h.type->type); + }, is_new_style_constructor(), extra...); + } +}; + +NAMESPACE_END(initimpl) +NAMESPACE_END(detail) +NAMESPACE_END(pybind11) diff --git a/ptocr/postprocess/piexlmerge/include/pybind11/detail/internals.h b/ptocr/postprocess/piexlmerge/include/pybind11/detail/internals.h new file mode 100644 index 0000000..6d7dc5c --- /dev/null +++ b/ptocr/postprocess/piexlmerge/include/pybind11/detail/internals.h @@ -0,0 +1,291 @@ +/* + pybind11/detail/internals.h: Internal data structure and related functions + + Copyright (c) 2017 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "../pytypes.h" + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +NAMESPACE_BEGIN(detail) +// Forward declarations +inline PyTypeObject *make_static_property_type(); +inline PyTypeObject *make_default_metaclass(); +inline PyObject *make_object_base_type(PyTypeObject *metaclass); + +// The old Python Thread Local Storage (TLS) API is deprecated in Python 3.7 in favor of the new +// Thread Specific Storage (TSS) API. +#if PY_VERSION_HEX >= 0x03070000 +# define PYBIND11_TLS_KEY_INIT(var) Py_tss_t *var = nullptr +# define PYBIND11_TLS_GET_VALUE(key) PyThread_tss_get((key)) +# define PYBIND11_TLS_REPLACE_VALUE(key, value) PyThread_tss_set((key), (tstate)) +# define PYBIND11_TLS_DELETE_VALUE(key) PyThread_tss_set((key), nullptr) +#else + // Usually an int but a long on Cygwin64 with Python 3.x +# define PYBIND11_TLS_KEY_INIT(var) decltype(PyThread_create_key()) var = 0 +# define PYBIND11_TLS_GET_VALUE(key) PyThread_get_key_value((key)) +# if PY_MAJOR_VERSION < 3 +# define PYBIND11_TLS_DELETE_VALUE(key) \ + PyThread_delete_key_value(key) +# define PYBIND11_TLS_REPLACE_VALUE(key, value) \ + do { \ + PyThread_delete_key_value((key)); \ + PyThread_set_key_value((key), (value)); \ + } while (false) +# else +# define PYBIND11_TLS_DELETE_VALUE(key) \ + PyThread_set_key_value((key), nullptr) +# define PYBIND11_TLS_REPLACE_VALUE(key, value) \ + PyThread_set_key_value((key), (value)) +# endif +#endif + +// Python loads modules by default with dlopen with the RTLD_LOCAL flag; under libc++ and possibly +// other STLs, this means `typeid(A)` from one module won't equal `typeid(A)` from another module +// even when `A` is the same, non-hidden-visibility type (e.g. from a common include). Under +// libstdc++, this doesn't happen: equality and the type_index hash are based on the type name, +// which works. If not under a known-good stl, provide our own name-based hash and equality +// functions that use the type name. +#if defined(__GLIBCXX__) +inline bool same_type(const std::type_info &lhs, const std::type_info &rhs) { return lhs == rhs; } +using type_hash = std::hash; +using type_equal_to = std::equal_to; +#else +inline bool same_type(const std::type_info &lhs, const std::type_info &rhs) { + return lhs.name() == rhs.name() || std::strcmp(lhs.name(), rhs.name()) == 0; +} + +struct type_hash { + size_t operator()(const std::type_index &t) const { + size_t hash = 5381; + const char *ptr = t.name(); + while (auto c = static_cast(*ptr++)) + hash = (hash * 33) ^ c; + return hash; + } +}; + +struct type_equal_to { + bool operator()(const std::type_index &lhs, const std::type_index &rhs) const { + return lhs.name() == rhs.name() || std::strcmp(lhs.name(), rhs.name()) == 0; + } +}; +#endif + +template +using type_map = std::unordered_map; + +struct overload_hash { + inline size_t operator()(const std::pair& v) const { + size_t value = std::hash()(v.first); + value ^= std::hash()(v.second) + 0x9e3779b9 + (value<<6) + (value>>2); + return value; + } +}; + +/// Internal data structure used to track registered instances and types. +/// Whenever binary incompatible changes are made to this structure, +/// `PYBIND11_INTERNALS_VERSION` must be incremented. +struct internals { + type_map registered_types_cpp; // std::type_index -> pybind11's type information + std::unordered_map> registered_types_py; // PyTypeObject* -> base type_info(s) + std::unordered_multimap registered_instances; // void * -> instance* + std::unordered_set, overload_hash> inactive_overload_cache; + type_map> direct_conversions; + std::unordered_map> patients; + std::forward_list registered_exception_translators; + std::unordered_map shared_data; // Custom data to be shared across extensions + std::vector loader_patient_stack; // Used by `loader_life_support` + std::forward_list static_strings; // Stores the std::strings backing detail::c_str() + PyTypeObject *static_property_type; + PyTypeObject *default_metaclass; + PyObject *instance_base; +#if defined(WITH_THREAD) + PYBIND11_TLS_KEY_INIT(tstate); + PyInterpreterState *istate = nullptr; +#endif +}; + +/// Additional type information which does not fit into the PyTypeObject. +/// Changes to this struct also require bumping `PYBIND11_INTERNALS_VERSION`. +struct type_info { + PyTypeObject *type; + const std::type_info *cpptype; + size_t type_size, type_align, holder_size_in_ptrs; + void *(*operator_new)(size_t); + void (*init_instance)(instance *, const void *); + void (*dealloc)(value_and_holder &v_h); + std::vector implicit_conversions; + std::vector> implicit_casts; + std::vector *direct_conversions; + buffer_info *(*get_buffer)(PyObject *, void *) = nullptr; + void *get_buffer_data = nullptr; + void *(*module_local_load)(PyObject *, const type_info *) = nullptr; + /* A simple type never occurs as a (direct or indirect) parent + * of a class that makes use of multiple inheritance */ + bool simple_type : 1; + /* True if there is no multiple inheritance in this type's inheritance tree */ + bool simple_ancestors : 1; + /* for base vs derived holder_type checks */ + bool default_holder : 1; + /* true if this is a type registered with py::module_local */ + bool module_local : 1; +}; + +/// Tracks the `internals` and `type_info` ABI version independent of the main library version +#define PYBIND11_INTERNALS_VERSION 3 + +#if defined(_DEBUG) +# define PYBIND11_BUILD_TYPE "_debug" +#else +# define PYBIND11_BUILD_TYPE "" +#endif + +#if defined(WITH_THREAD) +# define PYBIND11_INTERNALS_KIND "" +#else +# define PYBIND11_INTERNALS_KIND "_without_thread" +#endif + +#define PYBIND11_INTERNALS_ID "__pybind11_internals_v" \ + PYBIND11_TOSTRING(PYBIND11_INTERNALS_VERSION) PYBIND11_INTERNALS_KIND PYBIND11_BUILD_TYPE "__" + +#define PYBIND11_MODULE_LOCAL_ID "__pybind11_module_local_v" \ + PYBIND11_TOSTRING(PYBIND11_INTERNALS_VERSION) PYBIND11_INTERNALS_KIND PYBIND11_BUILD_TYPE "__" + +/// Each module locally stores a pointer to the `internals` data. The data +/// itself is shared among modules with the same `PYBIND11_INTERNALS_ID`. +inline internals **&get_internals_pp() { + static internals **internals_pp = nullptr; + return internals_pp; +} + +/// Return a reference to the current `internals` data +PYBIND11_NOINLINE inline internals &get_internals() { + auto **&internals_pp = get_internals_pp(); + if (internals_pp && *internals_pp) + return **internals_pp; + + constexpr auto *id = PYBIND11_INTERNALS_ID; + auto builtins = handle(PyEval_GetBuiltins()); + if (builtins.contains(id) && isinstance(builtins[id])) { + internals_pp = static_cast(capsule(builtins[id])); + + // We loaded builtins through python's builtins, which means that our `error_already_set` + // and `builtin_exception` may be different local classes than the ones set up in the + // initial exception translator, below, so add another for our local exception classes. + // + // libstdc++ doesn't require this (types there are identified only by name) +#if !defined(__GLIBCXX__) + (*internals_pp)->registered_exception_translators.push_front( + [](std::exception_ptr p) -> void { + try { + if (p) std::rethrow_exception(p); + } catch (error_already_set &e) { e.restore(); return; + } catch (const builtin_exception &e) { e.set_error(); return; + } + } + ); +#endif + } else { + if (!internals_pp) internals_pp = new internals*(); + auto *&internals_ptr = *internals_pp; + internals_ptr = new internals(); +#if defined(WITH_THREAD) + PyEval_InitThreads(); + PyThreadState *tstate = PyThreadState_Get(); + #if PY_VERSION_HEX >= 0x03070000 + internals_ptr->tstate = PyThread_tss_alloc(); + if (!internals_ptr->tstate || PyThread_tss_create(internals_ptr->tstate)) + pybind11_fail("get_internals: could not successfully initialize the TSS key!"); + PyThread_tss_set(internals_ptr->tstate, tstate); + #else + internals_ptr->tstate = PyThread_create_key(); + if (internals_ptr->tstate == -1) + pybind11_fail("get_internals: could not successfully initialize the TLS key!"); + PyThread_set_key_value(internals_ptr->tstate, tstate); + #endif + internals_ptr->istate = tstate->interp; +#endif + builtins[id] = capsule(internals_pp); + internals_ptr->registered_exception_translators.push_front( + [](std::exception_ptr p) -> void { + try { + if (p) std::rethrow_exception(p); + } catch (error_already_set &e) { e.restore(); return; + } catch (const builtin_exception &e) { e.set_error(); return; + } catch (const std::bad_alloc &e) { PyErr_SetString(PyExc_MemoryError, e.what()); return; + } catch (const std::domain_error &e) { PyErr_SetString(PyExc_ValueError, e.what()); return; + } catch (const std::invalid_argument &e) { PyErr_SetString(PyExc_ValueError, e.what()); return; + } catch (const std::length_error &e) { PyErr_SetString(PyExc_ValueError, e.what()); return; + } catch (const std::out_of_range &e) { PyErr_SetString(PyExc_IndexError, e.what()); return; + } catch (const std::range_error &e) { PyErr_SetString(PyExc_ValueError, e.what()); return; + } catch (const std::exception &e) { PyErr_SetString(PyExc_RuntimeError, e.what()); return; + } catch (...) { + PyErr_SetString(PyExc_RuntimeError, "Caught an unknown exception!"); + return; + } + } + ); + internals_ptr->static_property_type = make_static_property_type(); + internals_ptr->default_metaclass = make_default_metaclass(); + internals_ptr->instance_base = make_object_base_type(internals_ptr->default_metaclass); + } + return **internals_pp; +} + +/// Works like `internals.registered_types_cpp`, but for module-local registered types: +inline type_map ®istered_local_types_cpp() { + static type_map locals{}; + return locals; +} + +/// Constructs a std::string with the given arguments, stores it in `internals`, and returns its +/// `c_str()`. Such strings objects have a long storage duration -- the internal strings are only +/// cleared when the program exits or after interpreter shutdown (when embedding), and so are +/// suitable for c-style strings needed by Python internals (such as PyTypeObject's tp_name). +template +const char *c_str(Args &&...args) { + auto &strings = get_internals().static_strings; + strings.emplace_front(std::forward(args)...); + return strings.front().c_str(); +} + +NAMESPACE_END(detail) + +/// Returns a named pointer that is shared among all extension modules (using the same +/// pybind11 version) running in the current interpreter. Names starting with underscores +/// are reserved for internal usage. Returns `nullptr` if no matching entry was found. +inline PYBIND11_NOINLINE void *get_shared_data(const std::string &name) { + auto &internals = detail::get_internals(); + auto it = internals.shared_data.find(name); + return it != internals.shared_data.end() ? it->second : nullptr; +} + +/// Set the shared data that can be later recovered by `get_shared_data()`. +inline PYBIND11_NOINLINE void *set_shared_data(const std::string &name, void *data) { + detail::get_internals().shared_data[name] = data; + return data; +} + +/// Returns a typed reference to a shared data entry (by using `get_shared_data()`) if +/// such entry exists. Otherwise, a new object of default-constructible type `T` is +/// added to the shared data under the given name and a reference to it is returned. +template +T &get_or_create_shared_data(const std::string &name) { + auto &internals = detail::get_internals(); + auto it = internals.shared_data.find(name); + T *ptr = (T *) (it != internals.shared_data.end() ? it->second : nullptr); + if (!ptr) { + ptr = new T(); + internals.shared_data[name] = ptr; + } + return *ptr; +} + +NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/ptocr/postprocess/piexlmerge/include/pybind11/detail/typeid.h b/ptocr/postprocess/piexlmerge/include/pybind11/detail/typeid.h new file mode 100644 index 0000000..6f36aab --- /dev/null +++ b/ptocr/postprocess/piexlmerge/include/pybind11/detail/typeid.h @@ -0,0 +1,53 @@ +/* + pybind11/detail/typeid.h: Compiler-independent access to type identifiers + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include +#include + +#if defined(__GNUG__) +#include +#endif + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +NAMESPACE_BEGIN(detail) +/// Erase all occurrences of a substring +inline void erase_all(std::string &string, const std::string &search) { + for (size_t pos = 0;;) { + pos = string.find(search, pos); + if (pos == std::string::npos) break; + string.erase(pos, search.length()); + } +} + +PYBIND11_NOINLINE inline void clean_type_id(std::string &name) { +#if defined(__GNUG__) + int status = 0; + std::unique_ptr res { + abi::__cxa_demangle(name.c_str(), nullptr, nullptr, &status), std::free }; + if (status == 0) + name = res.get(); +#else + detail::erase_all(name, "class "); + detail::erase_all(name, "struct "); + detail::erase_all(name, "enum "); +#endif + detail::erase_all(name, "pybind11::"); +} +NAMESPACE_END(detail) + +/// Return a string representation of a C++ type +template static std::string type_id() { + std::string name(typeid(T).name()); + detail::clean_type_id(name); + return name; +} + +NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/ptocr/postprocess/piexlmerge/include/pybind11/eigen.h b/ptocr/postprocess/piexlmerge/include/pybind11/eigen.h new file mode 100644 index 0000000..d963d96 --- /dev/null +++ b/ptocr/postprocess/piexlmerge/include/pybind11/eigen.h @@ -0,0 +1,607 @@ +/* + pybind11/eigen.h: Transparent conversion for dense and sparse Eigen matrices + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "numpy.h" + +#if defined(__INTEL_COMPILER) +# pragma warning(disable: 1682) // implicit conversion of a 64-bit integral type to a smaller integral type (potential portability problem) +#elif defined(__GNUG__) || defined(__clang__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wconversion" +# pragma GCC diagnostic ignored "-Wdeprecated-declarations" +# ifdef __clang__ +// Eigen generates a bunch of implicit-copy-constructor-is-deprecated warnings with -Wdeprecated +// under Clang, so disable that warning here: +# pragma GCC diagnostic ignored "-Wdeprecated" +# endif +# if __GNUC__ >= 7 +# pragma GCC diagnostic ignored "-Wint-in-bool-context" +# endif +#endif + +#if defined(_MSC_VER) +# pragma warning(push) +# pragma warning(disable: 4127) // warning C4127: Conditional expression is constant +# pragma warning(disable: 4996) // warning C4996: std::unary_negate is deprecated in C++17 +#endif + +#include +#include + +// Eigen prior to 3.2.7 doesn't have proper move constructors--but worse, some classes get implicit +// move constructors that break things. We could detect this an explicitly copy, but an extra copy +// of matrices seems highly undesirable. +static_assert(EIGEN_VERSION_AT_LEAST(3,2,7), "Eigen support in pybind11 requires Eigen >= 3.2.7"); + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) + +// Provide a convenience alias for easier pass-by-ref usage with fully dynamic strides: +using EigenDStride = Eigen::Stride; +template using EigenDRef = Eigen::Ref; +template using EigenDMap = Eigen::Map; + +NAMESPACE_BEGIN(detail) + +#if EIGEN_VERSION_AT_LEAST(3,3,0) +using EigenIndex = Eigen::Index; +#else +using EigenIndex = EIGEN_DEFAULT_DENSE_INDEX_TYPE; +#endif + +// Matches Eigen::Map, Eigen::Ref, blocks, etc: +template using is_eigen_dense_map = all_of, std::is_base_of, T>>; +template using is_eigen_mutable_map = std::is_base_of, T>; +template using is_eigen_dense_plain = all_of>, is_template_base_of>; +template using is_eigen_sparse = is_template_base_of; +// Test for objects inheriting from EigenBase that aren't captured by the above. This +// basically covers anything that can be assigned to a dense matrix but that don't have a typical +// matrix data layout that can be copied from their .data(). For example, DiagonalMatrix and +// SelfAdjointView fall into this category. +template using is_eigen_other = all_of< + is_template_base_of, + negation, is_eigen_dense_plain, is_eigen_sparse>> +>; + +// Captures numpy/eigen conformability status (returned by EigenProps::conformable()): +template struct EigenConformable { + bool conformable = false; + EigenIndex rows = 0, cols = 0; + EigenDStride stride{0, 0}; // Only valid if negativestrides is false! + bool negativestrides = false; // If true, do not use stride! + + EigenConformable(bool fits = false) : conformable{fits} {} + // Matrix type: + EigenConformable(EigenIndex r, EigenIndex c, + EigenIndex rstride, EigenIndex cstride) : + conformable{true}, rows{r}, cols{c} { + // TODO: when Eigen bug #747 is fixed, remove the tests for non-negativity. http://eigen.tuxfamily.org/bz/show_bug.cgi?id=747 + if (rstride < 0 || cstride < 0) { + negativestrides = true; + } else { + stride = {EigenRowMajor ? rstride : cstride /* outer stride */, + EigenRowMajor ? cstride : rstride /* inner stride */ }; + } + } + // Vector type: + EigenConformable(EigenIndex r, EigenIndex c, EigenIndex stride) + : EigenConformable(r, c, r == 1 ? c*stride : stride, c == 1 ? r : r*stride) {} + + template bool stride_compatible() const { + // To have compatible strides, we need (on both dimensions) one of fully dynamic strides, + // matching strides, or a dimension size of 1 (in which case the stride value is irrelevant) + return + !negativestrides && + (props::inner_stride == Eigen::Dynamic || props::inner_stride == stride.inner() || + (EigenRowMajor ? cols : rows) == 1) && + (props::outer_stride == Eigen::Dynamic || props::outer_stride == stride.outer() || + (EigenRowMajor ? rows : cols) == 1); + } + operator bool() const { return conformable; } +}; + +template struct eigen_extract_stride { using type = Type; }; +template +struct eigen_extract_stride> { using type = StrideType; }; +template +struct eigen_extract_stride> { using type = StrideType; }; + +// Helper struct for extracting information from an Eigen type +template struct EigenProps { + using Type = Type_; + using Scalar = typename Type::Scalar; + using StrideType = typename eigen_extract_stride::type; + static constexpr EigenIndex + rows = Type::RowsAtCompileTime, + cols = Type::ColsAtCompileTime, + size = Type::SizeAtCompileTime; + static constexpr bool + row_major = Type::IsRowMajor, + vector = Type::IsVectorAtCompileTime, // At least one dimension has fixed size 1 + fixed_rows = rows != Eigen::Dynamic, + fixed_cols = cols != Eigen::Dynamic, + fixed = size != Eigen::Dynamic, // Fully-fixed size + dynamic = !fixed_rows && !fixed_cols; // Fully-dynamic size + + template using if_zero = std::integral_constant; + static constexpr EigenIndex inner_stride = if_zero::value, + outer_stride = if_zero::value; + static constexpr bool dynamic_stride = inner_stride == Eigen::Dynamic && outer_stride == Eigen::Dynamic; + static constexpr bool requires_row_major = !dynamic_stride && !vector && (row_major ? inner_stride : outer_stride) == 1; + static constexpr bool requires_col_major = !dynamic_stride && !vector && (row_major ? outer_stride : inner_stride) == 1; + + // Takes an input array and determines whether we can make it fit into the Eigen type. If + // the array is a vector, we attempt to fit it into either an Eigen 1xN or Nx1 vector + // (preferring the latter if it will fit in either, i.e. for a fully dynamic matrix type). + static EigenConformable conformable(const array &a) { + const auto dims = a.ndim(); + if (dims < 1 || dims > 2) + return false; + + if (dims == 2) { // Matrix type: require exact match (or dynamic) + + EigenIndex + np_rows = a.shape(0), + np_cols = a.shape(1), + np_rstride = a.strides(0) / static_cast(sizeof(Scalar)), + np_cstride = a.strides(1) / static_cast(sizeof(Scalar)); + if ((fixed_rows && np_rows != rows) || (fixed_cols && np_cols != cols)) + return false; + + return {np_rows, np_cols, np_rstride, np_cstride}; + } + + // Otherwise we're storing an n-vector. Only one of the strides will be used, but whichever + // is used, we want the (single) numpy stride value. + const EigenIndex n = a.shape(0), + stride = a.strides(0) / static_cast(sizeof(Scalar)); + + if (vector) { // Eigen type is a compile-time vector + if (fixed && size != n) + return false; // Vector size mismatch + return {rows == 1 ? 1 : n, cols == 1 ? 1 : n, stride}; + } + else if (fixed) { + // The type has a fixed size, but is not a vector: abort + return false; + } + else if (fixed_cols) { + // Since this isn't a vector, cols must be != 1. We allow this only if it exactly + // equals the number of elements (rows is Dynamic, and so 1 row is allowed). + if (cols != n) return false; + return {1, n, stride}; + } + else { + // Otherwise it's either fully dynamic, or column dynamic; both become a column vector + if (fixed_rows && rows != n) return false; + return {n, 1, stride}; + } + } + + static constexpr bool show_writeable = is_eigen_dense_map::value && is_eigen_mutable_map::value; + static constexpr bool show_order = is_eigen_dense_map::value; + static constexpr bool show_c_contiguous = show_order && requires_row_major; + static constexpr bool show_f_contiguous = !show_c_contiguous && show_order && requires_col_major; + + static constexpr auto descriptor = + _("numpy.ndarray[") + npy_format_descriptor::name + + _("[") + _(_<(size_t) rows>(), _("m")) + + _(", ") + _(_<(size_t) cols>(), _("n")) + + _("]") + + // For a reference type (e.g. Ref) we have other constraints that might need to be + // satisfied: writeable=True (for a mutable reference), and, depending on the map's stride + // options, possibly f_contiguous or c_contiguous. We include them in the descriptor output + // to provide some hint as to why a TypeError is occurring (otherwise it can be confusing to + // see that a function accepts a 'numpy.ndarray[float64[3,2]]' and an error message that you + // *gave* a numpy.ndarray of the right type and dimensions. + _(", flags.writeable", "") + + _(", flags.c_contiguous", "") + + _(", flags.f_contiguous", "") + + _("]"); +}; + +// Casts an Eigen type to numpy array. If given a base, the numpy array references the src data, +// otherwise it'll make a copy. writeable lets you turn off the writeable flag for the array. +template handle eigen_array_cast(typename props::Type const &src, handle base = handle(), bool writeable = true) { + constexpr ssize_t elem_size = sizeof(typename props::Scalar); + array a; + if (props::vector) + a = array({ src.size() }, { elem_size * src.innerStride() }, src.data(), base); + else + a = array({ src.rows(), src.cols() }, { elem_size * src.rowStride(), elem_size * src.colStride() }, + src.data(), base); + + if (!writeable) + array_proxy(a.ptr())->flags &= ~detail::npy_api::NPY_ARRAY_WRITEABLE_; + + return a.release(); +} + +// Takes an lvalue ref to some Eigen type and a (python) base object, creating a numpy array that +// reference the Eigen object's data with `base` as the python-registered base class (if omitted, +// the base will be set to None, and lifetime management is up to the caller). The numpy array is +// non-writeable if the given type is const. +template +handle eigen_ref_array(Type &src, handle parent = none()) { + // none here is to get past array's should-we-copy detection, which currently always + // copies when there is no base. Setting the base to None should be harmless. + return eigen_array_cast(src, parent, !std::is_const::value); +} + +// Takes a pointer to some dense, plain Eigen type, builds a capsule around it, then returns a numpy +// array that references the encapsulated data with a python-side reference to the capsule to tie +// its destruction to that of any dependent python objects. Const-ness is determined by whether or +// not the Type of the pointer given is const. +template ::value>> +handle eigen_encapsulate(Type *src) { + capsule base(src, [](void *o) { delete static_cast(o); }); + return eigen_ref_array(*src, base); +} + +// Type caster for regular, dense matrix types (e.g. MatrixXd), but not maps/refs/etc. of dense +// types. +template +struct type_caster::value>> { + using Scalar = typename Type::Scalar; + using props = EigenProps; + + bool load(handle src, bool convert) { + // If we're in no-convert mode, only load if given an array of the correct type + if (!convert && !isinstance>(src)) + return false; + + // Coerce into an array, but don't do type conversion yet; the copy below handles it. + auto buf = array::ensure(src); + + if (!buf) + return false; + + auto dims = buf.ndim(); + if (dims < 1 || dims > 2) + return false; + + auto fits = props::conformable(buf); + if (!fits) + return false; + + // Allocate the new type, then build a numpy reference into it + value = Type(fits.rows, fits.cols); + auto ref = reinterpret_steal(eigen_ref_array(value)); + if (dims == 1) ref = ref.squeeze(); + else if (ref.ndim() == 1) buf = buf.squeeze(); + + int result = detail::npy_api::get().PyArray_CopyInto_(ref.ptr(), buf.ptr()); + + if (result < 0) { // Copy failed! + PyErr_Clear(); + return false; + } + + return true; + } + +private: + + // Cast implementation + template + static handle cast_impl(CType *src, return_value_policy policy, handle parent) { + switch (policy) { + case return_value_policy::take_ownership: + case return_value_policy::automatic: + return eigen_encapsulate(src); + case return_value_policy::move: + return eigen_encapsulate(new CType(std::move(*src))); + case return_value_policy::copy: + return eigen_array_cast(*src); + case return_value_policy::reference: + case return_value_policy::automatic_reference: + return eigen_ref_array(*src); + case return_value_policy::reference_internal: + return eigen_ref_array(*src, parent); + default: + throw cast_error("unhandled return_value_policy: should not happen!"); + }; + } + +public: + + // Normal returned non-reference, non-const value: + static handle cast(Type &&src, return_value_policy /* policy */, handle parent) { + return cast_impl(&src, return_value_policy::move, parent); + } + // If you return a non-reference const, we mark the numpy array readonly: + static handle cast(const Type &&src, return_value_policy /* policy */, handle parent) { + return cast_impl(&src, return_value_policy::move, parent); + } + // lvalue reference return; default (automatic) becomes copy + static handle cast(Type &src, return_value_policy policy, handle parent) { + if (policy == return_value_policy::automatic || policy == return_value_policy::automatic_reference) + policy = return_value_policy::copy; + return cast_impl(&src, policy, parent); + } + // const lvalue reference return; default (automatic) becomes copy + static handle cast(const Type &src, return_value_policy policy, handle parent) { + if (policy == return_value_policy::automatic || policy == return_value_policy::automatic_reference) + policy = return_value_policy::copy; + return cast(&src, policy, parent); + } + // non-const pointer return + static handle cast(Type *src, return_value_policy policy, handle parent) { + return cast_impl(src, policy, parent); + } + // const pointer return + static handle cast(const Type *src, return_value_policy policy, handle parent) { + return cast_impl(src, policy, parent); + } + + static constexpr auto name = props::descriptor; + + operator Type*() { return &value; } + operator Type&() { return value; } + operator Type&&() && { return std::move(value); } + template using cast_op_type = movable_cast_op_type; + +private: + Type value; +}; + +// Base class for casting reference/map/block/etc. objects back to python. +template struct eigen_map_caster { +private: + using props = EigenProps; + +public: + + // Directly referencing a ref/map's data is a bit dangerous (whatever the map/ref points to has + // to stay around), but we'll allow it under the assumption that you know what you're doing (and + // have an appropriate keep_alive in place). We return a numpy array pointing directly at the + // ref's data (The numpy array ends up read-only if the ref was to a const matrix type.) Note + // that this means you need to ensure you don't destroy the object in some other way (e.g. with + // an appropriate keep_alive, or with a reference to a statically allocated matrix). + static handle cast(const MapType &src, return_value_policy policy, handle parent) { + switch (policy) { + case return_value_policy::copy: + return eigen_array_cast(src); + case return_value_policy::reference_internal: + return eigen_array_cast(src, parent, is_eigen_mutable_map::value); + case return_value_policy::reference: + case return_value_policy::automatic: + case return_value_policy::automatic_reference: + return eigen_array_cast(src, none(), is_eigen_mutable_map::value); + default: + // move, take_ownership don't make any sense for a ref/map: + pybind11_fail("Invalid return_value_policy for Eigen Map/Ref/Block type"); + } + } + + static constexpr auto name = props::descriptor; + + // Explicitly delete these: support python -> C++ conversion on these (i.e. these can be return + // types but not bound arguments). We still provide them (with an explicitly delete) so that + // you end up here if you try anyway. + bool load(handle, bool) = delete; + operator MapType() = delete; + template using cast_op_type = MapType; +}; + +// We can return any map-like object (but can only load Refs, specialized next): +template struct type_caster::value>> + : eigen_map_caster {}; + +// Loader for Ref<...> arguments. See the documentation for info on how to make this work without +// copying (it requires some extra effort in many cases). +template +struct type_caster< + Eigen::Ref, + enable_if_t>::value> +> : public eigen_map_caster> { +private: + using Type = Eigen::Ref; + using props = EigenProps; + using Scalar = typename props::Scalar; + using MapType = Eigen::Map; + using Array = array_t; + static constexpr bool need_writeable = is_eigen_mutable_map::value; + // Delay construction (these have no default constructor) + std::unique_ptr map; + std::unique_ptr ref; + // Our array. When possible, this is just a numpy array pointing to the source data, but + // sometimes we can't avoid copying (e.g. input is not a numpy array at all, has an incompatible + // layout, or is an array of a type that needs to be converted). Using a numpy temporary + // (rather than an Eigen temporary) saves an extra copy when we need both type conversion and + // storage order conversion. (Note that we refuse to use this temporary copy when loading an + // argument for a Ref with M non-const, i.e. a read-write reference). + Array copy_or_ref; +public: + bool load(handle src, bool convert) { + // First check whether what we have is already an array of the right type. If not, we can't + // avoid a copy (because the copy is also going to do type conversion). + bool need_copy = !isinstance(src); + + EigenConformable fits; + if (!need_copy) { + // We don't need a converting copy, but we also need to check whether the strides are + // compatible with the Ref's stride requirements + Array aref = reinterpret_borrow(src); + + if (aref && (!need_writeable || aref.writeable())) { + fits = props::conformable(aref); + if (!fits) return false; // Incompatible dimensions + if (!fits.template stride_compatible()) + need_copy = true; + else + copy_or_ref = std::move(aref); + } + else { + need_copy = true; + } + } + + if (need_copy) { + // We need to copy: If we need a mutable reference, or we're not supposed to convert + // (either because we're in the no-convert overload pass, or because we're explicitly + // instructed not to copy (via `py::arg().noconvert()`) we have to fail loading. + if (!convert || need_writeable) return false; + + Array copy = Array::ensure(src); + if (!copy) return false; + fits = props::conformable(copy); + if (!fits || !fits.template stride_compatible()) + return false; + copy_or_ref = std::move(copy); + loader_life_support::add_patient(copy_or_ref); + } + + ref.reset(); + map.reset(new MapType(data(copy_or_ref), fits.rows, fits.cols, make_stride(fits.stride.outer(), fits.stride.inner()))); + ref.reset(new Type(*map)); + + return true; + } + + operator Type*() { return ref.get(); } + operator Type&() { return *ref; } + template using cast_op_type = pybind11::detail::cast_op_type<_T>; + +private: + template ::value, int> = 0> + Scalar *data(Array &a) { return a.mutable_data(); } + + template ::value, int> = 0> + const Scalar *data(Array &a) { return a.data(); } + + // Attempt to figure out a constructor of `Stride` that will work. + // If both strides are fixed, use a default constructor: + template using stride_ctor_default = bool_constant< + S::InnerStrideAtCompileTime != Eigen::Dynamic && S::OuterStrideAtCompileTime != Eigen::Dynamic && + std::is_default_constructible::value>; + // Otherwise, if there is a two-index constructor, assume it is (outer,inner) like + // Eigen::Stride, and use it: + template using stride_ctor_dual = bool_constant< + !stride_ctor_default::value && std::is_constructible::value>; + // Otherwise, if there is a one-index constructor, and just one of the strides is dynamic, use + // it (passing whichever stride is dynamic). + template using stride_ctor_outer = bool_constant< + !any_of, stride_ctor_dual>::value && + S::OuterStrideAtCompileTime == Eigen::Dynamic && S::InnerStrideAtCompileTime != Eigen::Dynamic && + std::is_constructible::value>; + template using stride_ctor_inner = bool_constant< + !any_of, stride_ctor_dual>::value && + S::InnerStrideAtCompileTime == Eigen::Dynamic && S::OuterStrideAtCompileTime != Eigen::Dynamic && + std::is_constructible::value>; + + template ::value, int> = 0> + static S make_stride(EigenIndex, EigenIndex) { return S(); } + template ::value, int> = 0> + static S make_stride(EigenIndex outer, EigenIndex inner) { return S(outer, inner); } + template ::value, int> = 0> + static S make_stride(EigenIndex outer, EigenIndex) { return S(outer); } + template ::value, int> = 0> + static S make_stride(EigenIndex, EigenIndex inner) { return S(inner); } + +}; + +// type_caster for special matrix types (e.g. DiagonalMatrix), which are EigenBase, but not +// EigenDense (i.e. they don't have a data(), at least not with the usual matrix layout). +// load() is not supported, but we can cast them into the python domain by first copying to a +// regular Eigen::Matrix, then casting that. +template +struct type_caster::value>> { +protected: + using Matrix = Eigen::Matrix; + using props = EigenProps; +public: + static handle cast(const Type &src, return_value_policy /* policy */, handle /* parent */) { + handle h = eigen_encapsulate(new Matrix(src)); + return h; + } + static handle cast(const Type *src, return_value_policy policy, handle parent) { return cast(*src, policy, parent); } + + static constexpr auto name = props::descriptor; + + // Explicitly delete these: support python -> C++ conversion on these (i.e. these can be return + // types but not bound arguments). We still provide them (with an explicitly delete) so that + // you end up here if you try anyway. + bool load(handle, bool) = delete; + operator Type() = delete; + template using cast_op_type = Type; +}; + +template +struct type_caster::value>> { + typedef typename Type::Scalar Scalar; + typedef remove_reference_t().outerIndexPtr())> StorageIndex; + typedef typename Type::Index Index; + static constexpr bool rowMajor = Type::IsRowMajor; + + bool load(handle src, bool) { + if (!src) + return false; + + auto obj = reinterpret_borrow(src); + object sparse_module = module::import("scipy.sparse"); + object matrix_type = sparse_module.attr( + rowMajor ? "csr_matrix" : "csc_matrix"); + + if (!obj.get_type().is(matrix_type)) { + try { + obj = matrix_type(obj); + } catch (const error_already_set &) { + return false; + } + } + + auto values = array_t((object) obj.attr("data")); + auto innerIndices = array_t((object) obj.attr("indices")); + auto outerIndices = array_t((object) obj.attr("indptr")); + auto shape = pybind11::tuple((pybind11::object) obj.attr("shape")); + auto nnz = obj.attr("nnz").cast(); + + if (!values || !innerIndices || !outerIndices) + return false; + + value = Eigen::MappedSparseMatrix( + shape[0].cast(), shape[1].cast(), nnz, + outerIndices.mutable_data(), innerIndices.mutable_data(), values.mutable_data()); + + return true; + } + + static handle cast(const Type &src, return_value_policy /* policy */, handle /* parent */) { + const_cast(src).makeCompressed(); + + object matrix_type = module::import("scipy.sparse").attr( + rowMajor ? "csr_matrix" : "csc_matrix"); + + array data(src.nonZeros(), src.valuePtr()); + array outerIndices((rowMajor ? src.rows() : src.cols()) + 1, src.outerIndexPtr()); + array innerIndices(src.nonZeros(), src.innerIndexPtr()); + + return matrix_type( + std::make_tuple(data, innerIndices, outerIndices), + std::make_pair(src.rows(), src.cols()) + ).release(); + } + + PYBIND11_TYPE_CASTER(Type, _<(Type::IsRowMajor) != 0>("scipy.sparse.csr_matrix[", "scipy.sparse.csc_matrix[") + + npy_format_descriptor::name + _("]")); +}; + +NAMESPACE_END(detail) +NAMESPACE_END(PYBIND11_NAMESPACE) + +#if defined(__GNUG__) || defined(__clang__) +# pragma GCC diagnostic pop +#elif defined(_MSC_VER) +# pragma warning(pop) +#endif diff --git a/ptocr/postprocess/piexlmerge/include/pybind11/embed.h b/ptocr/postprocess/piexlmerge/include/pybind11/embed.h new file mode 100644 index 0000000..7265588 --- /dev/null +++ b/ptocr/postprocess/piexlmerge/include/pybind11/embed.h @@ -0,0 +1,200 @@ +/* + pybind11/embed.h: Support for embedding the interpreter + + Copyright (c) 2017 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "pybind11.h" +#include "eval.h" + +#if defined(PYPY_VERSION) +# error Embedding the interpreter is not supported with PyPy +#endif + +#if PY_MAJOR_VERSION >= 3 +# define PYBIND11_EMBEDDED_MODULE_IMPL(name) \ + extern "C" PyObject *pybind11_init_impl_##name() { \ + return pybind11_init_wrapper_##name(); \ + } +#else +# define PYBIND11_EMBEDDED_MODULE_IMPL(name) \ + extern "C" void pybind11_init_impl_##name() { \ + pybind11_init_wrapper_##name(); \ + } +#endif + +/** \rst + Add a new module to the table of builtins for the interpreter. Must be + defined in global scope. The first macro parameter is the name of the + module (without quotes). The second parameter is the variable which will + be used as the interface to add functions and classes to the module. + + .. code-block:: cpp + + PYBIND11_EMBEDDED_MODULE(example, m) { + // ... initialize functions and classes here + m.def("foo", []() { + return "Hello, World!"; + }); + } + \endrst */ +#define PYBIND11_EMBEDDED_MODULE(name, variable) \ + static void PYBIND11_CONCAT(pybind11_init_, name)(pybind11::module &); \ + static PyObject PYBIND11_CONCAT(*pybind11_init_wrapper_, name)() { \ + auto m = pybind11::module(PYBIND11_TOSTRING(name)); \ + try { \ + PYBIND11_CONCAT(pybind11_init_, name)(m); \ + return m.ptr(); \ + } catch (pybind11::error_already_set &e) { \ + PyErr_SetString(PyExc_ImportError, e.what()); \ + return nullptr; \ + } catch (const std::exception &e) { \ + PyErr_SetString(PyExc_ImportError, e.what()); \ + return nullptr; \ + } \ + } \ + PYBIND11_EMBEDDED_MODULE_IMPL(name) \ + pybind11::detail::embedded_module name(PYBIND11_TOSTRING(name), \ + PYBIND11_CONCAT(pybind11_init_impl_, name)); \ + void PYBIND11_CONCAT(pybind11_init_, name)(pybind11::module &variable) + + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +NAMESPACE_BEGIN(detail) + +/// Python 2.7/3.x compatible version of `PyImport_AppendInittab` and error checks. +struct embedded_module { +#if PY_MAJOR_VERSION >= 3 + using init_t = PyObject *(*)(); +#else + using init_t = void (*)(); +#endif + embedded_module(const char *name, init_t init) { + if (Py_IsInitialized()) + pybind11_fail("Can't add new modules after the interpreter has been initialized"); + + auto result = PyImport_AppendInittab(name, init); + if (result == -1) + pybind11_fail("Insufficient memory to add a new module"); + } +}; + +NAMESPACE_END(detail) + +/** \rst + Initialize the Python interpreter. No other pybind11 or CPython API functions can be + called before this is done; with the exception of `PYBIND11_EMBEDDED_MODULE`. The + optional parameter can be used to skip the registration of signal handlers (see the + `Python documentation`_ for details). Calling this function again after the interpreter + has already been initialized is a fatal error. + + If initializing the Python interpreter fails, then the program is terminated. (This + is controlled by the CPython runtime and is an exception to pybind11's normal behavior + of throwing exceptions on errors.) + + .. _Python documentation: https://docs.python.org/3/c-api/init.html#c.Py_InitializeEx + \endrst */ +inline void initialize_interpreter(bool init_signal_handlers = true) { + if (Py_IsInitialized()) + pybind11_fail("The interpreter is already running"); + + Py_InitializeEx(init_signal_handlers ? 1 : 0); + + // Make .py files in the working directory available by default + module::import("sys").attr("path").cast().append("."); +} + +/** \rst + Shut down the Python interpreter. No pybind11 or CPython API functions can be called + after this. In addition, pybind11 objects must not outlive the interpreter: + + .. code-block:: cpp + + { // BAD + py::initialize_interpreter(); + auto hello = py::str("Hello, World!"); + py::finalize_interpreter(); + } // <-- BOOM, hello's destructor is called after interpreter shutdown + + { // GOOD + py::initialize_interpreter(); + { // scoped + auto hello = py::str("Hello, World!"); + } // <-- OK, hello is cleaned up properly + py::finalize_interpreter(); + } + + { // BETTER + py::scoped_interpreter guard{}; + auto hello = py::str("Hello, World!"); + } + + .. warning:: + + The interpreter can be restarted by calling `initialize_interpreter` again. + Modules created using pybind11 can be safely re-initialized. However, Python + itself cannot completely unload binary extension modules and there are several + caveats with regard to interpreter restarting. All the details can be found + in the CPython documentation. In short, not all interpreter memory may be + freed, either due to reference cycles or user-created global data. + + \endrst */ +inline void finalize_interpreter() { + handle builtins(PyEval_GetBuiltins()); + const char *id = PYBIND11_INTERNALS_ID; + + // Get the internals pointer (without creating it if it doesn't exist). It's possible for the + // internals to be created during Py_Finalize() (e.g. if a py::capsule calls `get_internals()` + // during destruction), so we get the pointer-pointer here and check it after Py_Finalize(). + detail::internals **internals_ptr_ptr = detail::get_internals_pp(); + // It could also be stashed in builtins, so look there too: + if (builtins.contains(id) && isinstance(builtins[id])) + internals_ptr_ptr = capsule(builtins[id]); + + Py_Finalize(); + + if (internals_ptr_ptr) { + delete *internals_ptr_ptr; + *internals_ptr_ptr = nullptr; + } +} + +/** \rst + Scope guard version of `initialize_interpreter` and `finalize_interpreter`. + This a move-only guard and only a single instance can exist. + + .. code-block:: cpp + + #include + + int main() { + py::scoped_interpreter guard{}; + py::print(Hello, World!); + } // <-- interpreter shutdown + \endrst */ +class scoped_interpreter { +public: + scoped_interpreter(bool init_signal_handlers = true) { + initialize_interpreter(init_signal_handlers); + } + + scoped_interpreter(const scoped_interpreter &) = delete; + scoped_interpreter(scoped_interpreter &&other) noexcept { other.is_valid = false; } + scoped_interpreter &operator=(const scoped_interpreter &) = delete; + scoped_interpreter &operator=(scoped_interpreter &&) = delete; + + ~scoped_interpreter() { + if (is_valid) + finalize_interpreter(); + } + +private: + bool is_valid = true; +}; + +NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/ptocr/postprocess/piexlmerge/include/pybind11/eval.h b/ptocr/postprocess/piexlmerge/include/pybind11/eval.h new file mode 100644 index 0000000..ea85ba1 --- /dev/null +++ b/ptocr/postprocess/piexlmerge/include/pybind11/eval.h @@ -0,0 +1,117 @@ +/* + pybind11/exec.h: Support for evaluating Python expressions and statements + from strings and files + + Copyright (c) 2016 Klemens Morgenstern and + Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "pybind11.h" + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) + +enum eval_mode { + /// Evaluate a string containing an isolated expression + eval_expr, + + /// Evaluate a string containing a single statement. Returns \c none + eval_single_statement, + + /// Evaluate a string containing a sequence of statement. Returns \c none + eval_statements +}; + +template +object eval(str expr, object global = globals(), object local = object()) { + if (!local) + local = global; + + /* PyRun_String does not accept a PyObject / encoding specifier, + this seems to be the only alternative */ + std::string buffer = "# -*- coding: utf-8 -*-\n" + (std::string) expr; + + int start; + switch (mode) { + case eval_expr: start = Py_eval_input; break; + case eval_single_statement: start = Py_single_input; break; + case eval_statements: start = Py_file_input; break; + default: pybind11_fail("invalid evaluation mode"); + } + + PyObject *result = PyRun_String(buffer.c_str(), start, global.ptr(), local.ptr()); + if (!result) + throw error_already_set(); + return reinterpret_steal(result); +} + +template +object eval(const char (&s)[N], object global = globals(), object local = object()) { + /* Support raw string literals by removing common leading whitespace */ + auto expr = (s[0] == '\n') ? str(module::import("textwrap").attr("dedent")(s)) + : str(s); + return eval(expr, global, local); +} + +inline void exec(str expr, object global = globals(), object local = object()) { + eval(expr, global, local); +} + +template +void exec(const char (&s)[N], object global = globals(), object local = object()) { + eval(s, global, local); +} + +template +object eval_file(str fname, object global = globals(), object local = object()) { + if (!local) + local = global; + + int start; + switch (mode) { + case eval_expr: start = Py_eval_input; break; + case eval_single_statement: start = Py_single_input; break; + case eval_statements: start = Py_file_input; break; + default: pybind11_fail("invalid evaluation mode"); + } + + int closeFile = 1; + std::string fname_str = (std::string) fname; +#if PY_VERSION_HEX >= 0x03040000 + FILE *f = _Py_fopen_obj(fname.ptr(), "r"); +#elif PY_VERSION_HEX >= 0x03000000 + FILE *f = _Py_fopen(fname.ptr(), "r"); +#else + /* No unicode support in open() :( */ + auto fobj = reinterpret_steal(PyFile_FromString( + const_cast(fname_str.c_str()), + const_cast("r"))); + FILE *f = nullptr; + if (fobj) + f = PyFile_AsFile(fobj.ptr()); + closeFile = 0; +#endif + if (!f) { + PyErr_Clear(); + pybind11_fail("File \"" + fname_str + "\" could not be opened!"); + } + +#if PY_VERSION_HEX < 0x03000000 && defined(PYPY_VERSION) + PyObject *result = PyRun_File(f, fname_str.c_str(), start, global.ptr(), + local.ptr()); + (void) closeFile; +#else + PyObject *result = PyRun_FileEx(f, fname_str.c_str(), start, global.ptr(), + local.ptr(), closeFile); +#endif + + if (!result) + throw error_already_set(); + return reinterpret_steal(result); +} + +NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/ptocr/postprocess/piexlmerge/include/pybind11/functional.h b/ptocr/postprocess/piexlmerge/include/pybind11/functional.h new file mode 100644 index 0000000..9cdf21f --- /dev/null +++ b/ptocr/postprocess/piexlmerge/include/pybind11/functional.h @@ -0,0 +1,83 @@ +/* + pybind11/functional.h: std::function<> support + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "pybind11.h" +#include + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +NAMESPACE_BEGIN(detail) + +template +struct type_caster> { + using type = std::function; + using retval_type = conditional_t::value, void_type, Return>; + using function_type = Return (*) (Args...); + +public: + bool load(handle src, bool convert) { + if (src.is_none()) { + // Defer accepting None to other overloads (if we aren't in convert mode): + if (!convert) return false; + return true; + } + + if (!isinstance(src)) + return false; + + auto func = reinterpret_borrow(src); + + /* + When passing a C++ function as an argument to another C++ + function via Python, every function call would normally involve + a full C++ -> Python -> C++ roundtrip, which can be prohibitive. + Here, we try to at least detect the case where the function is + stateless (i.e. function pointer or lambda function without + captured variables), in which case the roundtrip can be avoided. + */ + if (auto cfunc = func.cpp_function()) { + auto c = reinterpret_borrow(PyCFunction_GET_SELF(cfunc.ptr())); + auto rec = (function_record *) c; + + if (rec && rec->is_stateless && + same_type(typeid(function_type), *reinterpret_cast(rec->data[1]))) { + struct capture { function_type f; }; + value = ((capture *) &rec->data)->f; + return true; + } + } + + value = [func](Args... args) -> Return { + gil_scoped_acquire acq; + object retval(func(std::forward(args)...)); + /* Visual studio 2015 parser issue: need parentheses around this expression */ + return (retval.template cast()); + }; + return true; + } + + template + static handle cast(Func &&f_, return_value_policy policy, handle /* parent */) { + if (!f_) + return none().inc_ref(); + + auto result = f_.template target(); + if (result) + return cpp_function(*result, policy).release(); + else + return cpp_function(std::forward(f_), policy).release(); + } + + PYBIND11_TYPE_CASTER(type, _("Callable[[") + concat(make_caster::name...) + _("], ") + + make_caster::name + _("]")); +}; + +NAMESPACE_END(detail) +NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/ptocr/postprocess/piexlmerge/include/pybind11/iostream.h b/ptocr/postprocess/piexlmerge/include/pybind11/iostream.h new file mode 100644 index 0000000..182e8ee --- /dev/null +++ b/ptocr/postprocess/piexlmerge/include/pybind11/iostream.h @@ -0,0 +1,200 @@ +/* + pybind11/iostream.h -- Tools to assist with redirecting cout and cerr to Python + + Copyright (c) 2017 Henry F. Schreiner + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "pybind11.h" + +#include +#include +#include +#include +#include + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +NAMESPACE_BEGIN(detail) + +// Buffer that writes to Python instead of C++ +class pythonbuf : public std::streambuf { +private: + using traits_type = std::streambuf::traits_type; + + char d_buffer[1024]; + object pywrite; + object pyflush; + + int overflow(int c) { + if (!traits_type::eq_int_type(c, traits_type::eof())) { + *pptr() = traits_type::to_char_type(c); + pbump(1); + } + return sync() == 0 ? traits_type::not_eof(c) : traits_type::eof(); + } + + int sync() { + if (pbase() != pptr()) { + // This subtraction cannot be negative, so dropping the sign + str line(pbase(), static_cast(pptr() - pbase())); + + pywrite(line); + pyflush(); + + setp(pbase(), epptr()); + } + return 0; + } + +public: + pythonbuf(object pyostream) + : pywrite(pyostream.attr("write")), + pyflush(pyostream.attr("flush")) { + setp(d_buffer, d_buffer + sizeof(d_buffer) - 1); + } + + /// Sync before destroy + ~pythonbuf() { + sync(); + } +}; + +NAMESPACE_END(detail) + + +/** \rst + This a move-only guard that redirects output. + + .. code-block:: cpp + + #include + + ... + + { + py::scoped_ostream_redirect output; + std::cout << "Hello, World!"; // Python stdout + } // <-- return std::cout to normal + + You can explicitly pass the c++ stream and the python object, + for example to guard stderr instead. + + .. code-block:: cpp + + { + py::scoped_ostream_redirect output{std::cerr, py::module::import("sys").attr("stderr")}; + std::cerr << "Hello, World!"; + } + \endrst */ +class scoped_ostream_redirect { +protected: + std::streambuf *old; + std::ostream &costream; + detail::pythonbuf buffer; + +public: + scoped_ostream_redirect( + std::ostream &costream = std::cout, + object pyostream = module::import("sys").attr("stdout")) + : costream(costream), buffer(pyostream) { + old = costream.rdbuf(&buffer); + } + + ~scoped_ostream_redirect() { + costream.rdbuf(old); + } + + scoped_ostream_redirect(const scoped_ostream_redirect &) = delete; + scoped_ostream_redirect(scoped_ostream_redirect &&other) = default; + scoped_ostream_redirect &operator=(const scoped_ostream_redirect &) = delete; + scoped_ostream_redirect &operator=(scoped_ostream_redirect &&) = delete; +}; + + +/** \rst + Like `scoped_ostream_redirect`, but redirects cerr by default. This class + is provided primary to make ``py::call_guard`` easier to make. + + .. code-block:: cpp + + m.def("noisy_func", &noisy_func, + py::call_guard()); + +\endrst */ +class scoped_estream_redirect : public scoped_ostream_redirect { +public: + scoped_estream_redirect( + std::ostream &costream = std::cerr, + object pyostream = module::import("sys").attr("stderr")) + : scoped_ostream_redirect(costream,pyostream) {} +}; + + +NAMESPACE_BEGIN(detail) + +// Class to redirect output as a context manager. C++ backend. +class OstreamRedirect { + bool do_stdout_; + bool do_stderr_; + std::unique_ptr redirect_stdout; + std::unique_ptr redirect_stderr; + +public: + OstreamRedirect(bool do_stdout = true, bool do_stderr = true) + : do_stdout_(do_stdout), do_stderr_(do_stderr) {} + + void enter() { + if (do_stdout_) + redirect_stdout.reset(new scoped_ostream_redirect()); + if (do_stderr_) + redirect_stderr.reset(new scoped_estream_redirect()); + } + + void exit() { + redirect_stdout.reset(); + redirect_stderr.reset(); + } +}; + +NAMESPACE_END(detail) + +/** \rst + This is a helper function to add a C++ redirect context manager to Python + instead of using a C++ guard. To use it, add the following to your binding code: + + .. code-block:: cpp + + #include + + ... + + py::add_ostream_redirect(m, "ostream_redirect"); + + You now have a Python context manager that redirects your output: + + .. code-block:: python + + with m.ostream_redirect(): + m.print_to_cout_function() + + This manager can optionally be told which streams to operate on: + + .. code-block:: python + + with m.ostream_redirect(stdout=true, stderr=true): + m.noisy_function_with_error_printing() + + \endrst */ +inline class_ add_ostream_redirect(module m, std::string name = "ostream_redirect") { + return class_(m, name.c_str(), module_local()) + .def(init(), arg("stdout")=true, arg("stderr")=true) + .def("__enter__", &detail::OstreamRedirect::enter) + .def("__exit__", [](detail::OstreamRedirect &self_, args) { self_.exit(); }); +} + +NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/ptocr/postprocess/piexlmerge/include/pybind11/numpy.h b/ptocr/postprocess/piexlmerge/include/pybind11/numpy.h new file mode 100644 index 0000000..37471d8 --- /dev/null +++ b/ptocr/postprocess/piexlmerge/include/pybind11/numpy.h @@ -0,0 +1,1610 @@ +/* + pybind11/numpy.h: Basic NumPy support, vectorize() wrapper + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "pybind11.h" +#include "complex.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(_MSC_VER) +# pragma warning(push) +# pragma warning(disable: 4127) // warning C4127: Conditional expression is constant +#endif + +/* This will be true on all flat address space platforms and allows us to reduce the + whole npy_intp / ssize_t / Py_intptr_t business down to just ssize_t for all size + and dimension types (e.g. shape, strides, indexing), instead of inflicting this + upon the library user. */ +static_assert(sizeof(ssize_t) == sizeof(Py_intptr_t), "ssize_t != Py_intptr_t"); + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) + +class array; // Forward declaration + +NAMESPACE_BEGIN(detail) +template struct npy_format_descriptor; + +struct PyArrayDescr_Proxy { + PyObject_HEAD + PyObject *typeobj; + char kind; + char type; + char byteorder; + char flags; + int type_num; + int elsize; + int alignment; + char *subarray; + PyObject *fields; + PyObject *names; +}; + +struct PyArray_Proxy { + PyObject_HEAD + char *data; + int nd; + ssize_t *dimensions; + ssize_t *strides; + PyObject *base; + PyObject *descr; + int flags; +}; + +struct PyVoidScalarObject_Proxy { + PyObject_VAR_HEAD + char *obval; + PyArrayDescr_Proxy *descr; + int flags; + PyObject *base; +}; + +struct numpy_type_info { + PyObject* dtype_ptr; + std::string format_str; +}; + +struct numpy_internals { + std::unordered_map registered_dtypes; + + numpy_type_info *get_type_info(const std::type_info& tinfo, bool throw_if_missing = true) { + auto it = registered_dtypes.find(std::type_index(tinfo)); + if (it != registered_dtypes.end()) + return &(it->second); + if (throw_if_missing) + pybind11_fail(std::string("NumPy type info missing for ") + tinfo.name()); + return nullptr; + } + + template numpy_type_info *get_type_info(bool throw_if_missing = true) { + return get_type_info(typeid(typename std::remove_cv::type), throw_if_missing); + } +}; + +inline PYBIND11_NOINLINE void load_numpy_internals(numpy_internals* &ptr) { + ptr = &get_or_create_shared_data("_numpy_internals"); +} + +inline numpy_internals& get_numpy_internals() { + static numpy_internals* ptr = nullptr; + if (!ptr) + load_numpy_internals(ptr); + return *ptr; +} + +struct npy_api { + enum constants { + NPY_ARRAY_C_CONTIGUOUS_ = 0x0001, + NPY_ARRAY_F_CONTIGUOUS_ = 0x0002, + NPY_ARRAY_OWNDATA_ = 0x0004, + NPY_ARRAY_FORCECAST_ = 0x0010, + NPY_ARRAY_ENSUREARRAY_ = 0x0040, + NPY_ARRAY_ALIGNED_ = 0x0100, + NPY_ARRAY_WRITEABLE_ = 0x0400, + NPY_BOOL_ = 0, + NPY_BYTE_, NPY_UBYTE_, + NPY_SHORT_, NPY_USHORT_, + NPY_INT_, NPY_UINT_, + NPY_LONG_, NPY_ULONG_, + NPY_LONGLONG_, NPY_ULONGLONG_, + NPY_FLOAT_, NPY_DOUBLE_, NPY_LONGDOUBLE_, + NPY_CFLOAT_, NPY_CDOUBLE_, NPY_CLONGDOUBLE_, + NPY_OBJECT_ = 17, + NPY_STRING_, NPY_UNICODE_, NPY_VOID_ + }; + + typedef struct { + Py_intptr_t *ptr; + int len; + } PyArray_Dims; + + static npy_api& get() { + static npy_api api = lookup(); + return api; + } + + bool PyArray_Check_(PyObject *obj) const { + return (bool) PyObject_TypeCheck(obj, PyArray_Type_); + } + bool PyArrayDescr_Check_(PyObject *obj) const { + return (bool) PyObject_TypeCheck(obj, PyArrayDescr_Type_); + } + + unsigned int (*PyArray_GetNDArrayCFeatureVersion_)(); + PyObject *(*PyArray_DescrFromType_)(int); + PyObject *(*PyArray_NewFromDescr_) + (PyTypeObject *, PyObject *, int, Py_intptr_t *, + Py_intptr_t *, void *, int, PyObject *); + PyObject *(*PyArray_DescrNewFromType_)(int); + int (*PyArray_CopyInto_)(PyObject *, PyObject *); + PyObject *(*PyArray_NewCopy_)(PyObject *, int); + PyTypeObject *PyArray_Type_; + PyTypeObject *PyVoidArrType_Type_; + PyTypeObject *PyArrayDescr_Type_; + PyObject *(*PyArray_DescrFromScalar_)(PyObject *); + PyObject *(*PyArray_FromAny_) (PyObject *, PyObject *, int, int, int, PyObject *); + int (*PyArray_DescrConverter_) (PyObject *, PyObject **); + bool (*PyArray_EquivTypes_) (PyObject *, PyObject *); + int (*PyArray_GetArrayParamsFromObject_)(PyObject *, PyObject *, char, PyObject **, int *, + Py_ssize_t *, PyObject **, PyObject *); + PyObject *(*PyArray_Squeeze_)(PyObject *); + int (*PyArray_SetBaseObject_)(PyObject *, PyObject *); + PyObject* (*PyArray_Resize_)(PyObject*, PyArray_Dims*, int, int); +private: + enum functions { + API_PyArray_GetNDArrayCFeatureVersion = 211, + API_PyArray_Type = 2, + API_PyArrayDescr_Type = 3, + API_PyVoidArrType_Type = 39, + API_PyArray_DescrFromType = 45, + API_PyArray_DescrFromScalar = 57, + API_PyArray_FromAny = 69, + API_PyArray_Resize = 80, + API_PyArray_CopyInto = 82, + API_PyArray_NewCopy = 85, + API_PyArray_NewFromDescr = 94, + API_PyArray_DescrNewFromType = 9, + API_PyArray_DescrConverter = 174, + API_PyArray_EquivTypes = 182, + API_PyArray_GetArrayParamsFromObject = 278, + API_PyArray_Squeeze = 136, + API_PyArray_SetBaseObject = 282 + }; + + static npy_api lookup() { + module m = module::import("numpy.core.multiarray"); + auto c = m.attr("_ARRAY_API"); +#if PY_MAJOR_VERSION >= 3 + void **api_ptr = (void **) PyCapsule_GetPointer(c.ptr(), NULL); +#else + void **api_ptr = (void **) PyCObject_AsVoidPtr(c.ptr()); +#endif + npy_api api; +#define DECL_NPY_API(Func) api.Func##_ = (decltype(api.Func##_)) api_ptr[API_##Func]; + DECL_NPY_API(PyArray_GetNDArrayCFeatureVersion); + if (api.PyArray_GetNDArrayCFeatureVersion_() < 0x7) + pybind11_fail("pybind11 numpy support requires numpy >= 1.7.0"); + DECL_NPY_API(PyArray_Type); + DECL_NPY_API(PyVoidArrType_Type); + DECL_NPY_API(PyArrayDescr_Type); + DECL_NPY_API(PyArray_DescrFromType); + DECL_NPY_API(PyArray_DescrFromScalar); + DECL_NPY_API(PyArray_FromAny); + DECL_NPY_API(PyArray_Resize); + DECL_NPY_API(PyArray_CopyInto); + DECL_NPY_API(PyArray_NewCopy); + DECL_NPY_API(PyArray_NewFromDescr); + DECL_NPY_API(PyArray_DescrNewFromType); + DECL_NPY_API(PyArray_DescrConverter); + DECL_NPY_API(PyArray_EquivTypes); + DECL_NPY_API(PyArray_GetArrayParamsFromObject); + DECL_NPY_API(PyArray_Squeeze); + DECL_NPY_API(PyArray_SetBaseObject); +#undef DECL_NPY_API + return api; + } +}; + +inline PyArray_Proxy* array_proxy(void* ptr) { + return reinterpret_cast(ptr); +} + +inline const PyArray_Proxy* array_proxy(const void* ptr) { + return reinterpret_cast(ptr); +} + +inline PyArrayDescr_Proxy* array_descriptor_proxy(PyObject* ptr) { + return reinterpret_cast(ptr); +} + +inline const PyArrayDescr_Proxy* array_descriptor_proxy(const PyObject* ptr) { + return reinterpret_cast(ptr); +} + +inline bool check_flags(const void* ptr, int flag) { + return (flag == (array_proxy(ptr)->flags & flag)); +} + +template struct is_std_array : std::false_type { }; +template struct is_std_array> : std::true_type { }; +template struct is_complex : std::false_type { }; +template struct is_complex> : std::true_type { }; + +template struct array_info_scalar { + typedef T type; + static constexpr bool is_array = false; + static constexpr bool is_empty = false; + static constexpr auto extents = _(""); + static void append_extents(list& /* shape */) { } +}; +// Computes underlying type and a comma-separated list of extents for array +// types (any mix of std::array and built-in arrays). An array of char is +// treated as scalar because it gets special handling. +template struct array_info : array_info_scalar { }; +template struct array_info> { + using type = typename array_info::type; + static constexpr bool is_array = true; + static constexpr bool is_empty = (N == 0) || array_info::is_empty; + static constexpr size_t extent = N; + + // appends the extents to shape + static void append_extents(list& shape) { + shape.append(N); + array_info::append_extents(shape); + } + + static constexpr auto extents = _::is_array>( + concat(_(), array_info::extents), _() + ); +}; +// For numpy we have special handling for arrays of characters, so we don't include +// the size in the array extents. +template struct array_info : array_info_scalar { }; +template struct array_info> : array_info_scalar> { }; +template struct array_info : array_info> { }; +template using remove_all_extents_t = typename array_info::type; + +template using is_pod_struct = all_of< + std::is_standard_layout, // since we're accessing directly in memory we need a standard layout type +#if !defined(__GNUG__) || defined(_LIBCPP_VERSION) || defined(_GLIBCXX_USE_CXX11_ABI) + // _GLIBCXX_USE_CXX11_ABI indicates that we're using libstdc++ from GCC 5 or newer, independent + // of the actual compiler (Clang can also use libstdc++, but it always defines __GNUC__ == 4). + std::is_trivially_copyable, +#else + // GCC 4 doesn't implement is_trivially_copyable, so approximate it + std::is_trivially_destructible, + satisfies_any_of, +#endif + satisfies_none_of +>; + +template ssize_t byte_offset_unsafe(const Strides &) { return 0; } +template +ssize_t byte_offset_unsafe(const Strides &strides, ssize_t i, Ix... index) { + return i * strides[Dim] + byte_offset_unsafe(strides, index...); +} + +/** + * Proxy class providing unsafe, unchecked const access to array data. This is constructed through + * the `unchecked()` method of `array` or the `unchecked()` method of `array_t`. `Dims` + * will be -1 for dimensions determined at runtime. + */ +template +class unchecked_reference { +protected: + static constexpr bool Dynamic = Dims < 0; + const unsigned char *data_; + // Storing the shape & strides in local variables (i.e. these arrays) allows the compiler to + // make large performance gains on big, nested loops, but requires compile-time dimensions + conditional_t> + shape_, strides_; + const ssize_t dims_; + + friend class pybind11::array; + // Constructor for compile-time dimensions: + template + unchecked_reference(const void *data, const ssize_t *shape, const ssize_t *strides, enable_if_t) + : data_{reinterpret_cast(data)}, dims_{Dims} { + for (size_t i = 0; i < (size_t) dims_; i++) { + shape_[i] = shape[i]; + strides_[i] = strides[i]; + } + } + // Constructor for runtime dimensions: + template + unchecked_reference(const void *data, const ssize_t *shape, const ssize_t *strides, enable_if_t dims) + : data_{reinterpret_cast(data)}, shape_{shape}, strides_{strides}, dims_{dims} {} + +public: + /** + * Unchecked const reference access to data at the given indices. For a compile-time known + * number of dimensions, this requires the correct number of arguments; for run-time + * dimensionality, this is not checked (and so is up to the caller to use safely). + */ + template const T &operator()(Ix... index) const { + static_assert(ssize_t{sizeof...(Ix)} == Dims || Dynamic, + "Invalid number of indices for unchecked array reference"); + return *reinterpret_cast(data_ + byte_offset_unsafe(strides_, ssize_t(index)...)); + } + /** + * Unchecked const reference access to data; this operator only participates if the reference + * is to a 1-dimensional array. When present, this is exactly equivalent to `obj(index)`. + */ + template > + const T &operator[](ssize_t index) const { return operator()(index); } + + /// Pointer access to the data at the given indices. + template const T *data(Ix... ix) const { return &operator()(ssize_t(ix)...); } + + /// Returns the item size, i.e. sizeof(T) + constexpr static ssize_t itemsize() { return sizeof(T); } + + /// Returns the shape (i.e. size) of dimension `dim` + ssize_t shape(ssize_t dim) const { return shape_[(size_t) dim]; } + + /// Returns the number of dimensions of the array + ssize_t ndim() const { return dims_; } + + /// Returns the total number of elements in the referenced array, i.e. the product of the shapes + template + enable_if_t size() const { + return std::accumulate(shape_.begin(), shape_.end(), (ssize_t) 1, std::multiplies()); + } + template + enable_if_t size() const { + return std::accumulate(shape_, shape_ + ndim(), (ssize_t) 1, std::multiplies()); + } + + /// Returns the total number of bytes used by the referenced data. Note that the actual span in + /// memory may be larger if the referenced array has non-contiguous strides (e.g. for a slice). + ssize_t nbytes() const { + return size() * itemsize(); + } +}; + +template +class unchecked_mutable_reference : public unchecked_reference { + friend class pybind11::array; + using ConstBase = unchecked_reference; + using ConstBase::ConstBase; + using ConstBase::Dynamic; +public: + /// Mutable, unchecked access to data at the given indices. + template T& operator()(Ix... index) { + static_assert(ssize_t{sizeof...(Ix)} == Dims || Dynamic, + "Invalid number of indices for unchecked array reference"); + return const_cast(ConstBase::operator()(index...)); + } + /** + * Mutable, unchecked access data at the given index; this operator only participates if the + * reference is to a 1-dimensional array (or has runtime dimensions). When present, this is + * exactly equivalent to `obj(index)`. + */ + template > + T &operator[](ssize_t index) { return operator()(index); } + + /// Mutable pointer access to the data at the given indices. + template T *mutable_data(Ix... ix) { return &operator()(ssize_t(ix)...); } +}; + +template +struct type_caster> { + static_assert(Dim == 0 && Dim > 0 /* always fail */, "unchecked array proxy object is not castable"); +}; +template +struct type_caster> : type_caster> {}; + +NAMESPACE_END(detail) + +class dtype : public object { +public: + PYBIND11_OBJECT_DEFAULT(dtype, object, detail::npy_api::get().PyArrayDescr_Check_); + + explicit dtype(const buffer_info &info) { + dtype descr(_dtype_from_pep3118()(PYBIND11_STR_TYPE(info.format))); + // If info.itemsize == 0, use the value calculated from the format string + m_ptr = descr.strip_padding(info.itemsize ? info.itemsize : descr.itemsize()).release().ptr(); + } + + explicit dtype(const std::string &format) { + m_ptr = from_args(pybind11::str(format)).release().ptr(); + } + + dtype(const char *format) : dtype(std::string(format)) { } + + dtype(list names, list formats, list offsets, ssize_t itemsize) { + dict args; + args["names"] = names; + args["formats"] = formats; + args["offsets"] = offsets; + args["itemsize"] = pybind11::int_(itemsize); + m_ptr = from_args(args).release().ptr(); + } + + /// This is essentially the same as calling numpy.dtype(args) in Python. + static dtype from_args(object args) { + PyObject *ptr = nullptr; + if (!detail::npy_api::get().PyArray_DescrConverter_(args.ptr(), &ptr) || !ptr) + throw error_already_set(); + return reinterpret_steal(ptr); + } + + /// Return dtype associated with a C++ type. + template static dtype of() { + return detail::npy_format_descriptor::type>::dtype(); + } + + /// Size of the data type in bytes. + ssize_t itemsize() const { + return detail::array_descriptor_proxy(m_ptr)->elsize; + } + + /// Returns true for structured data types. + bool has_fields() const { + return detail::array_descriptor_proxy(m_ptr)->names != nullptr; + } + + /// Single-character type code. + char kind() const { + return detail::array_descriptor_proxy(m_ptr)->kind; + } + +private: + static object _dtype_from_pep3118() { + static PyObject *obj = module::import("numpy.core._internal") + .attr("_dtype_from_pep3118").cast().release().ptr(); + return reinterpret_borrow(obj); + } + + dtype strip_padding(ssize_t itemsize) { + // Recursively strip all void fields with empty names that are generated for + // padding fields (as of NumPy v1.11). + if (!has_fields()) + return *this; + + struct field_descr { PYBIND11_STR_TYPE name; object format; pybind11::int_ offset; }; + std::vector field_descriptors; + + for (auto field : attr("fields").attr("items")()) { + auto spec = field.cast(); + auto name = spec[0].cast(); + auto format = spec[1].cast()[0].cast(); + auto offset = spec[1].cast()[1].cast(); + if (!len(name) && format.kind() == 'V') + continue; + field_descriptors.push_back({(PYBIND11_STR_TYPE) name, format.strip_padding(format.itemsize()), offset}); + } + + std::sort(field_descriptors.begin(), field_descriptors.end(), + [](const field_descr& a, const field_descr& b) { + return a.offset.cast() < b.offset.cast(); + }); + + list names, formats, offsets; + for (auto& descr : field_descriptors) { + names.append(descr.name); + formats.append(descr.format); + offsets.append(descr.offset); + } + return dtype(names, formats, offsets, itemsize); + } +}; + +class array : public buffer { +public: + PYBIND11_OBJECT_CVT(array, buffer, detail::npy_api::get().PyArray_Check_, raw_array) + + enum { + c_style = detail::npy_api::NPY_ARRAY_C_CONTIGUOUS_, + f_style = detail::npy_api::NPY_ARRAY_F_CONTIGUOUS_, + forcecast = detail::npy_api::NPY_ARRAY_FORCECAST_ + }; + + array() : array({{0}}, static_cast(nullptr)) {} + + using ShapeContainer = detail::any_container; + using StridesContainer = detail::any_container; + + // Constructs an array taking shape/strides from arbitrary container types + array(const pybind11::dtype &dt, ShapeContainer shape, StridesContainer strides, + const void *ptr = nullptr, handle base = handle()) { + + if (strides->empty()) + *strides = c_strides(*shape, dt.itemsize()); + + auto ndim = shape->size(); + if (ndim != strides->size()) + pybind11_fail("NumPy: shape ndim doesn't match strides ndim"); + auto descr = dt; + + int flags = 0; + if (base && ptr) { + if (isinstance(base)) + /* Copy flags from base (except ownership bit) */ + flags = reinterpret_borrow(base).flags() & ~detail::npy_api::NPY_ARRAY_OWNDATA_; + else + /* Writable by default, easy to downgrade later on if needed */ + flags = detail::npy_api::NPY_ARRAY_WRITEABLE_; + } + + auto &api = detail::npy_api::get(); + auto tmp = reinterpret_steal(api.PyArray_NewFromDescr_( + api.PyArray_Type_, descr.release().ptr(), (int) ndim, shape->data(), strides->data(), + const_cast(ptr), flags, nullptr)); + if (!tmp) + throw error_already_set(); + if (ptr) { + if (base) { + api.PyArray_SetBaseObject_(tmp.ptr(), base.inc_ref().ptr()); + } else { + tmp = reinterpret_steal(api.PyArray_NewCopy_(tmp.ptr(), -1 /* any order */)); + } + } + m_ptr = tmp.release().ptr(); + } + + array(const pybind11::dtype &dt, ShapeContainer shape, const void *ptr = nullptr, handle base = handle()) + : array(dt, std::move(shape), {}, ptr, base) { } + + template ::value && !std::is_same::value>> + array(const pybind11::dtype &dt, T count, const void *ptr = nullptr, handle base = handle()) + : array(dt, {{count}}, ptr, base) { } + + template + array(ShapeContainer shape, StridesContainer strides, const T *ptr, handle base = handle()) + : array(pybind11::dtype::of(), std::move(shape), std::move(strides), ptr, base) { } + + template + array(ShapeContainer shape, const T *ptr, handle base = handle()) + : array(std::move(shape), {}, ptr, base) { } + + template + explicit array(ssize_t count, const T *ptr, handle base = handle()) : array({count}, {}, ptr, base) { } + + explicit array(const buffer_info &info) + : array(pybind11::dtype(info), info.shape, info.strides, info.ptr) { } + + /// Array descriptor (dtype) + pybind11::dtype dtype() const { + return reinterpret_borrow(detail::array_proxy(m_ptr)->descr); + } + + /// Total number of elements + ssize_t size() const { + return std::accumulate(shape(), shape() + ndim(), (ssize_t) 1, std::multiplies()); + } + + /// Byte size of a single element + ssize_t itemsize() const { + return detail::array_descriptor_proxy(detail::array_proxy(m_ptr)->descr)->elsize; + } + + /// Total number of bytes + ssize_t nbytes() const { + return size() * itemsize(); + } + + /// Number of dimensions + ssize_t ndim() const { + return detail::array_proxy(m_ptr)->nd; + } + + /// Base object + object base() const { + return reinterpret_borrow(detail::array_proxy(m_ptr)->base); + } + + /// Dimensions of the array + const ssize_t* shape() const { + return detail::array_proxy(m_ptr)->dimensions; + } + + /// Dimension along a given axis + ssize_t shape(ssize_t dim) const { + if (dim >= ndim()) + fail_dim_check(dim, "invalid axis"); + return shape()[dim]; + } + + /// Strides of the array + const ssize_t* strides() const { + return detail::array_proxy(m_ptr)->strides; + } + + /// Stride along a given axis + ssize_t strides(ssize_t dim) const { + if (dim >= ndim()) + fail_dim_check(dim, "invalid axis"); + return strides()[dim]; + } + + /// Return the NumPy array flags + int flags() const { + return detail::array_proxy(m_ptr)->flags; + } + + /// If set, the array is writeable (otherwise the buffer is read-only) + bool writeable() const { + return detail::check_flags(m_ptr, detail::npy_api::NPY_ARRAY_WRITEABLE_); + } + + /// If set, the array owns the data (will be freed when the array is deleted) + bool owndata() const { + return detail::check_flags(m_ptr, detail::npy_api::NPY_ARRAY_OWNDATA_); + } + + /// Pointer to the contained data. If index is not provided, points to the + /// beginning of the buffer. May throw if the index would lead to out of bounds access. + template const void* data(Ix... index) const { + return static_cast(detail::array_proxy(m_ptr)->data + offset_at(index...)); + } + + /// Mutable pointer to the contained data. If index is not provided, points to the + /// beginning of the buffer. May throw if the index would lead to out of bounds access. + /// May throw if the array is not writeable. + template void* mutable_data(Ix... index) { + check_writeable(); + return static_cast(detail::array_proxy(m_ptr)->data + offset_at(index...)); + } + + /// Byte offset from beginning of the array to a given index (full or partial). + /// May throw if the index would lead to out of bounds access. + template ssize_t offset_at(Ix... index) const { + if ((ssize_t) sizeof...(index) > ndim()) + fail_dim_check(sizeof...(index), "too many indices for an array"); + return byte_offset(ssize_t(index)...); + } + + ssize_t offset_at() const { return 0; } + + /// Item count from beginning of the array to a given index (full or partial). + /// May throw if the index would lead to out of bounds access. + template ssize_t index_at(Ix... index) const { + return offset_at(index...) / itemsize(); + } + + /** + * Returns a proxy object that provides access to the array's data without bounds or + * dimensionality checking. Will throw if the array is missing the `writeable` flag. Use with + * care: the array must not be destroyed or reshaped for the duration of the returned object, + * and the caller must take care not to access invalid dimensions or dimension indices. + */ + template detail::unchecked_mutable_reference mutable_unchecked() & { + if (Dims >= 0 && ndim() != Dims) + throw std::domain_error("array has incorrect number of dimensions: " + std::to_string(ndim()) + + "; expected " + std::to_string(Dims)); + return detail::unchecked_mutable_reference(mutable_data(), shape(), strides(), ndim()); + } + + /** + * Returns a proxy object that provides const access to the array's data without bounds or + * dimensionality checking. Unlike `mutable_unchecked()`, this does not require that the + * underlying array have the `writable` flag. Use with care: the array must not be destroyed or + * reshaped for the duration of the returned object, and the caller must take care not to access + * invalid dimensions or dimension indices. + */ + template detail::unchecked_reference unchecked() const & { + if (Dims >= 0 && ndim() != Dims) + throw std::domain_error("array has incorrect number of dimensions: " + std::to_string(ndim()) + + "; expected " + std::to_string(Dims)); + return detail::unchecked_reference(data(), shape(), strides(), ndim()); + } + + /// Return a new view with all of the dimensions of length 1 removed + array squeeze() { + auto& api = detail::npy_api::get(); + return reinterpret_steal(api.PyArray_Squeeze_(m_ptr)); + } + + /// Resize array to given shape + /// If refcheck is true and more that one reference exist to this array + /// then resize will succeed only if it makes a reshape, i.e. original size doesn't change + void resize(ShapeContainer new_shape, bool refcheck = true) { + detail::npy_api::PyArray_Dims d = { + new_shape->data(), int(new_shape->size()) + }; + // try to resize, set ordering param to -1 cause it's not used anyway + object new_array = reinterpret_steal( + detail::npy_api::get().PyArray_Resize_(m_ptr, &d, int(refcheck), -1) + ); + if (!new_array) throw error_already_set(); + if (isinstance(new_array)) { *this = std::move(new_array); } + } + + /// Ensure that the argument is a NumPy array + /// In case of an error, nullptr is returned and the Python error is cleared. + static array ensure(handle h, int ExtraFlags = 0) { + auto result = reinterpret_steal(raw_array(h.ptr(), ExtraFlags)); + if (!result) + PyErr_Clear(); + return result; + } + +protected: + template friend struct detail::npy_format_descriptor; + + void fail_dim_check(ssize_t dim, const std::string& msg) const { + throw index_error(msg + ": " + std::to_string(dim) + + " (ndim = " + std::to_string(ndim()) + ")"); + } + + template ssize_t byte_offset(Ix... index) const { + check_dimensions(index...); + return detail::byte_offset_unsafe(strides(), ssize_t(index)...); + } + + void check_writeable() const { + if (!writeable()) + throw std::domain_error("array is not writeable"); + } + + // Default, C-style strides + static std::vector c_strides(const std::vector &shape, ssize_t itemsize) { + auto ndim = shape.size(); + std::vector strides(ndim, itemsize); + if (ndim > 0) + for (size_t i = ndim - 1; i > 0; --i) + strides[i - 1] = strides[i] * shape[i]; + return strides; + } + + // F-style strides; default when constructing an array_t with `ExtraFlags & f_style` + static std::vector f_strides(const std::vector &shape, ssize_t itemsize) { + auto ndim = shape.size(); + std::vector strides(ndim, itemsize); + for (size_t i = 1; i < ndim; ++i) + strides[i] = strides[i - 1] * shape[i - 1]; + return strides; + } + + template void check_dimensions(Ix... index) const { + check_dimensions_impl(ssize_t(0), shape(), ssize_t(index)...); + } + + void check_dimensions_impl(ssize_t, const ssize_t*) const { } + + template void check_dimensions_impl(ssize_t axis, const ssize_t* shape, ssize_t i, Ix... index) const { + if (i >= *shape) { + throw index_error(std::string("index ") + std::to_string(i) + + " is out of bounds for axis " + std::to_string(axis) + + " with size " + std::to_string(*shape)); + } + check_dimensions_impl(axis + 1, shape + 1, index...); + } + + /// Create array from any object -- always returns a new reference + static PyObject *raw_array(PyObject *ptr, int ExtraFlags = 0) { + if (ptr == nullptr) { + PyErr_SetString(PyExc_ValueError, "cannot create a pybind11::array from a nullptr"); + return nullptr; + } + return detail::npy_api::get().PyArray_FromAny_( + ptr, nullptr, 0, 0, detail::npy_api::NPY_ARRAY_ENSUREARRAY_ | ExtraFlags, nullptr); + } +}; + +template class array_t : public array { +private: + struct private_ctor {}; + // Delegating constructor needed when both moving and accessing in the same constructor + array_t(private_ctor, ShapeContainer &&shape, StridesContainer &&strides, const T *ptr, handle base) + : array(std::move(shape), std::move(strides), ptr, base) {} +public: + static_assert(!detail::array_info::is_array, "Array types cannot be used with array_t"); + + using value_type = T; + + array_t() : array(0, static_cast(nullptr)) {} + array_t(handle h, borrowed_t) : array(h, borrowed_t{}) { } + array_t(handle h, stolen_t) : array(h, stolen_t{}) { } + + PYBIND11_DEPRECATED("Use array_t::ensure() instead") + array_t(handle h, bool is_borrowed) : array(raw_array_t(h.ptr()), stolen_t{}) { + if (!m_ptr) PyErr_Clear(); + if (!is_borrowed) Py_XDECREF(h.ptr()); + } + + array_t(const object &o) : array(raw_array_t(o.ptr()), stolen_t{}) { + if (!m_ptr) throw error_already_set(); + } + + explicit array_t(const buffer_info& info) : array(info) { } + + array_t(ShapeContainer shape, StridesContainer strides, const T *ptr = nullptr, handle base = handle()) + : array(std::move(shape), std::move(strides), ptr, base) { } + + explicit array_t(ShapeContainer shape, const T *ptr = nullptr, handle base = handle()) + : array_t(private_ctor{}, std::move(shape), + ExtraFlags & f_style ? f_strides(*shape, itemsize()) : c_strides(*shape, itemsize()), + ptr, base) { } + + explicit array_t(size_t count, const T *ptr = nullptr, handle base = handle()) + : array({count}, {}, ptr, base) { } + + constexpr ssize_t itemsize() const { + return sizeof(T); + } + + template ssize_t index_at(Ix... index) const { + return offset_at(index...) / itemsize(); + } + + template const T* data(Ix... index) const { + return static_cast(array::data(index...)); + } + + template T* mutable_data(Ix... index) { + return static_cast(array::mutable_data(index...)); + } + + // Reference to element at a given index + template const T& at(Ix... index) const { + if (sizeof...(index) != ndim()) + fail_dim_check(sizeof...(index), "index dimension mismatch"); + return *(static_cast(array::data()) + byte_offset(ssize_t(index)...) / itemsize()); + } + + // Mutable reference to element at a given index + template T& mutable_at(Ix... index) { + if (sizeof...(index) != ndim()) + fail_dim_check(sizeof...(index), "index dimension mismatch"); + return *(static_cast(array::mutable_data()) + byte_offset(ssize_t(index)...) / itemsize()); + } + + /** + * Returns a proxy object that provides access to the array's data without bounds or + * dimensionality checking. Will throw if the array is missing the `writeable` flag. Use with + * care: the array must not be destroyed or reshaped for the duration of the returned object, + * and the caller must take care not to access invalid dimensions or dimension indices. + */ + template detail::unchecked_mutable_reference mutable_unchecked() & { + return array::mutable_unchecked(); + } + + /** + * Returns a proxy object that provides const access to the array's data without bounds or + * dimensionality checking. Unlike `unchecked()`, this does not require that the underlying + * array have the `writable` flag. Use with care: the array must not be destroyed or reshaped + * for the duration of the returned object, and the caller must take care not to access invalid + * dimensions or dimension indices. + */ + template detail::unchecked_reference unchecked() const & { + return array::unchecked(); + } + + /// Ensure that the argument is a NumPy array of the correct dtype (and if not, try to convert + /// it). In case of an error, nullptr is returned and the Python error is cleared. + static array_t ensure(handle h) { + auto result = reinterpret_steal(raw_array_t(h.ptr())); + if (!result) + PyErr_Clear(); + return result; + } + + static bool check_(handle h) { + const auto &api = detail::npy_api::get(); + return api.PyArray_Check_(h.ptr()) + && api.PyArray_EquivTypes_(detail::array_proxy(h.ptr())->descr, dtype::of().ptr()); + } + +protected: + /// Create array from any object -- always returns a new reference + static PyObject *raw_array_t(PyObject *ptr) { + if (ptr == nullptr) { + PyErr_SetString(PyExc_ValueError, "cannot create a pybind11::array_t from a nullptr"); + return nullptr; + } + return detail::npy_api::get().PyArray_FromAny_( + ptr, dtype::of().release().ptr(), 0, 0, + detail::npy_api::NPY_ARRAY_ENSUREARRAY_ | ExtraFlags, nullptr); + } +}; + +template +struct format_descriptor::value>> { + static std::string format() { + return detail::npy_format_descriptor::type>::format(); + } +}; + +template struct format_descriptor { + static std::string format() { return std::to_string(N) + "s"; } +}; +template struct format_descriptor> { + static std::string format() { return std::to_string(N) + "s"; } +}; + +template +struct format_descriptor::value>> { + static std::string format() { + return format_descriptor< + typename std::remove_cv::type>::type>::format(); + } +}; + +template +struct format_descriptor::is_array>> { + static std::string format() { + using namespace detail; + static constexpr auto extents = _("(") + array_info::extents + _(")"); + return extents.text + format_descriptor>::format(); + } +}; + +NAMESPACE_BEGIN(detail) +template +struct pyobject_caster> { + using type = array_t; + + bool load(handle src, bool convert) { + if (!convert && !type::check_(src)) + return false; + value = type::ensure(src); + return static_cast(value); + } + + static handle cast(const handle &src, return_value_policy /* policy */, handle /* parent */) { + return src.inc_ref(); + } + PYBIND11_TYPE_CASTER(type, handle_type_name::name); +}; + +template +struct compare_buffer_info::value>> { + static bool compare(const buffer_info& b) { + return npy_api::get().PyArray_EquivTypes_(dtype::of().ptr(), dtype(b).ptr()); + } +}; + +template +struct npy_format_descriptor_name; + +template +struct npy_format_descriptor_name::value>> { + static constexpr auto name = _::value>( + _("bool"), _::value>("int", "uint") + _() + ); +}; + +template +struct npy_format_descriptor_name::value>> { + static constexpr auto name = _::value || std::is_same::value>( + _("float") + _(), _("longdouble") + ); +}; + +template +struct npy_format_descriptor_name::value>> { + static constexpr auto name = _::value + || std::is_same::value>( + _("complex") + _(), _("longcomplex") + ); +}; + +template +struct npy_format_descriptor::value>> + : npy_format_descriptor_name { +private: + // NB: the order here must match the one in common.h + constexpr static const int values[15] = { + npy_api::NPY_BOOL_, + npy_api::NPY_BYTE_, npy_api::NPY_UBYTE_, npy_api::NPY_SHORT_, npy_api::NPY_USHORT_, + npy_api::NPY_INT_, npy_api::NPY_UINT_, npy_api::NPY_LONGLONG_, npy_api::NPY_ULONGLONG_, + npy_api::NPY_FLOAT_, npy_api::NPY_DOUBLE_, npy_api::NPY_LONGDOUBLE_, + npy_api::NPY_CFLOAT_, npy_api::NPY_CDOUBLE_, npy_api::NPY_CLONGDOUBLE_ + }; + +public: + static constexpr int value = values[detail::is_fmt_numeric::index]; + + static pybind11::dtype dtype() { + if (auto ptr = npy_api::get().PyArray_DescrFromType_(value)) + return reinterpret_borrow(ptr); + pybind11_fail("Unsupported buffer format!"); + } +}; + +#define PYBIND11_DECL_CHAR_FMT \ + static constexpr auto name = _("S") + _(); \ + static pybind11::dtype dtype() { return pybind11::dtype(std::string("S") + std::to_string(N)); } +template struct npy_format_descriptor { PYBIND11_DECL_CHAR_FMT }; +template struct npy_format_descriptor> { PYBIND11_DECL_CHAR_FMT }; +#undef PYBIND11_DECL_CHAR_FMT + +template struct npy_format_descriptor::is_array>> { +private: + using base_descr = npy_format_descriptor::type>; +public: + static_assert(!array_info::is_empty, "Zero-sized arrays are not supported"); + + static constexpr auto name = _("(") + array_info::extents + _(")") + base_descr::name; + static pybind11::dtype dtype() { + list shape; + array_info::append_extents(shape); + return pybind11::dtype::from_args(pybind11::make_tuple(base_descr::dtype(), shape)); + } +}; + +template struct npy_format_descriptor::value>> { +private: + using base_descr = npy_format_descriptor::type>; +public: + static constexpr auto name = base_descr::name; + static pybind11::dtype dtype() { return base_descr::dtype(); } +}; + +struct field_descriptor { + const char *name; + ssize_t offset; + ssize_t size; + std::string format; + dtype descr; +}; + +inline PYBIND11_NOINLINE void register_structured_dtype( + any_container fields, + const std::type_info& tinfo, ssize_t itemsize, + bool (*direct_converter)(PyObject *, void *&)) { + + auto& numpy_internals = get_numpy_internals(); + if (numpy_internals.get_type_info(tinfo, false)) + pybind11_fail("NumPy: dtype is already registered"); + + list names, formats, offsets; + for (auto field : *fields) { + if (!field.descr) + pybind11_fail(std::string("NumPy: unsupported field dtype: `") + + field.name + "` @ " + tinfo.name()); + names.append(PYBIND11_STR_TYPE(field.name)); + formats.append(field.descr); + offsets.append(pybind11::int_(field.offset)); + } + auto dtype_ptr = pybind11::dtype(names, formats, offsets, itemsize).release().ptr(); + + // There is an existing bug in NumPy (as of v1.11): trailing bytes are + // not encoded explicitly into the format string. This will supposedly + // get fixed in v1.12; for further details, see these: + // - https://github.com/numpy/numpy/issues/7797 + // - https://github.com/numpy/numpy/pull/7798 + // Because of this, we won't use numpy's logic to generate buffer format + // strings and will just do it ourselves. + std::vector ordered_fields(std::move(fields)); + std::sort(ordered_fields.begin(), ordered_fields.end(), + [](const field_descriptor &a, const field_descriptor &b) { return a.offset < b.offset; }); + ssize_t offset = 0; + std::ostringstream oss; + // mark the structure as unaligned with '^', because numpy and C++ don't + // always agree about alignment (particularly for complex), and we're + // explicitly listing all our padding. This depends on none of the fields + // overriding the endianness. Putting the ^ in front of individual fields + // isn't guaranteed to work due to https://github.com/numpy/numpy/issues/9049 + oss << "^T{"; + for (auto& field : ordered_fields) { + if (field.offset > offset) + oss << (field.offset - offset) << 'x'; + oss << field.format << ':' << field.name << ':'; + offset = field.offset + field.size; + } + if (itemsize > offset) + oss << (itemsize - offset) << 'x'; + oss << '}'; + auto format_str = oss.str(); + + // Sanity check: verify that NumPy properly parses our buffer format string + auto& api = npy_api::get(); + auto arr = array(buffer_info(nullptr, itemsize, format_str, 1)); + if (!api.PyArray_EquivTypes_(dtype_ptr, arr.dtype().ptr())) + pybind11_fail("NumPy: invalid buffer descriptor!"); + + auto tindex = std::type_index(tinfo); + numpy_internals.registered_dtypes[tindex] = { dtype_ptr, format_str }; + get_internals().direct_conversions[tindex].push_back(direct_converter); +} + +template struct npy_format_descriptor { + static_assert(is_pod_struct::value, "Attempt to use a non-POD or unimplemented POD type as a numpy dtype"); + + static constexpr auto name = make_caster::name; + + static pybind11::dtype dtype() { + return reinterpret_borrow(dtype_ptr()); + } + + static std::string format() { + static auto format_str = get_numpy_internals().get_type_info(true)->format_str; + return format_str; + } + + static void register_dtype(any_container fields) { + register_structured_dtype(std::move(fields), typeid(typename std::remove_cv::type), + sizeof(T), &direct_converter); + } + +private: + static PyObject* dtype_ptr() { + static PyObject* ptr = get_numpy_internals().get_type_info(true)->dtype_ptr; + return ptr; + } + + static bool direct_converter(PyObject *obj, void*& value) { + auto& api = npy_api::get(); + if (!PyObject_TypeCheck(obj, api.PyVoidArrType_Type_)) + return false; + if (auto descr = reinterpret_steal(api.PyArray_DescrFromScalar_(obj))) { + if (api.PyArray_EquivTypes_(dtype_ptr(), descr.ptr())) { + value = ((PyVoidScalarObject_Proxy *) obj)->obval; + return true; + } + } + return false; + } +}; + +#ifdef __CLION_IDE__ // replace heavy macro with dummy code for the IDE (doesn't affect code) +# define PYBIND11_NUMPY_DTYPE(Type, ...) ((void)0) +# define PYBIND11_NUMPY_DTYPE_EX(Type, ...) ((void)0) +#else + +#define PYBIND11_FIELD_DESCRIPTOR_EX(T, Field, Name) \ + ::pybind11::detail::field_descriptor { \ + Name, offsetof(T, Field), sizeof(decltype(std::declval().Field)), \ + ::pybind11::format_descriptor().Field)>::format(), \ + ::pybind11::detail::npy_format_descriptor().Field)>::dtype() \ + } + +// Extract name, offset and format descriptor for a struct field +#define PYBIND11_FIELD_DESCRIPTOR(T, Field) PYBIND11_FIELD_DESCRIPTOR_EX(T, Field, #Field) + +// The main idea of this macro is borrowed from https://github.com/swansontec/map-macro +// (C) William Swanson, Paul Fultz +#define PYBIND11_EVAL0(...) __VA_ARGS__ +#define PYBIND11_EVAL1(...) PYBIND11_EVAL0 (PYBIND11_EVAL0 (PYBIND11_EVAL0 (__VA_ARGS__))) +#define PYBIND11_EVAL2(...) PYBIND11_EVAL1 (PYBIND11_EVAL1 (PYBIND11_EVAL1 (__VA_ARGS__))) +#define PYBIND11_EVAL3(...) PYBIND11_EVAL2 (PYBIND11_EVAL2 (PYBIND11_EVAL2 (__VA_ARGS__))) +#define PYBIND11_EVAL4(...) PYBIND11_EVAL3 (PYBIND11_EVAL3 (PYBIND11_EVAL3 (__VA_ARGS__))) +#define PYBIND11_EVAL(...) PYBIND11_EVAL4 (PYBIND11_EVAL4 (PYBIND11_EVAL4 (__VA_ARGS__))) +#define PYBIND11_MAP_END(...) +#define PYBIND11_MAP_OUT +#define PYBIND11_MAP_COMMA , +#define PYBIND11_MAP_GET_END() 0, PYBIND11_MAP_END +#define PYBIND11_MAP_NEXT0(test, next, ...) next PYBIND11_MAP_OUT +#define PYBIND11_MAP_NEXT1(test, next) PYBIND11_MAP_NEXT0 (test, next, 0) +#define PYBIND11_MAP_NEXT(test, next) PYBIND11_MAP_NEXT1 (PYBIND11_MAP_GET_END test, next) +#ifdef _MSC_VER // MSVC is not as eager to expand macros, hence this workaround +#define PYBIND11_MAP_LIST_NEXT1(test, next) \ + PYBIND11_EVAL0 (PYBIND11_MAP_NEXT0 (test, PYBIND11_MAP_COMMA next, 0)) +#else +#define PYBIND11_MAP_LIST_NEXT1(test, next) \ + PYBIND11_MAP_NEXT0 (test, PYBIND11_MAP_COMMA next, 0) +#endif +#define PYBIND11_MAP_LIST_NEXT(test, next) \ + PYBIND11_MAP_LIST_NEXT1 (PYBIND11_MAP_GET_END test, next) +#define PYBIND11_MAP_LIST0(f, t, x, peek, ...) \ + f(t, x) PYBIND11_MAP_LIST_NEXT (peek, PYBIND11_MAP_LIST1) (f, t, peek, __VA_ARGS__) +#define PYBIND11_MAP_LIST1(f, t, x, peek, ...) \ + f(t, x) PYBIND11_MAP_LIST_NEXT (peek, PYBIND11_MAP_LIST0) (f, t, peek, __VA_ARGS__) +// PYBIND11_MAP_LIST(f, t, a1, a2, ...) expands to f(t, a1), f(t, a2), ... +#define PYBIND11_MAP_LIST(f, t, ...) \ + PYBIND11_EVAL (PYBIND11_MAP_LIST1 (f, t, __VA_ARGS__, (), 0)) + +#define PYBIND11_NUMPY_DTYPE(Type, ...) \ + ::pybind11::detail::npy_format_descriptor::register_dtype \ + (::std::vector<::pybind11::detail::field_descriptor> \ + {PYBIND11_MAP_LIST (PYBIND11_FIELD_DESCRIPTOR, Type, __VA_ARGS__)}) + +#ifdef _MSC_VER +#define PYBIND11_MAP2_LIST_NEXT1(test, next) \ + PYBIND11_EVAL0 (PYBIND11_MAP_NEXT0 (test, PYBIND11_MAP_COMMA next, 0)) +#else +#define PYBIND11_MAP2_LIST_NEXT1(test, next) \ + PYBIND11_MAP_NEXT0 (test, PYBIND11_MAP_COMMA next, 0) +#endif +#define PYBIND11_MAP2_LIST_NEXT(test, next) \ + PYBIND11_MAP2_LIST_NEXT1 (PYBIND11_MAP_GET_END test, next) +#define PYBIND11_MAP2_LIST0(f, t, x1, x2, peek, ...) \ + f(t, x1, x2) PYBIND11_MAP2_LIST_NEXT (peek, PYBIND11_MAP2_LIST1) (f, t, peek, __VA_ARGS__) +#define PYBIND11_MAP2_LIST1(f, t, x1, x2, peek, ...) \ + f(t, x1, x2) PYBIND11_MAP2_LIST_NEXT (peek, PYBIND11_MAP2_LIST0) (f, t, peek, __VA_ARGS__) +// PYBIND11_MAP2_LIST(f, t, a1, a2, ...) expands to f(t, a1, a2), f(t, a3, a4), ... +#define PYBIND11_MAP2_LIST(f, t, ...) \ + PYBIND11_EVAL (PYBIND11_MAP2_LIST1 (f, t, __VA_ARGS__, (), 0)) + +#define PYBIND11_NUMPY_DTYPE_EX(Type, ...) \ + ::pybind11::detail::npy_format_descriptor::register_dtype \ + (::std::vector<::pybind11::detail::field_descriptor> \ + {PYBIND11_MAP2_LIST (PYBIND11_FIELD_DESCRIPTOR_EX, Type, __VA_ARGS__)}) + +#endif // __CLION_IDE__ + +template +using array_iterator = typename std::add_pointer::type; + +template +array_iterator array_begin(const buffer_info& buffer) { + return array_iterator(reinterpret_cast(buffer.ptr)); +} + +template +array_iterator array_end(const buffer_info& buffer) { + return array_iterator(reinterpret_cast(buffer.ptr) + buffer.size); +} + +class common_iterator { +public: + using container_type = std::vector; + using value_type = container_type::value_type; + using size_type = container_type::size_type; + + common_iterator() : p_ptr(0), m_strides() {} + + common_iterator(void* ptr, const container_type& strides, const container_type& shape) + : p_ptr(reinterpret_cast(ptr)), m_strides(strides.size()) { + m_strides.back() = static_cast(strides.back()); + for (size_type i = m_strides.size() - 1; i != 0; --i) { + size_type j = i - 1; + value_type s = static_cast(shape[i]); + m_strides[j] = strides[j] + m_strides[i] - strides[i] * s; + } + } + + void increment(size_type dim) { + p_ptr += m_strides[dim]; + } + + void* data() const { + return p_ptr; + } + +private: + char* p_ptr; + container_type m_strides; +}; + +template class multi_array_iterator { +public: + using container_type = std::vector; + + multi_array_iterator(const std::array &buffers, + const container_type &shape) + : m_shape(shape.size()), m_index(shape.size(), 0), + m_common_iterator() { + + // Manual copy to avoid conversion warning if using std::copy + for (size_t i = 0; i < shape.size(); ++i) + m_shape[i] = shape[i]; + + container_type strides(shape.size()); + for (size_t i = 0; i < N; ++i) + init_common_iterator(buffers[i], shape, m_common_iterator[i], strides); + } + + multi_array_iterator& operator++() { + for (size_t j = m_index.size(); j != 0; --j) { + size_t i = j - 1; + if (++m_index[i] != m_shape[i]) { + increment_common_iterator(i); + break; + } else { + m_index[i] = 0; + } + } + return *this; + } + + template T* data() const { + return reinterpret_cast(m_common_iterator[K].data()); + } + +private: + + using common_iter = common_iterator; + + void init_common_iterator(const buffer_info &buffer, + const container_type &shape, + common_iter &iterator, + container_type &strides) { + auto buffer_shape_iter = buffer.shape.rbegin(); + auto buffer_strides_iter = buffer.strides.rbegin(); + auto shape_iter = shape.rbegin(); + auto strides_iter = strides.rbegin(); + + while (buffer_shape_iter != buffer.shape.rend()) { + if (*shape_iter == *buffer_shape_iter) + *strides_iter = *buffer_strides_iter; + else + *strides_iter = 0; + + ++buffer_shape_iter; + ++buffer_strides_iter; + ++shape_iter; + ++strides_iter; + } + + std::fill(strides_iter, strides.rend(), 0); + iterator = common_iter(buffer.ptr, strides, shape); + } + + void increment_common_iterator(size_t dim) { + for (auto &iter : m_common_iterator) + iter.increment(dim); + } + + container_type m_shape; + container_type m_index; + std::array m_common_iterator; +}; + +enum class broadcast_trivial { non_trivial, c_trivial, f_trivial }; + +// Populates the shape and number of dimensions for the set of buffers. Returns a broadcast_trivial +// enum value indicating whether the broadcast is "trivial"--that is, has each buffer being either a +// singleton or a full-size, C-contiguous (`c_trivial`) or Fortran-contiguous (`f_trivial`) storage +// buffer; returns `non_trivial` otherwise. +template +broadcast_trivial broadcast(const std::array &buffers, ssize_t &ndim, std::vector &shape) { + ndim = std::accumulate(buffers.begin(), buffers.end(), ssize_t(0), [](ssize_t res, const buffer_info &buf) { + return std::max(res, buf.ndim); + }); + + shape.clear(); + shape.resize((size_t) ndim, 1); + + // Figure out the output size, and make sure all input arrays conform (i.e. are either size 1 or + // the full size). + for (size_t i = 0; i < N; ++i) { + auto res_iter = shape.rbegin(); + auto end = buffers[i].shape.rend(); + for (auto shape_iter = buffers[i].shape.rbegin(); shape_iter != end; ++shape_iter, ++res_iter) { + const auto &dim_size_in = *shape_iter; + auto &dim_size_out = *res_iter; + + // Each input dimension can either be 1 or `n`, but `n` values must match across buffers + if (dim_size_out == 1) + dim_size_out = dim_size_in; + else if (dim_size_in != 1 && dim_size_in != dim_size_out) + pybind11_fail("pybind11::vectorize: incompatible size/dimension of inputs!"); + } + } + + bool trivial_broadcast_c = true; + bool trivial_broadcast_f = true; + for (size_t i = 0; i < N && (trivial_broadcast_c || trivial_broadcast_f); ++i) { + if (buffers[i].size == 1) + continue; + + // Require the same number of dimensions: + if (buffers[i].ndim != ndim) + return broadcast_trivial::non_trivial; + + // Require all dimensions be full-size: + if (!std::equal(buffers[i].shape.cbegin(), buffers[i].shape.cend(), shape.cbegin())) + return broadcast_trivial::non_trivial; + + // Check for C contiguity (but only if previous inputs were also C contiguous) + if (trivial_broadcast_c) { + ssize_t expect_stride = buffers[i].itemsize; + auto end = buffers[i].shape.crend(); + for (auto shape_iter = buffers[i].shape.crbegin(), stride_iter = buffers[i].strides.crbegin(); + trivial_broadcast_c && shape_iter != end; ++shape_iter, ++stride_iter) { + if (expect_stride == *stride_iter) + expect_stride *= *shape_iter; + else + trivial_broadcast_c = false; + } + } + + // Check for Fortran contiguity (if previous inputs were also F contiguous) + if (trivial_broadcast_f) { + ssize_t expect_stride = buffers[i].itemsize; + auto end = buffers[i].shape.cend(); + for (auto shape_iter = buffers[i].shape.cbegin(), stride_iter = buffers[i].strides.cbegin(); + trivial_broadcast_f && shape_iter != end; ++shape_iter, ++stride_iter) { + if (expect_stride == *stride_iter) + expect_stride *= *shape_iter; + else + trivial_broadcast_f = false; + } + } + } + + return + trivial_broadcast_c ? broadcast_trivial::c_trivial : + trivial_broadcast_f ? broadcast_trivial::f_trivial : + broadcast_trivial::non_trivial; +} + +template +struct vectorize_arg { + static_assert(!std::is_rvalue_reference::value, "Functions with rvalue reference arguments cannot be vectorized"); + // The wrapped function gets called with this type: + using call_type = remove_reference_t; + // Is this a vectorized argument? + static constexpr bool vectorize = + satisfies_any_of::value && + satisfies_none_of::value && + (!std::is_reference::value || + (std::is_lvalue_reference::value && std::is_const::value)); + // Accept this type: an array for vectorized types, otherwise the type as-is: + using type = conditional_t, array::forcecast>, T>; +}; + +template +struct vectorize_helper { +private: + static constexpr size_t N = sizeof...(Args); + static constexpr size_t NVectorized = constexpr_sum(vectorize_arg::vectorize...); + static_assert(NVectorized >= 1, + "pybind11::vectorize(...) requires a function with at least one vectorizable argument"); + +public: + template + explicit vectorize_helper(T &&f) : f(std::forward(f)) { } + + object operator()(typename vectorize_arg::type... args) { + return run(args..., + make_index_sequence(), + select_indices::vectorize...>(), + make_index_sequence()); + } + +private: + remove_reference_t f; + + // Internal compiler error in MSVC 19.16.27025.1 (Visual Studio 2017 15.9.4), when compiling with "/permissive-" flag + // when arg_call_types is manually inlined. + using arg_call_types = std::tuple::call_type...>; + template using param_n_t = typename std::tuple_element::type; + + // Runs a vectorized function given arguments tuple and three index sequences: + // - Index is the full set of 0 ... (N-1) argument indices; + // - VIndex is the subset of argument indices with vectorized parameters, letting us access + // vectorized arguments (anything not in this sequence is passed through) + // - BIndex is a incremental sequence (beginning at 0) of the same size as VIndex, so that + // we can store vectorized buffer_infos in an array (argument VIndex has its buffer at + // index BIndex in the array). + template object run( + typename vectorize_arg::type &...args, + index_sequence i_seq, index_sequence vi_seq, index_sequence bi_seq) { + + // Pointers to values the function was called with; the vectorized ones set here will start + // out as array_t pointers, but they will be changed them to T pointers before we make + // call the wrapped function. Non-vectorized pointers are left as-is. + std::array params{{ &args... }}; + + // The array of `buffer_info`s of vectorized arguments: + std::array buffers{{ reinterpret_cast(params[VIndex])->request()... }}; + + /* Determine dimensions parameters of output array */ + ssize_t nd = 0; + std::vector shape(0); + auto trivial = broadcast(buffers, nd, shape); + size_t ndim = (size_t) nd; + + size_t size = std::accumulate(shape.begin(), shape.end(), (size_t) 1, std::multiplies()); + + // If all arguments are 0-dimension arrays (i.e. single values) return a plain value (i.e. + // not wrapped in an array). + if (size == 1 && ndim == 0) { + PYBIND11_EXPAND_SIDE_EFFECTS(params[VIndex] = buffers[BIndex].ptr); + return cast(f(*reinterpret_cast *>(params[Index])...)); + } + + array_t result; + if (trivial == broadcast_trivial::f_trivial) result = array_t(shape); + else result = array_t(shape); + + if (size == 0) return result; + + /* Call the function */ + if (trivial == broadcast_trivial::non_trivial) + apply_broadcast(buffers, params, result, i_seq, vi_seq, bi_seq); + else + apply_trivial(buffers, params, result.mutable_data(), size, i_seq, vi_seq, bi_seq); + + return result; + } + + template + void apply_trivial(std::array &buffers, + std::array ¶ms, + Return *out, + size_t size, + index_sequence, index_sequence, index_sequence) { + + // Initialize an array of mutable byte references and sizes with references set to the + // appropriate pointer in `params`; as we iterate, we'll increment each pointer by its size + // (except for singletons, which get an increment of 0). + std::array, NVectorized> vecparams{{ + std::pair( + reinterpret_cast(params[VIndex] = buffers[BIndex].ptr), + buffers[BIndex].size == 1 ? 0 : sizeof(param_n_t) + )... + }}; + + for (size_t i = 0; i < size; ++i) { + out[i] = f(*reinterpret_cast *>(params[Index])...); + for (auto &x : vecparams) x.first += x.second; + } + } + + template + void apply_broadcast(std::array &buffers, + std::array ¶ms, + array_t &output_array, + index_sequence, index_sequence, index_sequence) { + + buffer_info output = output_array.request(); + multi_array_iterator input_iter(buffers, output.shape); + + for (array_iterator iter = array_begin(output), end = array_end(output); + iter != end; + ++iter, ++input_iter) { + PYBIND11_EXPAND_SIDE_EFFECTS(( + params[VIndex] = input_iter.template data() + )); + *iter = f(*reinterpret_cast *>(std::get(params))...); + } + } +}; + +template +vectorize_helper +vectorize_extractor(const Func &f, Return (*) (Args ...)) { + return detail::vectorize_helper(f); +} + +template struct handle_type_name> { + static constexpr auto name = _("numpy.ndarray[") + npy_format_descriptor::name + _("]"); +}; + +NAMESPACE_END(detail) + +// Vanilla pointer vectorizer: +template +detail::vectorize_helper +vectorize(Return (*f) (Args ...)) { + return detail::vectorize_helper(f); +} + +// lambda vectorizer: +template ::value, int> = 0> +auto vectorize(Func &&f) -> decltype( + detail::vectorize_extractor(std::forward(f), (detail::function_signature_t *) nullptr)) { + return detail::vectorize_extractor(std::forward(f), (detail::function_signature_t *) nullptr); +} + +// Vectorize a class method (non-const): +template ())), Return, Class *, Args...>> +Helper vectorize(Return (Class::*f)(Args...)) { + return Helper(std::mem_fn(f)); +} + +// Vectorize a class method (const): +template ())), Return, const Class *, Args...>> +Helper vectorize(Return (Class::*f)(Args...) const) { + return Helper(std::mem_fn(f)); +} + +NAMESPACE_END(PYBIND11_NAMESPACE) + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif diff --git a/ptocr/postprocess/piexlmerge/include/pybind11/operators.h b/ptocr/postprocess/piexlmerge/include/pybind11/operators.h new file mode 100644 index 0000000..b3dd62c --- /dev/null +++ b/ptocr/postprocess/piexlmerge/include/pybind11/operators.h @@ -0,0 +1,168 @@ +/* + pybind11/operator.h: Metatemplates for operator overloading + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "pybind11.h" + +#if defined(__clang__) && !defined(__INTEL_COMPILER) +# pragma clang diagnostic ignored "-Wunsequenced" // multiple unsequenced modifications to 'self' (when using def(py::self OP Type())) +#elif defined(_MSC_VER) +# pragma warning(push) +# pragma warning(disable: 4127) // warning C4127: Conditional expression is constant +#endif + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +NAMESPACE_BEGIN(detail) + +/// Enumeration with all supported operator types +enum op_id : int { + op_add, op_sub, op_mul, op_div, op_mod, op_divmod, op_pow, op_lshift, + op_rshift, op_and, op_xor, op_or, op_neg, op_pos, op_abs, op_invert, + op_int, op_long, op_float, op_str, op_cmp, op_gt, op_ge, op_lt, op_le, + op_eq, op_ne, op_iadd, op_isub, op_imul, op_idiv, op_imod, op_ilshift, + op_irshift, op_iand, op_ixor, op_ior, op_complex, op_bool, op_nonzero, + op_repr, op_truediv, op_itruediv, op_hash +}; + +enum op_type : int { + op_l, /* base type on left */ + op_r, /* base type on right */ + op_u /* unary operator */ +}; + +struct self_t { }; +static const self_t self = self_t(); + +/// Type for an unused type slot +struct undefined_t { }; + +/// Don't warn about an unused variable +inline self_t __self() { return self; } + +/// base template of operator implementations +template struct op_impl { }; + +/// Operator implementation generator +template struct op_ { + template void execute(Class &cl, const Extra&... extra) const { + using Base = typename Class::type; + using L_type = conditional_t::value, Base, L>; + using R_type = conditional_t::value, Base, R>; + using op = op_impl; + cl.def(op::name(), &op::execute, is_operator(), extra...); + #if PY_MAJOR_VERSION < 3 + if (id == op_truediv || id == op_itruediv) + cl.def(id == op_itruediv ? "__idiv__" : ot == op_l ? "__div__" : "__rdiv__", + &op::execute, is_operator(), extra...); + #endif + } + template void execute_cast(Class &cl, const Extra&... extra) const { + using Base = typename Class::type; + using L_type = conditional_t::value, Base, L>; + using R_type = conditional_t::value, Base, R>; + using op = op_impl; + cl.def(op::name(), &op::execute_cast, is_operator(), extra...); + #if PY_MAJOR_VERSION < 3 + if (id == op_truediv || id == op_itruediv) + cl.def(id == op_itruediv ? "__idiv__" : ot == op_l ? "__div__" : "__rdiv__", + &op::execute, is_operator(), extra...); + #endif + } +}; + +#define PYBIND11_BINARY_OPERATOR(id, rid, op, expr) \ +template struct op_impl { \ + static char const* name() { return "__" #id "__"; } \ + static auto execute(const L &l, const R &r) -> decltype(expr) { return (expr); } \ + static B execute_cast(const L &l, const R &r) { return B(expr); } \ +}; \ +template struct op_impl { \ + static char const* name() { return "__" #rid "__"; } \ + static auto execute(const R &r, const L &l) -> decltype(expr) { return (expr); } \ + static B execute_cast(const R &r, const L &l) { return B(expr); } \ +}; \ +inline op_ op(const self_t &, const self_t &) { \ + return op_(); \ +} \ +template op_ op(const self_t &, const T &) { \ + return op_(); \ +} \ +template op_ op(const T &, const self_t &) { \ + return op_(); \ +} + +#define PYBIND11_INPLACE_OPERATOR(id, op, expr) \ +template struct op_impl { \ + static char const* name() { return "__" #id "__"; } \ + static auto execute(L &l, const R &r) -> decltype(expr) { return expr; } \ + static B execute_cast(L &l, const R &r) { return B(expr); } \ +}; \ +template op_ op(const self_t &, const T &) { \ + return op_(); \ +} + +#define PYBIND11_UNARY_OPERATOR(id, op, expr) \ +template struct op_impl { \ + static char const* name() { return "__" #id "__"; } \ + static auto execute(const L &l) -> decltype(expr) { return expr; } \ + static B execute_cast(const L &l) { return B(expr); } \ +}; \ +inline op_ op(const self_t &) { \ + return op_(); \ +} + +PYBIND11_BINARY_OPERATOR(sub, rsub, operator-, l - r) +PYBIND11_BINARY_OPERATOR(add, radd, operator+, l + r) +PYBIND11_BINARY_OPERATOR(mul, rmul, operator*, l * r) +PYBIND11_BINARY_OPERATOR(truediv, rtruediv, operator/, l / r) +PYBIND11_BINARY_OPERATOR(mod, rmod, operator%, l % r) +PYBIND11_BINARY_OPERATOR(lshift, rlshift, operator<<, l << r) +PYBIND11_BINARY_OPERATOR(rshift, rrshift, operator>>, l >> r) +PYBIND11_BINARY_OPERATOR(and, rand, operator&, l & r) +PYBIND11_BINARY_OPERATOR(xor, rxor, operator^, l ^ r) +PYBIND11_BINARY_OPERATOR(eq, eq, operator==, l == r) +PYBIND11_BINARY_OPERATOR(ne, ne, operator!=, l != r) +PYBIND11_BINARY_OPERATOR(or, ror, operator|, l | r) +PYBIND11_BINARY_OPERATOR(gt, lt, operator>, l > r) +PYBIND11_BINARY_OPERATOR(ge, le, operator>=, l >= r) +PYBIND11_BINARY_OPERATOR(lt, gt, operator<, l < r) +PYBIND11_BINARY_OPERATOR(le, ge, operator<=, l <= r) +//PYBIND11_BINARY_OPERATOR(pow, rpow, pow, std::pow(l, r)) +PYBIND11_INPLACE_OPERATOR(iadd, operator+=, l += r) +PYBIND11_INPLACE_OPERATOR(isub, operator-=, l -= r) +PYBIND11_INPLACE_OPERATOR(imul, operator*=, l *= r) +PYBIND11_INPLACE_OPERATOR(itruediv, operator/=, l /= r) +PYBIND11_INPLACE_OPERATOR(imod, operator%=, l %= r) +PYBIND11_INPLACE_OPERATOR(ilshift, operator<<=, l <<= r) +PYBIND11_INPLACE_OPERATOR(irshift, operator>>=, l >>= r) +PYBIND11_INPLACE_OPERATOR(iand, operator&=, l &= r) +PYBIND11_INPLACE_OPERATOR(ixor, operator^=, l ^= r) +PYBIND11_INPLACE_OPERATOR(ior, operator|=, l |= r) +PYBIND11_UNARY_OPERATOR(neg, operator-, -l) +PYBIND11_UNARY_OPERATOR(pos, operator+, +l) +PYBIND11_UNARY_OPERATOR(abs, abs, std::abs(l)) +PYBIND11_UNARY_OPERATOR(hash, hash, std::hash()(l)) +PYBIND11_UNARY_OPERATOR(invert, operator~, (~l)) +PYBIND11_UNARY_OPERATOR(bool, operator!, !!l) +PYBIND11_UNARY_OPERATOR(int, int_, (int) l) +PYBIND11_UNARY_OPERATOR(float, float_, (double) l) + +#undef PYBIND11_BINARY_OPERATOR +#undef PYBIND11_INPLACE_OPERATOR +#undef PYBIND11_UNARY_OPERATOR +NAMESPACE_END(detail) + +using detail::self; + +NAMESPACE_END(PYBIND11_NAMESPACE) + +#if defined(_MSC_VER) +# pragma warning(pop) +#endif diff --git a/ptocr/postprocess/piexlmerge/include/pybind11/options.h b/ptocr/postprocess/piexlmerge/include/pybind11/options.h new file mode 100644 index 0000000..cc1e1f6 --- /dev/null +++ b/ptocr/postprocess/piexlmerge/include/pybind11/options.h @@ -0,0 +1,65 @@ +/* + pybind11/options.h: global settings that are configurable at runtime. + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "detail/common.h" + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) + +class options { +public: + + // Default RAII constructor, which leaves settings as they currently are. + options() : previous_state(global_state()) {} + + // Class is non-copyable. + options(const options&) = delete; + options& operator=(const options&) = delete; + + // Destructor, which restores settings that were in effect before. + ~options() { + global_state() = previous_state; + } + + // Setter methods (affect the global state): + + options& disable_user_defined_docstrings() & { global_state().show_user_defined_docstrings = false; return *this; } + + options& enable_user_defined_docstrings() & { global_state().show_user_defined_docstrings = true; return *this; } + + options& disable_function_signatures() & { global_state().show_function_signatures = false; return *this; } + + options& enable_function_signatures() & { global_state().show_function_signatures = true; return *this; } + + // Getter methods (return the global state): + + static bool show_user_defined_docstrings() { return global_state().show_user_defined_docstrings; } + + static bool show_function_signatures() { return global_state().show_function_signatures; } + + // This type is not meant to be allocated on the heap. + void* operator new(size_t) = delete; + +private: + + struct state { + bool show_user_defined_docstrings = true; //< Include user-supplied texts in docstrings. + bool show_function_signatures = true; //< Include auto-generated function signatures in docstrings. + }; + + static state &global_state() { + static state instance; + return instance; + } + + state previous_state; +}; + +NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/ptocr/postprocess/piexlmerge/include/pybind11/pybind11.h b/ptocr/postprocess/piexlmerge/include/pybind11/pybind11.h new file mode 100644 index 0000000..7fa0f0e --- /dev/null +++ b/ptocr/postprocess/piexlmerge/include/pybind11/pybind11.h @@ -0,0 +1,2094 @@ +/* + pybind11/pybind11.h: Main header file of the C++11 python + binding generator library + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#if defined(__INTEL_COMPILER) +# pragma warning push +# pragma warning disable 68 // integer conversion resulted in a change of sign +# pragma warning disable 186 // pointless comparison of unsigned integer with zero +# pragma warning disable 878 // incompatible exception specifications +# pragma warning disable 1334 // the "template" keyword used for syntactic disambiguation may only be used within a template +# pragma warning disable 1682 // implicit conversion of a 64-bit integral type to a smaller integral type (potential portability problem) +# pragma warning disable 1786 // function "strdup" was declared deprecated +# pragma warning disable 1875 // offsetof applied to non-POD (Plain Old Data) types is nonstandard +# pragma warning disable 2196 // warning #2196: routine is both "inline" and "noinline" +#elif defined(_MSC_VER) +# pragma warning(push) +# pragma warning(disable: 4100) // warning C4100: Unreferenced formal parameter +# pragma warning(disable: 4127) // warning C4127: Conditional expression is constant +# pragma warning(disable: 4512) // warning C4512: Assignment operator was implicitly defined as deleted +# pragma warning(disable: 4800) // warning C4800: 'int': forcing value to bool 'true' or 'false' (performance warning) +# pragma warning(disable: 4996) // warning C4996: The POSIX name for this item is deprecated. Instead, use the ISO C and C++ conformant name +# pragma warning(disable: 4702) // warning C4702: unreachable code +# pragma warning(disable: 4522) // warning C4522: multiple assignment operators specified +#elif defined(__GNUG__) && !defined(__clang__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wunused-but-set-parameter" +# pragma GCC diagnostic ignored "-Wunused-but-set-variable" +# pragma GCC diagnostic ignored "-Wmissing-field-initializers" +# pragma GCC diagnostic ignored "-Wstrict-aliasing" +# pragma GCC diagnostic ignored "-Wattributes" +# if __GNUC__ >= 7 +# pragma GCC diagnostic ignored "-Wnoexcept-type" +# endif +#endif + +#include "attr.h" +#include "options.h" +#include "detail/class.h" +#include "detail/init.h" + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) + +/// Wraps an arbitrary C++ function/method/lambda function/.. into a callable Python object +class cpp_function : public function { +public: + cpp_function() { } + cpp_function(std::nullptr_t) { } + + /// Construct a cpp_function from a vanilla function pointer + template + cpp_function(Return (*f)(Args...), const Extra&... extra) { + initialize(f, f, extra...); + } + + /// Construct a cpp_function from a lambda function (possibly with internal state) + template ::value>> + cpp_function(Func &&f, const Extra&... extra) { + initialize(std::forward(f), + (detail::function_signature_t *) nullptr, extra...); + } + + /// Construct a cpp_function from a class method (non-const) + template + cpp_function(Return (Class::*f)(Arg...), const Extra&... extra) { + initialize([f](Class *c, Arg... args) -> Return { return (c->*f)(args...); }, + (Return (*) (Class *, Arg...)) nullptr, extra...); + } + + /// Construct a cpp_function from a class method (const) + template + cpp_function(Return (Class::*f)(Arg...) const, const Extra&... extra) { + initialize([f](const Class *c, Arg... args) -> Return { return (c->*f)(args...); }, + (Return (*)(const Class *, Arg ...)) nullptr, extra...); + } + + /// Return the function name + object name() const { return attr("__name__"); } + +protected: + /// Space optimization: don't inline this frequently instantiated fragment + PYBIND11_NOINLINE detail::function_record *make_function_record() { + return new detail::function_record(); + } + + /// Special internal constructor for functors, lambda functions, etc. + template + void initialize(Func &&f, Return (*)(Args...), const Extra&... extra) { + using namespace detail; + struct capture { remove_reference_t f; }; + + /* Store the function including any extra state it might have (e.g. a lambda capture object) */ + auto rec = make_function_record(); + + /* Store the capture object directly in the function record if there is enough space */ + if (sizeof(capture) <= sizeof(rec->data)) { + /* Without these pragmas, GCC warns that there might not be + enough space to use the placement new operator. However, the + 'if' statement above ensures that this is the case. */ +#if defined(__GNUG__) && !defined(__clang__) && __GNUC__ >= 6 +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wplacement-new" +#endif + new ((capture *) &rec->data) capture { std::forward(f) }; +#if defined(__GNUG__) && !defined(__clang__) && __GNUC__ >= 6 +# pragma GCC diagnostic pop +#endif + if (!std::is_trivially_destructible::value) + rec->free_data = [](function_record *r) { ((capture *) &r->data)->~capture(); }; + } else { + rec->data[0] = new capture { std::forward(f) }; + rec->free_data = [](function_record *r) { delete ((capture *) r->data[0]); }; + } + + /* Type casters for the function arguments and return value */ + using cast_in = argument_loader; + using cast_out = make_caster< + conditional_t::value, void_type, Return> + >; + + static_assert(expected_num_args(sizeof...(Args), cast_in::has_args, cast_in::has_kwargs), + "The number of argument annotations does not match the number of function arguments"); + + /* Dispatch code which converts function arguments and performs the actual function call */ + rec->impl = [](function_call &call) -> handle { + cast_in args_converter; + + /* Try to cast the function arguments into the C++ domain */ + if (!args_converter.load_args(call)) + return PYBIND11_TRY_NEXT_OVERLOAD; + + /* Invoke call policy pre-call hook */ + process_attributes::precall(call); + + /* Get a pointer to the capture object */ + auto data = (sizeof(capture) <= sizeof(call.func.data) + ? &call.func.data : call.func.data[0]); + capture *cap = const_cast(reinterpret_cast(data)); + + /* Override policy for rvalues -- usually to enforce rvp::move on an rvalue */ + return_value_policy policy = return_value_policy_override::policy(call.func.policy); + + /* Function scope guard -- defaults to the compile-to-nothing `void_type` */ + using Guard = extract_guard_t; + + /* Perform the function call */ + handle result = cast_out::cast( + std::move(args_converter).template call(cap->f), policy, call.parent); + + /* Invoke call policy post-call hook */ + process_attributes::postcall(call, result); + + return result; + }; + + /* Process any user-provided function attributes */ + process_attributes::init(extra..., rec); + + /* Generate a readable signature describing the function's arguments and return value types */ + static constexpr auto signature = _("(") + cast_in::arg_names + _(") -> ") + cast_out::name; + PYBIND11_DESCR_CONSTEXPR auto types = decltype(signature)::types(); + + /* Register the function with Python from generic (non-templated) code */ + initialize_generic(rec, signature.text, types.data(), sizeof...(Args)); + + if (cast_in::has_args) rec->has_args = true; + if (cast_in::has_kwargs) rec->has_kwargs = true; + + /* Stash some additional information used by an important optimization in 'functional.h' */ + using FunctionType = Return (*)(Args...); + constexpr bool is_function_ptr = + std::is_convertible::value && + sizeof(capture) == sizeof(void *); + if (is_function_ptr) { + rec->is_stateless = true; + rec->data[1] = const_cast(reinterpret_cast(&typeid(FunctionType))); + } + } + + /// Register a function call with Python (generic non-templated code goes here) + void initialize_generic(detail::function_record *rec, const char *text, + const std::type_info *const *types, size_t args) { + + /* Create copies of all referenced C-style strings */ + rec->name = strdup(rec->name ? rec->name : ""); + if (rec->doc) rec->doc = strdup(rec->doc); + for (auto &a: rec->args) { + if (a.name) + a.name = strdup(a.name); + if (a.descr) + a.descr = strdup(a.descr); + else if (a.value) + a.descr = strdup(a.value.attr("__repr__")().cast().c_str()); + } + + rec->is_constructor = !strcmp(rec->name, "__init__") || !strcmp(rec->name, "__setstate__"); + +#if !defined(NDEBUG) && !defined(PYBIND11_DISABLE_NEW_STYLE_INIT_WARNING) + if (rec->is_constructor && !rec->is_new_style_constructor) { + const auto class_name = std::string(((PyTypeObject *) rec->scope.ptr())->tp_name); + const auto func_name = std::string(rec->name); + PyErr_WarnEx( + PyExc_FutureWarning, + ("pybind11-bound class '" + class_name + "' is using an old-style " + "placement-new '" + func_name + "' which has been deprecated. See " + "the upgrade guide in pybind11's docs. This message is only visible " + "when compiled in debug mode.").c_str(), 0 + ); + } +#endif + + /* Generate a proper function signature */ + std::string signature; + size_t type_index = 0, arg_index = 0; + for (auto *pc = text; *pc != '\0'; ++pc) { + const auto c = *pc; + + if (c == '{') { + // Write arg name for everything except *args and **kwargs. + if (*(pc + 1) == '*') + continue; + + if (arg_index < rec->args.size() && rec->args[arg_index].name) { + signature += rec->args[arg_index].name; + } else if (arg_index == 0 && rec->is_method) { + signature += "self"; + } else { + signature += "arg" + std::to_string(arg_index - (rec->is_method ? 1 : 0)); + } + signature += ": "; + } else if (c == '}') { + // Write default value if available. + if (arg_index < rec->args.size() && rec->args[arg_index].descr) { + signature += " = "; + signature += rec->args[arg_index].descr; + } + arg_index++; + } else if (c == '%') { + const std::type_info *t = types[type_index++]; + if (!t) + pybind11_fail("Internal error while parsing type signature (1)"); + if (auto tinfo = detail::get_type_info(*t)) { + handle th((PyObject *) tinfo->type); + signature += + th.attr("__module__").cast() + "." + + th.attr("__qualname__").cast(); // Python 3.3+, but we backport it to earlier versions + } else if (rec->is_new_style_constructor && arg_index == 0) { + // A new-style `__init__` takes `self` as `value_and_holder`. + // Rewrite it to the proper class type. + signature += + rec->scope.attr("__module__").cast() + "." + + rec->scope.attr("__qualname__").cast(); + } else { + std::string tname(t->name()); + detail::clean_type_id(tname); + signature += tname; + } + } else { + signature += c; + } + } + if (arg_index != args || types[type_index] != nullptr) + pybind11_fail("Internal error while parsing type signature (2)"); + +#if PY_MAJOR_VERSION < 3 + if (strcmp(rec->name, "__next__") == 0) { + std::free(rec->name); + rec->name = strdup("next"); + } else if (strcmp(rec->name, "__bool__") == 0) { + std::free(rec->name); + rec->name = strdup("__nonzero__"); + } +#endif + rec->signature = strdup(signature.c_str()); + rec->args.shrink_to_fit(); + rec->nargs = (std::uint16_t) args; + + if (rec->sibling && PYBIND11_INSTANCE_METHOD_CHECK(rec->sibling.ptr())) + rec->sibling = PYBIND11_INSTANCE_METHOD_GET_FUNCTION(rec->sibling.ptr()); + + detail::function_record *chain = nullptr, *chain_start = rec; + if (rec->sibling) { + if (PyCFunction_Check(rec->sibling.ptr())) { + auto rec_capsule = reinterpret_borrow(PyCFunction_GET_SELF(rec->sibling.ptr())); + chain = (detail::function_record *) rec_capsule; + /* Never append a method to an overload chain of a parent class; + instead, hide the parent's overloads in this case */ + if (!chain->scope.is(rec->scope)) + chain = nullptr; + } + // Don't trigger for things like the default __init__, which are wrapper_descriptors that we are intentionally replacing + else if (!rec->sibling.is_none() && rec->name[0] != '_') + pybind11_fail("Cannot overload existing non-function object \"" + std::string(rec->name) + + "\" with a function of the same name"); + } + + if (!chain) { + /* No existing overload was found, create a new function object */ + rec->def = new PyMethodDef(); + std::memset(rec->def, 0, sizeof(PyMethodDef)); + rec->def->ml_name = rec->name; + rec->def->ml_meth = reinterpret_cast(reinterpret_cast(*dispatcher)); + rec->def->ml_flags = METH_VARARGS | METH_KEYWORDS; + + capsule rec_capsule(rec, [](void *ptr) { + destruct((detail::function_record *) ptr); + }); + + object scope_module; + if (rec->scope) { + if (hasattr(rec->scope, "__module__")) { + scope_module = rec->scope.attr("__module__"); + } else if (hasattr(rec->scope, "__name__")) { + scope_module = rec->scope.attr("__name__"); + } + } + + m_ptr = PyCFunction_NewEx(rec->def, rec_capsule.ptr(), scope_module.ptr()); + if (!m_ptr) + pybind11_fail("cpp_function::cpp_function(): Could not allocate function object"); + } else { + /* Append at the end of the overload chain */ + m_ptr = rec->sibling.ptr(); + inc_ref(); + chain_start = chain; + if (chain->is_method != rec->is_method) + pybind11_fail("overloading a method with both static and instance methods is not supported; " + #if defined(NDEBUG) + "compile in debug mode for more details" + #else + "error while attempting to bind " + std::string(rec->is_method ? "instance" : "static") + " method " + + std::string(pybind11::str(rec->scope.attr("__name__"))) + "." + std::string(rec->name) + signature + #endif + ); + while (chain->next) + chain = chain->next; + chain->next = rec; + } + + std::string signatures; + int index = 0; + /* Create a nice pydoc rec including all signatures and + docstrings of the functions in the overload chain */ + if (chain && options::show_function_signatures()) { + // First a generic signature + signatures += rec->name; + signatures += "(*args, **kwargs)\n"; + signatures += "Overloaded function.\n\n"; + } + // Then specific overload signatures + bool first_user_def = true; + for (auto it = chain_start; it != nullptr; it = it->next) { + if (options::show_function_signatures()) { + if (index > 0) signatures += "\n"; + if (chain) + signatures += std::to_string(++index) + ". "; + signatures += rec->name; + signatures += it->signature; + signatures += "\n"; + } + if (it->doc && strlen(it->doc) > 0 && options::show_user_defined_docstrings()) { + // If we're appending another docstring, and aren't printing function signatures, we + // need to append a newline first: + if (!options::show_function_signatures()) { + if (first_user_def) first_user_def = false; + else signatures += "\n"; + } + if (options::show_function_signatures()) signatures += "\n"; + signatures += it->doc; + if (options::show_function_signatures()) signatures += "\n"; + } + } + + /* Install docstring */ + PyCFunctionObject *func = (PyCFunctionObject *) m_ptr; + if (func->m_ml->ml_doc) + std::free(const_cast(func->m_ml->ml_doc)); + func->m_ml->ml_doc = strdup(signatures.c_str()); + + if (rec->is_method) { + m_ptr = PYBIND11_INSTANCE_METHOD_NEW(m_ptr, rec->scope.ptr()); + if (!m_ptr) + pybind11_fail("cpp_function::cpp_function(): Could not allocate instance method object"); + Py_DECREF(func); + } + } + + /// When a cpp_function is GCed, release any memory allocated by pybind11 + static void destruct(detail::function_record *rec) { + while (rec) { + detail::function_record *next = rec->next; + if (rec->free_data) + rec->free_data(rec); + std::free((char *) rec->name); + std::free((char *) rec->doc); + std::free((char *) rec->signature); + for (auto &arg: rec->args) { + std::free(const_cast(arg.name)); + std::free(const_cast(arg.descr)); + arg.value.dec_ref(); + } + if (rec->def) { + std::free(const_cast(rec->def->ml_doc)); + delete rec->def; + } + delete rec; + rec = next; + } + } + + /// Main dispatch logic for calls to functions bound using pybind11 + static PyObject *dispatcher(PyObject *self, PyObject *args_in, PyObject *kwargs_in) { + using namespace detail; + + /* Iterator over the list of potentially admissible overloads */ + const function_record *overloads = (function_record *) PyCapsule_GetPointer(self, nullptr), + *it = overloads; + + /* Need to know how many arguments + keyword arguments there are to pick the right overload */ + const size_t n_args_in = (size_t) PyTuple_GET_SIZE(args_in); + + handle parent = n_args_in > 0 ? PyTuple_GET_ITEM(args_in, 0) : nullptr, + result = PYBIND11_TRY_NEXT_OVERLOAD; + + auto self_value_and_holder = value_and_holder(); + if (overloads->is_constructor) { + const auto tinfo = get_type_info((PyTypeObject *) overloads->scope.ptr()); + const auto pi = reinterpret_cast(parent.ptr()); + self_value_and_holder = pi->get_value_and_holder(tinfo, false); + + if (!self_value_and_holder.type || !self_value_and_holder.inst) { + PyErr_SetString(PyExc_TypeError, "__init__(self, ...) called with invalid `self` argument"); + return nullptr; + } + + // If this value is already registered it must mean __init__ is invoked multiple times; + // we really can't support that in C++, so just ignore the second __init__. + if (self_value_and_holder.instance_registered()) + return none().release().ptr(); + } + + try { + // We do this in two passes: in the first pass, we load arguments with `convert=false`; + // in the second, we allow conversion (except for arguments with an explicit + // py::arg().noconvert()). This lets us prefer calls without conversion, with + // conversion as a fallback. + std::vector second_pass; + + // However, if there are no overloads, we can just skip the no-convert pass entirely + const bool overloaded = it != nullptr && it->next != nullptr; + + for (; it != nullptr; it = it->next) { + + /* For each overload: + 1. Copy all positional arguments we were given, also checking to make sure that + named positional arguments weren't *also* specified via kwarg. + 2. If we weren't given enough, try to make up the omitted ones by checking + whether they were provided by a kwarg matching the `py::arg("name")` name. If + so, use it (and remove it from kwargs; if not, see if the function binding + provided a default that we can use. + 3. Ensure that either all keyword arguments were "consumed", or that the function + takes a kwargs argument to accept unconsumed kwargs. + 4. Any positional arguments still left get put into a tuple (for args), and any + leftover kwargs get put into a dict. + 5. Pack everything into a vector; if we have py::args or py::kwargs, they are an + extra tuple or dict at the end of the positional arguments. + 6. Call the function call dispatcher (function_record::impl) + + If one of these fail, move on to the next overload and keep trying until we get a + result other than PYBIND11_TRY_NEXT_OVERLOAD. + */ + + const function_record &func = *it; + size_t pos_args = func.nargs; // Number of positional arguments that we need + if (func.has_args) --pos_args; // (but don't count py::args + if (func.has_kwargs) --pos_args; // or py::kwargs) + + if (!func.has_args && n_args_in > pos_args) + continue; // Too many arguments for this overload + + if (n_args_in < pos_args && func.args.size() < pos_args) + continue; // Not enough arguments given, and not enough defaults to fill in the blanks + + function_call call(func, parent); + + size_t args_to_copy = std::min(pos_args, n_args_in); + size_t args_copied = 0; + + // 0. Inject new-style `self` argument + if (func.is_new_style_constructor) { + // The `value` may have been preallocated by an old-style `__init__` + // if it was a preceding candidate for overload resolution. + if (self_value_and_holder) + self_value_and_holder.type->dealloc(self_value_and_holder); + + call.init_self = PyTuple_GET_ITEM(args_in, 0); + call.args.push_back(reinterpret_cast(&self_value_and_holder)); + call.args_convert.push_back(false); + ++args_copied; + } + + // 1. Copy any position arguments given. + bool bad_arg = false; + for (; args_copied < args_to_copy; ++args_copied) { + const argument_record *arg_rec = args_copied < func.args.size() ? &func.args[args_copied] : nullptr; + if (kwargs_in && arg_rec && arg_rec->name && PyDict_GetItemString(kwargs_in, arg_rec->name)) { + bad_arg = true; + break; + } + + handle arg(PyTuple_GET_ITEM(args_in, args_copied)); + if (arg_rec && !arg_rec->none && arg.is_none()) { + bad_arg = true; + break; + } + call.args.push_back(arg); + call.args_convert.push_back(arg_rec ? arg_rec->convert : true); + } + if (bad_arg) + continue; // Maybe it was meant for another overload (issue #688) + + // We'll need to copy this if we steal some kwargs for defaults + dict kwargs = reinterpret_borrow(kwargs_in); + + // 2. Check kwargs and, failing that, defaults that may help complete the list + if (args_copied < pos_args) { + bool copied_kwargs = false; + + for (; args_copied < pos_args; ++args_copied) { + const auto &arg = func.args[args_copied]; + + handle value; + if (kwargs_in && arg.name) + value = PyDict_GetItemString(kwargs.ptr(), arg.name); + + if (value) { + // Consume a kwargs value + if (!copied_kwargs) { + kwargs = reinterpret_steal(PyDict_Copy(kwargs.ptr())); + copied_kwargs = true; + } + PyDict_DelItemString(kwargs.ptr(), arg.name); + } else if (arg.value) { + value = arg.value; + } + + if (value) { + call.args.push_back(value); + call.args_convert.push_back(arg.convert); + } + else + break; + } + + if (args_copied < pos_args) + continue; // Not enough arguments, defaults, or kwargs to fill the positional arguments + } + + // 3. Check everything was consumed (unless we have a kwargs arg) + if (kwargs && kwargs.size() > 0 && !func.has_kwargs) + continue; // Unconsumed kwargs, but no py::kwargs argument to accept them + + // 4a. If we have a py::args argument, create a new tuple with leftovers + if (func.has_args) { + tuple extra_args; + if (args_to_copy == 0) { + // We didn't copy out any position arguments from the args_in tuple, so we + // can reuse it directly without copying: + extra_args = reinterpret_borrow(args_in); + } else if (args_copied >= n_args_in) { + extra_args = tuple(0); + } else { + size_t args_size = n_args_in - args_copied; + extra_args = tuple(args_size); + for (size_t i = 0; i < args_size; ++i) { + extra_args[i] = PyTuple_GET_ITEM(args_in, args_copied + i); + } + } + call.args.push_back(extra_args); + call.args_convert.push_back(false); + call.args_ref = std::move(extra_args); + } + + // 4b. If we have a py::kwargs, pass on any remaining kwargs + if (func.has_kwargs) { + if (!kwargs.ptr()) + kwargs = dict(); // If we didn't get one, send an empty one + call.args.push_back(kwargs); + call.args_convert.push_back(false); + call.kwargs_ref = std::move(kwargs); + } + + // 5. Put everything in a vector. Not technically step 5, we've been building it + // in `call.args` all along. + #if !defined(NDEBUG) + if (call.args.size() != func.nargs || call.args_convert.size() != func.nargs) + pybind11_fail("Internal error: function call dispatcher inserted wrong number of arguments!"); + #endif + + std::vector second_pass_convert; + if (overloaded) { + // We're in the first no-convert pass, so swap out the conversion flags for a + // set of all-false flags. If the call fails, we'll swap the flags back in for + // the conversion-allowed call below. + second_pass_convert.resize(func.nargs, false); + call.args_convert.swap(second_pass_convert); + } + + // 6. Call the function. + try { + loader_life_support guard{}; + result = func.impl(call); + } catch (reference_cast_error &) { + result = PYBIND11_TRY_NEXT_OVERLOAD; + } + + if (result.ptr() != PYBIND11_TRY_NEXT_OVERLOAD) + break; + + if (overloaded) { + // The (overloaded) call failed; if the call has at least one argument that + // permits conversion (i.e. it hasn't been explicitly specified `.noconvert()`) + // then add this call to the list of second pass overloads to try. + for (size_t i = func.is_method ? 1 : 0; i < pos_args; i++) { + if (second_pass_convert[i]) { + // Found one: swap the converting flags back in and store the call for + // the second pass. + call.args_convert.swap(second_pass_convert); + second_pass.push_back(std::move(call)); + break; + } + } + } + } + + if (overloaded && !second_pass.empty() && result.ptr() == PYBIND11_TRY_NEXT_OVERLOAD) { + // The no-conversion pass finished without success, try again with conversion allowed + for (auto &call : second_pass) { + try { + loader_life_support guard{}; + result = call.func.impl(call); + } catch (reference_cast_error &) { + result = PYBIND11_TRY_NEXT_OVERLOAD; + } + + if (result.ptr() != PYBIND11_TRY_NEXT_OVERLOAD) { + // The error reporting logic below expects 'it' to be valid, as it would be + // if we'd encountered this failure in the first-pass loop. + if (!result) + it = &call.func; + break; + } + } + } + } catch (error_already_set &e) { + e.restore(); + return nullptr; + } catch (...) { + /* When an exception is caught, give each registered exception + translator a chance to translate it to a Python exception + in reverse order of registration. + + A translator may choose to do one of the following: + + - catch the exception and call PyErr_SetString or PyErr_SetObject + to set a standard (or custom) Python exception, or + - do nothing and let the exception fall through to the next translator, or + - delegate translation to the next translator by throwing a new type of exception. */ + + auto last_exception = std::current_exception(); + auto ®istered_exception_translators = get_internals().registered_exception_translators; + for (auto& translator : registered_exception_translators) { + try { + translator(last_exception); + } catch (...) { + last_exception = std::current_exception(); + continue; + } + return nullptr; + } + PyErr_SetString(PyExc_SystemError, "Exception escaped from default exception translator!"); + return nullptr; + } + + auto append_note_if_missing_header_is_suspected = [](std::string &msg) { + if (msg.find("std::") != std::string::npos) { + msg += "\n\n" + "Did you forget to `#include `? Or ,\n" + ", , etc. Some automatic\n" + "conversions are optional and require extra headers to be included\n" + "when compiling your pybind11 module."; + } + }; + + if (result.ptr() == PYBIND11_TRY_NEXT_OVERLOAD) { + if (overloads->is_operator) + return handle(Py_NotImplemented).inc_ref().ptr(); + + std::string msg = std::string(overloads->name) + "(): incompatible " + + std::string(overloads->is_constructor ? "constructor" : "function") + + " arguments. The following argument types are supported:\n"; + + int ctr = 0; + for (const function_record *it2 = overloads; it2 != nullptr; it2 = it2->next) { + msg += " "+ std::to_string(++ctr) + ". "; + + bool wrote_sig = false; + if (overloads->is_constructor) { + // For a constructor, rewrite `(self: Object, arg0, ...) -> NoneType` as `Object(arg0, ...)` + std::string sig = it2->signature; + size_t start = sig.find('(') + 7; // skip "(self: " + if (start < sig.size()) { + // End at the , for the next argument + size_t end = sig.find(", "), next = end + 2; + size_t ret = sig.rfind(" -> "); + // Or the ), if there is no comma: + if (end >= sig.size()) next = end = sig.find(')'); + if (start < end && next < sig.size()) { + msg.append(sig, start, end - start); + msg += '('; + msg.append(sig, next, ret - next); + wrote_sig = true; + } + } + } + if (!wrote_sig) msg += it2->signature; + + msg += "\n"; + } + msg += "\nInvoked with: "; + auto args_ = reinterpret_borrow(args_in); + bool some_args = false; + for (size_t ti = overloads->is_constructor ? 1 : 0; ti < args_.size(); ++ti) { + if (!some_args) some_args = true; + else msg += ", "; + msg += pybind11::repr(args_[ti]); + } + if (kwargs_in) { + auto kwargs = reinterpret_borrow(kwargs_in); + if (kwargs.size() > 0) { + if (some_args) msg += "; "; + msg += "kwargs: "; + bool first = true; + for (auto kwarg : kwargs) { + if (first) first = false; + else msg += ", "; + msg += pybind11::str("{}={!r}").format(kwarg.first, kwarg.second); + } + } + } + + append_note_if_missing_header_is_suspected(msg); + PyErr_SetString(PyExc_TypeError, msg.c_str()); + return nullptr; + } else if (!result) { + std::string msg = "Unable to convert function return value to a " + "Python type! The signature was\n\t"; + msg += it->signature; + append_note_if_missing_header_is_suspected(msg); + PyErr_SetString(PyExc_TypeError, msg.c_str()); + return nullptr; + } else { + if (overloads->is_constructor && !self_value_and_holder.holder_constructed()) { + auto *pi = reinterpret_cast(parent.ptr()); + self_value_and_holder.type->init_instance(pi, nullptr); + } + return result.ptr(); + } + } +}; + +/// Wrapper for Python extension modules +class module : public object { +public: + PYBIND11_OBJECT_DEFAULT(module, object, PyModule_Check) + + /// Create a new top-level Python module with the given name and docstring + explicit module(const char *name, const char *doc = nullptr) { + if (!options::show_user_defined_docstrings()) doc = nullptr; +#if PY_MAJOR_VERSION >= 3 + PyModuleDef *def = new PyModuleDef(); + std::memset(def, 0, sizeof(PyModuleDef)); + def->m_name = name; + def->m_doc = doc; + def->m_size = -1; + Py_INCREF(def); + m_ptr = PyModule_Create(def); +#else + m_ptr = Py_InitModule3(name, nullptr, doc); +#endif + if (m_ptr == nullptr) + pybind11_fail("Internal error in module::module()"); + inc_ref(); + } + + /** \rst + Create Python binding for a new function within the module scope. ``Func`` + can be a plain C++ function, a function pointer, or a lambda function. For + details on the ``Extra&& ... extra`` argument, see section :ref:`extras`. + \endrst */ + template + module &def(const char *name_, Func &&f, const Extra& ... extra) { + cpp_function func(std::forward(f), name(name_), scope(*this), + sibling(getattr(*this, name_, none())), extra...); + // NB: allow overwriting here because cpp_function sets up a chain with the intention of + // overwriting (and has already checked internally that it isn't overwriting non-functions). + add_object(name_, func, true /* overwrite */); + return *this; + } + + /** \rst + Create and return a new Python submodule with the given name and docstring. + This also works recursively, i.e. + + .. code-block:: cpp + + py::module m("example", "pybind11 example plugin"); + py::module m2 = m.def_submodule("sub", "A submodule of 'example'"); + py::module m3 = m2.def_submodule("subsub", "A submodule of 'example.sub'"); + \endrst */ + module def_submodule(const char *name, const char *doc = nullptr) { + std::string full_name = std::string(PyModule_GetName(m_ptr)) + + std::string(".") + std::string(name); + auto result = reinterpret_borrow(PyImport_AddModule(full_name.c_str())); + if (doc && options::show_user_defined_docstrings()) + result.attr("__doc__") = pybind11::str(doc); + attr(name) = result; + return result; + } + + /// Import and return a module or throws `error_already_set`. + static module import(const char *name) { + PyObject *obj = PyImport_ImportModule(name); + if (!obj) + throw error_already_set(); + return reinterpret_steal(obj); + } + + /// Reload the module or throws `error_already_set`. + void reload() { + PyObject *obj = PyImport_ReloadModule(ptr()); + if (!obj) + throw error_already_set(); + *this = reinterpret_steal(obj); + } + + // Adds an object to the module using the given name. Throws if an object with the given name + // already exists. + // + // overwrite should almost always be false: attempting to overwrite objects that pybind11 has + // established will, in most cases, break things. + PYBIND11_NOINLINE void add_object(const char *name, handle obj, bool overwrite = false) { + if (!overwrite && hasattr(*this, name)) + pybind11_fail("Error during initialization: multiple incompatible definitions with name \"" + + std::string(name) + "\""); + + PyModule_AddObject(ptr(), name, obj.inc_ref().ptr() /* steals a reference */); + } +}; + +/// \ingroup python_builtins +/// Return a dictionary representing the global variables in the current execution frame, +/// or ``__main__.__dict__`` if there is no frame (usually when the interpreter is embedded). +inline dict globals() { + PyObject *p = PyEval_GetGlobals(); + return reinterpret_borrow(p ? p : module::import("__main__").attr("__dict__").ptr()); +} + +NAMESPACE_BEGIN(detail) +/// Generic support for creating new Python heap types +class generic_type : public object { + template friend class class_; +public: + PYBIND11_OBJECT_DEFAULT(generic_type, object, PyType_Check) +protected: + void initialize(const type_record &rec) { + if (rec.scope && hasattr(rec.scope, rec.name)) + pybind11_fail("generic_type: cannot initialize type \"" + std::string(rec.name) + + "\": an object with that name is already defined"); + + if (rec.module_local ? get_local_type_info(*rec.type) : get_global_type_info(*rec.type)) + pybind11_fail("generic_type: type \"" + std::string(rec.name) + + "\" is already registered!"); + + m_ptr = make_new_python_type(rec); + + /* Register supplemental type information in C++ dict */ + auto *tinfo = new detail::type_info(); + tinfo->type = (PyTypeObject *) m_ptr; + tinfo->cpptype = rec.type; + tinfo->type_size = rec.type_size; + tinfo->type_align = rec.type_align; + tinfo->operator_new = rec.operator_new; + tinfo->holder_size_in_ptrs = size_in_ptrs(rec.holder_size); + tinfo->init_instance = rec.init_instance; + tinfo->dealloc = rec.dealloc; + tinfo->simple_type = true; + tinfo->simple_ancestors = true; + tinfo->default_holder = rec.default_holder; + tinfo->module_local = rec.module_local; + + auto &internals = get_internals(); + auto tindex = std::type_index(*rec.type); + tinfo->direct_conversions = &internals.direct_conversions[tindex]; + if (rec.module_local) + registered_local_types_cpp()[tindex] = tinfo; + else + internals.registered_types_cpp[tindex] = tinfo; + internals.registered_types_py[(PyTypeObject *) m_ptr] = { tinfo }; + + if (rec.bases.size() > 1 || rec.multiple_inheritance) { + mark_parents_nonsimple(tinfo->type); + tinfo->simple_ancestors = false; + } + else if (rec.bases.size() == 1) { + auto parent_tinfo = get_type_info((PyTypeObject *) rec.bases[0].ptr()); + tinfo->simple_ancestors = parent_tinfo->simple_ancestors; + } + + if (rec.module_local) { + // Stash the local typeinfo and loader so that external modules can access it. + tinfo->module_local_load = &type_caster_generic::local_load; + setattr(m_ptr, PYBIND11_MODULE_LOCAL_ID, capsule(tinfo)); + } + } + + /// Helper function which tags all parents of a type using mult. inheritance + void mark_parents_nonsimple(PyTypeObject *value) { + auto t = reinterpret_borrow(value->tp_bases); + for (handle h : t) { + auto tinfo2 = get_type_info((PyTypeObject *) h.ptr()); + if (tinfo2) + tinfo2->simple_type = false; + mark_parents_nonsimple((PyTypeObject *) h.ptr()); + } + } + + void install_buffer_funcs( + buffer_info *(*get_buffer)(PyObject *, void *), + void *get_buffer_data) { + PyHeapTypeObject *type = (PyHeapTypeObject*) m_ptr; + auto tinfo = detail::get_type_info(&type->ht_type); + + if (!type->ht_type.tp_as_buffer) + pybind11_fail( + "To be able to register buffer protocol support for the type '" + + std::string(tinfo->type->tp_name) + + "' the associated class<>(..) invocation must " + "include the pybind11::buffer_protocol() annotation!"); + + tinfo->get_buffer = get_buffer; + tinfo->get_buffer_data = get_buffer_data; + } + + // rec_func must be set for either fget or fset. + void def_property_static_impl(const char *name, + handle fget, handle fset, + detail::function_record *rec_func) { + const auto is_static = rec_func && !(rec_func->is_method && rec_func->scope); + const auto has_doc = rec_func && rec_func->doc && pybind11::options::show_user_defined_docstrings(); + auto property = handle((PyObject *) (is_static ? get_internals().static_property_type + : &PyProperty_Type)); + attr(name) = property(fget.ptr() ? fget : none(), + fset.ptr() ? fset : none(), + /*deleter*/none(), + pybind11::str(has_doc ? rec_func->doc : "")); + } +}; + +/// Set the pointer to operator new if it exists. The cast is needed because it can be overloaded. +template (T::operator new))>> +void set_operator_new(type_record *r) { r->operator_new = &T::operator new; } + +template void set_operator_new(...) { } + +template struct has_operator_delete : std::false_type { }; +template struct has_operator_delete(T::operator delete))>> + : std::true_type { }; +template struct has_operator_delete_size : std::false_type { }; +template struct has_operator_delete_size(T::operator delete))>> + : std::true_type { }; +/// Call class-specific delete if it exists or global otherwise. Can also be an overload set. +template ::value, int> = 0> +void call_operator_delete(T *p, size_t, size_t) { T::operator delete(p); } +template ::value && has_operator_delete_size::value, int> = 0> +void call_operator_delete(T *p, size_t s, size_t) { T::operator delete(p, s); } + +inline void call_operator_delete(void *p, size_t s, size_t a) { + (void)s; (void)a; +#if defined(PYBIND11_CPP17) + if (a > __STDCPP_DEFAULT_NEW_ALIGNMENT__) + ::operator delete(p, s, std::align_val_t(a)); + else + ::operator delete(p, s); +#else + ::operator delete(p); +#endif +} + +NAMESPACE_END(detail) + +/// Given a pointer to a member function, cast it to its `Derived` version. +/// Forward everything else unchanged. +template +auto method_adaptor(F &&f) -> decltype(std::forward(f)) { return std::forward(f); } + +template +auto method_adaptor(Return (Class::*pmf)(Args...)) -> Return (Derived::*)(Args...) { + static_assert(detail::is_accessible_base_of::value, + "Cannot bind an inaccessible base class method; use a lambda definition instead"); + return pmf; +} + +template +auto method_adaptor(Return (Class::*pmf)(Args...) const) -> Return (Derived::*)(Args...) const { + static_assert(detail::is_accessible_base_of::value, + "Cannot bind an inaccessible base class method; use a lambda definition instead"); + return pmf; +} + +template +class class_ : public detail::generic_type { + template using is_holder = detail::is_holder_type; + template using is_subtype = detail::is_strict_base_of; + template using is_base = detail::is_strict_base_of; + // struct instead of using here to help MSVC: + template struct is_valid_class_option : + detail::any_of, is_subtype, is_base> {}; + +public: + using type = type_; + using type_alias = detail::exactly_one_t; + constexpr static bool has_alias = !std::is_void::value; + using holder_type = detail::exactly_one_t, options...>; + + static_assert(detail::all_of...>::value, + "Unknown/invalid class_ template parameters provided"); + + static_assert(!has_alias || std::is_polymorphic::value, + "Cannot use an alias class with a non-polymorphic type"); + + PYBIND11_OBJECT(class_, generic_type, PyType_Check) + + template + class_(handle scope, const char *name, const Extra &... extra) { + using namespace detail; + + // MI can only be specified via class_ template options, not constructor parameters + static_assert( + none_of...>::value || // no base class arguments, or: + ( constexpr_sum(is_pyobject::value...) == 1 && // Exactly one base + constexpr_sum(is_base::value...) == 0 && // no template option bases + none_of...>::value), // no multiple_inheritance attr + "Error: multiple inheritance bases must be specified via class_ template options"); + + type_record record; + record.scope = scope; + record.name = name; + record.type = &typeid(type); + record.type_size = sizeof(conditional_t); + record.type_align = alignof(conditional_t&); + record.holder_size = sizeof(holder_type); + record.init_instance = init_instance; + record.dealloc = dealloc; + record.default_holder = detail::is_instantiation::value; + + set_operator_new(&record); + + /* Register base classes specified via template arguments to class_, if any */ + PYBIND11_EXPAND_SIDE_EFFECTS(add_base(record)); + + /* Process optional arguments, if any */ + process_attributes::init(extra..., &record); + + generic_type::initialize(record); + + if (has_alias) { + auto &instances = record.module_local ? registered_local_types_cpp() : get_internals().registered_types_cpp; + instances[std::type_index(typeid(type_alias))] = instances[std::type_index(typeid(type))]; + } + } + + template ::value, int> = 0> + static void add_base(detail::type_record &rec) { + rec.add_base(typeid(Base), [](void *src) -> void * { + return static_cast(reinterpret_cast(src)); + }); + } + + template ::value, int> = 0> + static void add_base(detail::type_record &) { } + + template + class_ &def(const char *name_, Func&& f, const Extra&... extra) { + cpp_function cf(method_adaptor(std::forward(f)), name(name_), is_method(*this), + sibling(getattr(*this, name_, none())), extra...); + attr(cf.name()) = cf; + return *this; + } + + template class_ & + def_static(const char *name_, Func &&f, const Extra&... extra) { + static_assert(!std::is_member_function_pointer::value, + "def_static(...) called with a non-static member function pointer"); + cpp_function cf(std::forward(f), name(name_), scope(*this), + sibling(getattr(*this, name_, none())), extra...); + attr(cf.name()) = cf; + return *this; + } + + template + class_ &def(const detail::op_ &op, const Extra&... extra) { + op.execute(*this, extra...); + return *this; + } + + template + class_ & def_cast(const detail::op_ &op, const Extra&... extra) { + op.execute_cast(*this, extra...); + return *this; + } + + template + class_ &def(const detail::initimpl::constructor &init, const Extra&... extra) { + init.execute(*this, extra...); + return *this; + } + + template + class_ &def(const detail::initimpl::alias_constructor &init, const Extra&... extra) { + init.execute(*this, extra...); + return *this; + } + + template + class_ &def(detail::initimpl::factory &&init, const Extra&... extra) { + std::move(init).execute(*this, extra...); + return *this; + } + + template + class_ &def(detail::initimpl::pickle_factory &&pf, const Extra &...extra) { + std::move(pf).execute(*this, extra...); + return *this; + } + + template class_& def_buffer(Func &&func) { + struct capture { Func func; }; + capture *ptr = new capture { std::forward(func) }; + install_buffer_funcs([](PyObject *obj, void *ptr) -> buffer_info* { + detail::make_caster caster; + if (!caster.load(obj, false)) + return nullptr; + return new buffer_info(((capture *) ptr)->func(caster)); + }, ptr); + return *this; + } + + template + class_ &def_buffer(Return (Class::*func)(Args...)) { + return def_buffer([func] (type &obj) { return (obj.*func)(); }); + } + + template + class_ &def_buffer(Return (Class::*func)(Args...) const) { + return def_buffer([func] (const type &obj) { return (obj.*func)(); }); + } + + template + class_ &def_readwrite(const char *name, D C::*pm, const Extra&... extra) { + static_assert(std::is_base_of::value, "def_readwrite() requires a class member (or base class member)"); + cpp_function fget([pm](const type &c) -> const D &{ return c.*pm; }, is_method(*this)), + fset([pm](type &c, const D &value) { c.*pm = value; }, is_method(*this)); + def_property(name, fget, fset, return_value_policy::reference_internal, extra...); + return *this; + } + + template + class_ &def_readonly(const char *name, const D C::*pm, const Extra& ...extra) { + static_assert(std::is_base_of::value, "def_readonly() requires a class member (or base class member)"); + cpp_function fget([pm](const type &c) -> const D &{ return c.*pm; }, is_method(*this)); + def_property_readonly(name, fget, return_value_policy::reference_internal, extra...); + return *this; + } + + template + class_ &def_readwrite_static(const char *name, D *pm, const Extra& ...extra) { + cpp_function fget([pm](object) -> const D &{ return *pm; }, scope(*this)), + fset([pm](object, const D &value) { *pm = value; }, scope(*this)); + def_property_static(name, fget, fset, return_value_policy::reference, extra...); + return *this; + } + + template + class_ &def_readonly_static(const char *name, const D *pm, const Extra& ...extra) { + cpp_function fget([pm](object) -> const D &{ return *pm; }, scope(*this)); + def_property_readonly_static(name, fget, return_value_policy::reference, extra...); + return *this; + } + + /// Uses return_value_policy::reference_internal by default + template + class_ &def_property_readonly(const char *name, const Getter &fget, const Extra& ...extra) { + return def_property_readonly(name, cpp_function(method_adaptor(fget)), + return_value_policy::reference_internal, extra...); + } + + /// Uses cpp_function's return_value_policy by default + template + class_ &def_property_readonly(const char *name, const cpp_function &fget, const Extra& ...extra) { + return def_property(name, fget, nullptr, extra...); + } + + /// Uses return_value_policy::reference by default + template + class_ &def_property_readonly_static(const char *name, const Getter &fget, const Extra& ...extra) { + return def_property_readonly_static(name, cpp_function(fget), return_value_policy::reference, extra...); + } + + /// Uses cpp_function's return_value_policy by default + template + class_ &def_property_readonly_static(const char *name, const cpp_function &fget, const Extra& ...extra) { + return def_property_static(name, fget, nullptr, extra...); + } + + /// Uses return_value_policy::reference_internal by default + template + class_ &def_property(const char *name, const Getter &fget, const Setter &fset, const Extra& ...extra) { + return def_property(name, fget, cpp_function(method_adaptor(fset)), extra...); + } + template + class_ &def_property(const char *name, const Getter &fget, const cpp_function &fset, const Extra& ...extra) { + return def_property(name, cpp_function(method_adaptor(fget)), fset, + return_value_policy::reference_internal, extra...); + } + + /// Uses cpp_function's return_value_policy by default + template + class_ &def_property(const char *name, const cpp_function &fget, const cpp_function &fset, const Extra& ...extra) { + return def_property_static(name, fget, fset, is_method(*this), extra...); + } + + /// Uses return_value_policy::reference by default + template + class_ &def_property_static(const char *name, const Getter &fget, const cpp_function &fset, const Extra& ...extra) { + return def_property_static(name, cpp_function(fget), fset, return_value_policy::reference, extra...); + } + + /// Uses cpp_function's return_value_policy by default + template + class_ &def_property_static(const char *name, const cpp_function &fget, const cpp_function &fset, const Extra& ...extra) { + auto rec_fget = get_function_record(fget), rec_fset = get_function_record(fset); + auto *rec_active = rec_fget; + if (rec_fget) { + char *doc_prev = rec_fget->doc; /* 'extra' field may include a property-specific documentation string */ + detail::process_attributes::init(extra..., rec_fget); + if (rec_fget->doc && rec_fget->doc != doc_prev) { + free(doc_prev); + rec_fget->doc = strdup(rec_fget->doc); + } + } + if (rec_fset) { + char *doc_prev = rec_fset->doc; + detail::process_attributes::init(extra..., rec_fset); + if (rec_fset->doc && rec_fset->doc != doc_prev) { + free(doc_prev); + rec_fset->doc = strdup(rec_fset->doc); + } + if (! rec_active) rec_active = rec_fset; + } + def_property_static_impl(name, fget, fset, rec_active); + return *this; + } + +private: + /// Initialize holder object, variant 1: object derives from enable_shared_from_this + template + static void init_holder(detail::instance *inst, detail::value_and_holder &v_h, + const holder_type * /* unused */, const std::enable_shared_from_this * /* dummy */) { + try { + auto sh = std::dynamic_pointer_cast( + v_h.value_ptr()->shared_from_this()); + if (sh) { + new (std::addressof(v_h.holder())) holder_type(std::move(sh)); + v_h.set_holder_constructed(); + } + } catch (const std::bad_weak_ptr &) {} + + if (!v_h.holder_constructed() && inst->owned) { + new (std::addressof(v_h.holder())) holder_type(v_h.value_ptr()); + v_h.set_holder_constructed(); + } + } + + static void init_holder_from_existing(const detail::value_and_holder &v_h, + const holder_type *holder_ptr, std::true_type /*is_copy_constructible*/) { + new (std::addressof(v_h.holder())) holder_type(*reinterpret_cast(holder_ptr)); + } + + static void init_holder_from_existing(const detail::value_and_holder &v_h, + const holder_type *holder_ptr, std::false_type /*is_copy_constructible*/) { + new (std::addressof(v_h.holder())) holder_type(std::move(*const_cast(holder_ptr))); + } + + /// Initialize holder object, variant 2: try to construct from existing holder object, if possible + static void init_holder(detail::instance *inst, detail::value_and_holder &v_h, + const holder_type *holder_ptr, const void * /* dummy -- not enable_shared_from_this) */) { + if (holder_ptr) { + init_holder_from_existing(v_h, holder_ptr, std::is_copy_constructible()); + v_h.set_holder_constructed(); + } else if (inst->owned || detail::always_construct_holder::value) { + new (std::addressof(v_h.holder())) holder_type(v_h.value_ptr()); + v_h.set_holder_constructed(); + } + } + + /// Performs instance initialization including constructing a holder and registering the known + /// instance. Should be called as soon as the `type` value_ptr is set for an instance. Takes an + /// optional pointer to an existing holder to use; if not specified and the instance is + /// `.owned`, a new holder will be constructed to manage the value pointer. + static void init_instance(detail::instance *inst, const void *holder_ptr) { + auto v_h = inst->get_value_and_holder(detail::get_type_info(typeid(type))); + if (!v_h.instance_registered()) { + register_instance(inst, v_h.value_ptr(), v_h.type); + v_h.set_instance_registered(); + } + init_holder(inst, v_h, (const holder_type *) holder_ptr, v_h.value_ptr()); + } + + /// Deallocates an instance; via holder, if constructed; otherwise via operator delete. + static void dealloc(detail::value_and_holder &v_h) { + if (v_h.holder_constructed()) { + v_h.holder().~holder_type(); + v_h.set_holder_constructed(false); + } + else { + detail::call_operator_delete(v_h.value_ptr(), + v_h.type->type_size, + v_h.type->type_align + ); + } + v_h.value_ptr() = nullptr; + } + + static detail::function_record *get_function_record(handle h) { + h = detail::get_function(h); + return h ? (detail::function_record *) reinterpret_borrow(PyCFunction_GET_SELF(h.ptr())) + : nullptr; + } +}; + +/// Binds an existing constructor taking arguments Args... +template detail::initimpl::constructor init() { return {}; } +/// Like `init()`, but the instance is always constructed through the alias class (even +/// when not inheriting on the Python side). +template detail::initimpl::alias_constructor init_alias() { return {}; } + +/// Binds a factory function as a constructor +template > +Ret init(Func &&f) { return {std::forward(f)}; } + +/// Dual-argument factory function: the first function is called when no alias is needed, the second +/// when an alias is needed (i.e. due to python-side inheritance). Arguments must be identical. +template > +Ret init(CFunc &&c, AFunc &&a) { + return {std::forward(c), std::forward(a)}; +} + +/// Binds pickling functions `__getstate__` and `__setstate__` and ensures that the type +/// returned by `__getstate__` is the same as the argument accepted by `__setstate__`. +template +detail::initimpl::pickle_factory pickle(GetState &&g, SetState &&s) { + return {std::forward(g), std::forward(s)}; +} + +NAMESPACE_BEGIN(detail) +struct enum_base { + enum_base(handle base, handle parent) : m_base(base), m_parent(parent) { } + + PYBIND11_NOINLINE void init(bool is_arithmetic, bool is_convertible) { + m_base.attr("__entries") = dict(); + auto property = handle((PyObject *) &PyProperty_Type); + auto static_property = handle((PyObject *) get_internals().static_property_type); + + m_base.attr("__repr__") = cpp_function( + [](handle arg) -> str { + handle type = arg.get_type(); + object type_name = type.attr("__name__"); + dict entries = type.attr("__entries"); + for (const auto &kv : entries) { + object other = kv.second[int_(0)]; + if (other.equal(arg)) + return pybind11::str("{}.{}").format(type_name, kv.first); + } + return pybind11::str("{}.???").format(type_name); + }, is_method(m_base) + ); + + m_base.attr("name") = property(cpp_function( + [](handle arg) -> str { + dict entries = arg.get_type().attr("__entries"); + for (const auto &kv : entries) { + if (handle(kv.second[int_(0)]).equal(arg)) + return pybind11::str(kv.first); + } + return "???"; + }, is_method(m_base) + )); + + m_base.attr("__doc__") = static_property(cpp_function( + [](handle arg) -> std::string { + std::string docstring; + dict entries = arg.attr("__entries"); + if (((PyTypeObject *) arg.ptr())->tp_doc) + docstring += std::string(((PyTypeObject *) arg.ptr())->tp_doc) + "\n\n"; + docstring += "Members:"; + for (const auto &kv : entries) { + auto key = std::string(pybind11::str(kv.first)); + auto comment = kv.second[int_(1)]; + docstring += "\n\n " + key; + if (!comment.is_none()) + docstring += " : " + (std::string) pybind11::str(comment); + } + return docstring; + } + ), none(), none(), ""); + + m_base.attr("__members__") = static_property(cpp_function( + [](handle arg) -> dict { + dict entries = arg.attr("__entries"), m; + for (const auto &kv : entries) + m[kv.first] = kv.second[int_(0)]; + return m; + }), none(), none(), "" + ); + + #define PYBIND11_ENUM_OP_STRICT(op, expr, strict_behavior) \ + m_base.attr(op) = cpp_function( \ + [](object a, object b) { \ + if (!a.get_type().is(b.get_type())) \ + strict_behavior; \ + return expr; \ + }, \ + is_method(m_base)) + + #define PYBIND11_ENUM_OP_CONV(op, expr) \ + m_base.attr(op) = cpp_function( \ + [](object a_, object b_) { \ + int_ a(a_), b(b_); \ + return expr; \ + }, \ + is_method(m_base)) + + if (is_convertible) { + PYBIND11_ENUM_OP_CONV("__eq__", !b.is_none() && a.equal(b)); + PYBIND11_ENUM_OP_CONV("__ne__", b.is_none() || !a.equal(b)); + + if (is_arithmetic) { + PYBIND11_ENUM_OP_CONV("__lt__", a < b); + PYBIND11_ENUM_OP_CONV("__gt__", a > b); + PYBIND11_ENUM_OP_CONV("__le__", a <= b); + PYBIND11_ENUM_OP_CONV("__ge__", a >= b); + PYBIND11_ENUM_OP_CONV("__and__", a & b); + PYBIND11_ENUM_OP_CONV("__rand__", a & b); + PYBIND11_ENUM_OP_CONV("__or__", a | b); + PYBIND11_ENUM_OP_CONV("__ror__", a | b); + PYBIND11_ENUM_OP_CONV("__xor__", a ^ b); + PYBIND11_ENUM_OP_CONV("__rxor__", a ^ b); + } + } else { + PYBIND11_ENUM_OP_STRICT("__eq__", int_(a).equal(int_(b)), return false); + PYBIND11_ENUM_OP_STRICT("__ne__", !int_(a).equal(int_(b)), return true); + + if (is_arithmetic) { + #define PYBIND11_THROW throw type_error("Expected an enumeration of matching type!"); + PYBIND11_ENUM_OP_STRICT("__lt__", int_(a) < int_(b), PYBIND11_THROW); + PYBIND11_ENUM_OP_STRICT("__gt__", int_(a) > int_(b), PYBIND11_THROW); + PYBIND11_ENUM_OP_STRICT("__le__", int_(a) <= int_(b), PYBIND11_THROW); + PYBIND11_ENUM_OP_STRICT("__ge__", int_(a) >= int_(b), PYBIND11_THROW); + #undef PYBIND11_THROW + } + } + + #undef PYBIND11_ENUM_OP_CONV + #undef PYBIND11_ENUM_OP_STRICT + + object getstate = cpp_function( + [](object arg) { return int_(arg); }, is_method(m_base)); + + m_base.attr("__getstate__") = getstate; + m_base.attr("__hash__") = getstate; + } + + PYBIND11_NOINLINE void value(char const* name_, object value, const char *doc = nullptr) { + dict entries = m_base.attr("__entries"); + str name(name_); + if (entries.contains(name)) { + std::string type_name = (std::string) str(m_base.attr("__name__")); + throw value_error(type_name + ": element \"" + std::string(name_) + "\" already exists!"); + } + + entries[name] = std::make_pair(value, doc); + m_base.attr(name) = value; + } + + PYBIND11_NOINLINE void export_values() { + dict entries = m_base.attr("__entries"); + for (const auto &kv : entries) + m_parent.attr(kv.first) = kv.second[int_(0)]; + } + + handle m_base; + handle m_parent; +}; + +NAMESPACE_END(detail) + +/// Binds C++ enumerations and enumeration classes to Python +template class enum_ : public class_ { +public: + using Base = class_; + using Base::def; + using Base::attr; + using Base::def_property_readonly; + using Base::def_property_readonly_static; + using Scalar = typename std::underlying_type::type; + + template + enum_(const handle &scope, const char *name, const Extra&... extra) + : class_(scope, name, extra...), m_base(*this, scope) { + constexpr bool is_arithmetic = detail::any_of...>::value; + constexpr bool is_convertible = std::is_convertible::value; + m_base.init(is_arithmetic, is_convertible); + + def(init([](Scalar i) { return static_cast(i); })); + def("__int__", [](Type value) { return (Scalar) value; }); + #if PY_MAJOR_VERSION < 3 + def("__long__", [](Type value) { return (Scalar) value; }); + #endif + cpp_function setstate( + [](Type &value, Scalar arg) { value = static_cast(arg); }, + is_method(*this)); + attr("__setstate__") = setstate; + } + + /// Export enumeration entries into the parent scope + enum_& export_values() { + m_base.export_values(); + return *this; + } + + /// Add an enumeration entry + enum_& value(char const* name, Type value, const char *doc = nullptr) { + m_base.value(name, pybind11::cast(value, return_value_policy::copy), doc); + return *this; + } + +private: + detail::enum_base m_base; +}; + +NAMESPACE_BEGIN(detail) + + +inline void keep_alive_impl(handle nurse, handle patient) { + if (!nurse || !patient) + pybind11_fail("Could not activate keep_alive!"); + + if (patient.is_none() || nurse.is_none()) + return; /* Nothing to keep alive or nothing to be kept alive by */ + + auto tinfo = all_type_info(Py_TYPE(nurse.ptr())); + if (!tinfo.empty()) { + /* It's a pybind-registered type, so we can store the patient in the + * internal list. */ + add_patient(nurse.ptr(), patient.ptr()); + } + else { + /* Fall back to clever approach based on weak references taken from + * Boost.Python. This is not used for pybind-registered types because + * the objects can be destroyed out-of-order in a GC pass. */ + cpp_function disable_lifesupport( + [patient](handle weakref) { patient.dec_ref(); weakref.dec_ref(); }); + + weakref wr(nurse, disable_lifesupport); + + patient.inc_ref(); /* reference patient and leak the weak reference */ + (void) wr.release(); + } +} + +PYBIND11_NOINLINE inline void keep_alive_impl(size_t Nurse, size_t Patient, function_call &call, handle ret) { + auto get_arg = [&](size_t n) { + if (n == 0) + return ret; + else if (n == 1 && call.init_self) + return call.init_self; + else if (n <= call.args.size()) + return call.args[n - 1]; + return handle(); + }; + + keep_alive_impl(get_arg(Nurse), get_arg(Patient)); +} + +inline std::pair all_type_info_get_cache(PyTypeObject *type) { + auto res = get_internals().registered_types_py +#ifdef __cpp_lib_unordered_map_try_emplace + .try_emplace(type); +#else + .emplace(type, std::vector()); +#endif + if (res.second) { + // New cache entry created; set up a weak reference to automatically remove it if the type + // gets destroyed: + weakref((PyObject *) type, cpp_function([type](handle wr) { + get_internals().registered_types_py.erase(type); + wr.dec_ref(); + })).release(); + } + + return res; +} + +template +struct iterator_state { + Iterator it; + Sentinel end; + bool first_or_done; +}; + +NAMESPACE_END(detail) + +/// Makes a python iterator from a first and past-the-end C++ InputIterator. +template ()), + typename... Extra> +iterator make_iterator(Iterator first, Sentinel last, Extra &&... extra) { + typedef detail::iterator_state state; + + if (!detail::get_type_info(typeid(state), false)) { + class_(handle(), "iterator", pybind11::module_local()) + .def("__iter__", [](state &s) -> state& { return s; }) + .def("__next__", [](state &s) -> ValueType { + if (!s.first_or_done) + ++s.it; + else + s.first_or_done = false; + if (s.it == s.end) { + s.first_or_done = true; + throw stop_iteration(); + } + return *s.it; + }, std::forward(extra)..., Policy); + } + + return cast(state{first, last, true}); +} + +/// Makes an python iterator over the keys (`.first`) of a iterator over pairs from a +/// first and past-the-end InputIterator. +template ()).first), + typename... Extra> +iterator make_key_iterator(Iterator first, Sentinel last, Extra &&... extra) { + typedef detail::iterator_state state; + + if (!detail::get_type_info(typeid(state), false)) { + class_(handle(), "iterator", pybind11::module_local()) + .def("__iter__", [](state &s) -> state& { return s; }) + .def("__next__", [](state &s) -> KeyType { + if (!s.first_or_done) + ++s.it; + else + s.first_or_done = false; + if (s.it == s.end) { + s.first_or_done = true; + throw stop_iteration(); + } + return (*s.it).first; + }, std::forward(extra)..., Policy); + } + + return cast(state{first, last, true}); +} + +/// Makes an iterator over values of an stl container or other container supporting +/// `std::begin()`/`std::end()` +template iterator make_iterator(Type &value, Extra&&... extra) { + return make_iterator(std::begin(value), std::end(value), extra...); +} + +/// Makes an iterator over the keys (`.first`) of a stl map-like container supporting +/// `std::begin()`/`std::end()` +template iterator make_key_iterator(Type &value, Extra&&... extra) { + return make_key_iterator(std::begin(value), std::end(value), extra...); +} + +template void implicitly_convertible() { + struct set_flag { + bool &flag; + set_flag(bool &flag) : flag(flag) { flag = true; } + ~set_flag() { flag = false; } + }; + auto implicit_caster = [](PyObject *obj, PyTypeObject *type) -> PyObject * { + static bool currently_used = false; + if (currently_used) // implicit conversions are non-reentrant + return nullptr; + set_flag flag_helper(currently_used); + if (!detail::make_caster().load(obj, false)) + return nullptr; + tuple args(1); + args[0] = obj; + PyObject *result = PyObject_Call((PyObject *) type, args.ptr(), nullptr); + if (result == nullptr) + PyErr_Clear(); + return result; + }; + + if (auto tinfo = detail::get_type_info(typeid(OutputType))) + tinfo->implicit_conversions.push_back(implicit_caster); + else + pybind11_fail("implicitly_convertible: Unable to find type " + type_id()); +} + +template +void register_exception_translator(ExceptionTranslator&& translator) { + detail::get_internals().registered_exception_translators.push_front( + std::forward(translator)); +} + +/** + * Wrapper to generate a new Python exception type. + * + * This should only be used with PyErr_SetString for now. + * It is not (yet) possible to use as a py::base. + * Template type argument is reserved for future use. + */ +template +class exception : public object { +public: + exception() = default; + exception(handle scope, const char *name, PyObject *base = PyExc_Exception) { + std::string full_name = scope.attr("__name__").cast() + + std::string(".") + name; + m_ptr = PyErr_NewException(const_cast(full_name.c_str()), base, NULL); + if (hasattr(scope, name)) + pybind11_fail("Error during initialization: multiple incompatible " + "definitions with name \"" + std::string(name) + "\""); + scope.attr(name) = *this; + } + + // Sets the current python exception to this exception object with the given message + void operator()(const char *message) { + PyErr_SetString(m_ptr, message); + } +}; + +NAMESPACE_BEGIN(detail) +// Returns a reference to a function-local static exception object used in the simple +// register_exception approach below. (It would be simpler to have the static local variable +// directly in register_exception, but that makes clang <3.5 segfault - issue #1349). +template +exception &get_exception_object() { static exception ex; return ex; } +NAMESPACE_END(detail) + +/** + * Registers a Python exception in `m` of the given `name` and installs an exception translator to + * translate the C++ exception to the created Python exception using the exceptions what() method. + * This is intended for simple exception translations; for more complex translation, register the + * exception object and translator directly. + */ +template +exception ®ister_exception(handle scope, + const char *name, + PyObject *base = PyExc_Exception) { + auto &ex = detail::get_exception_object(); + if (!ex) ex = exception(scope, name, base); + + register_exception_translator([](std::exception_ptr p) { + if (!p) return; + try { + std::rethrow_exception(p); + } catch (const CppException &e) { + detail::get_exception_object()(e.what()); + } + }); + return ex; +} + +NAMESPACE_BEGIN(detail) +PYBIND11_NOINLINE inline void print(tuple args, dict kwargs) { + auto strings = tuple(args.size()); + for (size_t i = 0; i < args.size(); ++i) { + strings[i] = str(args[i]); + } + auto sep = kwargs.contains("sep") ? kwargs["sep"] : cast(" "); + auto line = sep.attr("join")(strings); + + object file; + if (kwargs.contains("file")) { + file = kwargs["file"].cast(); + } else { + try { + file = module::import("sys").attr("stdout"); + } catch (const error_already_set &) { + /* If print() is called from code that is executed as + part of garbage collection during interpreter shutdown, + importing 'sys' can fail. Give up rather than crashing the + interpreter in this case. */ + return; + } + } + + auto write = file.attr("write"); + write(line); + write(kwargs.contains("end") ? kwargs["end"] : cast("\n")); + + if (kwargs.contains("flush") && kwargs["flush"].cast()) + file.attr("flush")(); +} +NAMESPACE_END(detail) + +template +void print(Args &&...args) { + auto c = detail::collect_arguments(std::forward(args)...); + detail::print(c.args(), c.kwargs()); +} + +#if defined(WITH_THREAD) && !defined(PYPY_VERSION) + +/* The functions below essentially reproduce the PyGILState_* API using a RAII + * pattern, but there are a few important differences: + * + * 1. When acquiring the GIL from an non-main thread during the finalization + * phase, the GILState API blindly terminates the calling thread, which + * is often not what is wanted. This API does not do this. + * + * 2. The gil_scoped_release function can optionally cut the relationship + * of a PyThreadState and its associated thread, which allows moving it to + * another thread (this is a fairly rare/advanced use case). + * + * 3. The reference count of an acquired thread state can be controlled. This + * can be handy to prevent cases where callbacks issued from an external + * thread would otherwise constantly construct and destroy thread state data + * structures. + * + * See the Python bindings of NanoGUI (http://github.com/wjakob/nanogui) for an + * example which uses features 2 and 3 to migrate the Python thread of + * execution to another thread (to run the event loop on the original thread, + * in this case). + */ + +class gil_scoped_acquire { +public: + PYBIND11_NOINLINE gil_scoped_acquire() { + auto const &internals = detail::get_internals(); + tstate = (PyThreadState *) PYBIND11_TLS_GET_VALUE(internals.tstate); + + if (!tstate) { + /* Check if the GIL was acquired using the PyGILState_* API instead (e.g. if + calling from a Python thread). Since we use a different key, this ensures + we don't create a new thread state and deadlock in PyEval_AcquireThread + below. Note we don't save this state with internals.tstate, since we don't + create it we would fail to clear it (its reference count should be > 0). */ + tstate = PyGILState_GetThisThreadState(); + } + + if (!tstate) { + tstate = PyThreadState_New(internals.istate); + #if !defined(NDEBUG) + if (!tstate) + pybind11_fail("scoped_acquire: could not create thread state!"); + #endif + tstate->gilstate_counter = 0; + PYBIND11_TLS_REPLACE_VALUE(internals.tstate, tstate); + } else { + release = detail::get_thread_state_unchecked() != tstate; + } + + if (release) { + /* Work around an annoying assertion in PyThreadState_Swap */ + #if defined(Py_DEBUG) + PyInterpreterState *interp = tstate->interp; + tstate->interp = nullptr; + #endif + PyEval_AcquireThread(tstate); + #if defined(Py_DEBUG) + tstate->interp = interp; + #endif + } + + inc_ref(); + } + + void inc_ref() { + ++tstate->gilstate_counter; + } + + PYBIND11_NOINLINE void dec_ref() { + --tstate->gilstate_counter; + #if !defined(NDEBUG) + if (detail::get_thread_state_unchecked() != tstate) + pybind11_fail("scoped_acquire::dec_ref(): thread state must be current!"); + if (tstate->gilstate_counter < 0) + pybind11_fail("scoped_acquire::dec_ref(): reference count underflow!"); + #endif + if (tstate->gilstate_counter == 0) { + #if !defined(NDEBUG) + if (!release) + pybind11_fail("scoped_acquire::dec_ref(): internal error!"); + #endif + PyThreadState_Clear(tstate); + PyThreadState_DeleteCurrent(); + PYBIND11_TLS_DELETE_VALUE(detail::get_internals().tstate); + release = false; + } + } + + PYBIND11_NOINLINE ~gil_scoped_acquire() { + dec_ref(); + if (release) + PyEval_SaveThread(); + } +private: + PyThreadState *tstate = nullptr; + bool release = true; +}; + +class gil_scoped_release { +public: + explicit gil_scoped_release(bool disassoc = false) : disassoc(disassoc) { + // `get_internals()` must be called here unconditionally in order to initialize + // `internals.tstate` for subsequent `gil_scoped_acquire` calls. Otherwise, an + // initialization race could occur as multiple threads try `gil_scoped_acquire`. + const auto &internals = detail::get_internals(); + tstate = PyEval_SaveThread(); + if (disassoc) { + auto key = internals.tstate; + PYBIND11_TLS_DELETE_VALUE(key); + } + } + ~gil_scoped_release() { + if (!tstate) + return; + PyEval_RestoreThread(tstate); + if (disassoc) { + auto key = detail::get_internals().tstate; + PYBIND11_TLS_REPLACE_VALUE(key, tstate); + } + } +private: + PyThreadState *tstate; + bool disassoc; +}; +#elif defined(PYPY_VERSION) +class gil_scoped_acquire { + PyGILState_STATE state; +public: + gil_scoped_acquire() { state = PyGILState_Ensure(); } + ~gil_scoped_acquire() { PyGILState_Release(state); } +}; + +class gil_scoped_release { + PyThreadState *state; +public: + gil_scoped_release() { state = PyEval_SaveThread(); } + ~gil_scoped_release() { PyEval_RestoreThread(state); } +}; +#else +class gil_scoped_acquire { }; +class gil_scoped_release { }; +#endif + +error_already_set::~error_already_set() { + if (type) { + error_scope scope; + gil_scoped_acquire gil; + type.release().dec_ref(); + value.release().dec_ref(); + trace.release().dec_ref(); + } +} + +inline function get_type_overload(const void *this_ptr, const detail::type_info *this_type, const char *name) { + handle self = detail::get_object_handle(this_ptr, this_type); + if (!self) + return function(); + handle type = self.get_type(); + auto key = std::make_pair(type.ptr(), name); + + /* Cache functions that aren't overloaded in Python to avoid + many costly Python dictionary lookups below */ + auto &cache = detail::get_internals().inactive_overload_cache; + if (cache.find(key) != cache.end()) + return function(); + + function overload = getattr(self, name, function()); + if (overload.is_cpp_function()) { + cache.insert(key); + return function(); + } + + /* Don't call dispatch code if invoked from overridden function. + Unfortunately this doesn't work on PyPy. */ +#if !defined(PYPY_VERSION) + PyFrameObject *frame = PyThreadState_Get()->frame; + if (frame && (std::string) str(frame->f_code->co_name) == name && + frame->f_code->co_argcount > 0) { + PyFrame_FastToLocals(frame); + PyObject *self_caller = PyDict_GetItem( + frame->f_locals, PyTuple_GET_ITEM(frame->f_code->co_varnames, 0)); + if (self_caller == self.ptr()) + return function(); + } +#else + /* PyPy currently doesn't provide a detailed cpyext emulation of + frame objects, so we have to emulate this using Python. This + is going to be slow..*/ + dict d; d["self"] = self; d["name"] = pybind11::str(name); + PyObject *result = PyRun_String( + "import inspect\n" + "frame = inspect.currentframe()\n" + "if frame is not None:\n" + " frame = frame.f_back\n" + " if frame is not None and str(frame.f_code.co_name) == name and " + "frame.f_code.co_argcount > 0:\n" + " self_caller = frame.f_locals[frame.f_code.co_varnames[0]]\n" + " if self_caller == self:\n" + " self = None\n", + Py_file_input, d.ptr(), d.ptr()); + if (result == nullptr) + throw error_already_set(); + if (d["self"].is_none()) + return function(); + Py_DECREF(result); +#endif + + return overload; +} + +template function get_overload(const T *this_ptr, const char *name) { + auto tinfo = detail::get_type_info(typeid(T)); + return tinfo ? get_type_overload(this_ptr, tinfo, name) : function(); +} + +#define PYBIND11_OVERLOAD_INT(ret_type, cname, name, ...) { \ + pybind11::gil_scoped_acquire gil; \ + pybind11::function overload = pybind11::get_overload(static_cast(this), name); \ + if (overload) { \ + auto o = overload(__VA_ARGS__); \ + if (pybind11::detail::cast_is_temporary_value_reference::value) { \ + static pybind11::detail::overload_caster_t caster; \ + return pybind11::detail::cast_ref(std::move(o), caster); \ + } \ + else return pybind11::detail::cast_safe(std::move(o)); \ + } \ + } + +#define PYBIND11_OVERLOAD_NAME(ret_type, cname, name, fn, ...) \ + PYBIND11_OVERLOAD_INT(PYBIND11_TYPE(ret_type), PYBIND11_TYPE(cname), name, __VA_ARGS__) \ + return cname::fn(__VA_ARGS__) + +#define PYBIND11_OVERLOAD_PURE_NAME(ret_type, cname, name, fn, ...) \ + PYBIND11_OVERLOAD_INT(PYBIND11_TYPE(ret_type), PYBIND11_TYPE(cname), name, __VA_ARGS__) \ + pybind11::pybind11_fail("Tried to call pure virtual function \"" PYBIND11_STRINGIFY(cname) "::" name "\""); + +#define PYBIND11_OVERLOAD(ret_type, cname, fn, ...) \ + PYBIND11_OVERLOAD_NAME(PYBIND11_TYPE(ret_type), PYBIND11_TYPE(cname), #fn, fn, __VA_ARGS__) + +#define PYBIND11_OVERLOAD_PURE(ret_type, cname, fn, ...) \ + PYBIND11_OVERLOAD_PURE_NAME(PYBIND11_TYPE(ret_type), PYBIND11_TYPE(cname), #fn, fn, __VA_ARGS__) + +NAMESPACE_END(PYBIND11_NAMESPACE) + +#if defined(_MSC_VER) && !defined(__INTEL_COMPILER) +# pragma warning(pop) +#elif defined(__GNUG__) && !defined(__clang__) +# pragma GCC diagnostic pop +#endif diff --git a/ptocr/postprocess/piexlmerge/include/pybind11/pytypes.h b/ptocr/postprocess/piexlmerge/include/pybind11/pytypes.h new file mode 100644 index 0000000..3329fda --- /dev/null +++ b/ptocr/postprocess/piexlmerge/include/pybind11/pytypes.h @@ -0,0 +1,1438 @@ +/* + pybind11/pytypes.h: Convenience wrapper classes for basic Python types + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "detail/common.h" +#include "buffer_info.h" +#include +#include + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) + +/* A few forward declarations */ +class handle; class object; +class str; class iterator; +struct arg; struct arg_v; + +NAMESPACE_BEGIN(detail) +class args_proxy; +inline bool isinstance_generic(handle obj, const std::type_info &tp); + +// Accessor forward declarations +template class accessor; +namespace accessor_policies { + struct obj_attr; + struct str_attr; + struct generic_item; + struct sequence_item; + struct list_item; + struct tuple_item; +} +using obj_attr_accessor = accessor; +using str_attr_accessor = accessor; +using item_accessor = accessor; +using sequence_accessor = accessor; +using list_accessor = accessor; +using tuple_accessor = accessor; + +/// Tag and check to identify a class which implements the Python object API +class pyobject_tag { }; +template using is_pyobject = std::is_base_of>; + +/** \rst + A mixin class which adds common functions to `handle`, `object` and various accessors. + The only requirement for `Derived` is to implement ``PyObject *Derived::ptr() const``. +\endrst */ +template +class object_api : public pyobject_tag { + const Derived &derived() const { return static_cast(*this); } + +public: + /** \rst + Return an iterator equivalent to calling ``iter()`` in Python. The object + must be a collection which supports the iteration protocol. + \endrst */ + iterator begin() const; + /// Return a sentinel which ends iteration. + iterator end() const; + + /** \rst + Return an internal functor to invoke the object's sequence protocol. Casting + the returned ``detail::item_accessor`` instance to a `handle` or `object` + subclass causes a corresponding call to ``__getitem__``. Assigning a `handle` + or `object` subclass causes a call to ``__setitem__``. + \endrst */ + item_accessor operator[](handle key) const; + /// See above (the only difference is that they key is provided as a string literal) + item_accessor operator[](const char *key) const; + + /** \rst + Return an internal functor to access the object's attributes. Casting the + returned ``detail::obj_attr_accessor`` instance to a `handle` or `object` + subclass causes a corresponding call to ``getattr``. Assigning a `handle` + or `object` subclass causes a call to ``setattr``. + \endrst */ + obj_attr_accessor attr(handle key) const; + /// See above (the only difference is that they key is provided as a string literal) + str_attr_accessor attr(const char *key) const; + + /** \rst + Matches * unpacking in Python, e.g. to unpack arguments out of a ``tuple`` + or ``list`` for a function call. Applying another * to the result yields + ** unpacking, e.g. to unpack a dict as function keyword arguments. + See :ref:`calling_python_functions`. + \endrst */ + args_proxy operator*() const; + + /// Check if the given item is contained within this object, i.e. ``item in obj``. + template bool contains(T &&item) const; + + /** \rst + Assuming the Python object is a function or implements the ``__call__`` + protocol, ``operator()`` invokes the underlying function, passing an + arbitrary set of parameters. The result is returned as a `object` and + may need to be converted back into a Python object using `handle::cast()`. + + When some of the arguments cannot be converted to Python objects, the + function will throw a `cast_error` exception. When the Python function + call fails, a `error_already_set` exception is thrown. + \endrst */ + template + object operator()(Args &&...args) const; + template + PYBIND11_DEPRECATED("call(...) was deprecated in favor of operator()(...)") + object call(Args&&... args) const; + + /// Equivalent to ``obj is other`` in Python. + bool is(object_api const& other) const { return derived().ptr() == other.derived().ptr(); } + /// Equivalent to ``obj is None`` in Python. + bool is_none() const { return derived().ptr() == Py_None; } + /// Equivalent to obj == other in Python + bool equal(object_api const &other) const { return rich_compare(other, Py_EQ); } + bool not_equal(object_api const &other) const { return rich_compare(other, Py_NE); } + bool operator<(object_api const &other) const { return rich_compare(other, Py_LT); } + bool operator<=(object_api const &other) const { return rich_compare(other, Py_LE); } + bool operator>(object_api const &other) const { return rich_compare(other, Py_GT); } + bool operator>=(object_api const &other) const { return rich_compare(other, Py_GE); } + + object operator-() const; + object operator~() const; + object operator+(object_api const &other) const; + object operator+=(object_api const &other) const; + object operator-(object_api const &other) const; + object operator-=(object_api const &other) const; + object operator*(object_api const &other) const; + object operator*=(object_api const &other) const; + object operator/(object_api const &other) const; + object operator/=(object_api const &other) const; + object operator|(object_api const &other) const; + object operator|=(object_api const &other) const; + object operator&(object_api const &other) const; + object operator&=(object_api const &other) const; + object operator^(object_api const &other) const; + object operator^=(object_api const &other) const; + object operator<<(object_api const &other) const; + object operator<<=(object_api const &other) const; + object operator>>(object_api const &other) const; + object operator>>=(object_api const &other) const; + + PYBIND11_DEPRECATED("Use py::str(obj) instead") + pybind11::str str() const; + + /// Get or set the object's docstring, i.e. ``obj.__doc__``. + str_attr_accessor doc() const; + + /// Return the object's current reference count + int ref_count() const { return static_cast(Py_REFCNT(derived().ptr())); } + /// Return a handle to the Python type object underlying the instance + handle get_type() const; + +private: + bool rich_compare(object_api const &other, int value) const; +}; + +NAMESPACE_END(detail) + +/** \rst + Holds a reference to a Python object (no reference counting) + + The `handle` class is a thin wrapper around an arbitrary Python object (i.e. a + ``PyObject *`` in Python's C API). It does not perform any automatic reference + counting and merely provides a basic C++ interface to various Python API functions. + + .. seealso:: + The `object` class inherits from `handle` and adds automatic reference + counting features. +\endrst */ +class handle : public detail::object_api { +public: + /// The default constructor creates a handle with a ``nullptr``-valued pointer + handle() = default; + /// Creates a ``handle`` from the given raw Python object pointer + handle(PyObject *ptr) : m_ptr(ptr) { } // Allow implicit conversion from PyObject* + + /// Return the underlying ``PyObject *`` pointer + PyObject *ptr() const { return m_ptr; } + PyObject *&ptr() { return m_ptr; } + + /** \rst + Manually increase the reference count of the Python object. Usually, it is + preferable to use the `object` class which derives from `handle` and calls + this function automatically. Returns a reference to itself. + \endrst */ + const handle& inc_ref() const & { Py_XINCREF(m_ptr); return *this; } + + /** \rst + Manually decrease the reference count of the Python object. Usually, it is + preferable to use the `object` class which derives from `handle` and calls + this function automatically. Returns a reference to itself. + \endrst */ + const handle& dec_ref() const & { Py_XDECREF(m_ptr); return *this; } + + /** \rst + Attempt to cast the Python object into the given C++ type. A `cast_error` + will be throw upon failure. + \endrst */ + template T cast() const; + /// Return ``true`` when the `handle` wraps a valid Python object + explicit operator bool() const { return m_ptr != nullptr; } + /** \rst + Deprecated: Check that the underlying pointers are the same. + Equivalent to ``obj1 is obj2`` in Python. + \endrst */ + PYBIND11_DEPRECATED("Use obj1.is(obj2) instead") + bool operator==(const handle &h) const { return m_ptr == h.m_ptr; } + PYBIND11_DEPRECATED("Use !obj1.is(obj2) instead") + bool operator!=(const handle &h) const { return m_ptr != h.m_ptr; } + PYBIND11_DEPRECATED("Use handle::operator bool() instead") + bool check() const { return m_ptr != nullptr; } +protected: + PyObject *m_ptr = nullptr; +}; + +/** \rst + Holds a reference to a Python object (with reference counting) + + Like `handle`, the `object` class is a thin wrapper around an arbitrary Python + object (i.e. a ``PyObject *`` in Python's C API). In contrast to `handle`, it + optionally increases the object's reference count upon construction, and it + *always* decreases the reference count when the `object` instance goes out of + scope and is destructed. When using `object` instances consistently, it is much + easier to get reference counting right at the first attempt. +\endrst */ +class object : public handle { +public: + object() = default; + PYBIND11_DEPRECATED("Use reinterpret_borrow() or reinterpret_steal()") + object(handle h, bool is_borrowed) : handle(h) { if (is_borrowed) inc_ref(); } + /// Copy constructor; always increases the reference count + object(const object &o) : handle(o) { inc_ref(); } + /// Move constructor; steals the object from ``other`` and preserves its reference count + object(object &&other) noexcept { m_ptr = other.m_ptr; other.m_ptr = nullptr; } + /// Destructor; automatically calls `handle::dec_ref()` + ~object() { dec_ref(); } + + /** \rst + Resets the internal pointer to ``nullptr`` without without decreasing the + object's reference count. The function returns a raw handle to the original + Python object. + \endrst */ + handle release() { + PyObject *tmp = m_ptr; + m_ptr = nullptr; + return handle(tmp); + } + + object& operator=(const object &other) { + other.inc_ref(); + dec_ref(); + m_ptr = other.m_ptr; + return *this; + } + + object& operator=(object &&other) noexcept { + if (this != &other) { + handle temp(m_ptr); + m_ptr = other.m_ptr; + other.m_ptr = nullptr; + temp.dec_ref(); + } + return *this; + } + + // Calling cast() on an object lvalue just copies (via handle::cast) + template T cast() const &; + // Calling on an object rvalue does a move, if needed and/or possible + template T cast() &&; + +protected: + // Tags for choosing constructors from raw PyObject * + struct borrowed_t { }; + struct stolen_t { }; + + template friend T reinterpret_borrow(handle); + template friend T reinterpret_steal(handle); + +public: + // Only accessible from derived classes and the reinterpret_* functions + object(handle h, borrowed_t) : handle(h) { inc_ref(); } + object(handle h, stolen_t) : handle(h) { } +}; + +/** \rst + Declare that a `handle` or ``PyObject *`` is a certain type and borrow the reference. + The target type ``T`` must be `object` or one of its derived classes. The function + doesn't do any conversions or checks. It's up to the user to make sure that the + target type is correct. + + .. code-block:: cpp + + PyObject *p = PyList_GetItem(obj, index); + py::object o = reinterpret_borrow(p); + // or + py::tuple t = reinterpret_borrow(p); // <-- `p` must be already be a `tuple` +\endrst */ +template T reinterpret_borrow(handle h) { return {h, object::borrowed_t{}}; } + +/** \rst + Like `reinterpret_borrow`, but steals the reference. + + .. code-block:: cpp + + PyObject *p = PyObject_Str(obj); + py::str s = reinterpret_steal(p); // <-- `p` must be already be a `str` +\endrst */ +template T reinterpret_steal(handle h) { return {h, object::stolen_t{}}; } + +NAMESPACE_BEGIN(detail) +inline std::string error_string(); +NAMESPACE_END(detail) + +/// Fetch and hold an error which was already set in Python. An instance of this is typically +/// thrown to propagate python-side errors back through C++ which can either be caught manually or +/// else falls back to the function dispatcher (which then raises the captured error back to +/// python). +class error_already_set : public std::runtime_error { +public: + /// Constructs a new exception from the current Python error indicator, if any. The current + /// Python error indicator will be cleared. + error_already_set() : std::runtime_error(detail::error_string()) { + PyErr_Fetch(&type.ptr(), &value.ptr(), &trace.ptr()); + } + + error_already_set(const error_already_set &) = default; + error_already_set(error_already_set &&) = default; + + inline ~error_already_set(); + + /// Give the currently-held error back to Python, if any. If there is currently a Python error + /// already set it is cleared first. After this call, the current object no longer stores the + /// error variables (but the `.what()` string is still available). + void restore() { PyErr_Restore(type.release().ptr(), value.release().ptr(), trace.release().ptr()); } + + // Does nothing; provided for backwards compatibility. + PYBIND11_DEPRECATED("Use of error_already_set.clear() is deprecated") + void clear() {} + + /// Check if the currently trapped error type matches the given Python exception class (or a + /// subclass thereof). May also be passed a tuple to search for any exception class matches in + /// the given tuple. + bool matches(handle ex) const { return PyErr_GivenExceptionMatches(ex.ptr(), type.ptr()); } + +private: + object type, value, trace; +}; + +/** \defgroup python_builtins _ + Unless stated otherwise, the following C++ functions behave the same + as their Python counterparts. + */ + +/** \ingroup python_builtins + \rst + Return true if ``obj`` is an instance of ``T``. Type ``T`` must be a subclass of + `object` or a class which was exposed to Python as ``py::class_``. +\endrst */ +template ::value, int> = 0> +bool isinstance(handle obj) { return T::check_(obj); } + +template ::value, int> = 0> +bool isinstance(handle obj) { return detail::isinstance_generic(obj, typeid(T)); } + +template <> inline bool isinstance(handle obj) = delete; +template <> inline bool isinstance(handle obj) { return obj.ptr() != nullptr; } + +/// \ingroup python_builtins +/// Return true if ``obj`` is an instance of the ``type``. +inline bool isinstance(handle obj, handle type) { + const auto result = PyObject_IsInstance(obj.ptr(), type.ptr()); + if (result == -1) + throw error_already_set(); + return result != 0; +} + +/// \addtogroup python_builtins +/// @{ +inline bool hasattr(handle obj, handle name) { + return PyObject_HasAttr(obj.ptr(), name.ptr()) == 1; +} + +inline bool hasattr(handle obj, const char *name) { + return PyObject_HasAttrString(obj.ptr(), name) == 1; +} + +inline void delattr(handle obj, handle name) { + if (PyObject_DelAttr(obj.ptr(), name.ptr()) != 0) { throw error_already_set(); } +} + +inline void delattr(handle obj, const char *name) { + if (PyObject_DelAttrString(obj.ptr(), name) != 0) { throw error_already_set(); } +} + +inline object getattr(handle obj, handle name) { + PyObject *result = PyObject_GetAttr(obj.ptr(), name.ptr()); + if (!result) { throw error_already_set(); } + return reinterpret_steal(result); +} + +inline object getattr(handle obj, const char *name) { + PyObject *result = PyObject_GetAttrString(obj.ptr(), name); + if (!result) { throw error_already_set(); } + return reinterpret_steal(result); +} + +inline object getattr(handle obj, handle name, handle default_) { + if (PyObject *result = PyObject_GetAttr(obj.ptr(), name.ptr())) { + return reinterpret_steal(result); + } else { + PyErr_Clear(); + return reinterpret_borrow(default_); + } +} + +inline object getattr(handle obj, const char *name, handle default_) { + if (PyObject *result = PyObject_GetAttrString(obj.ptr(), name)) { + return reinterpret_steal(result); + } else { + PyErr_Clear(); + return reinterpret_borrow(default_); + } +} + +inline void setattr(handle obj, handle name, handle value) { + if (PyObject_SetAttr(obj.ptr(), name.ptr(), value.ptr()) != 0) { throw error_already_set(); } +} + +inline void setattr(handle obj, const char *name, handle value) { + if (PyObject_SetAttrString(obj.ptr(), name, value.ptr()) != 0) { throw error_already_set(); } +} + +inline ssize_t hash(handle obj) { + auto h = PyObject_Hash(obj.ptr()); + if (h == -1) { throw error_already_set(); } + return h; +} + +/// @} python_builtins + +NAMESPACE_BEGIN(detail) +inline handle get_function(handle value) { + if (value) { +#if PY_MAJOR_VERSION >= 3 + if (PyInstanceMethod_Check(value.ptr())) + value = PyInstanceMethod_GET_FUNCTION(value.ptr()); + else +#endif + if (PyMethod_Check(value.ptr())) + value = PyMethod_GET_FUNCTION(value.ptr()); + } + return value; +} + +// Helper aliases/functions to support implicit casting of values given to python accessors/methods. +// When given a pyobject, this simply returns the pyobject as-is; for other C++ type, the value goes +// through pybind11::cast(obj) to convert it to an `object`. +template ::value, int> = 0> +auto object_or_cast(T &&o) -> decltype(std::forward(o)) { return std::forward(o); } +// The following casting version is implemented in cast.h: +template ::value, int> = 0> +object object_or_cast(T &&o); +// Match a PyObject*, which we want to convert directly to handle via its converting constructor +inline handle object_or_cast(PyObject *ptr) { return ptr; } + +template +class accessor : public object_api> { + using key_type = typename Policy::key_type; + +public: + accessor(handle obj, key_type key) : obj(obj), key(std::move(key)) { } + accessor(const accessor &) = default; + accessor(accessor &&) = default; + + // accessor overload required to override default assignment operator (templates are not allowed + // to replace default compiler-generated assignments). + void operator=(const accessor &a) && { std::move(*this).operator=(handle(a)); } + void operator=(const accessor &a) & { operator=(handle(a)); } + + template void operator=(T &&value) && { + Policy::set(obj, key, object_or_cast(std::forward(value))); + } + template void operator=(T &&value) & { + get_cache() = reinterpret_borrow(object_or_cast(std::forward(value))); + } + + template + PYBIND11_DEPRECATED("Use of obj.attr(...) as bool is deprecated in favor of pybind11::hasattr(obj, ...)") + explicit operator enable_if_t::value || + std::is_same::value, bool>() const { + return hasattr(obj, key); + } + template + PYBIND11_DEPRECATED("Use of obj[key] as bool is deprecated in favor of obj.contains(key)") + explicit operator enable_if_t::value, bool>() const { + return obj.contains(key); + } + + operator object() const { return get_cache(); } + PyObject *ptr() const { return get_cache().ptr(); } + template T cast() const { return get_cache().template cast(); } + +private: + object &get_cache() const { + if (!cache) { cache = Policy::get(obj, key); } + return cache; + } + +private: + handle obj; + key_type key; + mutable object cache; +}; + +NAMESPACE_BEGIN(accessor_policies) +struct obj_attr { + using key_type = object; + static object get(handle obj, handle key) { return getattr(obj, key); } + static void set(handle obj, handle key, handle val) { setattr(obj, key, val); } +}; + +struct str_attr { + using key_type = const char *; + static object get(handle obj, const char *key) { return getattr(obj, key); } + static void set(handle obj, const char *key, handle val) { setattr(obj, key, val); } +}; + +struct generic_item { + using key_type = object; + + static object get(handle obj, handle key) { + PyObject *result = PyObject_GetItem(obj.ptr(), key.ptr()); + if (!result) { throw error_already_set(); } + return reinterpret_steal(result); + } + + static void set(handle obj, handle key, handle val) { + if (PyObject_SetItem(obj.ptr(), key.ptr(), val.ptr()) != 0) { throw error_already_set(); } + } +}; + +struct sequence_item { + using key_type = size_t; + + static object get(handle obj, size_t index) { + PyObject *result = PySequence_GetItem(obj.ptr(), static_cast(index)); + if (!result) { throw error_already_set(); } + return reinterpret_steal(result); + } + + static void set(handle obj, size_t index, handle val) { + // PySequence_SetItem does not steal a reference to 'val' + if (PySequence_SetItem(obj.ptr(), static_cast(index), val.ptr()) != 0) { + throw error_already_set(); + } + } +}; + +struct list_item { + using key_type = size_t; + + static object get(handle obj, size_t index) { + PyObject *result = PyList_GetItem(obj.ptr(), static_cast(index)); + if (!result) { throw error_already_set(); } + return reinterpret_borrow(result); + } + + static void set(handle obj, size_t index, handle val) { + // PyList_SetItem steals a reference to 'val' + if (PyList_SetItem(obj.ptr(), static_cast(index), val.inc_ref().ptr()) != 0) { + throw error_already_set(); + } + } +}; + +struct tuple_item { + using key_type = size_t; + + static object get(handle obj, size_t index) { + PyObject *result = PyTuple_GetItem(obj.ptr(), static_cast(index)); + if (!result) { throw error_already_set(); } + return reinterpret_borrow(result); + } + + static void set(handle obj, size_t index, handle val) { + // PyTuple_SetItem steals a reference to 'val' + if (PyTuple_SetItem(obj.ptr(), static_cast(index), val.inc_ref().ptr()) != 0) { + throw error_already_set(); + } + } +}; +NAMESPACE_END(accessor_policies) + +/// STL iterator template used for tuple, list, sequence and dict +template +class generic_iterator : public Policy { + using It = generic_iterator; + +public: + using difference_type = ssize_t; + using iterator_category = typename Policy::iterator_category; + using value_type = typename Policy::value_type; + using reference = typename Policy::reference; + using pointer = typename Policy::pointer; + + generic_iterator() = default; + generic_iterator(handle seq, ssize_t index) : Policy(seq, index) { } + + reference operator*() const { return Policy::dereference(); } + reference operator[](difference_type n) const { return *(*this + n); } + pointer operator->() const { return **this; } + + It &operator++() { Policy::increment(); return *this; } + It operator++(int) { auto copy = *this; Policy::increment(); return copy; } + It &operator--() { Policy::decrement(); return *this; } + It operator--(int) { auto copy = *this; Policy::decrement(); return copy; } + It &operator+=(difference_type n) { Policy::advance(n); return *this; } + It &operator-=(difference_type n) { Policy::advance(-n); return *this; } + + friend It operator+(const It &a, difference_type n) { auto copy = a; return copy += n; } + friend It operator+(difference_type n, const It &b) { return b + n; } + friend It operator-(const It &a, difference_type n) { auto copy = a; return copy -= n; } + friend difference_type operator-(const It &a, const It &b) { return a.distance_to(b); } + + friend bool operator==(const It &a, const It &b) { return a.equal(b); } + friend bool operator!=(const It &a, const It &b) { return !(a == b); } + friend bool operator< (const It &a, const It &b) { return b - a > 0; } + friend bool operator> (const It &a, const It &b) { return b < a; } + friend bool operator>=(const It &a, const It &b) { return !(a < b); } + friend bool operator<=(const It &a, const It &b) { return !(a > b); } +}; + +NAMESPACE_BEGIN(iterator_policies) +/// Quick proxy class needed to implement ``operator->`` for iterators which can't return pointers +template +struct arrow_proxy { + T value; + + arrow_proxy(T &&value) : value(std::move(value)) { } + T *operator->() const { return &value; } +}; + +/// Lightweight iterator policy using just a simple pointer: see ``PySequence_Fast_ITEMS`` +class sequence_fast_readonly { +protected: + using iterator_category = std::random_access_iterator_tag; + using value_type = handle; + using reference = const handle; + using pointer = arrow_proxy; + + sequence_fast_readonly(handle obj, ssize_t n) : ptr(PySequence_Fast_ITEMS(obj.ptr()) + n) { } + + reference dereference() const { return *ptr; } + void increment() { ++ptr; } + void decrement() { --ptr; } + void advance(ssize_t n) { ptr += n; } + bool equal(const sequence_fast_readonly &b) const { return ptr == b.ptr; } + ssize_t distance_to(const sequence_fast_readonly &b) const { return ptr - b.ptr; } + +private: + PyObject **ptr; +}; + +/// Full read and write access using the sequence protocol: see ``detail::sequence_accessor`` +class sequence_slow_readwrite { +protected: + using iterator_category = std::random_access_iterator_tag; + using value_type = object; + using reference = sequence_accessor; + using pointer = arrow_proxy; + + sequence_slow_readwrite(handle obj, ssize_t index) : obj(obj), index(index) { } + + reference dereference() const { return {obj, static_cast(index)}; } + void increment() { ++index; } + void decrement() { --index; } + void advance(ssize_t n) { index += n; } + bool equal(const sequence_slow_readwrite &b) const { return index == b.index; } + ssize_t distance_to(const sequence_slow_readwrite &b) const { return index - b.index; } + +private: + handle obj; + ssize_t index; +}; + +/// Python's dictionary protocol permits this to be a forward iterator +class dict_readonly { +protected: + using iterator_category = std::forward_iterator_tag; + using value_type = std::pair; + using reference = const value_type; + using pointer = arrow_proxy; + + dict_readonly() = default; + dict_readonly(handle obj, ssize_t pos) : obj(obj), pos(pos) { increment(); } + + reference dereference() const { return {key, value}; } + void increment() { if (!PyDict_Next(obj.ptr(), &pos, &key, &value)) { pos = -1; } } + bool equal(const dict_readonly &b) const { return pos == b.pos; } + +private: + handle obj; + PyObject *key, *value; + ssize_t pos = -1; +}; +NAMESPACE_END(iterator_policies) + +#if !defined(PYPY_VERSION) +using tuple_iterator = generic_iterator; +using list_iterator = generic_iterator; +#else +using tuple_iterator = generic_iterator; +using list_iterator = generic_iterator; +#endif + +using sequence_iterator = generic_iterator; +using dict_iterator = generic_iterator; + +inline bool PyIterable_Check(PyObject *obj) { + PyObject *iter = PyObject_GetIter(obj); + if (iter) { + Py_DECREF(iter); + return true; + } else { + PyErr_Clear(); + return false; + } +} + +inline bool PyNone_Check(PyObject *o) { return o == Py_None; } +#if PY_MAJOR_VERSION >= 3 +inline bool PyEllipsis_Check(PyObject *o) { return o == Py_Ellipsis; } +#endif + +inline bool PyUnicode_Check_Permissive(PyObject *o) { return PyUnicode_Check(o) || PYBIND11_BYTES_CHECK(o); } + +class kwargs_proxy : public handle { +public: + explicit kwargs_proxy(handle h) : handle(h) { } +}; + +class args_proxy : public handle { +public: + explicit args_proxy(handle h) : handle(h) { } + kwargs_proxy operator*() const { return kwargs_proxy(*this); } +}; + +/// Python argument categories (using PEP 448 terms) +template using is_keyword = std::is_base_of; +template using is_s_unpacking = std::is_same; // * unpacking +template using is_ds_unpacking = std::is_same; // ** unpacking +template using is_positional = satisfies_none_of; +template using is_keyword_or_ds = satisfies_any_of; + +// Call argument collector forward declarations +template +class simple_collector; +template +class unpacking_collector; + +NAMESPACE_END(detail) + +// TODO: After the deprecated constructors are removed, this macro can be simplified by +// inheriting ctors: `using Parent::Parent`. It's not an option right now because +// the `using` statement triggers the parent deprecation warning even if the ctor +// isn't even used. +#define PYBIND11_OBJECT_COMMON(Name, Parent, CheckFun) \ + public: \ + PYBIND11_DEPRECATED("Use reinterpret_borrow<"#Name">() or reinterpret_steal<"#Name">()") \ + Name(handle h, bool is_borrowed) : Parent(is_borrowed ? Parent(h, borrowed_t{}) : Parent(h, stolen_t{})) { } \ + Name(handle h, borrowed_t) : Parent(h, borrowed_t{}) { } \ + Name(handle h, stolen_t) : Parent(h, stolen_t{}) { } \ + PYBIND11_DEPRECATED("Use py::isinstance(obj) instead") \ + bool check() const { return m_ptr != nullptr && (bool) CheckFun(m_ptr); } \ + static bool check_(handle h) { return h.ptr() != nullptr && CheckFun(h.ptr()); } + +#define PYBIND11_OBJECT_CVT(Name, Parent, CheckFun, ConvertFun) \ + PYBIND11_OBJECT_COMMON(Name, Parent, CheckFun) \ + /* This is deliberately not 'explicit' to allow implicit conversion from object: */ \ + Name(const object &o) \ + : Parent(check_(o) ? o.inc_ref().ptr() : ConvertFun(o.ptr()), stolen_t{}) \ + { if (!m_ptr) throw error_already_set(); } \ + Name(object &&o) \ + : Parent(check_(o) ? o.release().ptr() : ConvertFun(o.ptr()), stolen_t{}) \ + { if (!m_ptr) throw error_already_set(); } \ + template \ + Name(const ::pybind11::detail::accessor &a) : Name(object(a)) { } + +#define PYBIND11_OBJECT(Name, Parent, CheckFun) \ + PYBIND11_OBJECT_COMMON(Name, Parent, CheckFun) \ + /* This is deliberately not 'explicit' to allow implicit conversion from object: */ \ + Name(const object &o) : Parent(o) { } \ + Name(object &&o) : Parent(std::move(o)) { } + +#define PYBIND11_OBJECT_DEFAULT(Name, Parent, CheckFun) \ + PYBIND11_OBJECT(Name, Parent, CheckFun) \ + Name() : Parent() { } + +/// \addtogroup pytypes +/// @{ + +/** \rst + Wraps a Python iterator so that it can also be used as a C++ input iterator + + Caveat: copying an iterator does not (and cannot) clone the internal + state of the Python iterable. This also applies to the post-increment + operator. This iterator should only be used to retrieve the current + value using ``operator*()``. +\endrst */ +class iterator : public object { +public: + using iterator_category = std::input_iterator_tag; + using difference_type = ssize_t; + using value_type = handle; + using reference = const handle; + using pointer = const handle *; + + PYBIND11_OBJECT_DEFAULT(iterator, object, PyIter_Check) + + iterator& operator++() { + advance(); + return *this; + } + + iterator operator++(int) { + auto rv = *this; + advance(); + return rv; + } + + reference operator*() const { + if (m_ptr && !value.ptr()) { + auto& self = const_cast(*this); + self.advance(); + } + return value; + } + + pointer operator->() const { operator*(); return &value; } + + /** \rst + The value which marks the end of the iteration. ``it == iterator::sentinel()`` + is equivalent to catching ``StopIteration`` in Python. + + .. code-block:: cpp + + void foo(py::iterator it) { + while (it != py::iterator::sentinel()) { + // use `*it` + ++it; + } + } + \endrst */ + static iterator sentinel() { return {}; } + + friend bool operator==(const iterator &a, const iterator &b) { return a->ptr() == b->ptr(); } + friend bool operator!=(const iterator &a, const iterator &b) { return a->ptr() != b->ptr(); } + +private: + void advance() { + value = reinterpret_steal(PyIter_Next(m_ptr)); + if (PyErr_Occurred()) { throw error_already_set(); } + } + +private: + object value = {}; +}; + +class iterable : public object { +public: + PYBIND11_OBJECT_DEFAULT(iterable, object, detail::PyIterable_Check) +}; + +class bytes; + +class str : public object { +public: + PYBIND11_OBJECT_CVT(str, object, detail::PyUnicode_Check_Permissive, raw_str) + + str(const char *c, size_t n) + : object(PyUnicode_FromStringAndSize(c, (ssize_t) n), stolen_t{}) { + if (!m_ptr) pybind11_fail("Could not allocate string object!"); + } + + // 'explicit' is explicitly omitted from the following constructors to allow implicit conversion to py::str from C++ string-like objects + str(const char *c = "") + : object(PyUnicode_FromString(c), stolen_t{}) { + if (!m_ptr) pybind11_fail("Could not allocate string object!"); + } + + str(const std::string &s) : str(s.data(), s.size()) { } + + explicit str(const bytes &b); + + /** \rst + Return a string representation of the object. This is analogous to + the ``str()`` function in Python. + \endrst */ + explicit str(handle h) : object(raw_str(h.ptr()), stolen_t{}) { } + + operator std::string() const { + object temp = *this; + if (PyUnicode_Check(m_ptr)) { + temp = reinterpret_steal(PyUnicode_AsUTF8String(m_ptr)); + if (!temp) + pybind11_fail("Unable to extract string contents! (encoding issue)"); + } + char *buffer; + ssize_t length; + if (PYBIND11_BYTES_AS_STRING_AND_SIZE(temp.ptr(), &buffer, &length)) + pybind11_fail("Unable to extract string contents! (invalid type)"); + return std::string(buffer, (size_t) length); + } + + template + str format(Args &&...args) const { + return attr("format")(std::forward(args)...); + } + +private: + /// Return string representation -- always returns a new reference, even if already a str + static PyObject *raw_str(PyObject *op) { + PyObject *str_value = PyObject_Str(op); +#if PY_MAJOR_VERSION < 3 + if (!str_value) throw error_already_set(); + PyObject *unicode = PyUnicode_FromEncodedObject(str_value, "utf-8", nullptr); + Py_XDECREF(str_value); str_value = unicode; +#endif + return str_value; + } +}; +/// @} pytypes + +inline namespace literals { +/** \rst + String literal version of `str` + \endrst */ +inline str operator"" _s(const char *s, size_t size) { return {s, size}; } +} + +/// \addtogroup pytypes +/// @{ +class bytes : public object { +public: + PYBIND11_OBJECT(bytes, object, PYBIND11_BYTES_CHECK) + + // Allow implicit conversion: + bytes(const char *c = "") + : object(PYBIND11_BYTES_FROM_STRING(c), stolen_t{}) { + if (!m_ptr) pybind11_fail("Could not allocate bytes object!"); + } + + bytes(const char *c, size_t n) + : object(PYBIND11_BYTES_FROM_STRING_AND_SIZE(c, (ssize_t) n), stolen_t{}) { + if (!m_ptr) pybind11_fail("Could not allocate bytes object!"); + } + + // Allow implicit conversion: + bytes(const std::string &s) : bytes(s.data(), s.size()) { } + + explicit bytes(const pybind11::str &s); + + operator std::string() const { + char *buffer; + ssize_t length; + if (PYBIND11_BYTES_AS_STRING_AND_SIZE(m_ptr, &buffer, &length)) + pybind11_fail("Unable to extract bytes contents!"); + return std::string(buffer, (size_t) length); + } +}; + +inline bytes::bytes(const pybind11::str &s) { + object temp = s; + if (PyUnicode_Check(s.ptr())) { + temp = reinterpret_steal(PyUnicode_AsUTF8String(s.ptr())); + if (!temp) + pybind11_fail("Unable to extract string contents! (encoding issue)"); + } + char *buffer; + ssize_t length; + if (PYBIND11_BYTES_AS_STRING_AND_SIZE(temp.ptr(), &buffer, &length)) + pybind11_fail("Unable to extract string contents! (invalid type)"); + auto obj = reinterpret_steal(PYBIND11_BYTES_FROM_STRING_AND_SIZE(buffer, length)); + if (!obj) + pybind11_fail("Could not allocate bytes object!"); + m_ptr = obj.release().ptr(); +} + +inline str::str(const bytes& b) { + char *buffer; + ssize_t length; + if (PYBIND11_BYTES_AS_STRING_AND_SIZE(b.ptr(), &buffer, &length)) + pybind11_fail("Unable to extract bytes contents!"); + auto obj = reinterpret_steal(PyUnicode_FromStringAndSize(buffer, (ssize_t) length)); + if (!obj) + pybind11_fail("Could not allocate string object!"); + m_ptr = obj.release().ptr(); +} + +class none : public object { +public: + PYBIND11_OBJECT(none, object, detail::PyNone_Check) + none() : object(Py_None, borrowed_t{}) { } +}; + +#if PY_MAJOR_VERSION >= 3 +class ellipsis : public object { +public: + PYBIND11_OBJECT(ellipsis, object, detail::PyEllipsis_Check) + ellipsis() : object(Py_Ellipsis, borrowed_t{}) { } +}; +#endif + +class bool_ : public object { +public: + PYBIND11_OBJECT_CVT(bool_, object, PyBool_Check, raw_bool) + bool_() : object(Py_False, borrowed_t{}) { } + // Allow implicit conversion from and to `bool`: + bool_(bool value) : object(value ? Py_True : Py_False, borrowed_t{}) { } + operator bool() const { return m_ptr && PyLong_AsLong(m_ptr) != 0; } + +private: + /// Return the truth value of an object -- always returns a new reference + static PyObject *raw_bool(PyObject *op) { + const auto value = PyObject_IsTrue(op); + if (value == -1) return nullptr; + return handle(value ? Py_True : Py_False).inc_ref().ptr(); + } +}; + +NAMESPACE_BEGIN(detail) +// Converts a value to the given unsigned type. If an error occurs, you get back (Unsigned) -1; +// otherwise you get back the unsigned long or unsigned long long value cast to (Unsigned). +// (The distinction is critically important when casting a returned -1 error value to some other +// unsigned type: (A)-1 != (B)-1 when A and B are unsigned types of different sizes). +template +Unsigned as_unsigned(PyObject *o) { + if (sizeof(Unsigned) <= sizeof(unsigned long) +#if PY_VERSION_HEX < 0x03000000 + || PyInt_Check(o) +#endif + ) { + unsigned long v = PyLong_AsUnsignedLong(o); + return v == (unsigned long) -1 && PyErr_Occurred() ? (Unsigned) -1 : (Unsigned) v; + } + else { + unsigned long long v = PyLong_AsUnsignedLongLong(o); + return v == (unsigned long long) -1 && PyErr_Occurred() ? (Unsigned) -1 : (Unsigned) v; + } +} +NAMESPACE_END(detail) + +class int_ : public object { +public: + PYBIND11_OBJECT_CVT(int_, object, PYBIND11_LONG_CHECK, PyNumber_Long) + int_() : object(PyLong_FromLong(0), stolen_t{}) { } + // Allow implicit conversion from C++ integral types: + template ::value, int> = 0> + int_(T value) { + if (sizeof(T) <= sizeof(long)) { + if (std::is_signed::value) + m_ptr = PyLong_FromLong((long) value); + else + m_ptr = PyLong_FromUnsignedLong((unsigned long) value); + } else { + if (std::is_signed::value) + m_ptr = PyLong_FromLongLong((long long) value); + else + m_ptr = PyLong_FromUnsignedLongLong((unsigned long long) value); + } + if (!m_ptr) pybind11_fail("Could not allocate int object!"); + } + + template ::value, int> = 0> + operator T() const { + return std::is_unsigned::value + ? detail::as_unsigned(m_ptr) + : sizeof(T) <= sizeof(long) + ? (T) PyLong_AsLong(m_ptr) + : (T) PYBIND11_LONG_AS_LONGLONG(m_ptr); + } +}; + +class float_ : public object { +public: + PYBIND11_OBJECT_CVT(float_, object, PyFloat_Check, PyNumber_Float) + // Allow implicit conversion from float/double: + float_(float value) : object(PyFloat_FromDouble((double) value), stolen_t{}) { + if (!m_ptr) pybind11_fail("Could not allocate float object!"); + } + float_(double value = .0) : object(PyFloat_FromDouble((double) value), stolen_t{}) { + if (!m_ptr) pybind11_fail("Could not allocate float object!"); + } + operator float() const { return (float) PyFloat_AsDouble(m_ptr); } + operator double() const { return (double) PyFloat_AsDouble(m_ptr); } +}; + +class weakref : public object { +public: + PYBIND11_OBJECT_DEFAULT(weakref, object, PyWeakref_Check) + explicit weakref(handle obj, handle callback = {}) + : object(PyWeakref_NewRef(obj.ptr(), callback.ptr()), stolen_t{}) { + if (!m_ptr) pybind11_fail("Could not allocate weak reference!"); + } +}; + +class slice : public object { +public: + PYBIND11_OBJECT_DEFAULT(slice, object, PySlice_Check) + slice(ssize_t start_, ssize_t stop_, ssize_t step_) { + int_ start(start_), stop(stop_), step(step_); + m_ptr = PySlice_New(start.ptr(), stop.ptr(), step.ptr()); + if (!m_ptr) pybind11_fail("Could not allocate slice object!"); + } + bool compute(size_t length, size_t *start, size_t *stop, size_t *step, + size_t *slicelength) const { + return PySlice_GetIndicesEx((PYBIND11_SLICE_OBJECT *) m_ptr, + (ssize_t) length, (ssize_t *) start, + (ssize_t *) stop, (ssize_t *) step, + (ssize_t *) slicelength) == 0; + } +}; + +class capsule : public object { +public: + PYBIND11_OBJECT_DEFAULT(capsule, object, PyCapsule_CheckExact) + PYBIND11_DEPRECATED("Use reinterpret_borrow() or reinterpret_steal()") + capsule(PyObject *ptr, bool is_borrowed) : object(is_borrowed ? object(ptr, borrowed_t{}) : object(ptr, stolen_t{})) { } + + explicit capsule(const void *value, const char *name = nullptr, void (*destructor)(PyObject *) = nullptr) + : object(PyCapsule_New(const_cast(value), name, destructor), stolen_t{}) { + if (!m_ptr) + pybind11_fail("Could not allocate capsule object!"); + } + + PYBIND11_DEPRECATED("Please pass a destructor that takes a void pointer as input") + capsule(const void *value, void (*destruct)(PyObject *)) + : object(PyCapsule_New(const_cast(value), nullptr, destruct), stolen_t{}) { + if (!m_ptr) + pybind11_fail("Could not allocate capsule object!"); + } + + capsule(const void *value, void (*destructor)(void *)) { + m_ptr = PyCapsule_New(const_cast(value), nullptr, [](PyObject *o) { + auto destructor = reinterpret_cast(PyCapsule_GetContext(o)); + void *ptr = PyCapsule_GetPointer(o, nullptr); + destructor(ptr); + }); + + if (!m_ptr) + pybind11_fail("Could not allocate capsule object!"); + + if (PyCapsule_SetContext(m_ptr, (void *) destructor) != 0) + pybind11_fail("Could not set capsule context!"); + } + + capsule(void (*destructor)()) { + m_ptr = PyCapsule_New(reinterpret_cast(destructor), nullptr, [](PyObject *o) { + auto destructor = reinterpret_cast(PyCapsule_GetPointer(o, nullptr)); + destructor(); + }); + + if (!m_ptr) + pybind11_fail("Could not allocate capsule object!"); + } + + template operator T *() const { + auto name = this->name(); + T * result = static_cast(PyCapsule_GetPointer(m_ptr, name)); + if (!result) pybind11_fail("Unable to extract capsule contents!"); + return result; + } + + const char *name() const { return PyCapsule_GetName(m_ptr); } +}; + +class tuple : public object { +public: + PYBIND11_OBJECT_CVT(tuple, object, PyTuple_Check, PySequence_Tuple) + explicit tuple(size_t size = 0) : object(PyTuple_New((ssize_t) size), stolen_t{}) { + if (!m_ptr) pybind11_fail("Could not allocate tuple object!"); + } + size_t size() const { return (size_t) PyTuple_Size(m_ptr); } + detail::tuple_accessor operator[](size_t index) const { return {*this, index}; } + detail::item_accessor operator[](handle h) const { return object::operator[](h); } + detail::tuple_iterator begin() const { return {*this, 0}; } + detail::tuple_iterator end() const { return {*this, PyTuple_GET_SIZE(m_ptr)}; } +}; + +class dict : public object { +public: + PYBIND11_OBJECT_CVT(dict, object, PyDict_Check, raw_dict) + dict() : object(PyDict_New(), stolen_t{}) { + if (!m_ptr) pybind11_fail("Could not allocate dict object!"); + } + template ...>::value>, + // MSVC workaround: it can't compile an out-of-line definition, so defer the collector + typename collector = detail::deferred_t, Args...>> + explicit dict(Args &&...args) : dict(collector(std::forward(args)...).kwargs()) { } + + size_t size() const { return (size_t) PyDict_Size(m_ptr); } + detail::dict_iterator begin() const { return {*this, 0}; } + detail::dict_iterator end() const { return {}; } + void clear() const { PyDict_Clear(ptr()); } + bool contains(handle key) const { return PyDict_Contains(ptr(), key.ptr()) == 1; } + bool contains(const char *key) const { return PyDict_Contains(ptr(), pybind11::str(key).ptr()) == 1; } + +private: + /// Call the `dict` Python type -- always returns a new reference + static PyObject *raw_dict(PyObject *op) { + if (PyDict_Check(op)) + return handle(op).inc_ref().ptr(); + return PyObject_CallFunctionObjArgs((PyObject *) &PyDict_Type, op, nullptr); + } +}; + +class sequence : public object { +public: + PYBIND11_OBJECT_DEFAULT(sequence, object, PySequence_Check) + size_t size() const { return (size_t) PySequence_Size(m_ptr); } + detail::sequence_accessor operator[](size_t index) const { return {*this, index}; } + detail::item_accessor operator[](handle h) const { return object::operator[](h); } + detail::sequence_iterator begin() const { return {*this, 0}; } + detail::sequence_iterator end() const { return {*this, PySequence_Size(m_ptr)}; } +}; + +class list : public object { +public: + PYBIND11_OBJECT_CVT(list, object, PyList_Check, PySequence_List) + explicit list(size_t size = 0) : object(PyList_New((ssize_t) size), stolen_t{}) { + if (!m_ptr) pybind11_fail("Could not allocate list object!"); + } + size_t size() const { return (size_t) PyList_Size(m_ptr); } + detail::list_accessor operator[](size_t index) const { return {*this, index}; } + detail::item_accessor operator[](handle h) const { return object::operator[](h); } + detail::list_iterator begin() const { return {*this, 0}; } + detail::list_iterator end() const { return {*this, PyList_GET_SIZE(m_ptr)}; } + template void append(T &&val) const { + PyList_Append(m_ptr, detail::object_or_cast(std::forward(val)).ptr()); + } +}; + +class args : public tuple { PYBIND11_OBJECT_DEFAULT(args, tuple, PyTuple_Check) }; +class kwargs : public dict { PYBIND11_OBJECT_DEFAULT(kwargs, dict, PyDict_Check) }; + +class set : public object { +public: + PYBIND11_OBJECT_CVT(set, object, PySet_Check, PySet_New) + set() : object(PySet_New(nullptr), stolen_t{}) { + if (!m_ptr) pybind11_fail("Could not allocate set object!"); + } + size_t size() const { return (size_t) PySet_Size(m_ptr); } + template bool add(T &&val) const { + return PySet_Add(m_ptr, detail::object_or_cast(std::forward(val)).ptr()) == 0; + } + void clear() const { PySet_Clear(m_ptr); } +}; + +class function : public object { +public: + PYBIND11_OBJECT_DEFAULT(function, object, PyCallable_Check) + handle cpp_function() const { + handle fun = detail::get_function(m_ptr); + if (fun && PyCFunction_Check(fun.ptr())) + return fun; + return handle(); + } + bool is_cpp_function() const { return (bool) cpp_function(); } +}; + +class buffer : public object { +public: + PYBIND11_OBJECT_DEFAULT(buffer, object, PyObject_CheckBuffer) + + buffer_info request(bool writable = false) { + int flags = PyBUF_STRIDES | PyBUF_FORMAT; + if (writable) flags |= PyBUF_WRITABLE; + Py_buffer *view = new Py_buffer(); + if (PyObject_GetBuffer(m_ptr, view, flags) != 0) { + delete view; + throw error_already_set(); + } + return buffer_info(view); + } +}; + +class memoryview : public object { +public: + explicit memoryview(const buffer_info& info) { + static Py_buffer buf { }; + // Py_buffer uses signed sizes, strides and shape!.. + static std::vector py_strides { }; + static std::vector py_shape { }; + buf.buf = info.ptr; + buf.itemsize = info.itemsize; + buf.format = const_cast(info.format.c_str()); + buf.ndim = (int) info.ndim; + buf.len = info.size; + py_strides.clear(); + py_shape.clear(); + for (size_t i = 0; i < (size_t) info.ndim; ++i) { + py_strides.push_back(info.strides[i]); + py_shape.push_back(info.shape[i]); + } + buf.strides = py_strides.data(); + buf.shape = py_shape.data(); + buf.suboffsets = nullptr; + buf.readonly = false; + buf.internal = nullptr; + + m_ptr = PyMemoryView_FromBuffer(&buf); + if (!m_ptr) + pybind11_fail("Unable to create memoryview from buffer descriptor"); + } + + PYBIND11_OBJECT_CVT(memoryview, object, PyMemoryView_Check, PyMemoryView_FromObject) +}; +/// @} pytypes + +/// \addtogroup python_builtins +/// @{ +inline size_t len(handle h) { + ssize_t result = PyObject_Length(h.ptr()); + if (result < 0) + pybind11_fail("Unable to compute length of object"); + return (size_t) result; +} + +inline str repr(handle h) { + PyObject *str_value = PyObject_Repr(h.ptr()); + if (!str_value) throw error_already_set(); +#if PY_MAJOR_VERSION < 3 + PyObject *unicode = PyUnicode_FromEncodedObject(str_value, "utf-8", nullptr); + Py_XDECREF(str_value); str_value = unicode; + if (!str_value) throw error_already_set(); +#endif + return reinterpret_steal(str_value); +} + +inline iterator iter(handle obj) { + PyObject *result = PyObject_GetIter(obj.ptr()); + if (!result) { throw error_already_set(); } + return reinterpret_steal(result); +} +/// @} python_builtins + +NAMESPACE_BEGIN(detail) +template iterator object_api::begin() const { return iter(derived()); } +template iterator object_api::end() const { return iterator::sentinel(); } +template item_accessor object_api::operator[](handle key) const { + return {derived(), reinterpret_borrow(key)}; +} +template item_accessor object_api::operator[](const char *key) const { + return {derived(), pybind11::str(key)}; +} +template obj_attr_accessor object_api::attr(handle key) const { + return {derived(), reinterpret_borrow(key)}; +} +template str_attr_accessor object_api::attr(const char *key) const { + return {derived(), key}; +} +template args_proxy object_api::operator*() const { + return args_proxy(derived().ptr()); +} +template template bool object_api::contains(T &&item) const { + return attr("__contains__")(std::forward(item)).template cast(); +} + +template +pybind11::str object_api::str() const { return pybind11::str(derived()); } + +template +str_attr_accessor object_api::doc() const { return attr("__doc__"); } + +template +handle object_api::get_type() const { return (PyObject *) Py_TYPE(derived().ptr()); } + +template +bool object_api::rich_compare(object_api const &other, int value) const { + int rv = PyObject_RichCompareBool(derived().ptr(), other.derived().ptr(), value); + if (rv == -1) + throw error_already_set(); + return rv == 1; +} + +#define PYBIND11_MATH_OPERATOR_UNARY(op, fn) \ + template object object_api::op() const { \ + object result = reinterpret_steal(fn(derived().ptr())); \ + if (!result.ptr()) \ + throw error_already_set(); \ + return result; \ + } + +#define PYBIND11_MATH_OPERATOR_BINARY(op, fn) \ + template \ + object object_api::op(object_api const &other) const { \ + object result = reinterpret_steal( \ + fn(derived().ptr(), other.derived().ptr())); \ + if (!result.ptr()) \ + throw error_already_set(); \ + return result; \ + } + +PYBIND11_MATH_OPERATOR_UNARY (operator~, PyNumber_Invert) +PYBIND11_MATH_OPERATOR_UNARY (operator-, PyNumber_Negative) +PYBIND11_MATH_OPERATOR_BINARY(operator+, PyNumber_Add) +PYBIND11_MATH_OPERATOR_BINARY(operator+=, PyNumber_InPlaceAdd) +PYBIND11_MATH_OPERATOR_BINARY(operator-, PyNumber_Subtract) +PYBIND11_MATH_OPERATOR_BINARY(operator-=, PyNumber_InPlaceSubtract) +PYBIND11_MATH_OPERATOR_BINARY(operator*, PyNumber_Multiply) +PYBIND11_MATH_OPERATOR_BINARY(operator*=, PyNumber_InPlaceMultiply) +PYBIND11_MATH_OPERATOR_BINARY(operator/, PyNumber_TrueDivide) +PYBIND11_MATH_OPERATOR_BINARY(operator/=, PyNumber_InPlaceTrueDivide) +PYBIND11_MATH_OPERATOR_BINARY(operator|, PyNumber_Or) +PYBIND11_MATH_OPERATOR_BINARY(operator|=, PyNumber_InPlaceOr) +PYBIND11_MATH_OPERATOR_BINARY(operator&, PyNumber_And) +PYBIND11_MATH_OPERATOR_BINARY(operator&=, PyNumber_InPlaceAnd) +PYBIND11_MATH_OPERATOR_BINARY(operator^, PyNumber_Xor) +PYBIND11_MATH_OPERATOR_BINARY(operator^=, PyNumber_InPlaceXor) +PYBIND11_MATH_OPERATOR_BINARY(operator<<, PyNumber_Lshift) +PYBIND11_MATH_OPERATOR_BINARY(operator<<=, PyNumber_InPlaceLshift) +PYBIND11_MATH_OPERATOR_BINARY(operator>>, PyNumber_Rshift) +PYBIND11_MATH_OPERATOR_BINARY(operator>>=, PyNumber_InPlaceRshift) + +#undef PYBIND11_MATH_OPERATOR_UNARY +#undef PYBIND11_MATH_OPERATOR_BINARY + +NAMESPACE_END(detail) +NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/ptocr/postprocess/piexlmerge/include/pybind11/stl.h b/ptocr/postprocess/piexlmerge/include/pybind11/stl.h new file mode 100644 index 0000000..32f8d29 --- /dev/null +++ b/ptocr/postprocess/piexlmerge/include/pybind11/stl.h @@ -0,0 +1,386 @@ +/* + pybind11/stl.h: Transparent conversion for STL data types + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "pybind11.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable: 4127) // warning C4127: Conditional expression is constant +#endif + +#ifdef __has_include +// std::optional (but including it in c++14 mode isn't allowed) +# if defined(PYBIND11_CPP17) && __has_include() +# include +# define PYBIND11_HAS_OPTIONAL 1 +# endif +// std::experimental::optional (but not allowed in c++11 mode) +# if defined(PYBIND11_CPP14) && (__has_include() && \ + !__has_include()) +# include +# define PYBIND11_HAS_EXP_OPTIONAL 1 +# endif +// std::variant +# if defined(PYBIND11_CPP17) && __has_include() +# include +# define PYBIND11_HAS_VARIANT 1 +# endif +#elif defined(_MSC_VER) && defined(PYBIND11_CPP17) +# include +# include +# define PYBIND11_HAS_OPTIONAL 1 +# define PYBIND11_HAS_VARIANT 1 +#endif + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +NAMESPACE_BEGIN(detail) + +/// Extracts an const lvalue reference or rvalue reference for U based on the type of T (e.g. for +/// forwarding a container element). Typically used indirect via forwarded_type(), below. +template +using forwarded_type = conditional_t< + std::is_lvalue_reference::value, remove_reference_t &, remove_reference_t &&>; + +/// Forwards a value U as rvalue or lvalue according to whether T is rvalue or lvalue; typically +/// used for forwarding a container's elements. +template +forwarded_type forward_like(U &&u) { + return std::forward>(std::forward(u)); +} + +template struct set_caster { + using type = Type; + using key_conv = make_caster; + + bool load(handle src, bool convert) { + if (!isinstance(src)) + return false; + auto s = reinterpret_borrow(src); + value.clear(); + for (auto entry : s) { + key_conv conv; + if (!conv.load(entry, convert)) + return false; + value.insert(cast_op(std::move(conv))); + } + return true; + } + + template + static handle cast(T &&src, return_value_policy policy, handle parent) { + if (!std::is_lvalue_reference::value) + policy = return_value_policy_override::policy(policy); + pybind11::set s; + for (auto &&value : src) { + auto value_ = reinterpret_steal(key_conv::cast(forward_like(value), policy, parent)); + if (!value_ || !s.add(value_)) + return handle(); + } + return s.release(); + } + + PYBIND11_TYPE_CASTER(type, _("Set[") + key_conv::name + _("]")); +}; + +template struct map_caster { + using key_conv = make_caster; + using value_conv = make_caster; + + bool load(handle src, bool convert) { + if (!isinstance(src)) + return false; + auto d = reinterpret_borrow(src); + value.clear(); + for (auto it : d) { + key_conv kconv; + value_conv vconv; + if (!kconv.load(it.first.ptr(), convert) || + !vconv.load(it.second.ptr(), convert)) + return false; + value.emplace(cast_op(std::move(kconv)), cast_op(std::move(vconv))); + } + return true; + } + + template + static handle cast(T &&src, return_value_policy policy, handle parent) { + dict d; + return_value_policy policy_key = policy; + return_value_policy policy_value = policy; + if (!std::is_lvalue_reference::value) { + policy_key = return_value_policy_override::policy(policy_key); + policy_value = return_value_policy_override::policy(policy_value); + } + for (auto &&kv : src) { + auto key = reinterpret_steal(key_conv::cast(forward_like(kv.first), policy_key, parent)); + auto value = reinterpret_steal(value_conv::cast(forward_like(kv.second), policy_value, parent)); + if (!key || !value) + return handle(); + d[key] = value; + } + return d.release(); + } + + PYBIND11_TYPE_CASTER(Type, _("Dict[") + key_conv::name + _(", ") + value_conv::name + _("]")); +}; + +template struct list_caster { + using value_conv = make_caster; + + bool load(handle src, bool convert) { + if (!isinstance(src) || isinstance(src)) + return false; + auto s = reinterpret_borrow(src); + value.clear(); + reserve_maybe(s, &value); + for (auto it : s) { + value_conv conv; + if (!conv.load(it, convert)) + return false; + value.push_back(cast_op(std::move(conv))); + } + return true; + } + +private: + template ().reserve(0)), void>::value, int> = 0> + void reserve_maybe(sequence s, Type *) { value.reserve(s.size()); } + void reserve_maybe(sequence, void *) { } + +public: + template + static handle cast(T &&src, return_value_policy policy, handle parent) { + if (!std::is_lvalue_reference::value) + policy = return_value_policy_override::policy(policy); + list l(src.size()); + size_t index = 0; + for (auto &&value : src) { + auto value_ = reinterpret_steal(value_conv::cast(forward_like(value), policy, parent)); + if (!value_) + return handle(); + PyList_SET_ITEM(l.ptr(), (ssize_t) index++, value_.release().ptr()); // steals a reference + } + return l.release(); + } + + PYBIND11_TYPE_CASTER(Type, _("List[") + value_conv::name + _("]")); +}; + +template struct type_caster> + : list_caster, Type> { }; + +template struct type_caster> + : list_caster, Type> { }; + +template struct type_caster> + : list_caster, Type> { }; + +template struct array_caster { + using value_conv = make_caster; + +private: + template + bool require_size(enable_if_t size) { + if (value.size() != size) + value.resize(size); + return true; + } + template + bool require_size(enable_if_t size) { + return size == Size; + } + +public: + bool load(handle src, bool convert) { + if (!isinstance(src)) + return false; + auto l = reinterpret_borrow(src); + if (!require_size(l.size())) + return false; + size_t ctr = 0; + for (auto it : l) { + value_conv conv; + if (!conv.load(it, convert)) + return false; + value[ctr++] = cast_op(std::move(conv)); + } + return true; + } + + template + static handle cast(T &&src, return_value_policy policy, handle parent) { + list l(src.size()); + size_t index = 0; + for (auto &&value : src) { + auto value_ = reinterpret_steal(value_conv::cast(forward_like(value), policy, parent)); + if (!value_) + return handle(); + PyList_SET_ITEM(l.ptr(), (ssize_t) index++, value_.release().ptr()); // steals a reference + } + return l.release(); + } + + PYBIND11_TYPE_CASTER(ArrayType, _("List[") + value_conv::name + _(_(""), _("[") + _() + _("]")) + _("]")); +}; + +template struct type_caster> + : array_caster, Type, false, Size> { }; + +template struct type_caster> + : array_caster, Type, true> { }; + +template struct type_caster> + : set_caster, Key> { }; + +template struct type_caster> + : set_caster, Key> { }; + +template struct type_caster> + : map_caster, Key, Value> { }; + +template struct type_caster> + : map_caster, Key, Value> { }; + +// This type caster is intended to be used for std::optional and std::experimental::optional +template struct optional_caster { + using value_conv = make_caster; + + template + static handle cast(T_ &&src, return_value_policy policy, handle parent) { + if (!src) + return none().inc_ref(); + policy = return_value_policy_override::policy(policy); + return value_conv::cast(*std::forward(src), policy, parent); + } + + bool load(handle src, bool convert) { + if (!src) { + return false; + } else if (src.is_none()) { + return true; // default-constructed value is already empty + } + value_conv inner_caster; + if (!inner_caster.load(src, convert)) + return false; + + value.emplace(cast_op(std::move(inner_caster))); + return true; + } + + PYBIND11_TYPE_CASTER(T, _("Optional[") + value_conv::name + _("]")); +}; + +#if PYBIND11_HAS_OPTIONAL +template struct type_caster> + : public optional_caster> {}; + +template<> struct type_caster + : public void_caster {}; +#endif + +#if PYBIND11_HAS_EXP_OPTIONAL +template struct type_caster> + : public optional_caster> {}; + +template<> struct type_caster + : public void_caster {}; +#endif + +/// Visit a variant and cast any found type to Python +struct variant_caster_visitor { + return_value_policy policy; + handle parent; + + using result_type = handle; // required by boost::variant in C++11 + + template + result_type operator()(T &&src) const { + return make_caster::cast(std::forward(src), policy, parent); + } +}; + +/// Helper class which abstracts away variant's `visit` function. `std::variant` and similar +/// `namespace::variant` types which provide a `namespace::visit()` function are handled here +/// automatically using argument-dependent lookup. Users can provide specializations for other +/// variant-like classes, e.g. `boost::variant` and `boost::apply_visitor`. +template class Variant> +struct visit_helper { + template + static auto call(Args &&...args) -> decltype(visit(std::forward(args)...)) { + return visit(std::forward(args)...); + } +}; + +/// Generic variant caster +template struct variant_caster; + +template class V, typename... Ts> +struct variant_caster> { + static_assert(sizeof...(Ts) > 0, "Variant must consist of at least one alternative."); + + template + bool load_alternative(handle src, bool convert, type_list) { + auto caster = make_caster(); + if (caster.load(src, convert)) { + value = cast_op(caster); + return true; + } + return load_alternative(src, convert, type_list{}); + } + + bool load_alternative(handle, bool, type_list<>) { return false; } + + bool load(handle src, bool convert) { + // Do a first pass without conversions to improve constructor resolution. + // E.g. `py::int_(1).cast>()` needs to fill the `int` + // slot of the variant. Without two-pass loading `double` would be filled + // because it appears first and a conversion is possible. + if (convert && load_alternative(src, false, type_list{})) + return true; + return load_alternative(src, convert, type_list{}); + } + + template + static handle cast(Variant &&src, return_value_policy policy, handle parent) { + return visit_helper::call(variant_caster_visitor{policy, parent}, + std::forward(src)); + } + + using Type = V; + PYBIND11_TYPE_CASTER(Type, _("Union[") + detail::concat(make_caster::name...) + _("]")); +}; + +#if PYBIND11_HAS_VARIANT +template +struct type_caster> : variant_caster> { }; +#endif + +NAMESPACE_END(detail) + +inline std::ostream &operator<<(std::ostream &os, const handle &obj) { + os << (std::string) str(obj); + return os; +} + +NAMESPACE_END(PYBIND11_NAMESPACE) + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif diff --git a/ptocr/postprocess/piexlmerge/include/pybind11/stl_bind.h b/ptocr/postprocess/piexlmerge/include/pybind11/stl_bind.h new file mode 100644 index 0000000..38dd68f --- /dev/null +++ b/ptocr/postprocess/piexlmerge/include/pybind11/stl_bind.h @@ -0,0 +1,599 @@ +/* + pybind11/std_bind.h: Binding generators for STL data types + + Copyright (c) 2016 Sergey Lyskov and Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "detail/common.h" +#include "operators.h" + +#include +#include + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +NAMESPACE_BEGIN(detail) + +/* SFINAE helper class used by 'is_comparable */ +template struct container_traits { + template static std::true_type test_comparable(decltype(std::declval() == std::declval())*); + template static std::false_type test_comparable(...); + template static std::true_type test_value(typename T2::value_type *); + template static std::false_type test_value(...); + template static std::true_type test_pair(typename T2::first_type *, typename T2::second_type *); + template static std::false_type test_pair(...); + + static constexpr const bool is_comparable = std::is_same(nullptr))>::value; + static constexpr const bool is_pair = std::is_same(nullptr, nullptr))>::value; + static constexpr const bool is_vector = std::is_same(nullptr))>::value; + static constexpr const bool is_element = !is_pair && !is_vector; +}; + +/* Default: is_comparable -> std::false_type */ +template +struct is_comparable : std::false_type { }; + +/* For non-map data structures, check whether operator== can be instantiated */ +template +struct is_comparable< + T, enable_if_t::is_element && + container_traits::is_comparable>> + : std::true_type { }; + +/* For a vector/map data structure, recursively check the value type (which is std::pair for maps) */ +template +struct is_comparable::is_vector>> { + static constexpr const bool value = + is_comparable::value; +}; + +/* For pairs, recursively check the two data types */ +template +struct is_comparable::is_pair>> { + static constexpr const bool value = + is_comparable::value && + is_comparable::value; +}; + +/* Fallback functions */ +template void vector_if_copy_constructible(const Args &...) { } +template void vector_if_equal_operator(const Args &...) { } +template void vector_if_insertion_operator(const Args &...) { } +template void vector_modifiers(const Args &...) { } + +template +void vector_if_copy_constructible(enable_if_t::value, Class_> &cl) { + cl.def(init(), "Copy constructor"); +} + +template +void vector_if_equal_operator(enable_if_t::value, Class_> &cl) { + using T = typename Vector::value_type; + + cl.def(self == self); + cl.def(self != self); + + cl.def("count", + [](const Vector &v, const T &x) { + return std::count(v.begin(), v.end(), x); + }, + arg("x"), + "Return the number of times ``x`` appears in the list" + ); + + cl.def("remove", [](Vector &v, const T &x) { + auto p = std::find(v.begin(), v.end(), x); + if (p != v.end()) + v.erase(p); + else + throw value_error(); + }, + arg("x"), + "Remove the first item from the list whose value is x. " + "It is an error if there is no such item." + ); + + cl.def("__contains__", + [](const Vector &v, const T &x) { + return std::find(v.begin(), v.end(), x) != v.end(); + }, + arg("x"), + "Return true the container contains ``x``" + ); +} + +// Vector modifiers -- requires a copyable vector_type: +// (Technically, some of these (pop and __delitem__) don't actually require copyability, but it seems +// silly to allow deletion but not insertion, so include them here too.) +template +void vector_modifiers(enable_if_t::value, Class_> &cl) { + using T = typename Vector::value_type; + using SizeType = typename Vector::size_type; + using DiffType = typename Vector::difference_type; + + cl.def("append", + [](Vector &v, const T &value) { v.push_back(value); }, + arg("x"), + "Add an item to the end of the list"); + + cl.def(init([](iterable it) { + auto v = std::unique_ptr(new Vector()); + v->reserve(len(it)); + for (handle h : it) + v->push_back(h.cast()); + return v.release(); + })); + + cl.def("extend", + [](Vector &v, const Vector &src) { + v.insert(v.end(), src.begin(), src.end()); + }, + arg("L"), + "Extend the list by appending all the items in the given list" + ); + + cl.def("insert", + [](Vector &v, SizeType i, const T &x) { + if (i > v.size()) + throw index_error(); + v.insert(v.begin() + (DiffType) i, x); + }, + arg("i") , arg("x"), + "Insert an item at a given position." + ); + + cl.def("pop", + [](Vector &v) { + if (v.empty()) + throw index_error(); + T t = v.back(); + v.pop_back(); + return t; + }, + "Remove and return the last item" + ); + + cl.def("pop", + [](Vector &v, SizeType i) { + if (i >= v.size()) + throw index_error(); + T t = v[i]; + v.erase(v.begin() + (DiffType) i); + return t; + }, + arg("i"), + "Remove and return the item at index ``i``" + ); + + cl.def("__setitem__", + [](Vector &v, SizeType i, const T &t) { + if (i >= v.size()) + throw index_error(); + v[i] = t; + } + ); + + /// Slicing protocol + cl.def("__getitem__", + [](const Vector &v, slice slice) -> Vector * { + size_t start, stop, step, slicelength; + + if (!slice.compute(v.size(), &start, &stop, &step, &slicelength)) + throw error_already_set(); + + Vector *seq = new Vector(); + seq->reserve((size_t) slicelength); + + for (size_t i=0; ipush_back(v[start]); + start += step; + } + return seq; + }, + arg("s"), + "Retrieve list elements using a slice object" + ); + + cl.def("__setitem__", + [](Vector &v, slice slice, const Vector &value) { + size_t start, stop, step, slicelength; + if (!slice.compute(v.size(), &start, &stop, &step, &slicelength)) + throw error_already_set(); + + if (slicelength != value.size()) + throw std::runtime_error("Left and right hand size of slice assignment have different sizes!"); + + for (size_t i=0; i= v.size()) + throw index_error(); + v.erase(v.begin() + DiffType(i)); + }, + "Delete the list elements at index ``i``" + ); + + cl.def("__delitem__", + [](Vector &v, slice slice) { + size_t start, stop, step, slicelength; + + if (!slice.compute(v.size(), &start, &stop, &step, &slicelength)) + throw error_already_set(); + + if (step == 1 && false) { + v.erase(v.begin() + (DiffType) start, v.begin() + DiffType(start + slicelength)); + } else { + for (size_t i = 0; i < slicelength; ++i) { + v.erase(v.begin() + DiffType(start)); + start += step - 1; + } + } + }, + "Delete list elements using a slice object" + ); + +} + +// If the type has an operator[] that doesn't return a reference (most notably std::vector), +// we have to access by copying; otherwise we return by reference. +template using vector_needs_copy = negation< + std::is_same()[typename Vector::size_type()]), typename Vector::value_type &>>; + +// The usual case: access and iterate by reference +template +void vector_accessor(enable_if_t::value, Class_> &cl) { + using T = typename Vector::value_type; + using SizeType = typename Vector::size_type; + using ItType = typename Vector::iterator; + + cl.def("__getitem__", + [](Vector &v, SizeType i) -> T & { + if (i >= v.size()) + throw index_error(); + return v[i]; + }, + return_value_policy::reference_internal // ref + keepalive + ); + + cl.def("__iter__", + [](Vector &v) { + return make_iterator< + return_value_policy::reference_internal, ItType, ItType, T&>( + v.begin(), v.end()); + }, + keep_alive<0, 1>() /* Essential: keep list alive while iterator exists */ + ); +} + +// The case for special objects, like std::vector, that have to be returned-by-copy: +template +void vector_accessor(enable_if_t::value, Class_> &cl) { + using T = typename Vector::value_type; + using SizeType = typename Vector::size_type; + using ItType = typename Vector::iterator; + cl.def("__getitem__", + [](const Vector &v, SizeType i) -> T { + if (i >= v.size()) + throw index_error(); + return v[i]; + } + ); + + cl.def("__iter__", + [](Vector &v) { + return make_iterator< + return_value_policy::copy, ItType, ItType, T>( + v.begin(), v.end()); + }, + keep_alive<0, 1>() /* Essential: keep list alive while iterator exists */ + ); +} + +template auto vector_if_insertion_operator(Class_ &cl, std::string const &name) + -> decltype(std::declval() << std::declval(), void()) { + using size_type = typename Vector::size_type; + + cl.def("__repr__", + [name](Vector &v) { + std::ostringstream s; + s << name << '['; + for (size_type i=0; i < v.size(); ++i) { + s << v[i]; + if (i != v.size() - 1) + s << ", "; + } + s << ']'; + return s.str(); + }, + "Return the canonical string representation of this list." + ); +} + +// Provide the buffer interface for vectors if we have data() and we have a format for it +// GCC seems to have "void std::vector::data()" - doing SFINAE on the existence of data() is insufficient, we need to check it returns an appropriate pointer +template +struct vector_has_data_and_format : std::false_type {}; +template +struct vector_has_data_and_format::format(), std::declval().data()), typename Vector::value_type*>::value>> : std::true_type {}; + +// Add the buffer interface to a vector +template +enable_if_t...>::value> +vector_buffer(Class_& cl) { + using T = typename Vector::value_type; + + static_assert(vector_has_data_and_format::value, "There is not an appropriate format descriptor for this vector"); + + // numpy.h declares this for arbitrary types, but it may raise an exception and crash hard at runtime if PYBIND11_NUMPY_DTYPE hasn't been called, so check here + format_descriptor::format(); + + cl.def_buffer([](Vector& v) -> buffer_info { + return buffer_info(v.data(), static_cast(sizeof(T)), format_descriptor::format(), 1, {v.size()}, {sizeof(T)}); + }); + + cl.def(init([](buffer buf) { + auto info = buf.request(); + if (info.ndim != 1 || info.strides[0] % static_cast(sizeof(T))) + throw type_error("Only valid 1D buffers can be copied to a vector"); + if (!detail::compare_buffer_info::compare(info) || (ssize_t) sizeof(T) != info.itemsize) + throw type_error("Format mismatch (Python: " + info.format + " C++: " + format_descriptor::format() + ")"); + + auto vec = std::unique_ptr(new Vector()); + vec->reserve((size_t) info.shape[0]); + T *p = static_cast(info.ptr); + ssize_t step = info.strides[0] / static_cast(sizeof(T)); + T *end = p + info.shape[0] * step; + for (; p != end; p += step) + vec->push_back(*p); + return vec.release(); + })); + + return; +} + +template +enable_if_t...>::value> vector_buffer(Class_&) {} + +NAMESPACE_END(detail) + +// +// std::vector +// +template , typename... Args> +class_ bind_vector(handle scope, std::string const &name, Args&&... args) { + using Class_ = class_; + + // If the value_type is unregistered (e.g. a converting type) or is itself registered + // module-local then make the vector binding module-local as well: + using vtype = typename Vector::value_type; + auto vtype_info = detail::get_type_info(typeid(vtype)); + bool local = !vtype_info || vtype_info->module_local; + + Class_ cl(scope, name.c_str(), pybind11::module_local(local), std::forward(args)...); + + // Declare the buffer interface if a buffer_protocol() is passed in + detail::vector_buffer(cl); + + cl.def(init<>()); + + // Register copy constructor (if possible) + detail::vector_if_copy_constructible(cl); + + // Register comparison-related operators and functions (if possible) + detail::vector_if_equal_operator(cl); + + // Register stream insertion operator (if possible) + detail::vector_if_insertion_operator(cl, name); + + // Modifiers require copyable vector value type + detail::vector_modifiers(cl); + + // Accessor and iterator; return by value if copyable, otherwise we return by ref + keep-alive + detail::vector_accessor(cl); + + cl.def("__bool__", + [](const Vector &v) -> bool { + return !v.empty(); + }, + "Check whether the list is nonempty" + ); + + cl.def("__len__", &Vector::size); + + + + +#if 0 + // C++ style functions deprecated, leaving it here as an example + cl.def(init()); + + cl.def("resize", + (void (Vector::*) (size_type count)) & Vector::resize, + "changes the number of elements stored"); + + cl.def("erase", + [](Vector &v, SizeType i) { + if (i >= v.size()) + throw index_error(); + v.erase(v.begin() + i); + }, "erases element at index ``i``"); + + cl.def("empty", &Vector::empty, "checks whether the container is empty"); + cl.def("size", &Vector::size, "returns the number of elements"); + cl.def("push_back", (void (Vector::*)(const T&)) &Vector::push_back, "adds an element to the end"); + cl.def("pop_back", &Vector::pop_back, "removes the last element"); + + cl.def("max_size", &Vector::max_size, "returns the maximum possible number of elements"); + cl.def("reserve", &Vector::reserve, "reserves storage"); + cl.def("capacity", &Vector::capacity, "returns the number of elements that can be held in currently allocated storage"); + cl.def("shrink_to_fit", &Vector::shrink_to_fit, "reduces memory usage by freeing unused memory"); + + cl.def("clear", &Vector::clear, "clears the contents"); + cl.def("swap", &Vector::swap, "swaps the contents"); + + cl.def("front", [](Vector &v) { + if (v.size()) return v.front(); + else throw index_error(); + }, "access the first element"); + + cl.def("back", [](Vector &v) { + if (v.size()) return v.back(); + else throw index_error(); + }, "access the last element "); + +#endif + + return cl; +} + + + +// +// std::map, std::unordered_map +// + +NAMESPACE_BEGIN(detail) + +/* Fallback functions */ +template void map_if_insertion_operator(const Args &...) { } +template void map_assignment(const Args &...) { } + +// Map assignment when copy-assignable: just copy the value +template +void map_assignment(enable_if_t::value, Class_> &cl) { + using KeyType = typename Map::key_type; + using MappedType = typename Map::mapped_type; + + cl.def("__setitem__", + [](Map &m, const KeyType &k, const MappedType &v) { + auto it = m.find(k); + if (it != m.end()) it->second = v; + else m.emplace(k, v); + } + ); +} + +// Not copy-assignable, but still copy-constructible: we can update the value by erasing and reinserting +template +void map_assignment(enable_if_t< + !std::is_copy_assignable::value && + is_copy_constructible::value, + Class_> &cl) { + using KeyType = typename Map::key_type; + using MappedType = typename Map::mapped_type; + + cl.def("__setitem__", + [](Map &m, const KeyType &k, const MappedType &v) { + // We can't use m[k] = v; because value type might not be default constructable + auto r = m.emplace(k, v); + if (!r.second) { + // value type is not copy assignable so the only way to insert it is to erase it first... + m.erase(r.first); + m.emplace(k, v); + } + } + ); +} + + +template auto map_if_insertion_operator(Class_ &cl, std::string const &name) +-> decltype(std::declval() << std::declval() << std::declval(), void()) { + + cl.def("__repr__", + [name](Map &m) { + std::ostringstream s; + s << name << '{'; + bool f = false; + for (auto const &kv : m) { + if (f) + s << ", "; + s << kv.first << ": " << kv.second; + f = true; + } + s << '}'; + return s.str(); + }, + "Return the canonical string representation of this map." + ); +} + + +NAMESPACE_END(detail) + +template , typename... Args> +class_ bind_map(handle scope, const std::string &name, Args&&... args) { + using KeyType = typename Map::key_type; + using MappedType = typename Map::mapped_type; + using Class_ = class_; + + // If either type is a non-module-local bound type then make the map binding non-local as well; + // otherwise (e.g. both types are either module-local or converting) the map will be + // module-local. + auto tinfo = detail::get_type_info(typeid(MappedType)); + bool local = !tinfo || tinfo->module_local; + if (local) { + tinfo = detail::get_type_info(typeid(KeyType)); + local = !tinfo || tinfo->module_local; + } + + Class_ cl(scope, name.c_str(), pybind11::module_local(local), std::forward(args)...); + + cl.def(init<>()); + + // Register stream insertion operator (if possible) + detail::map_if_insertion_operator(cl, name); + + cl.def("__bool__", + [](const Map &m) -> bool { return !m.empty(); }, + "Check whether the map is nonempty" + ); + + cl.def("__iter__", + [](Map &m) { return make_key_iterator(m.begin(), m.end()); }, + keep_alive<0, 1>() /* Essential: keep list alive while iterator exists */ + ); + + cl.def("items", + [](Map &m) { return make_iterator(m.begin(), m.end()); }, + keep_alive<0, 1>() /* Essential: keep list alive while iterator exists */ + ); + + cl.def("__getitem__", + [](Map &m, const KeyType &k) -> MappedType & { + auto it = m.find(k); + if (it == m.end()) + throw key_error(); + return it->second; + }, + return_value_policy::reference_internal // ref + keepalive + ); + + // Assignment provided only if the type is copyable + detail::map_assignment(cl); + + cl.def("__delitem__", + [](Map &m, const KeyType &k) { + auto it = m.find(k); + if (it == m.end()) + throw key_error(); + m.erase(it); + } + ); + + cl.def("__len__", &Map::size); + + return cl; +} + +NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/ptocr/postprocess/piexlmerge/include/pybind11/typeid.h b/ptocr/postprocess/piexlmerge/include/pybind11/typeid.h new file mode 100644 index 0000000..c903fb1 --- /dev/null +++ b/ptocr/postprocess/piexlmerge/include/pybind11/typeid.h @@ -0,0 +1,53 @@ +/* + pybind11/typeid.h: Compiler-independent access to type identifiers + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include +#include + +#if defined(__GNUG__) +#include +#endif + +NAMESPACE_BEGIN(pybind11) +NAMESPACE_BEGIN(detail) +/// Erase all occurrences of a substring +inline void erase_all(std::string &string, const std::string &search) { + for (size_t pos = 0;;) { + pos = string.find(search, pos); + if (pos == std::string::npos) break; + string.erase(pos, search.length()); + } +} + +PYBIND11_NOINLINE inline void clean_type_id(std::string &name) { +#if defined(__GNUG__) + int status = 0; + std::unique_ptr res { + abi::__cxa_demangle(name.c_str(), nullptr, nullptr, &status), std::free }; + if (status == 0) + name = res.get(); +#else + detail::erase_all(name, "class "); + detail::erase_all(name, "struct "); + detail::erase_all(name, "enum "); +#endif + detail::erase_all(name, "pybind11::"); +} +NAMESPACE_END(detail) + +/// Return a string representation of a C++ type +template static std::string type_id() { + std::string name(typeid(T).name()); + detail::clean_type_id(name); + return name; +} + +NAMESPACE_END(pybind11) diff --git a/ptocr/postprocess/piexlmerge/lanms.h b/ptocr/postprocess/piexlmerge/lanms.h new file mode 100644 index 0000000..679666c --- /dev/null +++ b/ptocr/postprocess/piexlmerge/lanms.h @@ -0,0 +1,234 @@ +#pragma once + +#include "clipper/clipper.hpp" + +// locality-aware NMS +namespace lanms { + + namespace cl = ClipperLib; + + struct Polygon { + cl::Path poly; + float score; + }; + + float paths_area(const ClipperLib::Paths &ps) { + float area = 0; + for (auto &&p: ps) + area += cl::Area(p); + return area; + } + + float poly_iou(const Polygon &a, const Polygon &b) { + cl::Clipper clpr; + clpr.AddPath(a.poly, cl::ptSubject, true); + clpr.AddPath(b.poly, cl::ptClip, true); + + cl::Paths inter, uni; + clpr.Execute(cl::ctIntersection, inter, cl::pftEvenOdd); + clpr.Execute(cl::ctUnion, uni, cl::pftEvenOdd); + + auto inter_area = paths_area(inter), + uni_area = paths_area(uni); + return std::abs(inter_area) / std::max(std::abs(uni_area), 1.0f); + } + + bool should_merge(const Polygon &a, const Polygon &b, float iou_threshold) { + return poly_iou(a, b) > iou_threshold; + } + + /** + * Incrementally merge polygons + */ + class PolyMerger { + public: + PolyMerger(): score(0), nr_polys(0) { + memset(data, 0, sizeof(data)); + } + + /** + * Add a new polygon to be merged. + */ + void add(const Polygon &p_given) { + Polygon p; + if (nr_polys > 0) { + // vertices of two polygons to merge may not in the same order; + // we match their vertices by choosing the ordering that + // minimizes the total squared distance. + // see function normalize_poly for details. + p = normalize_poly(get(), p_given); + } else { + p = p_given; + } + assert(p.poly.size() == 4); + auto &poly = p.poly; + auto s = p.score; + data[0] += poly[0].X * s; + data[1] += poly[0].Y * s; + + data[2] += poly[1].X * s; + data[3] += poly[1].Y * s; + + data[4] += poly[2].X * s; + data[5] += poly[2].Y * s; + + data[6] += poly[3].X * s; + data[7] += poly[3].Y * s; + + score += p.score; + + nr_polys += 1; + } + + inline std::int64_t sqr(std::int64_t x) { return x * x; } + + Polygon normalize_poly( + const Polygon &ref, + const Polygon &p) { + + std::int64_t min_d = std::numeric_limits::max(); + size_t best_start = 0, best_order = 0; + + for (size_t start = 0; start < 4; start ++) { + size_t j = start; + std::int64_t d = ( + sqr(ref.poly[(j + 0) % 4].X - p.poly[(j + 0) % 4].X) + + sqr(ref.poly[(j + 0) % 4].Y - p.poly[(j + 0) % 4].Y) + + sqr(ref.poly[(j + 1) % 4].X - p.poly[(j + 1) % 4].X) + + sqr(ref.poly[(j + 1) % 4].Y - p.poly[(j + 1) % 4].Y) + + sqr(ref.poly[(j + 2) % 4].X - p.poly[(j + 2) % 4].X) + + sqr(ref.poly[(j + 2) % 4].Y - p.poly[(j + 2) % 4].Y) + + sqr(ref.poly[(j + 3) % 4].X - p.poly[(j + 3) % 4].X) + + sqr(ref.poly[(j + 3) % 4].Y - p.poly[(j + 3) % 4].Y) + ); + if (d < min_d) { + min_d = d; + best_start = start; + best_order = 0; + } + + d = ( + sqr(ref.poly[(j + 0) % 4].X - p.poly[(j + 3) % 4].X) + + sqr(ref.poly[(j + 0) % 4].Y - p.poly[(j + 3) % 4].Y) + + sqr(ref.poly[(j + 1) % 4].X - p.poly[(j + 2) % 4].X) + + sqr(ref.poly[(j + 1) % 4].Y - p.poly[(j + 2) % 4].Y) + + sqr(ref.poly[(j + 2) % 4].X - p.poly[(j + 1) % 4].X) + + sqr(ref.poly[(j + 2) % 4].Y - p.poly[(j + 1) % 4].Y) + + sqr(ref.poly[(j + 3) % 4].X - p.poly[(j + 0) % 4].X) + + sqr(ref.poly[(j + 3) % 4].Y - p.poly[(j + 0) % 4].Y) + ); + if (d < min_d) { + min_d = d; + best_start = start; + best_order = 1; + } + } + + Polygon r; + r.poly.resize(4); + auto j = best_start; + if (best_order == 0) { + for (size_t i = 0; i < 4; i ++) + r.poly[i] = p.poly[(j + i) % 4]; + } else { + for (size_t i = 0; i < 4; i ++) + r.poly[i] = p.poly[(j + 4 - i - 1) % 4]; + } + r.score = p.score; + return r; + } + + Polygon get() const { + Polygon p; + + auto &poly = p.poly; + poly.resize(4); + auto score_inv = 1.0f / std::max(1e-8f, score); + poly[0].X = data[0] * score_inv; + poly[0].Y = data[1] * score_inv; + poly[1].X = data[2] * score_inv; + poly[1].Y = data[3] * score_inv; + poly[2].X = data[4] * score_inv; + poly[2].Y = data[5] * score_inv; + poly[3].X = data[6] * score_inv; + poly[3].Y = data[7] * score_inv; + + assert(score > 0); + p.score = score; + + return p; + } + + private: + std::int64_t data[8]; + float score; + std::int32_t nr_polys; + }; + + + /** + * The standard NMS algorithm. + */ + std::vector standard_nms(std::vector &polys, float iou_threshold) { + size_t n = polys.size(); + if (n == 0) + return {}; + std::vector indices(n); + std::iota(std::begin(indices), std::end(indices), 0); + std::sort(std::begin(indices), std::end(indices), [&](size_t i, size_t j) { return polys[i].score > polys[j].score; }); + + std::vector keep; + while (indices.size()) { + size_t p = 0, cur = indices[0]; + keep.emplace_back(cur); + for (size_t i = 1; i < indices.size(); i ++) { + if (!should_merge(polys[cur], polys[indices[i]], iou_threshold)) { + indices[p ++] = indices[i]; + } + } + indices.resize(p); + } + + std::vector ret; + for (auto &&i: keep) { + ret.emplace_back(polys[i]); + } + return ret; + } + + std::vector + merge_quadrangle_n9(const float *data, size_t n, float iou_threshold) { + using cInt = cl::cInt; + + // first pass + std::vector polys; + for (size_t i = 0; i < n; i ++) { + auto p = data + i * 9; + Polygon poly{ + { + {cInt(p[0]), cInt(p[1])}, + {cInt(p[2]), cInt(p[3])}, + {cInt(p[4]), cInt(p[5])}, + {cInt(p[6]), cInt(p[7])}, + }, + p[8], + }; + + if (polys.size()) { + // merge with the last one + auto &bpoly = polys.back(); + if (should_merge(poly, bpoly, iou_threshold)) { + PolyMerger merger; + merger.add(bpoly); + merger.add(poly); + bpoly = merger.get(); + } else { + polys.emplace_back(poly); + } + } else { + polys.emplace_back(poly); + } + } + return standard_nms(polys, iou_threshold); + } +} diff --git a/ptocr/postprocess/piexlmerge/pixelmerge.cpp b/ptocr/postprocess/piexlmerge/pixelmerge.cpp new file mode 100644 index 0000000..70b62a2 --- /dev/null +++ b/ptocr/postprocess/piexlmerge/pixelmerge.cpp @@ -0,0 +1,310 @@ +#include +#include +#include +#include +#include +#include +#include "include/pybind11/pybind11.h" +#include "include/pybind11/numpy.h" +#include "include/pybind11/stl.h" +#include "include/pybind11/stl_bind.h" + +#include +#include +#include +#include + +using namespace std; +using namespace cv; + +namespace py = pybind11; + + + +namespace pixelmerge{ + + + void get_kernals(const int *data, vector data_shape, vector &kernals) { + for (int i = 0; i < data_shape[0]; ++i) { + Mat kernal = Mat::zeros(data_shape[1], data_shape[2], CV_8UC1); + for (int x = 0; x < kernal.rows; ++x) { + for (int y = 0; y < kernal.cols; ++y) { + kernal.at(x, y) = data[i * data_shape[1] * data_shape[2] + x * data_shape[2] + y]; + } + } + kernals.emplace_back(kernal); + } + } + + void growing_text_line(vector &kernals, vector> &text_line, float min_area) { + + Mat label_mat; + int label_num = connectedComponents(kernals[kernals.size() - 1], label_mat, 4); + + // cout << "label num: " << label_num << endl; + + int area[label_num + 1]; + memset(area, 0, sizeof(area)); + for (int x = 0; x < label_mat.rows; ++x) { + for (int y = 0; y < label_mat.cols; ++y) { + int label = label_mat.at(x, y); + if (label == 0) continue; + area[label] += 1; + } + } + + queue queue, next_queue; + for (int x = 0; x < label_mat.rows; ++x) { + vector row(label_mat.cols); + for (int y = 0; y < label_mat.cols; ++y) { + int label = label_mat.at(x, y); + + if (label == 0) continue; + if (area[label] < min_area) continue; + + Point point(x, y); + queue.push(point); + row[y] = label; + } + text_line.emplace_back(row); + } + + // cout << "ok" << endl; + + int dx[] = {-1, 1, 0, 0}; + int dy[] = {0, 0, -1, 1}; + + for (int kernal_id = kernals.size() - 2; kernal_id >= 0; --kernal_id) { + while (!queue.empty()) { + Point point = queue.front(); queue.pop(); + int x = point.x; + int y = point.y; + int label = text_line[x][y]; + // cout << text_line.size() << ' ' << text_line[0].size() << ' ' << x << ' ' << y << endl; + + bool is_edge = true; + for (int d = 0; d < 4; ++d) { + int tmp_x = x + dx[d]; + int tmp_y = y + dy[d]; + + if (tmp_x < 0 || tmp_x >= (int)text_line.size()) continue; + if (tmp_y < 0 || tmp_y >= (int)text_line[1].size()) continue; + if (kernals[kernal_id].at(tmp_x, tmp_y) == 0) continue; + if (text_line[tmp_x][tmp_y] > 0) continue; + + Point point(tmp_x, tmp_y); + queue.push(point); + text_line[tmp_x][tmp_y] = label; + is_edge = false; + } + + if (is_edge) { + next_queue.push(point); + } + } + swap(queue, next_queue); + } + } + + vector> pse(py::array_t quad_n9, float min_area) { + auto buf = quad_n9.request(); + auto data = static_cast(buf.ptr); + vector kernals; + get_kernals(data, buf.shape, kernals); + + // cout << "min_area: " << min_area << endl; + // for (int i = 0; i < kernals.size(); ++i) { + // cout << "kernal" << i <<" shape: " << kernals[i].rows << ' ' << kernals[i].cols << endl; + // } + + vector> text_line; + growing_text_line(kernals, text_line, min_area); + + return text_line; + } + + + py::array_t pan( + py::array_t text, + py::array_t similarity_vectors, + py::array_t label_map, + int label_num, + float dis_threshold = 0.8) + { + auto pbuf_text = text.request(); + auto pbuf_similarity_vectors = similarity_vectors.request(); + auto pbuf_label_map = label_map.request(); + if (pbuf_label_map.ndim != 2 || pbuf_label_map.shape[0]==0 || pbuf_label_map.shape[1]==0) + throw std::runtime_error("label map must have a shape of (h>0, w>0)"); + int h = pbuf_label_map.shape[0]; + int w = pbuf_label_map.shape[1]; + if (pbuf_similarity_vectors.ndim != 3 || pbuf_similarity_vectors.shape[0]!=h || pbuf_similarity_vectors.shape[1]!=w || pbuf_similarity_vectors.shape[2]!=4 || + pbuf_text.shape[0]!=h || pbuf_text.shape[1]!=w) + throw std::runtime_error("similarity_vectors must have a shape of (h,w,4) and text must have a shape of (h,w,4)"); + //初始化结果 + auto res = py::array_t(pbuf_text.size); + auto pbuf_res = res.request(); + // 获取 text similarity_vectors 和 label_map的指针 + auto ptr_label_map = static_cast(pbuf_label_map.ptr); + auto ptr_text = static_cast(pbuf_text.ptr); + auto ptr_similarity_vectors = static_cast(pbuf_similarity_vectors.ptr); + auto ptr_res = static_cast(pbuf_res.ptr); + + std::queue> q; + // 计算各个kernel的similarity_vectors + float kernel_vector[label_num][5] = {0}; + + // 文本像素入队列 + for (int i = 0; i0) + { + kernel_vector[label][0] += p_similarity_vectors[k]; + kernel_vector[label][1] += p_similarity_vectors[k+1]; + kernel_vector[label][2] += p_similarity_vectors[k+2]; + kernel_vector[label][3] += p_similarity_vectors[k+3]; + kernel_vector[label][4] += 1; + q.push(std::make_tuple(i, j, label)); + } + p_res[j] = label; + } + } + + for(int i=0;i(q_n); + int x = std::get<1>(q_n); + int32_t l = std::get<2>(q_n); + //store the edge pixel after one expansion + auto kernel_cv = kernel_vector[l]; + for (int idx=0; idx<4; idx++) + { + int tmpy = y + dy[idx]; + int tmpx = x + dx[idx]; + auto p_res = ptr_res + tmpy*w; + if (tmpy<0 || tmpy>=h || tmpx<0 || tmpx>=w) + continue; + if (!ptr_text[tmpy*w+tmpx] || p_res[tmpx]>0) + continue; + // 计算距离 + float dis = 0; + auto p_similarity_vectors = ptr_similarity_vectors + tmpy * w*4; + for(size_t i=0;i<4;i++) + { + dis += pow(kernel_cv[i] - p_similarity_vectors[tmpx*4 + i],2); + } + dis = sqrt(dis); + if(dis >= dis_threshold) + continue; + q.push(std::make_tuple(tmpy, tmpx, l)); + p_res[tmpx]=l; + } + } + return res; + } + + std::map> get_points( + py::array_t label_map, + py::array_t score_map, + int label_num) + { + auto pbuf_label_map = label_map.request(); + auto pbuf_score_map = score_map.request(); + auto ptr_label_map = static_cast(pbuf_label_map.ptr); + auto ptr_score_map = static_cast(pbuf_score_map.ptr); + int h = pbuf_label_map.shape[0]; + int w = pbuf_label_map.shape[1]; + + std::map> point_dict; + std::vector> point_vector; + for(int i=0;i point; + point.push_back(0); + point.push_back(0); + point_vector.push_back(point); + } + for (int i = 0; i 2) + { + point_vector[i][0] /= point_vector[i][1]; + point_dict[i] = point_vector[i]; + } + } + return point_dict; + } + std::vector get_num( + py::array_t label_map, + int label_num) + { + auto pbuf_label_map = label_map.request(); + auto ptr_label_map = static_cast(pbuf_label_map.ptr); + int h = pbuf_label_map.shape[0]; + int w = pbuf_label_map.shape[1]; + + std::vector point_vector; + for(int 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
50VUAsY>~DXSho06cT7~WYEz~mR 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

OXcwn$an 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 literal 0 HcmV?d00001 diff --git a/ptocr/utils/__init__.py b/ptocr/utils/__init__.py new file mode 100644 index 0000000..35de2ad --- /dev/null +++ b/ptocr/utils/__init__.py @@ -0,0 +1,6 @@ +#-*- coding:utf-8 _*- +""" +@author:fxw +@file: __init__.py.py +@time: 2020/08/07 +""" diff --git a/ptocr/utils/__pycache__/__init__.cpython-36.pyc b/ptocr/utils/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..152883379ca44f1ca2b26a4a2846eae67122cc41 GIT binary patch literal 173 zcmXr!<>fLBZA%DZU|@I*#Bjg}WH|tFF$a)HVTfW#VGL%_WU4ada!4#K$;dCVN~}zQ`1q9! sMNB|5!Ne~~J^g}`{Ny72(vr-aVtpioZgJS;=BJeAq}s88oB_lP0L|Ab$^ZZW literal 0 HcmV?d00001 diff --git a/ptocr/utils/__pycache__/cal_iou_acc.cpython-36.pyc b/ptocr/utils/__pycache__/cal_iou_acc.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8a42d2c91101339a80376b3a2ccce3d6614edc44 GIT binary patch literal 2126 zcmaJ?-EQ1O6rS?rqIwJh2FrbQWFv(RZ#>fe-al&l`3wmN|y0Bi8q_w?bs`t zXw1!qN8llN3V01hLPFvK#{Ob+ zj}3hfZ}~NZU@;S1SVJCj(_67EdcqOzIgcH2OL)SE=89zzh!C15BGEZ#u`jw}34I`} z=d6GG?2~9OomNLO zUEmYg{@)}S`}4ERZ8R35ckq6JxBL-8SpzoUGd8ehthN-p9w}>u)vn;mMRSh%E)!O( zJG)G|1G{nt?u^&2;tQ)hP+#zU26d3ljZ6J0h)8 zZD*6Ic1P2b$*K0zvO1mQ+8Rx?Ta2phE$vMwn3n74Ft3s_8_T@xhq3>CF-qlW8Apdz z((1Zb$+Q?1qr>DREsx9io}7-FVP451Yv*aNI>IJLac3fP(Hi6MYM;1kPPv@TlV6y; zg%AE`?eWH>8fS82S{1``<8@T@GL4%rX+9GP!^#y1#(Vq@@0xbm%iFNujMF(huj5qN z14pshV%+}^Ew}Oz`?sNW=_Ry+Ikdu?(2|tt{YX+GNy;qgL1?!c%j`(|WpQ{iE`;>S z6Of>+lXQF8f#`>rn!l<_9U#O;lU>;L-34^WC70ldLjw$rwcoDMQ3HyZ)T6x#l141k zZaoS=d7I$B1KU;b?=J>_72b?)K@WWl>4qe~K~a{nSLUcudkO>>>?cduZEr>g&MK>Y z#ao;6hA%L{0fuYB)FEgUqPd1*yw!QdD|#pm zuMnPKP%)G~K0~QKw&X+Tmasq;hSJt(ys{NW%2tl@W^V0Q4rK*K?#w|k3xvJOluz3TOOsT zf#GyizMSUyS#EA4DQWv1`GEFWBSE$08VN}$FW)DzPJ+UtZTvE{jeo}?T{=XUXHWN& zXV1Ud#LSXNM1?9sqd2ah$v$iu@tz)vCeh#UW%lA|19fg$*v zP@KRb{1!|!oF+f712Go^%Tz{`mFDVw|Hf<5<>ZGXJ|eMD3p~2qm!I#zRZ>M_e4Phq z@)7htmlXDeF4TUKh;f!A4J#;JnrcRc+Qhy=fkI;TnLE@RZ>mtRdomW&Vg4ztFDaKD MI@yZ4XuTl%4+HttRR910 literal 0 HcmV?d00001 diff --git a/ptocr/utils/__pycache__/gen_teacher_model.cpython-36.pyc b/ptocr/utils/__pycache__/gen_teacher_model.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6d93c1cd6d06fa07c6e06078c3e18b9ed4875215 GIT binary patch literal 2191 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 E1%`iR^#A|> literal 0 HcmV?d00001 diff --git a/ptocr/utils/__pycache__/metrics.cpython-36.pyc b/ptocr/utils/__pycache__/metrics.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..69e442b356dcca531aa50f8a69962db2893db7ef GIT binary patch literal 1905 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 literal 0 HcmV?d00001 diff --git a/ptocr/utils/__pycache__/transform_label.cpython-36.pyc b/ptocr/utils/__pycache__/transform_label.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b70e4b6b2226734f3875e32670479a72f51180c0 GIT binary patch literal 6925 zcmc&(&2t>Z6`!83ogGPwEF6Df$lwCP5?eL~VsJT*z{Z4>OMvZ!)Pfn0wr8bXt!Ct& z5tf(T93m08I4KS}rE(9KoN`E2s&dF7e?(VqIdM%@E}ZgvulGxmofOF-qw3zC?w;<~ z-M{zV@3mfC4KlB`fLdj?%A5(C0pfMt}eE=?}baPb~n8kl`6@KR<1VUZnBbg7dP*R zOJ#clE$7djKX>-rg|p{h4!^>3tmV4U!A}y^PFA{UQ&1Hi2dRp51*v!$S!wloVbXcv z{OaUD^hHPPSkgUi4eY+%v8A^osC3hM&$?m#6x#&h{ADdv&)X_nXI`ag(yW=wcDAZ$ z8ZE-QquqQn&2&|ziR`wsw9sBrs`h5xX$0%({dhAe*R<1WOYL#i&+s0HHUJODn<{jb_uC?0aK#8MA3)>*(>2NMRT`KSTk@fubC@qY-=M`NtxC??G|abrTu1}wc4xdaeSslTf4MF3@nq) zQhgA|?W|qK@hQ9&Au<9`D15PC_f8x*_r+27CZUNQ!W``e@5IkgShWTYlDqw3X_e5R z9sFU{*9TtT>DW}$_h^rHgs`?>Hho4l=obxq?1^J~_5I@(bg$BPu}c;D;_l`5t-D9w zxBBitly1kP@%h);5iK$mBALZL-Q3#T%mKEQq-?Iy;TBLxfCLK~0*ny37i3gZrrTvi zAfs=(?Sf%uFh@-V0R-DhN-#CfjkMfS*%V%Rs9ZJI7T?awTWMBcv=6#zwpy;m=c4aM z!{Uo5E-o&PUO&mAGtrBK+ZQ7?A*p0>rr`Y0VKekM;}Krd1nVVHCrVKFwFg7Fnra9A zw4ZFk7)f;)XQjQ8q}0{1nDsy%p$3=6X!vDZl>$4X#wR?@Ew3uM|)cMgreW0;;=xqjZOA zc?|$%Xi9tCQ6Kd^{JQz)R42tTz|gd=+p`v00lm+yjz9c@+Zmo8L5(qA-=!I{-H|k% z%8msY{{|T=6B()2$O&0|X%8yf%Onr2iIwIuWU@&)20`EF86khag1_LX^(l6staieF zu!{A-oCFV`1j3@$3s9Q7|A3nKH{ofa7GaWO3$LbdP3c~EK}VYFQ9A>&aVS9B*m^!h zJ^tUA9?NzkX&31}jeETy2h+~8H(}HdO2HA+IWj|b- zzlmWWJ*7tx`k4mwE|3h-y>_`aod06<$spP*1AJ4nn<(hL5s|uRYs^;2_mHI}dtRJC zwhHfYdto2|MDjisf%FUeu1i*sr^XL;uG`vPJjY6l8dC3x1Nb_;L}sp;Z){$^K?Bn) zzSTQ@P(dLbz_WO%n>UkgaT$fthrL(L4uA9IfiF2Kxwj0G;gJLD>e}8@)IRaMq||OF zZ)fTW68=fb*sD3k)`s?tVCpHV{x-6D)z~mms;8;0j;v87?-!HE#(H@yvU&z>T6B3s zMw>&9QJqD$gjAeHX3fotKpaDI#WC2gns`B+6tfQ5GuSrd)Z&U$pk5ENZALkRHN2peajB1W~c1HI$AbG5rgw zt{nuy5k<94r13)syM$DnL-ud!AMt4yO|4O6!} z>`4|!9QzKF!C|HGfOi(7F_vMAEK1B8Xch4JFnS0ph*1lo>mGbfT8_?ympfTAZOb|m z!_^YKLnGlP1cla15BU#DJk^IN5LGM1j;$hCXT&u-#*f$?7bTIMDw19+lnjv=Jo}@xg*g79kxL?iZaQXsUk7J(ULMzDwjTrqXpv_;xUq|8uYvTuQ;g@79YGq7u-H#^<}blbDhBm%FVf&?i*M@6OF*sG1_VQz(hj z<|7wRV9)#Tm18-ai5%K%fd!WkjX8;fNe=AE$4PfUGMU)N#h%Ph?**Ed!D(JX7ex`C zdWEv@QO2tE11izN)EksdU^cz588W5e4w-rf-BtrB+`fw zX<&nyKrA5EK(ql8L2-tIIOF6n&VY`&+9i9a*)($K#H3Jt3!@^=U`O#L%4{&zaVkyr zhfNP{;b%z0-KVhOu;?AxBl$!lN#W_CeWLO^NQ@=P=ifVFD4-$XZMbdq%5NehJD6XOg*&`gxRD2| z?pGy(04&o=Q`eFeI9K3k10i)T6@|s>ZOR_$x(LLdA{p0Z2Y$`1xr%C=NaKgjg`R>e z)JUi zV18h8Xzw7j$2AZ_d#4ZeiV;n$ih(PAW4~Jl9K~nvG zB2_&!G$_2DXRC$^DOwgo51zl>?Z)aosvv))2-k-BGsQR86e^9hFscSw8~7TEp?X80lUnmB1cM_k|TEgam{ zv6rSJrF4Zxp<6W~JA@>hJh5A*^g~`_7O~f3u<`sOeaw1*pkjKmJFH78E zw4gYo%1}%Xb`h4c<4C9}DHPp)bX-zeT>Kq_GSLN}Z}_Tu4io;jU?odA0xK!p!@>F& zv^-RWj5BNPTU}G7-b)U^_BOHQ~F3?`2E<*$t?Ow`Z=>!)e z@fe)sO#e)h)sR_$VhB(^L(tp@Nh2kk!%OHlMSj`*6U6s5EnrY@;{5yuO zZ167;#Q&T3(Roul#r;D4h^8dJru}@SgI*lcGV5lBV0_s`ZN?5q2bWA#eVG8tI_$Eq TL8xRSSfac#|K9wQ^GE&#Q2-PI literal 0 HcmV?d00001 diff --git a/ptocr/utils/__pycache__/util_function.cpython-36.pyc b/ptocr/utils/__pycache__/util_function.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e1cb139ee9a3bb43e650620b2251bba056203603 GIT binary patch literal 6559 zcmcIo%X1t@8Si=S>}a);W!bUpL|Hp8)^=oDp>Qw(WXlf%j_i^}9#aXE(e&(UrP-Nv z&#Wvdvp^wNg#+PF$SDV?;!ohffuf+GIB}rnL=`7hapIWb!0+psT|Mk1RaCLkeBJ%^ z>+A2`{o44r{n;OW{-HR{*gx5UpN{$hzU1!!oH>juTn!aRk)`UWveX<+mb#S@a+0=lBc2O!F7{ODIq9m-#Cw zPx7zvCdy|p|2)5d`4@syyP7k@)d#Hk+Rjyb!Rx1MvAEXSdSoxOI$?0F(N8;}+v-Pt z+KHpN-nPAvcDlgNU!K2w>GJEBE?>3xz!tXDRC43ZuGbE3`zj&uqf*1Sh%adXq-0%tlA|GG8clX}1 zyL{)y{Ea)yO*Pj#-F9B-ByJm{f6auAZ9bo!yVOf#UtA*U6A2HVA+Ozk^RA?IC0=}c zf~d0qm|`osQddL`^)EgINS`VW0;m${skW|{#~L0&7+6D?07H^N?mWmA#1yC65^+}^ zu&D~#TT}@U@dX$|Ew3%UT~Y~pPXJSrNkrO;r~~6G0Q94+M!dd{M#k2msNl7dDj76wO_kr|MUx%9bmagWH~ zxo}H}STtI(=z3|;D1ULQk*)=eZp`~((CB&T+O@_<13?t-5olVuk@Uh&nrmSY<@M`H z5(rW&xms=(%jMNhw-<{v?5yVFMb#~q%*R}}=lL7ZG1tv2?I88iR5VS;F8bXh6^@a1 zdr6Xym8i(;2Ko36v>KrVMbgm7cE9>liZD0({L4~t{l^L12p^Ig%qpl9B zsUl6Ini*$UW=^wAKLdef`ZSr29lgvAInFM}*_nxP#*uMVDN2^vS+zx4I+mK5iB;?~ z3^fC_>KWEj2enM&`i3R`m0991m{ZBdxJiGRn%SoqFc!CX<&(-_JenQ$qpy03(J0wp zE&Hu(EUUHPO|tQ#Y}!k?{^0t9mA#*W6MGGSmD^Gy$X#rDVLxbUvb_(UL{y{R zOtlnK)uC>>V&T)323oo@t4u1VB*s=ws&!=w


Ge(xVJQPDtcq>`kLq@GeG|BXta zlF2EQc!|Kv z1f<`71+}JKIOYq4B}s@^3Ct0=MBp-kc>=Ey_&R~34*Z~CZ(x9of((MINzsugXCb>jSIPf$z$%QgwQs#DpD8xWlCmA zS5V;^SK$h(B~MFJ7Uw|7lXh!;Ifq3Pv&4CtTJSorcD=2_i2hfOA3Ml#*B4Rr4%Sbm z02tV;tFvJIlsXCLJ*|lE0-M|ZTHtT=V)&NN#}O8SaITlG&8433DWKAiBtUg(K(nY& z{9jkW7V@lG5&tPvBEK7E0J$0`xlU0q*VnOlZltmB(MY__psB+#AbbU40juQ}Ms!9_y&D|%pg?S0l#VBL>K1!b&!{3ndc^}_;#417$W`I>kqhcF z8)e8~dZk8x4kZZ7A6=@Mu zSR1ZFL{c8Jy8P8xI*!~{JF!WPP^>-wRG4!2+gGgU+#F3KSqoz)j9hHM6 z$%QkIXhB+;DNm9y9CV0PBQnT-Qz8UJ6iF}g*reZ;R5TU2`_uQupx#&Ri$RC>CGp7P zPaoJfRg3-4l=`ZP74%99#BtQVfQ+gVc?1Mz^-ZAtv6Px-sq`lm{E+^Q1h9pH8M1Q3+UZPLXS<{V5CCw9 z!=guVcm``0cxt^tu^Ku00h}&z$k-3y42d%j;Y`9|ZG6%OD@)>a>@S`Na4Ib~@naD< zR?A(Ld6Hwa+;lURg&)(+%J21aBZ9_l!y+zV7TlQ#_)*QADl_Gti2VQ-8?UZE!r;Q^ z!rM}Cn=b!!Rk-LIIaa#0P{ft$`A1YbY=GN|4Mu%p}T3EdZAO1W)WU-3MaDa2hNy8!QBVRnxox+aI3^@1R@GTr!coP*hQ{}dVdj=Uqotkkus~s*|TTMML{2K;7}iOJ<>3cc9AMMc0UQ+ zqPwV<`&+A>2zVcP{zkB!2 z+nY`|w^fWDIVXyvBj-eZ5K)qaY(UYHg~~vcB_e$$pDZYJz|ff z2%iAWKguC12~xSrr|6Vnj}Zg#XUmIgO%ZG888Lt&`G^5{{~9JZAU-*XWJkUj?8i%8 z+`t$?R-2o_7KEJSMt6gE1RNw`kJ4k862pjJp}P#=pkf29k*_3AK)#e#NPYoOq*=R6 z6f_`Q4%DbF>5+-dCn`ikLeY2GhACdp(3-(J#&#XS4euDl`gd5gpdcH8K-jvu3;(j! zAc5^NSn@P3<0{uP{TYUL5G{jmefS~lnPzJ=`#lz^@1hNhHkQQupb5!It?TvNF!nuq zyvS{t$Vr{usT9u&bInR27Ru7{8eXToO|KJrt6@M{g~!E*)GhgQNklY%qqYbYLrT0JvsR|XeT5{da$Ueb-H4xDwN-ZDb(SO zCsjcae8hV)fsrSE7mclDZ~>7BT)I6_DX+`a8NAl*s>0ryFIr{)IT!|#V9Kc=h4ZP7 z#2CqNF(yL{dwBjG_RuE27U_;roZs#woe0N|e6U5@yaojkGuRyovgyZBtJBVPPqY)q zfFE$L6vh0}gV(x&z@tzx;dhvx)Bq?m#>E&4e_9a~ddXZyKMl17dx=2G$93*T%6{aodcgV<} z1p*g4<`$4HdCY~M8>Q?+Z>8rNsLnNSvn^A^-0X|ytuEIgNzyhOYs%*&=7Cck&;&AY$AwI$iMLs*A zS_XjIDJAu?D_5eU8f5l+9A@}83>lF=1?|~6eu(s96yCZe`LJ@b6!9Jb2OzJwE{}a= zjC55I#0>Eh0{ez4Yhi+qavi#ZRd%{qsqxy+nX4yYNU9dSviGw(;g&`5GuCqhtv3qq_#Mjn thresh] = 1 + pred_binary = pred_binary.astype(np.int32) + gt_binary = gt_binarys.data.cpu().numpy() * training_masks + gt_binary = gt_binary.astype(np.int32) + running_metric_binary.update(gt_binary, pred_binary) + score_binary, _ = running_metric_binary.get_scores() + return score_binary + +def cal_text_score(texts, gt_texts, training_masks, running_metric_text, thresh=0.5): + training_masks = training_masks.data.cpu().numpy() + pred_text = torch.sigmoid(texts).data.cpu().numpy() * training_masks + pred_text[pred_text <= thresh] = 0 + pred_text[pred_text > thresh] = 1 + pred_text = pred_text.astype(np.int32) + gt_text = gt_texts.data.cpu().numpy() * training_masks + gt_text = gt_text.astype(np.int32) + running_metric_text.update(gt_text, pred_text) + score_text, _ = running_metric_text.get_scores() + return score_text + +def cal_kernel_score(kernels, gt_kernels, gt_texts, training_masks, running_metric_kernel, thresh=0.5): + mask = (gt_texts * training_masks).data.cpu().numpy() + kernel = kernels[:, -1, :, :] + gt_kernel = gt_kernels[:, -1, :, :] + pred_kernel = torch.sigmoid(kernel).data.cpu().numpy() + pred_kernel[pred_kernel <= thresh] = 0 + pred_kernel[pred_kernel > thresh] = 1 + pred_kernel = (pred_kernel * mask).astype(np.int32) + gt_kernel = gt_kernel.data.cpu().numpy() + gt_kernel = (gt_kernel * mask).astype(np.int32) + running_metric_kernel.update(gt_kernel, pred_kernel) + score_kernel, _ = running_metric_kernel.get_scores() + return score_kernel + +def cal_PAN_PSE(kernels, gt_kernels,texts ,gt_texts, training_masks, running_metric_text,running_metric_kernel): + if(len(kernels.shape)==3): + kernels = kernels.unsqueeze(1) + gt_kernels = gt_kernels.unsqueeze(1) + score_kernel = cal_kernel_score(kernels, gt_kernels, gt_texts, training_masks, running_metric_kernel) + score_text = cal_text_score(texts, gt_texts, training_masks, running_metric_text) + acc = (score_text['Mean Acc'] + score_kernel['Mean Acc'])/2 + iou = (score_text['Mean IoU'] + score_kernel['Mean IoU'])/2 + return iou,acc + +def cal_DB(texts ,gt_texts, training_masks, running_metric_text): + score_text = cal_binary_score(texts.squeeze(1), gt_texts.squeeze(1), training_masks.squeeze(1), running_metric_text) + acc = score_text['Mean Acc'] + iou = score_text['Mean IoU'] + return iou,acc + + diff --git a/ptocr/utils/gen_teacher_model.py b/ptocr/utils/gen_teacher_model.py new file mode 100644 index 0000000..5a42767 --- /dev/null +++ b/ptocr/utils/gen_teacher_model.py @@ -0,0 +1,57 @@ +#-*- coding:utf-8 _*- +""" +@author:fxw +@file: gen_teacher_model.py +@time: 2020/10/15 +""" +import torch +import torch.nn as nn +import torch.nn.functional as F +import yaml +from ptocr.utils.util_function import create_module,load_model + + +class DiceLoss(nn.Module): + def __init__(self,eps=1e-6): + super(DiceLoss,self).__init__() + self.eps = eps + def forward(self,pre_score,gt_score,train_mask): + pre_score = pre_score.contiguous().view(pre_score.size()[0], -1) + gt_score = gt_score.contiguous().view(gt_score.size()[0], -1) + train_mask = train_mask.contiguous().view(train_mask.size()[0], -1) + + pre_score = pre_score * train_mask + gt_score = gt_score * train_mask + + a = torch.sum(pre_score * gt_score, 1) + b = torch.sum(pre_score * pre_score, 1) + self.eps + c = torch.sum(gt_score * gt_score, 1) + self.eps + d = (2 * a) / (b + c) + dice_loss = torch.mean(d) + return 1 - dice_loss + +def GetTeacherModel(args): + config = yaml.load(open(args.t_config, 'r', encoding='utf-8'), Loader=yaml.FullLoader) + model = create_module(config['architectures']['model_function'])(config) + model = load_model(model,args.t_model_path) + if torch.cuda.is_available(): + model = model.cuda() + return model + +class DistilLoss(nn.Module): + def __init__(self): + + super(DistilLoss, self).__init__() + self.mse = nn.MSELoss() + self.diceloss = DiceLoss() + self.ignore = ['thresh'] + + def forward(self, s_map, t_map): + loss = 0 + for key in s_map.keys(): + if(key in self.ignore): + continue + loss += self.diceloss(s_map[key],t_map[key],torch.ones(t_map[key].shape).cuda()) + return loss + + diff --git a/ptocr/utils/logger.py b/ptocr/utils/logger.py new file mode 100644 index 0000000..16bc333 --- /dev/null +++ b/ptocr/utils/logger.py @@ -0,0 +1,94 @@ +#-*- coding:utf-8 _*- +# A simple torch style logger +# (C) Wei YANG 2017 +from __future__ import absolute_import + +import logging + +class TrainLog(object): + def __init__(self,LOG_FILE): + file_handler = logging.FileHandler(LOG_FILE) #输出到文件 + console_handler = logging.StreamHandler() #输出到控制台 + file_handler.setLevel('INFO') #error以上才输出到文件 + console_handler.setLevel('INFO') #info以上才输出到控制台 + + fmt = '%(asctime)s - %(funcName)s - %(lineno)s - %(levelname)s - %(message)s' + formatter = logging.Formatter(fmt) + file_handler.setFormatter(formatter) #设置输出内容的格式 + console_handler.setFormatter(formatter) + + logger = logging.getLogger('TrainLog') + logger.setLevel('INFO') #设置了这个才会把debug以上的输出到控制台 + + logger.addHandler(console_handler) + logger.addHandler(file_handler) + self.logger = logger + + def error(self,char): + self.logger.error(char) + def debug(self,char): + self.logger.debug(char) + def info(self,char): + self.logger.info(char) + +class Logger(object): + def __init__(self, fpath, title=None, resume=False): + self.file = None + self.resume = resume + self.title = '' if title == None else title + if fpath is not None: + if resume: + self.file = open(fpath, 'r') + name = self.file.readline() + self.names = name.rstrip().split('\t') + self.numbers = {} + for _, name in enumerate(self.names): + self.numbers[name] = [] + + for numbers in self.file: + numbers = numbers.rstrip().split('\t') + for i in range(0, len(numbers)): + self.numbers[self.names[i]].append(numbers[i]) + self.file.close() + self.file = open(fpath, 'a') + else: + self.file = open(fpath, 'w') + + def set_names(self, names): + if self.resume: + pass + self.numbers = {} + self.names = names + for _, name in enumerate(self.names): + self.file.write(name) + self.file.write('\t') + self.numbers[name] = [] + self.file.write('\n') + self.file.flush() + + def set_split(self, names): + if self.resume: + pass + self.numbers = {} + self.names = names + for _, name in enumerate(self.names): + self.file.write(name) + self.numbers[name] = [] + self.file.write('\n') + self.file.flush() + + def append(self, numbers): + assert len(self.names) == len(numbers), 'Numbers do not match names' + for index, num in enumerate(numbers): + self.file.write("{0:.6f}".format(num)) + self.file.write('\t') + self.numbers[self.names[index]].append(num) + self.file.write('\n') + self.file.flush() + + def close(self): + if self.file is not None: + self.file.close() + + + diff --git a/ptocr/utils/metrics.py b/ptocr/utils/metrics.py new file mode 100644 index 0000000..59d45e8 --- /dev/null +++ b/ptocr/utils/metrics.py @@ -0,0 +1,50 @@ +# Adapted from score written by wkentaro +# https://github.com/wkentaro/pytorch-fcn/blob/master/torchfcn/utils.py + +import numpy as np + +class runningScore(object): + + def __init__(self, n_classes): + self.n_classes = n_classes + self.confusion_matrix = np.zeros((n_classes, n_classes)) + + def _fast_hist(self, label_true, label_pred, n_class): + mask = (label_true >= 0) & (label_true < n_class) + + if np.sum((label_pred[mask] < 0)) > 0: + print (label_pred[label_pred < 0]) + hist = np.bincount( + n_class * label_true[mask].astype(int) + + label_pred[mask], minlength=n_class**2).reshape(n_class, n_class) + return hist + + def update(self, label_trues, label_preds): + # print label_trues.dtype, label_preds.dtype + for lt, lp in zip(label_trues, label_preds): + self.confusion_matrix += self._fast_hist(lt.flatten(), lp.flatten(), self.n_classes) + + def get_scores(self): + """Returns accuracy score evaluation result. + - overall accuracy + - mean accuracy + - mean IU + - fwavacc + """ + hist = self.confusion_matrix + acc = np.diag(hist).sum() / (hist.sum() + 0.0001) + acc_cls = np.diag(hist) / (hist.sum(axis=1) + 0.0001) + acc_cls = np.nanmean(acc_cls) + iu = np.diag(hist) / (hist.sum(axis=1) + hist.sum(axis=0) - np.diag(hist) + 0.0001) + mean_iu = np.nanmean(iu) + freq = hist.sum(axis=1) / (hist.sum() + 0.0001) + fwavacc = (freq[freq > 0] * iu[freq > 0]).sum() + cls_iu = dict(zip(range(self.n_classes), iu)) + + return {'Overall Acc': acc, + 'Mean Acc': acc_cls, + 'FreqW Acc': fwavacc, + 'Mean IoU': mean_iu,}, cls_iu + + def reset(self): + self.confusion_matrix = np.zeros((self.n_classes, self.n_classes)) \ No newline at end of file diff --git a/ptocr/utils/prune_script.py b/ptocr/utils/prune_script.py new file mode 100644 index 0000000..918c827 --- /dev/null +++ b/ptocr/utils/prune_script.py @@ -0,0 +1,572 @@ +import torch +import torch.nn as nn +import numpy as np +import copy +from .util_function import load_model +import ptocr + +def updateBN(model,args): + for indedx,m in enumerate(model.modules()): +# if(indedx>187): +# break + if isinstance(m, nn.BatchNorm2d): + if hasattr(m.weight, 'data'): + m.weight.grad.data.add_(args.sr_lr*torch.sign(m.weight.data)) #L1正则 + + +def get_pruned_model_backbone(model,new_model,prued_mask,bn_index): + keys = {} + tag = 0 + for k, m in enumerate(new_model.modules()): + if (isinstance(m, ptocr.model.backbone.det_mobilev3.Block)): + keys[tag] = k + tag += 1 + + #### step 1 + mg_1 = np.array([-3, 7, 16]) + block_idx = keys[0] + tag = 0 + for idx in mg_1 + block_idx: + if (tag == 0): + msk = prued_mask[bn_index.index(idx)] + else: + msk = msk | prued_mask[bn_index.index(idx)] + tag += 1 + + for idx in mg_1 + block_idx: + prued_mask[bn_index.index(idx)] = msk + msk_1 = msk.clone() + + #### step 2 + block_idx2 = np.array([keys[1], keys[2]]) + mg_2 = 7 + tag = 0 + for idx in mg_2 + block_idx2: + if (tag == 0): + msk = prued_mask[bn_index.index(idx)] + else: + msk = msk | prued_mask[bn_index.index(idx)] + tag += 1 + for idx in mg_2 + block_idx2: + prued_mask[bn_index.index(idx)] = msk + msk_2 = msk.clone() + + ####step 3 + block_idx3s = [keys[3], keys[4], keys[5]] + mg_3 = np.array([7, 16]) + tag = 0 + for block_idx3 in block_idx3s: + for idx in block_idx3 + mg_3: + if (tag == 0): + msk = prued_mask[bn_index.index(idx)] + else: + msk = msk | prued_mask[bn_index.index(idx)] + tag += 1 + for block_idx3 in block_idx3s: + for idx in block_idx3 + mg_3: + prued_mask[bn_index.index(idx)] = msk + msk_3 = msk.clone() + + ####step 4_1 + block_idx4_all = [] + + block_idx4 = keys[6] + + mg_4 = np.array([7, 16]) + block_idx4_all.extend((block_idx4 + mg_4).tolist()) + + ####step 4_2 + block_idx4 = keys[7] + mg_4 = np.array([7, 16]) + block_idx4_all.extend((block_idx4 + mg_4).tolist()) + tag = 0 + + for idx in block_idx4_all: + if (tag == 0): + msk = prued_mask[bn_index.index(idx)] + else: + msk = msk | prued_mask[bn_index.index(idx)] + tag += 1 + + for idx in block_idx4_all: + prued_mask[bn_index.index(idx)] = msk + msk_4 = msk.clone() + + ####step 5 + block_idx5s = [keys[8], keys[9], keys[10]] + mg_5 = np.array([7, 16]) + tag = 0 + for block_idx5 in block_idx5s: + for idx in block_idx5 + mg_5: + if (tag == 0): + msk = prued_mask[bn_index.index(idx)] + else: + msk = msk | prued_mask[bn_index.index(idx)] + tag += 1 + + for block_idx5 in block_idx5s: + for idx in block_idx5 + mg_5: + prued_mask[bn_index.index(idx)] = msk + msk_5 = msk.clone() + + group_index = [] + spl_index = [] + for i in range(11): + block_idx6 = keys[i] + tag = 0 + mg_6 = np.array([2, 5]) + for idx in mg_6 + block_idx6: + if (tag == 0): + msk = prued_mask[bn_index.index(idx)] + else: + msk = msk | prued_mask[bn_index.index(idx)] + tag += 1 + for idx in mg_6 + block_idx6: + prued_mask[bn_index.index(idx)] = msk + if (i == 6): + spl_index.extend([block_idx6 + 9, block_idx6 - 2]) + group_index.append(block_idx6 + 4) + + count_conv = 0 + count_bn = 0 + conv_in_mask = [torch.ones(3)] + conv_out_mask = [] + bn_mask = [] + tag = 0 + for k, m in enumerate(new_model.modules()): + if (tag > 187): + break + if isinstance(m, nn.Conv2d): + + if (tag in group_index): + m.groups = int(prued_mask[bn_index.index(tag + 1)].sum()) + m.out_channels = int(prued_mask[count_conv].sum()) + conv_out_mask.append(prued_mask[count_conv]) + if (count_conv > 0): + if (tag == spl_index[0]): + m.in_channels = int(prued_mask[bn_index.index(spl_index[1])].sum()) + conv_in_mask.append(prued_mask[bn_index.index(spl_index[1])]) + else: + m.in_channels = int(prued_mask[count_conv - 1].sum()) + conv_in_mask.append(prued_mask[count_conv - 1]) + + count_conv += 1 + elif isinstance(m, nn.BatchNorm2d): + m.num_features = prued_mask[count_bn].sum() + bn_mask.append(prued_mask[count_bn]) + count_bn += 1 + tag += 1 + + bn_i = 0 + conv_i = 0 + model_i = 0 + scale = [188, 192, 196, 200] + scale_mask = [msk_5, msk_4, msk_3, msk_2] + for [m0, m1] in zip(model.modules(), new_model.modules()): + if (model_i > 187): + if isinstance(m0, nn.Conv2d): + if (model_i in scale): + index = scale.index(model_i) + m1.in_channels = int(scale_mask[index].sum()) + idx0 = np.squeeze(np.argwhere(np.asarray(scale_mask[index].cpu().numpy()))) + idx1 = np.squeeze(np.argwhere(np.asarray(torch.ones(96).cpu().numpy()))) + if idx0.size == 1: + idx0 = np.resize(idx0, (1,)) + if idx1.size == 1: + idx1 = np.resize(idx1, (1,)) + w = m0.weight.data[:, idx0, :, :].clone() + m1.weight.data = w[idx1, :, :, :].clone() + if m1.bias is not None: + m1.bias.data = m0.bias.data[idx1].clone() + + else: + m1.weight.data = m0.weight.data.clone() + if m1.bias is not None: + m1.bias.data = m0.bias.data.clone() + + elif isinstance(m0, nn.BatchNorm2d): + m1.weight.data = m0.weight.data.clone() + if m1.bias is not None: + m1.bias.data = m0.bias.data.clone() + m1.running_mean = m0.running_mean.clone() + m1.running_var = m0.running_var.clone() + else: + if isinstance(m0, nn.BatchNorm2d): + idx1 = np.squeeze(np.argwhere(np.asarray(bn_mask[bn_i].cpu().numpy()))) + if idx1.size == 1: + idx1 = np.resize(idx1, (1,)) + m1.weight.data = m0.weight.data[idx1].clone() + if m1.bias is not None: + m1.bias.data = m0.bias.data[idx1].clone() + m1.running_mean = m0.running_mean[idx1].clone() + m1.running_var = m0.running_var[idx1].clone() + bn_i += 1 + elif isinstance(m0, nn.Conv2d): + if (isinstance(conv_in_mask[conv_i], list)): + idx0 = np.squeeze(np.argwhere(np.asarray(torch.cat(conv_in_mask[conv_i], 0).cpu().numpy()))) + else: + idx0 = np.squeeze(np.argwhere(np.asarray(conv_in_mask[conv_i].cpu().numpy()))) + idx1 = np.squeeze(np.argwhere(np.asarray(conv_out_mask[conv_i].cpu().numpy()))) + if idx0.size == 1: + idx0 = np.resize(idx0, (1,)) + if idx1.size == 1: + idx1 = np.resize(idx1, (1,)) + if (model_i in group_index): + m1.weight.data = m0.weight.data[idx1, :, :, :].clone() + if m1.bias is not None: + m1.bias.data = m0.bias.clone() + else: + w = m0.weight.data[:, idx0, :, :].clone() + m1.weight.data = w[idx1, :, :, :].clone() + if m1.bias is not None: + m1.bias.data = m0.bias.data[idx1].clone() + conv_i += 1 + model_i += 1 + return new_model + + +def get_pruned_model_total(model,new_model,prued_mask,bn_index): + +# new_model = create_module(config['architectures']['model_function'])(config).cuda() + + keys = {} + tag = 0 + for k, m in enumerate(new_model.modules()): + if(isinstance(m,ptocr.model.backbone.det_mobilev3.Block)): + keys[tag]=k + tag+=1 +# print(keys) + #### step 1 + mg_1 = np.array([-3,7,16]) + block_idx = keys[0] + tag = 0 + for idx in mg_1+block_idx: + if (tag == 0): + msk = prued_mask[bn_index.index(idx)] + else: + msk = msk | prued_mask[bn_index.index(idx)] + tag+=1 +# print('step1',idx) +# print(msk.sum()) + for idx in mg_1+block_idx: + prued_mask[bn_index.index(idx)] = msk + msk_1 = msk.clone() + + #### step 2 + block_idx2 = np.array([keys[1],keys[2]]) + mg_2 = 7 + tag = 0 + for idx in mg_2+block_idx2: +# print('step2',idx) + if(tag==0): + msk = prued_mask[bn_index.index(idx)] + else: + msk = msk|prued_mask[bn_index.index(idx)] + tag += 1 + for idx in mg_2+block_idx2: + prued_mask[bn_index.index(idx)] = msk +# print(msk.sum()) + msk_2 = msk.clone() + + ####step 3 + block_idx3s = [keys[3],keys[4],keys[5]] + mg_3 = np.array([7,16]) + tag = 0 + for block_idx3 in block_idx3s: + for idx in block_idx3+mg_3: +# print('step3',idx) + if (tag == 0): + msk = prued_mask[bn_index.index(idx)] + else: + msk = msk | prued_mask[bn_index.index(idx)] + tag += 1 + for block_idx3 in block_idx3s: + for idx in block_idx3+mg_3: + prued_mask[bn_index.index(idx)] = msk +# print(msk.sum()) + msk_3 = msk.clone() + + ####step 4_1 + block_idx4_all = [] + + block_idx4 = keys[6] + + mg_4 = np.array([7,16]) + block_idx4_all.extend((block_idx4+mg_4).tolist()) + + ####step 4_2 + block_idx4 = keys[7] + mg_4 = np.array([7,16]) + block_idx4_all.extend((block_idx4+mg_4).tolist()) + tag = 0 + + for idx in block_idx4_all: +# print('step4',idx) + if(tag==0): + msk = prued_mask[bn_index.index(idx)] + else: + msk = msk | prued_mask[bn_index.index(idx)] + tag += 1 + + for idx in block_idx4_all: + prued_mask[bn_index.index(idx)] = msk +# print(msk.sum()) + msk_4 = msk.clone() + + ####step 5 + block_idx5s = [keys[8],keys[9],keys[10]] + mg_5 = np.array([7,16]) + tag = 0 + for block_idx5 in block_idx5s: + for idx in block_idx5+mg_5: + if (tag == 0): + msk = prued_mask[bn_index.index(idx)] + else: + msk = msk | prued_mask[bn_index.index(idx)] + tag += 1 + + for block_idx5 in block_idx5s: + for idx in block_idx5+mg_5: + prued_mask[bn_index.index(idx)] = msk +# print(msk.sum()) + msk_5 = msk.clone() + + group_index = [] + spl_index = [] + for i in range(11): + block_idx6 = keys[i] + tag = 0 + mg_6 = np.array([2,5]) + for idx in mg_6+block_idx6: + if(tag==0): + msk = prued_mask[bn_index.index(idx)] + else: + msk = msk | prued_mask[bn_index.index(idx)] + tag+=1 + for idx in mg_6 + block_idx6: + prued_mask[bn_index.index(idx)] = msk + if(i==6): + spl_index.extend([block_idx6+9,block_idx6-2]) + group_index.append(block_idx6+4) + + + count_conv = 0 + count_bn = 0 + conv_in_mask = [torch.ones(3)] + conv_out_mask = [] + bn_mask = [] + tag = 0 + for k, m in enumerate(new_model.modules()): + if(tag>187 ): + continue + else: + if isinstance(m,nn.Conv2d): + + if(tag in group_index): + m.groups = int(prued_mask[bn_index.index(tag+1)].sum()) + m.out_channels = int(prued_mask[count_conv].sum()) + conv_out_mask.append(prued_mask[count_conv]) + if(count_conv>0): + if (tag == spl_index[0]): + m.in_channels = int(prued_mask[bn_index.index(spl_index[1])].sum()) + conv_in_mask.append(prued_mask[bn_index.index(spl_index[1])]) + else: + m.in_channels = int(prued_mask[count_conv-1].sum()) + conv_in_mask.append(prued_mask[count_conv-1]) + + count_conv+=1 + elif isinstance(m,nn.BatchNorm2d): + m.num_features = int(prued_mask[count_bn].sum()) + bn_mask.append(prued_mask[count_bn]) + count_bn+=1 + tag+=1 + + + head_bn_merge_idx1 = np.array([189,193,197,201]) + tag = 0 + for idx in head_bn_merge_idx1: + if(tag==0): + _msk1 = prued_mask[bn_index.index(idx)] + else: + _msk1 = _msk1 | prued_mask[bn_index.index(idx)] + tag += 1 + + head_bn_merge_idx2 = np.array([205,209,213,217]) + num_ch = 0 + head_mask_1 = [] + for idx in head_bn_merge_idx2: + num_ch += int(prued_mask[bn_index.index(idx)].sum()) + head_mask_1.append(prued_mask[bn_index.index(idx)]) + + +# print(new_model) + + head_bn_merge_idx2 = head_bn_merge_idx2 - 1 + + bn_i = 0 + conv_i = 0 + model_i = 0 + scale = [188,192,196,200] + scale_mask = [msk_5,msk_4,msk_3,msk_2] + + prued_mask_bk = copy.deepcopy(prued_mask) + for [m0, m1] in zip(model.modules(), new_model.modules()): + if(model_i>187): + if isinstance(m0, nn.Conv2d) or isinstance(m0, nn.ConvTranspose2d): + if(model_i in scale): + index = scale.index(model_i) + m1.in_channels = int(scale_mask[index].sum()) + m1.out_channels = int(_msk1.sum()) + idx0 = np.squeeze(np.argwhere(np.asarray(scale_mask[index].cpu().numpy()))) + idx1 = np.squeeze(np.argwhere(np.asarray(_msk1.cpu().numpy()))) + if idx0.size == 1: + idx0 = np.resize(idx0, (1,)) + if idx1.size == 1: + idx1 = np.resize(idx1, (1,)) + w = m0.weight.data[:, idx0, :, :].clone() + m1.weight.data = w[idx1, :, :, :].clone() + if m1.bias is not None: + m1.bias.data = m0.bias.data[idx0].clone() + elif(model_i in head_bn_merge_idx2.tolist()): + + m1.in_channels = int(_msk1.sum()) + index = head_bn_merge_idx2.tolist().index(model_i) + m1.out_channels = int(head_mask_1[index].sum()) + + idx1 = np.squeeze(np.argwhere(np.asarray(head_mask_1[index].cpu().numpy()))) + idx0 = np.squeeze(np.argwhere(np.asarray(_msk1.cpu().numpy()))) + if idx0.size == 1: + idx0 = np.resize(idx0, (1,)) + if idx1.size == 1: + idx1 = np.resize(idx1, (1,)) + w = m0.weight.data[:, idx0, :, :].clone() + m1.weight.data = w[idx1, :, :, :].clone() +# merge_weight_mask.append(m1.weight.data) + if m1.bias is not None: + m1.bias.data = m0.bias.data[idx0].clone() + elif(model_i==221 or model_i==230): + + merge_mask = torch.cat(head_mask_1,0) + m1.in_channels = num_ch + index = bn_index.index(model_i+1) + m1.out_channels = int(prued_mask[index].sum()) + + idx1 = np.squeeze(np.argwhere(np.asarray(prued_mask[index].cpu().numpy()))) + idx0 = np.squeeze(np.argwhere(np.asarray(merge_mask.cpu().numpy()))) + if idx0.size == 1: + idx0 = np.resize(idx0, (1,)) + if idx1.size == 1: + idx1 = np.resize(idx1, (1,)) + w = m0.weight.data[:, idx0, :, :].clone() + m1.weight.data = w[idx1, :, :, :].clone() + + if m1.bias is not None: + m1.bias.data = m0.bias.data[idx0].clone() + + elif(model_i==224 or model_i==233): + + m1.in_channels = int(prued_mask[bn_index.index(model_i-2)].sum()) + m1.out_channels = int(prued_mask[bn_index.index(model_i+1)].sum()) + idx0 = np.squeeze(np.argwhere(np.asarray(prued_mask[bn_index.index(model_i+1)].cpu().numpy()))) + idx1 = np.squeeze(np.argwhere(np.asarray(prued_mask[bn_index.index(model_i-2)].cpu().numpy()))) + if idx0.size == 1: + idx0 = np.resize(idx0, (1,)) + if idx1.size == 1: + idx1 = np.resize(idx1, (1,)) + w = m0.weight.data[:, idx0, :, :].clone() + m1.weight.data = w[idx1, :, :, :].clone() + if m1.bias is not None: + m1.bias.data = m0.bias.data[idx0].clone() + elif(model_i==227 or model_i==236): +# import pdb +# pdb.set_trace() + m1.in_channels = int(prued_mask[bn_index.index(model_i-2)].sum()) + idx1 = np.squeeze(np.argwhere(np.asarray(prued_mask[bn_index.index(model_i-2)].cpu().numpy()))) + idx0 = np.squeeze(np.argwhere(np.asarray(torch.ones(1).cpu().numpy()))) + if idx0.size == 1: + idx0 = np.resize(idx0, (1,)) + if idx1.size == 1: + idx1 = np.resize(idx1, (1,)) + w = m0.weight.data[:, idx0, :, :].clone() + m1.weight.data = w[idx1, :, :, :].clone() + if m1.bias is not None: + m1.bias.data = m0.bias.data[idx0].clone() + else: + m1.weight.data = m0.weight.data.clone() + if m1.bias is not None: + m1.bias.data = m0.bias.data.clone() + + elif isinstance(m0, nn.BatchNorm2d): + + if(model_i in [189,193,197,201]): + m1.num_features = int(_msk1.sum()) + idx1 = np.squeeze(np.argwhere(np.asarray(_msk1.cpu().numpy()))) + if idx1.size == 1: + idx1 = np.resize(idx1, (1,)) + m1.weight.data = m0.weight.data[idx1].clone() + if m1.bias is not None: + m1.bias.data = m0.bias.data[idx1].clone() + m1.running_mean = m0.running_mean[idx1].clone() + m1.running_var = m0.running_var[idx1].clone() + + else: + + index = bn_index.index(model_i) + m1.num_features = prued_mask[index].sum() + idx1 = np.squeeze(np.argwhere(np.asarray(prued_mask[index].cpu().numpy()))) + + if idx1.size == 1: + idx1 = np.resize(idx1, (1,)) + m1.weight.data = m0.weight.data[idx1].clone() + if m1.bias is not None: + m1.bias.data = m0.bias.data[idx1].clone() + m1.running_mean = m0.running_mean[idx1].clone() + m1.running_var = m0.running_var[idx1].clone() + + + else: + if isinstance(m0, nn.BatchNorm2d): + idx1 = np.squeeze(np.argwhere(np.asarray(bn_mask[bn_i].cpu().numpy()))) + if idx1.size == 1: + idx1 = np.resize(idx1, (1,)) + m1.weight.data = m0.weight.data[idx1].clone() + if m1.bias is not None: + m1.bias.data = m0.bias.data[idx1].clone() + m1.running_mean = m0.running_mean[idx1].clone() + m1.running_var = m0.running_var[idx1].clone() + bn_i += 1 + elif isinstance(m0, nn.Conv2d): + if (isinstance(conv_in_mask[conv_i], list)): + idx0 = np.squeeze(np.argwhere(np.asarray(torch.cat(conv_in_mask[conv_i], 0).cpu().numpy()))) + else: + idx0 = np.squeeze(np.argwhere(np.asarray(conv_in_mask[conv_i].cpu().numpy()))) + idx1 = np.squeeze(np.argwhere(np.asarray(conv_out_mask[conv_i].cpu().numpy()))) + if idx0.size == 1: + idx0 = np.resize(idx0, (1,)) + if idx1.size == 1: + idx1 = np.resize(idx1, (1,)) + if(model_i in group_index): + m1.weight.data = m0.weight.data[idx1, :, :, :].clone() + if m1.bias is not None: + m1.bias.data = m0.bias.clone() + else: + w = m0.weight.data[:, idx0, :, :].clone() + m1.weight.data = w[idx1, :, :, :].clone() + if m1.bias is not None: + m1.bias.data = m0.bias.data[idx0].clone() + conv_i += 1 + model_i+=1 + + return new_model + +def load_prune_model(model,model_path,pruned_model_dict_path,d_type = 'total'): + _load = torch.load(pruned_model_dict_path) + prued_mask = _load['prued_mask'] + bn_index = _load['bn_index'] + if d_type=='total': + prune_model = get_pruned_model_total(model, model, prued_mask, bn_index) + else: + prune_model = get_pruned_model_backbone(model, model, prued_mask, bn_index) + prune_model = load_model(prune_model,model_path) + return prune_model + diff --git a/ptocr/utils/transform_label.py b/ptocr/utils/transform_label.py new file mode 100644 index 0000000..27f4e11 --- /dev/null +++ b/ptocr/utils/transform_label.py @@ -0,0 +1,198 @@ +#-*- coding:utf-8 _*- +""" +@author:fxw +@file: transform_label.py +@time: 2020/07/24 +""" +import torch +import torch.nn as nn +from torch.autograd import Variable +import collections +# import chardet +import numpy as np +import sys +import abc + +def get_keys(key_path): + with open(key_path,'r',encoding='utf-8') as fid: + lines = fid.readlines()[0] + lines = lines.strip('\n') + return lines + +#☯ +class strLabelConverter(object): + """Convert between str and label. + + NOTE: + Insert `blank` to the alphabet for CTC. + + Args: + alphabet (str): set of the possible characters. + ignore_case (bool, default=True): whether or not to ignore all of the case. + """ + + def __init__(self, config): + alphabet = get_keys(config['trainload']['key_file']) + self.alphabet = alphabet + '-' # for `-1` index + self.dict = {} + for i, char in enumerate(alphabet): + # NOTE: 0 is reserved for 'blank' required by wrap_ctc + self.dict[char] = i + 1 + + def encode(self, text,t_step): + """Support batch or single str. + + Args: + text (str or list of str): texts to convert. + + Returns: + torch.IntTensor [length_0 + length_1 + ... length_{n - 1}]: encoded texts. + torch.IntTensor [n]: length of each text. + """ + length = [] + result = [] + + for i in range(len(text)): + length.append(len(text[i])) + for j in range(len(text[i])): + index = self.dict[text[i][j]] + result.append(index) + + text = result + return (torch.IntTensor(text), torch.IntTensor(length)) + + def decode(self, t, length, raw=False): + """Decode encoded texts back into strs. + + Args: + torch.IntTensor [length_0 + length_1 + ... length_{n - 1}]: encoded texts. + torch.IntTensor [n]: length of each text. + + Raises: + AssertionError: when the texts and its length does not match. + + Returns: + text (str or list of str): texts to convert. + """ + if length.numel() == 1: + length = length[0] + assert t.numel() == length, "text with length: {} does not match declared length: {}".format(t.numel(), + length) + if raw: + return ''.join([self.alphabet[i - 1] for i in t]) + else: + char_list = [] + for i in range(length): + if t[i] != 0 and (not (i > 0 and t[i - 1] == t[i])): + char_list.append(self.alphabet[t[i] - 1]) + return ''.join(char_list) + else: + # batch mode + assert t.numel() == length.sum(), "texts with length: {} does not match declared length: {}".format( + t.numel(), length.sum()) + texts = [] + index = 0 + for i in range(length.numel()): + l = length[i] + texts.append( + self.decode( + t[index:index + l], torch.IntTensor([l]), raw=raw)) + index += l + return texts + + +class averager(object): + """Compute average for `torch.Variable` and `torch.Tensor`. """ + + def __init__(self): + self.reset() + + def add(self, v): + if isinstance(v, Variable): + count = v.data.numel() + v = v.data.sum() + elif isinstance(v, torch.Tensor): + count = v.numel() + v = v.sum() + + self.n_count += count + self.sum += v + + def reset(self): + self.n_count = 0 + self.sum = 0 + + def val(self): + res = 0 + if self.n_count != 0: + res = self.sum / float(self.n_count) + return res + + + +class BaseConverter(object): + + def __init__(self, character): + self.character = list(character) + self.dict = {} + for i, char in enumerate(self.character): + self.dict[char] = i + + @abc.abstractmethod + def train_encode(self, *args, **kwargs): + '''encode text in train phase''' + + @abc.abstractmethod + def test_encode(self, *args, **kwargs): + '''encode text in test phase''' + + @abc.abstractmethod + def decode(self, *args, **kwargs): + '''decode label to text in train and test phase''' + + + +class FCConverter(BaseConverter): + + def __init__(self, config): + batch_max_length = config['base']['max_length'] + character = get_keys(config['trainload']['key_file']) + self.character = character + list_token = ['[s]'] + ignore_token = ['[ignore]'] + list_character = list(character) + self.batch_max_length = batch_max_length + 1 + super(FCConverter, self).__init__(character=list_token + list_character + ignore_token) + self.ignore_index = self.dict[ignore_token[0]] + + def encode(self, text): + length = [len(s) + 1 for s in text] # +1 for [s] at end of sentence. + batch_text = torch.LongTensor(len(text), self.batch_max_length).fill_(self.ignore_index) + for i, t in enumerate(text): + text = list(t) + text.append('[s]') + text = [self.dict[char] for char in text] + if self.batch_max_length>=len(text): + batch_text[i][:len(text)] = torch.LongTensor(text) + else: + batch_text[i][:self.batch_max_length] = torch.LongTensor(text)[:self.batch_max_length] + batch_text_input = batch_text + batch_text_target = batch_text + + return batch_text_input, torch.IntTensor(length), batch_text_target + + def train_encode(self, text): + return self.encode(text) + + def test_encode(self, text): + return self.encode(text) + + def decode(self, text_index): + texts = [] + batch_size = text_index.shape[0] + for index in range(batch_size): + text = ''.join([self.character[i] for i in text_index[index, :]]) + text = text[:text.find('[s]')] + texts.append(text) + + return texts \ No newline at end of file diff --git a/ptocr/utils/util_function.py b/ptocr/utils/util_function.py new file mode 100644 index 0000000..33c8dc3 --- /dev/null +++ b/ptocr/utils/util_function.py @@ -0,0 +1,245 @@ +#-*- coding:utf-8 _*- +""" +@author:fxw +@file: util_function.py +@time: 2020/08/07 +""" +import os +import importlib +import math +import cv2 +import torch +import numpy as np +from PIL import Image + +def PILImageToCV(img,is_gray=False): + img = np.asarray(img) + if not is_gray: + img = cv2.cvtColor(img, cv2.COLOR_RGB2BGR) + return img + +def CVImageToPIL(img,is_gray=False): + if not is_gray: + img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) + img = Image.fromarray(img) + return img + +def create_module(module_str): + tmpss = module_str.split(",") + assert len(tmpss) == 2, "Error formate\ + of the module path: {}".format(module_str) + module_name, function_name = tmpss[0], tmpss[1] + somemodule = importlib.import_module(module_name, __package__) + function = getattr(somemodule, function_name) + return function + + +def restore_training(model_file,model,optimizer): + checkpoint = torch.load(model_file) + start_epoch = checkpoint['epoch'] + try: + model.load_state_dict(checkpoint['state_dict']) + except: + state = model.state_dict() + for key in state.keys(): + state[key] = checkpoint['state_dict'][key[7:]] + model.load_state_dict(state) + optimizer.load_state_dict(checkpoint['optimizer']) + best_acc = checkpoint['best_acc'] + return model,optimizer,start_epoch,best_acc + + +def resize_image_batch(img,algorithm,side_len=1536,add_padding=True): + + if(algorithm=='SAST'): + stride = 128 + else: + stride = 32 + height, width, _ = img.shape + flag = None + if height > width: + flag = True + new_height = side_len + new_width = int(math.ceil(new_height / height * width / stride) * stride) + else: + flag = False + new_width = side_len + new_height = int(math.ceil(new_width / width * height / stride) * stride) + resized_img = cv2.resize(img, (new_width, new_height)) + scale = (float(width)/new_width,float(height)/new_height) + if add_padding is True: + if flag: + padded_image = cv2.copyMakeBorder(resized_img, 0, 0, + 0, side_len-new_width, cv2.BORDER_CONSTANT, value=(0,0,0)) + else: + padded_image = cv2.copyMakeBorder(resized_img, 0, side_len-new_height, + 0, 0, cv2.BORDER_CONSTANT, value=(0,0,0)) + else: + return resized_img,scale + return padded_image,scale + + +def resize_image(img,algorithm,side_len=736,stride = 128): + if algorithm == 'DB' or algorithm == 'PAN' or algorithm == 'CRNN': + height, width, _ = img.shape + if height < width: + new_height = side_len + new_width = int(math.ceil(new_height / height * width / stride) * stride) + else: + new_width = side_len + new_height = int(math.ceil(new_width / width * height / stride) * stride) + resized_img = cv2.resize(img, (new_width, new_height)) + else: + height, width, _ = img.shape + if height > width: + new_height = side_len + new_width = int(math.ceil(new_height / height * width / stride) * stride) + else: + new_width = side_len + new_height = int(math.ceil(new_width / width * height / stride) * stride) + resized_img = cv2.resize(img, (new_width, new_height)) + return resized_img + +def resize_image_crnn(img,max_width=100,side_len=32,stride =4): + + height, width, _ = img.shape + new_height = side_len + + new_width = int(math.ceil(new_height / height * width / stride) * stride) + if new_width>max_width: + resized_img = cv2.resize(img, (max_width, new_height)) + else: + resized_img = cv2.resize(img, (new_width, new_height)) + resized_img = cv2.copyMakeBorder(resized_img, 0, 0, + 0, max_width-new_width, cv2.BORDER_CONSTANT, value=(0,0,0)) + return resized_img + + +def save_checkpoint(state, checkpoint='checkpoint', filename='model.pth.tar'): + filepath = os.path.join(checkpoint, filename) + torch.save(state, filepath) + +class LossAccumulator(): + def __init__(self): + super(LossAccumulator,self).__init__() + self.loss_items = [] + def loss_add(self,loss): + self.loss_items.append(loss) + def loss_sum(self): + return sum(self.loss_items) + def loss_mean(self): + return sum(self.loss_items)/len(self.loss_items) + def loss_clear(self): + self.loss_items = [] + +def create_process_obj(algorithm,pred): + if(algorithm=='DB'): + return pred.cpu().numpy() + elif(algorithm=='SAST'): + pred['f_score'] = pred['f_score'].cpu().numpy() + pred['f_border'] = pred['f_border'].cpu().numpy() + pred['f_tvo'] = pred['f_tvo'].cpu().numpy() + pred['f_tco'] = pred['f_tco'].cpu().numpy() + return pred + else: + return pred + + +def create_loss_bin(algorithm,use_distil=False,use_center=False): + bin_dict = {} + if(algorithm=='DB'): + keys = ['loss_total','loss_l1', 'loss_bce', 'loss_thresh'] + elif(algorithm=='PSE'): + keys = ['loss_total','loss_kernel', 'loss_text'] + elif(algorithm=='PAN'): + keys = ['loss_total','loss_text', 'loss_agg', 'loss_kernel', 'loss_dis'] + elif (algorithm == 'SAST'): + keys = ['loss_total', 'loss_score', 'loss_border', 'loss_tvo', 'loss_tco'] + elif (algorithm == 'CRNN'): + if use_center: + keys = ['loss_total','loss_ctc','loss_center'] + else: + keys = ['loss_ctc'] + elif (algorithm == 'FC'): + keys = ['loss_fc'] + else: + assert 1==2,'only support algorithm DB,SAST,PSE,PAN,CRNN !!!' + + for key in keys: + bin_dict[key] = LossAccumulator() + if(use_distil): + bin_dict['loss_distil'] = LossAccumulator() + return bin_dict + +def set_seed(seed): + import numpy as np + import random + import torch + random.seed(seed) + np.random.seed(seed) + torch.manual_seed(seed) + torch.cuda.manual_seed(seed) + torch.cuda.manual_seed_all(seed) + +def create_dir(path): + if not (os.path.exists(path)): + os.mkdir(path) + +def load_model(model,model_path): + if torch.cuda.is_available(): + model_dict = torch.load(model_path) + else: + model_dict = torch.load(model_path,map_location='cpu') + if('state_dict' in model_dict.keys()): + model_dict = model_dict['state_dict'] + + try: + model.load_state_dict(model_dict) + except: + + state = model.state_dict() + for key in state.keys(): + state[key] = model_dict['module.' + key] + model.load_state_dict(state) + return model + +def merge_config(config,args): + for key_1 in config.keys(): + if(isinstance(config[key_1],dict)): + for key_2 in config[key_1].keys(): + if(key_2) in dir(args): + config[key_1][key_2] = getattr(args,key_2) + return config + + +def FreezeParameters(model,layer_name): + for name, parameter in model.named_parameters(): + if layer_name in name: + parameter.requires_grad = False + return filter(lambda p: p.requires_grad, model.parameters()) + +def ReleaseParameters(model,optimizer,layer_name): + for name, parameter in model.named_parameters(): + parameter_dict = {} + if layer_name in name: + parameter.requires_grad = True + parameter_dict['params'] = parameter + optimizer.add_param_group(parameter_dict) + return optimizer + +class AverageMeter(object): + """Computes and stores the average and current value""" + def __init__(self): + self.reset() + + def reset(self): + self.val = 0 + self.avg = 0 + self.sum = 0 + self.count = 0 + + def update(self, val, n=1): + self.val = val + self.sum += val * n + self.count += n + self.avg = self.sum / self.count \ No newline at end of file diff --git a/script/__init__.py b/script/__init__.py new file mode 100644 index 0000000..eab50c3 --- /dev/null +++ b/script/__init__.py @@ -0,0 +1,6 @@ +#-*- coding:utf-8 _*- +""" +@author:fxw +@file: __init__.py.py +@time: 2020/08/07 +""" \ No newline at end of file diff --git a/script/create_lmdb.py b/script/create_lmdb.py new file mode 100644 index 0000000..64e13eb --- /dev/null +++ b/script/create_lmdb.py @@ -0,0 +1,126 @@ +import os +import lmdb +import cv2 +import numpy as np +import argparse +import shutil +import sys +from tqdm import tqdm + +def checkImageIsValid(imageBin): + if imageBin is None: + return False + + try: + imageBuf = np.fromstring(imageBin, dtype=np.uint8) + img = cv2.imdecode(imageBuf, cv2.IMREAD_GRAYSCALE) + imgH, imgW = img.shape[0], img.shape[1] + except: + return False + else: + if imgH * imgW == 0: + return False + + return True + + +def writeCache(env, cache): + with env.begin(write=True) as txn: + for k, v in cache.items(): + if type(k) == str: + k = k.encode() + if type(v) == str: + v = v.encode() + txn.put(k,v) + +def createDataset(outputPath, imagePathList, labelList, lexiconList=None, checkValid=True): + """ + Create LMDB dataset for CRNN training. + ARGS: + outputPath : LMDB output path + imagePathList : list of image path + labelList : list of corresponding groundtruth texts + lexiconList : (optional) list of lexicon lists + checkValid : if true, check the validity of every image + """ + # If lmdb file already exists, remove it. Or the new data will add to it. + if os.path.exists(outputPath): + shutil.rmtree(outputPath) + os.makedirs(outputPath) + else: + os.makedirs(outputPath) + + assert (len(imagePathList) == len(labelList)) + nSamples = len(imagePathList) + env = lmdb.open(outputPath, map_size=1099511627776) + cache = {} + cnt = 1 + bar = tqdm(total=nSamples) + for i in range(nSamples): + bar.update(1) + imagePath = imagePathList[i] + label = labelList[i] + + if not os.path.exists(imagePath): + print('%s does not exist' % imagePath) + continue + with open(imagePath, 'rb') as f: + imageBin = f.read() + if checkValid: + if not checkImageIsValid(imageBin): + print('%s is not a valid image' % imagePath) + continue + + imageKey = 'image-%09d' % cnt + labelKey = 'label-%09d' % cnt + cache[imageKey] = imageBin + cache[labelKey] = label + if lexiconList: + lexiconKey = 'lexicon-%09d' % cnt + cache[lexiconKey] = ' '.join(lexiconList[i]) + if cnt % 1000 == 0: + writeCache(env, cache) + cache = {} +# print('Written %d / %d' % (cnt, nSamples)) + cnt += 1 + bar.close() + nSamples = cnt-1 + cache['num-samples'] = str(nSamples) + writeCache(env, cache) + env.close() + print('Created dataset with %d samples' % nSamples) + + +def read_data_from_file(file_path): + image_path_list = [] + label_list = [] + with open(file_path,'r',encoding='utf-8') as fid: + lines = fid.readlines() + for line in lines: + line = line.replace('\r', '').replace('\n', '').split('\t') + image_path_list.append(line[0]) + label_list.append(line[1]) + + return image_path_list, label_list + +def show_demo(demo_number, image_path_list, label_list): + print ('The first line is the path to image and the second line is the image label') + print ('###########################################################################') + for i in range(demo_number): + print ('image: %s\nlabel: %s\n' % (image_path_list[i], label_list[i])) + print ('###########################################################################') + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument('--out', type = str, required = True, help = 'lmdb data output path') + parser.add_argument('--file', type = str, help = 'path to file which contains the image path and label') + args = parser.parse_args() + + if args.file is not None: + image_path_list, label_list = read_data_from_file(args.file) + createDataset(args.out, image_path_list, label_list) + show_demo(2, image_path_list, label_list) + + else: + print ('Please use --file to assign the input. Use -h to see more.') + sys.exit() \ No newline at end of file diff --git a/script/create_lmdb_multiprocessing.py b/script/create_lmdb_multiprocessing.py new file mode 100644 index 0000000..3ed9b1d --- /dev/null +++ b/script/create_lmdb_multiprocessing.py @@ -0,0 +1,206 @@ +import os +import lmdb +import cv2 +import numpy as np +import argparse +import shutil +import sys +from tqdm import tqdm +import time +import six +from PIL import Image +from multiprocessing import Process + +def get_dict(char_list): + char_dict={} + for item in char_list: + if item in char_dict.keys(): + char_dict[item]+=1 + else: + char_dict[item]=1 + return char_dict + +def checklmdb(args): + env = lmdb.open( + args.out, + max_readers=2, + readonly=True, + lock=False, + readahead=False, + meminit=False) + + with env.begin(write=False) as txn: + nSamples = int(txn.get('num-samples'.encode('utf-8'))) + print('Check lmdb ok!!!') + print('lmdb Have {} samples'.format(nSamples)) + print('Print 5 samples:') + + for index in range(1,nSamples+1): + if index>5: + break + img_key = 'image-%09d' % index + imgbuf = txn.get(img_key.encode('utf-8')) + label_key = 'label-%09d' % index + label = txn.get(label_key.encode('utf-8')).decode() + print(index,label) + + +def checkImageIsValid(imageBin): + if imageBin is None: + return False + + try: + imageBuf = np.fromstring(imageBin, dtype=np.uint8) + img = cv2.imdecode(imageBuf, cv2.IMREAD_GRAYSCALE) + imgH, imgW = img.shape[0], img.shape[1] + except: + return False + else: + if imgH * imgW == 0: + return False + + return True + + +def writeCache(env, cache): + with env.begin(write=True) as txn: + for k, v in cache.items(): + if type(k) == str: + k = k.encode() + if type(v) == str: + v = v.encode() + txn.put(k,v) + + +def write(imagePathList,labelList,env,start,end): + cache = {} + cnt = 1 + bar = tqdm(total=end-start) + checkValid=True + lexiconList=None + for i in range(end-start): + bar.update(1) + imagePath = imagePathList[start+i] + label = labelList[start+i] + + if not os.path.exists(imagePath): + print('%s does not exist' % imagePath) + continue + with open(imagePath, 'rb') as f: + imageBin = f.read() + if checkValid: + if not checkImageIsValid(imageBin): + print('%s is not a valid image' % imagePath) + continue + + imageKey = 'image-%09d' % (start+cnt) + labelKey = 'label-%09d' % (start+cnt) + + cache[imageKey] = imageBin + cache[labelKey] = label + if lexiconList: + lexiconKey = 'lexicon-%09d' % cnt + cache[lexiconKey] = ' '.join(lexiconList[i]) + if (start+cnt) % 1000 == 0: + writeCache(env, cache) + cache = {} + cnt += 1 + bar.close() + writeCache(env, cache) + +def createDataset(outputPath, imagePathList, labelList, num=1,lexiconList=None, checkValid=True): + """ + Create LMDB dataset for CRNN training. + ARGS: + outputPath : LMDB output path + imagePathList : list of image path + labelList : list of corresponding groundtruth texts + lexiconList : (optional) list of lexicon lists + checkValid : if true, check the validity of every image + """ + # If lmdb file already exists, remove it. Or the new data will add to it. + if os.path.exists(outputPath): + shutil.rmtree(outputPath) + os.makedirs(outputPath) + else: + os.makedirs(outputPath) + + assert (len(imagePathList) == len(labelList)) + nSamples = len(imagePathList) + env = lmdb.open(outputPath, map_size=1099511627776) + cache = {} + index = [] + + if nSamples%num==0: + step = nSamples//num + for i in range(num): + index.append([i*step,(i+1)*step]) + else: + step = nSamples//num + for i in range(num): + index.append([i*step,(i+1)*step]) + index.append([num*step,nSamples]) + + p_list = [] + if nSamples%num==0: + for i in range(num): + p = Process(target=write,args=(imagePathList,labelList,env,index[i][0],index[i][1])) + p_list.append(p) + p.start() + else: + for i in range(num+1): + p = Process(target=write,args=(imagePathList,labelList,env,index[i][0],index[i][1])) + p_list.append(p) + p.start() + for p in p_list: + p.join() + cache['num-samples'] = str(nSamples) + writeCache(env, cache) + env.close() + print('Created dataset with %d samples' % nSamples) + + +def read_data_from_file(file_path): + image_path_list = [] + label_list = [] + with open(file_path,'r',encoding='utf-8') as fid: + lines = fid.readlines() + for line in lines: + line = line.replace('\r', '').replace('\n', '').split('\t') + image_path_list.append(line[0]) + label_list.append(line[1]) + + return image_path_list, label_list + +def show_demo(demo_number, image_path_list, label_list): + print ('The first line is the path to image and the second line is the image label') + print ('###########################################################################') + for i in range(demo_number): + print ('image: %s\nlabel: %s\n' % (image_path_list[i], label_list[i])) + print ('###########################################################################') + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument('--out', type = str, required = True, help = 'lmdb data output path') + parser.add_argument('--file', type = str,required = True, help = 'path to file which contains the image path and label') + parser.add_argument('--num_process', type = int, required = True, help = 'num_process to do') + args = parser.parse_args() + + if args.file is not None: + image_path_list, label_list = read_data_from_file(args.file) + show_demo(2, image_path_list, label_list) + s_time = time.time() + createDataset(args.out, image_path_list, label_list,num=args.num_process) + print('cost_time:'+str(time.time()-s_time)+'s') + else: + print ('Please use --file to assign the input. Use -h to see more.') + sys.exit() + print('lmdb generate ok!!!!') + checklmdb(args) + + + + + + + \ No newline at end of file diff --git a/script/get_key_label.py b/script/get_key_label.py new file mode 100644 index 0000000..99f276b --- /dev/null +++ b/script/get_key_label.py @@ -0,0 +1,31 @@ +""" +#!-*- coding=utf-8 -*- +@author: BADBADBADBADBOY +@contact: 2441124901@qq.com +@software: PyCharm Community Edition +@file: get_key_label.py +@time: 2020/11/9 20:33 + +""" + +train_list_file = './test_list.txt' +test_list_file = './train_list.txt' +keys_file = './key.txt' + + +fid_key = open(keys_file,'w+',encoding='utf-8') +keys = '' +with open(train_list_file,'r',encoding='utf-8') as fid_train: + lines = fid_train.readlines() + for line in lines: + line = line.strip().split('\t') + keys+=line[-1] + +with open(test_list_file,'r',encoding='utf-8') as fid_test: + lines = fid_test.readlines() + for line in lines: + line = line.strip().split('\t') + keys+=line[-1] + +key = ''.join(list(set(list(keys)))) +fid_key.write(key) \ No newline at end of file diff --git a/script/get_train_list.py b/script/get_train_list.py new file mode 100644 index 0000000..0b481b9 --- /dev/null +++ b/script/get_train_list.py @@ -0,0 +1,30 @@ +""" +#!-*- coding=utf-8 -*- +@author: BADBADBADBADBOY +@contact: 2441124901@qq.com +@software: PyCharm Community Edition +@file: test.py +@time: 2020/9/3 20:17 + +""" +import os +import argparse +def gen_train_file(args): + label_path = args.label_path + img_path = args.img_path + files = os.listdir(img_path) + with open(os.path.join(args.save_path,'train_list.txt'),'w+',encoding='utf-8') as fid: + for file in files: + label_str = os.path.join(img_path,file)+'\t'+os.path.join(label_path,os.path.splitext(file)[0]+'.txt')+'\n' + fid.write(label_str) + + + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description='Hyperparams') + parser.add_argument('--label_path', nargs='?', type=str, default=None) + parser.add_argument('--img_path', nargs='?', type=str, default=None) + parser.add_argument('--save_path', nargs='?', type=str, default=None) + args = parser.parse_args() + gen_train_file(args) \ No newline at end of file diff --git a/script/onnx_to_tensorrt.py b/script/onnx_to_tensorrt.py new file mode 100644 index 0000000..8d5126f --- /dev/null +++ b/script/onnx_to_tensorrt.py @@ -0,0 +1,188 @@ +import os +import tensorrt as trt +import pycuda.driver as cuda +import pycuda.autoinit +import onnx +import cv2 +import numpy as np +import time +import argparse +import math + +def resize_image(img,algorithm,side_len=1536,add_padding=True): + + if algorithm == 'CRNN': + img = transform_img_shape(img,[32,crnn_max_length]) + return img + + if(algorithm=='SAST'): + stride = 128 + else: + stride = 32 + height, width, _ = img.shape + flag = None + if height > width: + flag = True + new_height = side_len + new_width = int(math.ceil(new_height / height * width / stride) * stride) + else: + flag = False + new_width = side_len + new_height = int(math.ceil(new_width / width * height / stride) * stride) + resized_img = cv2.resize(img, (new_width, new_height)) + if add_padding is True: + if flag: + padded_image = cv2.copyMakeBorder(resized_img, 0, 0, + 0, side_len-new_width, cv2.BORDER_CONSTANT, value=(0,0,0)) + else: + padded_image = cv2.copyMakeBorder(resized_img, 0, side_len-new_height, + 0, 0, cv2.BORDER_CONSTANT, value=(0,0,0)) + else: + return resized_img + return padded_image + + +def build_engine(onnx_file_path,engine_file_path,batch_size=1,mode='fp16'): + TRT_LOGGER = trt.Logger(trt.Logger.WARNING) + if os.path.exists(engine_file_path): + # If a serialized engine exists, load it instead of building a new one. + print("Reading engine from file {}".format(engine_file_path)) + with open(engine_file_path, "rb") as f, trt.Runtime(TRT_LOGGER) as runtime: + return runtime.deserialize_cuda_engine(f.read()) + + + EXPLICIT_BATCH = 1 << int(trt.NetworkDefinitionCreationFlag.EXPLICIT_BATCH) + with trt.Builder(TRT_LOGGER) as builder, \ + builder.create_network(EXPLICIT_BATCH) as network, \ + trt.OnnxParser(network, TRT_LOGGER) as parser: + builder.max_batch_size = int(batch_size) + builder.fp16_mode = True if mode == 'fp16' else False + builder.int8_mode = True if mode == 'int8' else False + builder.strict_type_constraints = True + + builder.max_workspace_size = 1 << 32 # 1GB:30 + + with open(onnx_file_path, 'rb') as model: + parser.parse(model.read()) + + print('network layers is',len(network)) # Printed output == 0. Something is wrong. + last_layer = network.get_layer(network.num_layers - 1) + # Check if last layer recognizes it's output + if not last_layer.get_output(0): + # If not, then mark the output using TensorRT API + network.mark_output(last_layer.get_output(0)) + engine = builder.build_cuda_engine(network) + with open(engine_file_path, "wb") as f: + f.write(engine.serialize()) + return engine + + +class HostDeviceMem(object): + def __init__(self, host_mem, device_mem): + self.host = host_mem + self.device = device_mem + + def __str__(self): + return "Host:\n" + str(self.host) + "\nDevice:\n" + str(self.device) + + def __repr__(self): + return self.__str__() + +def allocate_buffers(engine): + inputs = [] + outputs = [] + bindings = [] + stream = cuda.Stream() + for binding in engine: + dtype = trt.nptype(engine.get_binding_dtype(binding)) + # Allocate host and device buffers + host_mem = cuda.pagelocked_empty(trt.volume(engine.get_binding_shape(binding)), dtype) + device_mem = cuda.mem_alloc(host_mem.nbytes) + # Append the device buffer to device bindings. + bindings.append(int(device_mem)) + # Append to the appropriate list. + if engine.binding_is_input(binding): + inputs.append(HostDeviceMem(host_mem, device_mem)) + else: + outputs.append(HostDeviceMem(host_mem, device_mem)) + return inputs, outputs, bindings, stream + +def do_inference(context, bindings, inputs, outputs, stream, batch_size=1): + + # Transfer input data to the GPU. + [cuda.memcpy_htod_async(inp.device, inp.host, stream) for inp in inputs] + # Run inference. + context.execute_async(batch_size=batch_size, bindings=bindings, stream_handle=stream.handle) + # Transfer predictions back from the GPU. + [cuda.memcpy_dtoh_async(out.host, out.device, stream) for out in outputs] + # Synchronize the stream + stream.synchronize() + # Return only the host outputs. + return [out.host for out in outputs] + +def get_img(args): + img = cv2.imread(args.img_path) + img = resize_image(img,args.algorithm,side_len=args.max_size,add_padding=args.add_padding) + print(img.shape) + cv2.imwrite('./onnx/'+args.algorithm+'_ori_img.jpg',img) + image = img / 255.0 + mean = np.array([0.485, 0.456, 0.406]) + std = np.array([0.229, 0.224, 0.225]) + image = (image - mean) / std + image = np.transpose(image, [2, 0, 1]) + image = np.expand_dims(image, axis=0).astype(np.float32) + return image + +def gen_trt_engine(args): + print("start check onnx file") + test = onnx.load(args.onnx_path) + onnx.checker.check_model(test) + print("check onnx file Passed") + print("build trt engine......") + engine = build_engine(args.onnx_path,args.trt_engine_path,batch_size=int(args.batch_size)) + inputs, outputs, bindings, stream = allocate_buffers(engine) + context = engine.create_execution_context() + print("build trt engine done") + + img1 = get_img(args) + h,w = img1.shape[2:4] + img_numpy = np.array([img1,img1]) + + inputs[0].host = img_numpy + + s = time.time() + output = do_inference(context, bindings=bindings, inputs=inputs, outputs=outputs, stream=stream,batch_size=int(args.batch_size)) + print(time.time()-s) + if args.algorithm=='DB': + out = output[0].reshape(int(args.batch_size),h,w) + + cv2.imwrite('./onnx/'+args.algorithm+'_trt_img1.jpg',out[0]*255) + cv2.imwrite('./onnx/'+args.algorithm+'_trt_img2.jpg',out[1]*255) + elif args.algorithm=='PAN': + out = output[0].reshape(int(args.batch_size),6,h,w) + cv2.imwrite('./onnx/'+args.algorithm+'_trt_img_text.jpg',out[1,0]*255) + cv2.imwrite('./onnx/'+args.algorithm+'_trt_img_kernel.jpg',out[1,1]*255) + elif args.algorithm=='PSE': + out = output[0].reshape(int(args.batch_size),7,h,w) + cv2.imwrite('./onnx/'+args.algorithm+'_trt_img_text.jpg',out[0,0]*255) + cv2.imwrite('./onnx/'+args.algorithm+'_trt_img_kernel.jpg',out[0,6]*255) + elif args.algorithm=='SAST': + out = output[0].reshape(int(args.batch_size),h//4,w//4) + out = out[0] + cv2.imwrite('./onnx/'+args.algorithm+'_trt_img.jpg',out*255) + else: + print('not support this algorithm!!') + + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description='Hyperparams') + parser.add_argument('--onnx_path', help='config file path') + parser.add_argument('--trt_engine_path', nargs='?', type=str, default=None) + parser.add_argument('--img_path', nargs='?', type=str, default=None) + parser.add_argument('--batch_size', nargs='?', type=str, default=None) + parser.add_argument('--algorithm', nargs='?', type=str, default=None) + parser.add_argument('--max_size', nargs='?', type=int, default=None) + parser.add_argument('--add_padding', action='store_true', default=False) + args = parser.parse_args() + gen_trt_engine(args) \ No newline at end of file diff --git a/script/pytorch_to_onnx.py b/script/pytorch_to_onnx.py new file mode 100644 index 0000000..e5c5d36 --- /dev/null +++ b/script/pytorch_to_onnx.py @@ -0,0 +1,144 @@ +import sys +sys.path.append('./') +import yaml +import math +import argparse +import torch.nn as nn +import torch +import cv2 +import numpy as np +import onnx +import time +import onnxruntime +from PIL import Image +import torchvision.transforms as transforms +from ptocr.utils.util_function import create_module,load_model + + +crnn_max_length = 280 + +def transform_img_shape(img,img_shape): + img= np.array(img) + H,W = img_shape + h,w = img.shape[:2] + new_w = int((h/H)*w) + if(new_w > W): + img = cv2.resize(img,(W,H)) + else: + img = cv2.resize(img,(new_w,H)) + img = cv2.copyMakeBorder(img, 0, 0, + 0, W-new_w, cv2.BORDER_CONSTANT, value=(0,0,0)) + return img + + +def resize_image(img,algorithm,side_len=1536,add_padding=True): + + if algorithm == 'CRNN': + img = transform_img_shape(img,[32,crnn_max_length]) + return img + + if(algorithm=='SAST'): + stride = 128 + else: + stride = 32 + height, width, _ = img.shape + flag = None + if height > width: + flag = True + new_height = side_len + new_width = int(math.ceil(new_height / height * width / stride) * stride) + else: + flag = False + new_width = side_len + new_height = int(math.ceil(new_width / width * height / stride) * stride) + resized_img = cv2.resize(img, (new_width, new_height)) + if add_padding is True: + if flag: + padded_image = cv2.copyMakeBorder(resized_img, 0, 0, + 0, side_len-new_width, cv2.BORDER_CONSTANT, value=(0,0,0)) + else: + padded_image = cv2.copyMakeBorder(resized_img, 0, side_len-new_height, + 0, 0, cv2.BORDER_CONSTANT, value=(0,0,0)) + else: + return resized_img + return padded_image + + +def gen_onnx(args): + stream = open(args.config, 'r', encoding='utf-8') + config = yaml.load(stream,Loader=yaml.FullLoader) + + model = create_module(config['architectures']['model_function'])(config) + + model = model.cuda() + model = load_model(model,args.model_path) + model.eval() + + print('load model ok.....') + + + img = cv2.imread(args.img_path) + img = resize_image(img,args.algorithm,side_len=args.max_size,add_padding=args.add_padding) + w,h = img.shape[:2] + print(w,h) + img1 = Image.fromarray(img).convert('RGB') + img1 = transforms.ToTensor()(img1) + img1 = transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])(img1).unsqueeze(0).cuda() + + s = time.time() + with torch.no_grad(): + out = model(img1) + print('cost time:',time.time()-s) + if isinstance(out,dict): + out = out['f_score'] + + cv2.imwrite('./onnx/ori_output.jpg',out[0,0].cpu().detach().numpy()*255) + + output_onnx = args.save_path + print("==> Exporting model to ONNX format at '{}'".format(output_onnx)) + input_names = ["input"] + # output_names = ["hm" , "wh" , "reg"] + output_names = ["out"] + inputs = torch.randn(args.batch_size, 3,w,h).cuda() + torch_out = torch.onnx._export(model, inputs, output_onnx, export_params=True, verbose=False,do_constant_folding=False,keep_initializers_as_inputs=True, + input_names=input_names, output_names=output_names) + + + onnx_path = args.save_path + session = onnxruntime.InferenceSession(onnx_path) + # session.get_modelmeta() + # input_name = session.get_inputs()[0].name + # output_name = session.get_outputs()[0].name + + image = img / 255.0 + mean = np.array([0.485, 0.456, 0.406]) + std = np.array([0.229, 0.224, 0.225]) + image = (image - mean) / std + image = np.transpose(image, [2, 0, 1]) + image = np.expand_dims(image, axis=0) + image = image.astype(np.float32) + + s = time.time() + preds = session.run(['out'], {'input': image}) + preds = preds[0] + print(time.time()-s) + if isinstance(preds,dict): + preds = preds['f_score'] + cv2.imwrite('./onnx/onnx_output.jpg',preds[0,0]*255) + + print('error_distance:',np.abs((out.cpu().detach().numpy()-preds)).mean()) + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description='Hyperparams') + parser.add_argument('--config', help='config file path') + parser.add_argument('--model_path', nargs='?', type=str, default=None) + parser.add_argument('--img_path', nargs='?', type=str, default=None) + parser.add_argument('--save_path', nargs='?', type=str, default=None) + parser.add_argument('--batch_size', nargs='?', type=int, default=None) + parser.add_argument('--max_size', nargs='?', type=int, default=None) + parser.add_argument('--algorithm', nargs='?', type=str, default=None) + parser.add_argument('--add_padding', action='store_true', default=False) + + args = parser.parse_args() + gen_onnx(args) + \ No newline at end of file diff --git a/to_onnx.sh b/to_onnx.sh new file mode 100644 index 0000000..77b3924 --- /dev/null +++ b/to_onnx.sh @@ -0,0 +1 @@ +python ./script/pytorch_to_onnx.py --config ./config/det_PSE_resnet50.yaml --model_path ./checkpoint/ag_PSE_bb_resnet50_he_FPN_Head_bs_8_ep_600_PSE20201004/PSE_best.pth.tar --img_path /src/notebooks/detect_text/icdar2015/ch4_test_images/img_10.jpg --save_path ./onnx/PSEnet_20201012.onnx --batch_size 2 --max_size 1536 --algorithm PSE --add_padding \ No newline at end of file diff --git a/to_tensorrt.sh b/to_tensorrt.sh new file mode 100644 index 0000000..b05f6c8 --- /dev/null +++ b/to_tensorrt.sh @@ -0,0 +1 @@ +CUDA_VISIBLE_DEVICES=2 python3 ./script/onnx_to_tensorrt.py --onnx_path ./onnx/PSEnet.onnx --trt_engine_path ./onnx/PSEnet_batch.engine --img_path /src/notebooks/detect_text/icdar2015/ch4_test_images/img_10.jpg --batch_size 2 --algorithm PSE --max_size 1536 --add_padding \ No newline at end of file diff --git a/tools/__init__.py b/tools/__init__.py new file mode 100644 index 0000000..35de2ad --- /dev/null +++ b/tools/__init__.py @@ -0,0 +1,6 @@ +#-*- coding:utf-8 _*- +""" +@author:fxw +@file: __init__.py.py +@time: 2020/08/07 +""" diff --git a/tools/cal.py b/tools/cal.py new file mode 100644 index 0000000..363509b --- /dev/null +++ b/tools/cal.py @@ -0,0 +1,11 @@ +import sys +sys.path.append('./') +from tools.cal_rescall.script import cal_recall_precison_f1 +from tools.cal_rescall.cal_det import cal_det_metrics + + +result = cal_recall_precison_f1('/src/notebooks/detect_text/icdar2015/ch4_test_gts/','/src/notebooks/detect_text/PytorchOCR3/result/result_txt') +print(result) + +out = cal_det_metrics('/src/notebooks/detect_text/icdar2015/ch4_test_gts/', '/src/notebooks/detect_text/PytorchOCR3/result/result_txt') +print(out) \ No newline at end of file diff --git a/tools/cal_rescall/__init__.py b/tools/cal_rescall/__init__.py new file mode 100644 index 0000000..4292570 --- /dev/null +++ b/tools/cal_rescall/__init__.py @@ -0,0 +1,6 @@ +#-*- coding:utf-8 _*- +""" +@author:fxw +@file: __init__.py.py +@time: 2020/08/13 +""" diff --git a/tools/cal_rescall/__pycache__/__init__.cpython-35.pyc b/tools/cal_rescall/__pycache__/__init__.cpython-35.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f055973cfdb7d3cb9ddf5dee18bcba277a13d7c5 GIT binary patch literal 225 zcmWgR<>flbVHzL7z`*brh~a<{$Z`PUVlE(&!oUy(BpDfkHJPeRxf~KpOEU6{tkNpV zxg63mb5gAo;^Q;(GE3s)^$IG1h|8fQGZ!doWME{VZ(yNsXw2oO$#{!BK0YNsIX-?R zLlG0uR50<&+u156v^ce>I3_JIFTJ9)JT)^WpfWilu_!m7C_gJTxuh7#FUc=T&hU2* oiYdv@&nb>cPRxlfN-YLbIWb69-{P>z%}*)KNws4GIUR@@0PMCuHvj+t literal 0 HcmV?d00001 diff --git a/tools/cal_rescall/__pycache__/__init__.cpython-36.pyc b/tools/cal_rescall/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..358aeb286a65d7620e63b1a2c3321cead929b57b GIT binary patch literal 179 zcmXr!<>k6Nb$@&i0|UcjAcg}*Aj<)Wi#dQq3PTh_3S%&XCR3FumqTJ{Nk)E=Ra!+k zmqS`+PO6nce0*kJW=VX!UO^=gaXFM^<^n~H42%r)4J`Bxjk)|Z8E>)2$EV~c$H%W^ xC}IMd2_}BY>*<%|=jRmbCnx5_7o`>hsT_SI<8E=-5y}v* z<^5#cLrV)z`>?-r4=n~HSPbQW^qyM$mBjoN31L5!J<{JnqF)STA1n>YfDD&bFM{o-T1VOQv{p7aQdR}5l=JjN`C$Jjt*P<^Gg4k!gI%H-b(v;# zGK$A#I;CZDn3ps$?s|kJ-6fQ(Pn-C5KA$C3I;Tk|OY#idYF->pr|dY%t3$?>eF$N? zU5F4=MdJW2nHXuWX{Qi%>}HH{!iPF-J(zw+{C!x?-rBw|7%Q9mSz0C#H7uo}2^HJ* ziCQaJO3vLIZ!TyxTQDIUV#kaP9GG4? z1OfWZOj_MQerHJdz)_CbBdSz8goTXA&Fl#z~f(ITyKX{bq`Q=M^B{F{a}i$pc2 zx`aSo#nlpr+7pz654@Tw~n?M2N&RvgA0{7xDZxeC#Hr_sUt?d>gr`M-Ax}rplYm94cSZ1 z>-jbuHXAUZo(C}1U=3jouxACZAHj})1l$SUoA~?|P|B^^d40m)1UmM$xtz6E`7zSh cyy~9f#&xhWXQVCZBmF;U^mC>C46JbDKXD|~5&!@I literal 0 HcmV?d00001 diff --git a/tools/cal_rescall/__pycache__/cal_iou.cpython-36.pyc b/tools/cal_rescall/__pycache__/cal_iou.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..152a5c36959465e77fb7f91cb622cae829f663c2 GIT binary patch literal 4942 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 QfxbhykHl2r3y7=#01L6vyZ`_I literal 0 HcmV?d00001 diff --git a/tools/cal_rescall/__pycache__/rrc_evaluation_funcs.cpython-35.pyc b/tools/cal_rescall/__pycache__/rrc_evaluation_funcs.cpython-35.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2067f973fc89572cbf35ff8c4ab66d6b866dd2aa GIT binary patch literal 14438 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 MvK~YIAg*)&3$xgW*8l(j literal 0 HcmV?d00001 diff --git a/tools/cal_rescall/__pycache__/script.cpython-35.pyc b/tools/cal_rescall/__pycache__/script.cpython-35.pyc new file mode 100644 index 0000000000000000000000000000000000000000..20d08d770ca75b7abe29800a475efdec7deef673 GIT binary patch literal 8636 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 R0-Zx!G1%o~`hUf#{{z|uj&%S4 literal 0 HcmV?d00001 diff --git a/tools/cal_rescall/__pycache__/script.cpython-36.pyc b/tools/cal_rescall/__pycache__/script.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5a56b7e9e7ecc00b81c095200c4bec67cad70042 GIT binary patch literal 7714 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 0: + confList = np.array(confList) + matchList = np.array(matchList) + sorted_ind = np.argsort(-confList) + confList = confList[sorted_ind] + matchList = matchList[sorted_ind] + for n in range(len(confList)): + match = matchList[n] + if match: + correct += 1 + AP += float(correct) / (n + 1) + + if numGtCare > 0: + AP /= numGtCare + + return AP + + perSampleMetrics = {} + + matchedSum = 0 + + Rectangle = namedtuple('Rectangle', 'xmin ymin xmax ymax') + + numGlobalCareGt = 0 + numGlobalCareDet = 0 + + arrGlobalConfidences = [] + arrGlobalMatches = [] + + recall = 0 + precision = 0 + hmean = 0 + + detMatched = 0 + + iouMat = np.empty([1, 1]) + + gtPols = [] + detPols = [] + + gtPolPoints = [] + detPolPoints = [] + + # Array of Ground Truth Polygons' keys marked as don't Care + gtDontCarePolsNum = [] + # Array of Detected Polygons' matched with a don't Care GT + detDontCarePolsNum = [] + + pairs = [] + detMatchedNums = [] + + arrSampleConfidences = [] + arrSampleMatch = [] + + evaluationLog = "" + + # print(len(gt)) + for n in range(len(gt)): + points = gt[n]['points'] + # transcription = gt[n]['text'] + dontCare = gt[n]['ignore'] +# points = Polygon(points) +# points = points.buffer(0) + if not Polygon(points).is_valid or not Polygon(points).is_simple: + continue + + gtPol = points + gtPols.append(gtPol) + gtPolPoints.append(points) + if dontCare: + gtDontCarePolsNum.append(len(gtPols) - 1) + + evaluationLog += "GT polygons: " + str(len(gtPols)) + ( + " (" + str(len(gtDontCarePolsNum)) + " don't care)\n" + if len(gtDontCarePolsNum) > 0 else "\n") + + for n in range(len(pred)): + points = pred[n]['points'] +# points = Polygon(points) +# points = points.buffer(0) + if not Polygon(points).is_valid or not Polygon(points).is_simple: + continue + + detPol = points + detPols.append(detPol) + detPolPoints.append(points) + if len(gtDontCarePolsNum) > 0: + for dontCarePol in gtDontCarePolsNum: + dontCarePol = gtPols[dontCarePol] + intersected_area = get_intersection(dontCarePol, detPol) + pdDimensions = Polygon(detPol).area + precision = 0 if pdDimensions == 0 else intersected_area / pdDimensions + if (precision > self.area_precision_constraint): + detDontCarePolsNum.append(len(detPols) - 1) + break + + evaluationLog += "DET polygons: " + str(len(detPols)) + ( + " (" + str(len(detDontCarePolsNum)) + " don't care)\n" + if len(detDontCarePolsNum) > 0 else "\n") + + if len(gtPols) > 0 and len(detPols) > 0: + # Calculate IoU and precision matrixs + outputShape = [len(gtPols), len(detPols)] + iouMat = np.empty(outputShape) + gtRectMat = np.zeros(len(gtPols), np.int8) + detRectMat = np.zeros(len(detPols), np.int8) + for gtNum in range(len(gtPols)): + for detNum in range(len(detPols)): + pG = gtPols[gtNum] + pD = detPols[detNum] + iouMat[gtNum, detNum] = get_intersection_over_union(pD, pG) + + for gtNum in range(len(gtPols)): + for detNum in range(len(detPols)): + if gtRectMat[gtNum] == 0 and detRectMat[ + detNum] == 0 and gtNum not in gtDontCarePolsNum and detNum not in detDontCarePolsNum: + if iouMat[gtNum, detNum] > self.iou_constraint: + gtRectMat[gtNum] = 1 + detRectMat[detNum] = 1 + detMatched += 1 + pairs.append({'gt': gtNum, 'det': detNum}) + detMatchedNums.append(detNum) + evaluationLog += "Match GT #" + \ + str(gtNum) + " with Det #" + str(detNum) + "\n" + + numGtCare = (len(gtPols) - len(gtDontCarePolsNum)) + numDetCare = (len(detPols) - len(detDontCarePolsNum)) + if numGtCare == 0: + recall = float(1) + precision = float(0) if numDetCare > 0 else float(1) + else: + recall = float(detMatched) / numGtCare + precision = 0 if numDetCare == 0 else float(detMatched) / numDetCare + + hmean = 0 if (precision + recall) == 0 else 2.0 * \ + precision * recall / (precision + recall) + + matchedSum += detMatched + numGlobalCareGt += numGtCare + numGlobalCareDet += numDetCare + + perSampleMetrics = { + 'precision': precision, + 'recall': recall, + 'hmean': hmean, + 'pairs': pairs, + 'iouMat': [] if len(detPols) > 100 else iouMat.tolist(), + 'gtPolPoints': gtPolPoints, + 'detPolPoints': detPolPoints, + 'gtCare': numGtCare, + 'detCare': numDetCare, + 'gtDontCare': gtDontCarePolsNum, + 'detDontCare': detDontCarePolsNum, + 'detMatched': detMatched, + 'evaluationLog': evaluationLog + } + + return perSampleMetrics + + def combine_results(self, results): + numGlobalCareGt = 0 + numGlobalCareDet = 0 + matchedSum = 0 + for result in results: + numGlobalCareGt += result['gtCare'] + numGlobalCareDet += result['detCare'] + matchedSum += result['detMatched'] + + methodRecall = 0 if numGlobalCareGt == 0 else float( + matchedSum) / numGlobalCareGt + methodPrecision = 0 if numGlobalCareDet == 0 else float( + matchedSum) / numGlobalCareDet + methodHmean = 0 if methodRecall + methodPrecision == 0 else 2 * \ + methodRecall * methodPrecision / (methodRecall + methodPrecision) + # print(methodRecall, methodPrecision, methodHmean) + # sys.exit(-1) + methodMetrics = { + 'precision': methodPrecision, + 'recall': methodRecall, + 'hmean': methodHmean + } + + return methodMetrics + + +if __name__ == '__main__': + evaluator = DetectionIoUEvaluator() + gts = [[{ + 'points': [(0, 0), (1, 0), (1, 1), (0, 1)], + 'text': 1234, + 'ignore': False, + }, { + 'points': [(2, 2), (3, 2), (3, 3), (2, 3)], + 'text': 5678, + 'ignore': False, + }]] + preds = [[{ + 'points': [(0.1, 0.1), (1, 0), (1, 1), (0, 1)], + 'text': 123, + 'ignore': False, + }]] + results = [] + for gt, pred in zip(gts, preds): + results.append(evaluator.evaluate_image(gt, pred)) + metrics = evaluator.combine_results(results) + print(metrics) diff --git a/tools/cal_rescall/rrc_evaluation_funcs.py b/tools/cal_rescall/rrc_evaluation_funcs.py new file mode 100644 index 0000000..b536b6a --- /dev/null +++ b/tools/cal_rescall/rrc_evaluation_funcs.py @@ -0,0 +1,414 @@ +# encoding: UTF-8 +import json +import sys; + +sys.path.append('./') +import zipfile +import re +import sys +import os +import codecs +import traceback +import importlib +from io import StringIO + + +def print_help(): + sys.stdout.write('Usage: python %s.py -g= -s= [-o= -p=]' % sys.argv[0]) + sys.exit(2) + + +def load_zip_file_keys(file, fileNameRegExp=''): + """ + Returns an array with the entries of the ZIP file that match with the regular expression. + The key's are the names or the file or the capturing group definied in the fileNameRegExp + """ + try: + archive = zipfile.ZipFile(file, mode='r', allowZip64=True) + except: + raise Exception('Error loading the ZIP archive.') + + pairs = [] + + for name in archive.namelist(): + addFile = True + keyName = name + if fileNameRegExp != "": + m = re.match(fileNameRegExp, name) + if m == None: + addFile = False + else: + if len(m.groups()) > 0: + keyName = m.group(1) + + if addFile: + pairs.append(keyName) + + return pairs + + +def load_zip_file(file, fileNameRegExp='', allEntries=False): + """ + Returns an array with the contents (filtered by fileNameRegExp) of a ZIP file. + The key's are the names or the file or the capturing group definied in the fileNameRegExp + allEntries validates that all entries in the ZIP file pass the fileNameRegExp + """ + try: + archive = zipfile.ZipFile(file, mode='r', allowZip64=True) + except: + raise Exception('Error loading the ZIP archive') + + pairs = [] + for name in archive.namelist(): + addFile = True + keyName = name + if fileNameRegExp != "": + m = re.match(fileNameRegExp, name) + if m == None: + addFile = False + else: + if len(m.groups()) > 0: + keyName = m.group(1) + + if addFile: + pairs.append([keyName, archive.read(name)]) + else: + if allEntries: + raise Exception('ZIP entry not valid: %s' % name) + + return dict(pairs) + + +def load_folder_file(file, fileNameRegExp='', allEntries=False): + """ + Returns an array with the contents (filtered by fileNameRegExp) of a ZIP file. + The key's are the names or the file or the capturing group definied in the fileNameRegExp + allEntries validates that all entries in the ZIP file pass the fileNameRegExp + """ + pairs = [] + for name in os.listdir(file): + addFile = True + keyName = name + if fileNameRegExp != "": + m = re.match(fileNameRegExp, name) + if m == None: + addFile = False + else: + if len(m.groups()) > 0: + keyName = m.group(1) + + if addFile: + pairs.append([keyName, open(os.path.join(file, name)).read()]) + else: + if allEntries: + raise Exception('ZIP entry not valid: %s' % name) + + return dict(pairs) + + +def decode_utf8(raw): + """ + Returns a Unicode object on success, or None on failure + """ + try: + raw = codecs.decode(raw, 'utf-8', 'replace') + # extracts BOM if exists + raw = raw.encode('utf8') + if raw.startswith(codecs.BOM_UTF8): + raw = raw.replace(codecs.BOM_UTF8, '', 1) + return raw.decode('utf-8') + except: + return None + + +def validate_lines_in_file(fileName, file_contents, CRLF=True, LTRB=True, withTranscription=False, withConfidence=False, + imWidth=0, imHeight=0): + """ + This function validates that all lines of the file calling the Line validation function for each line + """ + utf8File = decode_utf8(file_contents) + if (utf8File is None): + raise Exception("The file %s is not UTF-8" % fileName) + + lines = utf8File.split("\r\n" if CRLF else "\n") + for line in lines: + line = line.replace("\r", "").replace("\n", "") + if (line != ""): + try: + validate_tl_line(line, LTRB, withTranscription, withConfidence, imWidth, imHeight) + except Exception as e: + raise Exception( + ("Line in sample not valid. Sample: %s Line: %s Error: %s" % (fileName, line, str(e))).encode( + 'utf-8', 'replace')) + + +def validate_tl_line(line, LTRB=True, withTranscription=True, withConfidence=True, imWidth=0, imHeight=0): + """ + Validate the format of the line. If the line is not valid an exception will be raised. + If maxWidth and maxHeight are specified, all points must be inside the imgage bounds. + Posible values are: + LTRB=True: xmin,ymin,xmax,ymax[,confidence][,transcription] + LTRB=False: x1,y1,x2,y2,x3,y3,x4,y4[,confidence][,transcription] + """ + get_tl_line_values(line, LTRB, withTranscription, withConfidence, imWidth, imHeight) + + +def get_tl_line_values(line, LTRB=True, withTranscription=False, withConfidence=False, imWidth=0, imHeight=0): + """ + Validate the format of the line. If the line is not valid an exception will be raised. + If maxWidth and maxHeight are specified, all points must be inside the imgage bounds. + Posible values are: + LTRB=True: xmin,ymin,xmax,ymax[,confidence][,transcription] + LTRB=False: x1,y1,x2,y2,x3,y3,x4,y4[,confidence][,transcription] + Returns values from a textline. Points , [Confidences], [Transcriptions] + """ + confidence = 0.0 + transcription = ""; + points = [] + + numPoints = 4; + + if LTRB: + + numPoints = 4; + + if withTranscription and withConfidence: + m = re.match( + r'^\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-1].?[0-9]*)\s*,(.*)$', line) + if m == None: + m = re.match( + r'^\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-1].?[0-9]*)\s*,(.*)$', + line) + raise Exception("Format incorrect. Should be: xmin,ymin,xmax,ymax,confidence,transcription") + elif withConfidence: + m = re.match(r'^\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-1].?[0-9]*)\s*$', + line) + if m == None: + raise Exception("Format incorrect. Should be: xmin,ymin,xmax,ymax,confidence") + elif withTranscription: + m = re.match(r'^\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*,(.*)$', line) + if m == None: + raise Exception("Format incorrect. Should be: xmin,ymin,xmax,ymax,transcription") + else: + m = re.match(r'^\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*,?\s*$', line) + if m == None: + raise Exception("Format incorrect. Should be: xmin,ymin,xmax,ymax") + + xmin = int(m.group(1)) + ymin = int(m.group(2)) + xmax = int(m.group(3)) + ymax = int(m.group(4)) + if (xmax < xmin): + raise Exception("Xmax value (%s) not valid (Xmax < Xmin)." % (xmax)) + if (ymax < ymin): + raise Exception("Ymax value (%s) not valid (Ymax < Ymin)." % (ymax)) + + points = [float(m.group(i)) for i in range(1, (numPoints + 1))] + + if (imWidth > 0 and imHeight > 0): + validate_point_inside_bounds(xmin, ymin, imWidth, imHeight); + validate_point_inside_bounds(xmax, ymax, imWidth, imHeight); + + else: + + numPoints = 8; + + if withTranscription and withConfidence: + m = re.match( + r'^\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*([0-1].?[0-9]*)\s*,(.*)$', + line) + if m == None: + raise Exception("Format incorrect. Should be: x1,y1,x2,y2,x3,y3,x4,y4,confidence,transcription") + elif withConfidence: + m = re.match( + r'^\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*([0-1].?[0-9]*)\s*$', + line) + if m == None: + raise Exception("Format incorrect. Should be: x1,y1,x2,y2,x3,y3,x4,y4,confidence") + elif withTranscription: + m = re.match( + r'^\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,(.*)$', + line) + if m == None: + raise Exception("Format incorrect. Should be: x1,y1,x2,y2,x3,y3,x4,y4,transcription") + else: + m = re.match( + r'^\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*$', + line) + if m == None: + raise Exception("Format incorrect. Should be: x1,y1,x2,y2,x3,y3,x4,y4") + + points = [float(m.group(i)) for i in range(1, (numPoints + 1))] + + validate_clockwise_points(points) + + if (imWidth > 0 and imHeight > 0): + validate_point_inside_bounds(points[0], points[1], imWidth, imHeight); + validate_point_inside_bounds(points[2], points[3], imWidth, imHeight); + validate_point_inside_bounds(points[4], points[5], imWidth, imHeight); + validate_point_inside_bounds(points[6], points[7], imWidth, imHeight); + + if withConfidence: + try: + confidence = float(m.group(numPoints + 1)) + except ValueError: + raise Exception("Confidence value must be a float") + + if withTranscription: + posTranscription = numPoints + (2 if withConfidence else 1) + transcription = m.group(posTranscription) + m2 = re.match(r'^\s*\"(.*)\"\s*$', transcription) + if m2 != None: # Transcription with double quotes, we extract the value and replace escaped characters + transcription = m2.group(1).replace("\\\\", "\\").replace("\\\"", "\"") + + return points, confidence, transcription + + +def validate_point_inside_bounds(x, y, imWidth, imHeight): + if (x < 0 or x > imWidth): + raise Exception("X value (%s) not valid. Image dimensions: (%s,%s)" % (xmin, imWidth, imHeight)) + if (y < 0 or y > imHeight): + raise Exception( + "Y value (%s) not valid. Image dimensions: (%s,%s) Sample: %s Line:%s" % (ymin, imWidth, imHeight)) + + +def validate_clockwise_points(points): + """ + Validates that the points that the 4 points that dlimite a polygon are in clockwise order. + """ + + if len(points) != 8: + raise Exception("Points list not valid." + str(len(points))) + + point = [ + [int(points[0]), int(points[1])], + [int(points[2]), int(points[3])], + [int(points[4]), int(points[5])], + [int(points[6]), int(points[7])] + ] + edge = [ + (point[1][0] - point[0][0]) * (point[1][1] + point[0][1]), + (point[2][0] - point[1][0]) * (point[2][1] + point[1][1]), + (point[3][0] - point[2][0]) * (point[3][1] + point[2][1]), + (point[0][0] - point[3][0]) * (point[0][1] + point[3][1]) + ] + + summatory = edge[0] + edge[1] + edge[2] + edge[3]; + if summatory > 0: + raise Exception( + "Points are not clockwise. The coordinates of bounding quadrilaterals have to be given in clockwise order. Regarding the correct interpretation of 'clockwise' remember that the image coordinate system used is the standard one, with the image origin at the upper left, the X axis extending to the right and Y axis extending downwards.") + + +def get_tl_line_values_from_file_contents(content, CRLF=True, LTRB=True, withTranscription=False, withConfidence=False, + imWidth=0, imHeight=0, sort_by_confidences=True): + """ + Returns all points, confindences and transcriptions of a file in lists. Valid line formats: + xmin,ymin,xmax,ymax,[confidence],[transcription] + x1,y1,x2,y2,x3,y3,x4,y4,[confidence],[transcription] + """ + pointsList = [] + transcriptionsList = [] + confidencesList = [] + + lines = content.split("\r\n" if CRLF else "\n") + for line in lines: + line = line.replace("\r", "").replace("\n", "") + if (line != ""): + points, confidence, transcription = get_tl_line_values(line, LTRB, withTranscription, withConfidence, + imWidth, imHeight); + pointsList.append(points) + transcriptionsList.append(transcription) + confidencesList.append(confidence) + + if withConfidence and len(confidencesList) > 0 and sort_by_confidences: + import numpy as np + sorted_ind = np.argsort(-np.array(confidencesList)) + confidencesList = [confidencesList[i] for i in sorted_ind] + pointsList = [pointsList[i] for i in sorted_ind] + transcriptionsList = [transcriptionsList[i] for i in sorted_ind] + + return pointsList, confidencesList, transcriptionsList + + +def main_evaluation(p, default_evaluation_params_fn, validate_data_fn, evaluate_method_fn, show_result=True, + per_sample=True): + """ + This process validates a method, evaluates it and if it succed generates a ZIP file with a JSON entry for each sample. + Params: + p: Dictionary of parmeters with the GT/submission locations. If None is passed, the parameters send by the system are used. + default_evaluation_params_fn: points to a function that returns a dictionary with the default parameters used for the evaluation + validate_data_fn: points to a method that validates the corrct format of the submission + evaluate_method_fn: points to a function that evaluated the submission and return a Dictionary with the results + """ + evalParams = default_evaluation_params_fn() + if 'p' in p.keys(): + evalParams.update(p['p'] if isinstance(p['p'], dict) else json.loads(p['p'][1:-1])) + + resDict = {'calculated': True, 'Message': '', 'method': '{}', 'per_sample': '{}'} + try: + # validate_data_fn(p['g'], p['s'], evalParams) + evalData = evaluate_method_fn(p['g'], p['s'], evalParams) + resDict.update(evalData) + + except Exception as e: + traceback.print_exc() + resDict['Message'] = str(e) + resDict['calculated'] = False + + if 'o' in p: + if not os.path.exists(p['o']): + os.makedirs(p['o']) + + resultsOutputname = p['o'] + '/results.zip' + outZip = zipfile.ZipFile(resultsOutputname, mode='w', allowZip64=True) + + del resDict['per_sample'] + if 'output_items' in resDict.keys(): + del resDict['output_items'] + + outZip.writestr('method.json', json.dumps(resDict)) + + if not resDict['calculated']: + if show_result: + sys.stderr.write('Error!\n' + resDict['Message'] + '\n\n') + if 'o' in p: + outZip.close() + return resDict + + if 'o' in p: + if per_sample == True: + for k, v in evalData['per_sample'].iteritems(): + outZip.writestr(k + '.json', json.dumps(v)) + + if 'output_items' in evalData.keys(): + for k, v in evalData['output_items'].iteritems(): + outZip.writestr(k, v) + + outZip.close() + + if show_result: + sys.stdout.write("Calculated!") + sys.stdout.write(json.dumps(resDict['method'])) + + return resDict + + +def main_validation(default_evaluation_params_fn, validate_data_fn): + """ + This process validates a method + Params: + default_evaluation_params_fn: points to a function that returns a dictionary with the default parameters used for the evaluation + validate_data_fn: points to a method that validates the corrct format of the submission + """ + try: + p = dict([s[1:].split('=') for s in sys.argv[1:]]) + evalParams = default_evaluation_params_fn() + if 'p' in p.keys(): + evalParams.update(p['p'] if isinstance(p['p'], dict) else json.loads(p['p'][1:-1])) + + validate_data_fn(p['g'], p['s'], evalParams) + print('SUCCESS') + sys.exit(0) + except Exception as e: + print(str(e)) + sys.exit(101) \ No newline at end of file diff --git a/tools/cal_rescall/script.py b/tools/cal_rescall/script.py new file mode 100644 index 0000000..e406b4b --- /dev/null +++ b/tools/cal_rescall/script.py @@ -0,0 +1,323 @@ +# -*- coding: utf-8 -*- +from collections import namedtuple +from . import rrc_evaluation_funcs +import Polygon as plg +import numpy as np + + +def default_evaluation_params(): + """ + default_evaluation_params: Default parameters to use for the validation and evaluation. + """ + return { + 'IOU_CONSTRAINT': 0.5, + 'AREA_PRECISION_CONSTRAINT': 0.5, + 'GT_SAMPLE_NAME_2_ID': 'gt_img_([0-9]+).txt', + 'DET_SAMPLE_NAME_2_ID': 'res_img_([0-9]+).txt', + 'LTRB': False, # LTRB:2points(left,top,right,bottom) or 4 points(x1,y1,x2,y2,x3,y3,x4,y4) + 'CRLF': False, # Lines are delimited by Windows CRLF format + 'CONFIDENCES': False, # Detections must include confidence value. AP will be calculated + 'PER_SAMPLE_RESULTS': True # Generate per sample results and produce data for visualization + } + + +def validate_data(gtFilePath, submFilePath, evaluationParams): + """ + Method validate_data: validates that all files in the results folder are correct (have the correct name contents). + Validates also that there are no missing files in the folder. + If some error detected, the method raises the error + """ + gt = rrc_evaluation_funcs.load_folder_file(gtFilePath, evaluationParams['GT_SAMPLE_NAME_2_ID']) + + subm = rrc_evaluation_funcs.load_folder_file(submFilePath, evaluationParams['DET_SAMPLE_NAME_2_ID'], True) + + # Validate format of GroundTruth + for k in gt: + rrc_evaluation_funcs.validate_lines_in_file(k, gt[k], evaluationParams['CRLF'], evaluationParams['LTRB'], True) + + # Validate format of results + for k in subm: + if (k in gt) == False: + raise Exception("The sample %s not present in GT" % k) + + rrc_evaluation_funcs.validate_lines_in_file(k, subm[k], evaluationParams['CRLF'], evaluationParams['LTRB'], + False, evaluationParams['CONFIDENCES']) + + +def evaluate_method(gtFilePath, submFilePath, evaluationParams): + """ + Method evaluate_method: evaluate method and returns the results + Results. Dictionary with the following values: + - method (required) Global method metrics. Ex: { 'Precision':0.8,'Recall':0.9 } + - samples (optional) Per sample metrics. Ex: {'sample1' : { 'Precision':0.8,'Recall':0.9 } , 'sample2' : { 'Precision':0.8,'Recall':0.9 } + """ + + def polygon_from_points(points): + """ + Returns a Polygon object to use with the Polygon2 class from a list of 8 points: x1,y1,x2,y2,x3,y3,x4,y4 + """ + resBoxes = np.empty([1, 8], dtype='int32') + resBoxes[0, 0] = int(points[0]) + resBoxes[0, 4] = int(points[1]) + resBoxes[0, 1] = int(points[2]) + resBoxes[0, 5] = int(points[3]) + resBoxes[0, 2] = int(points[4]) + resBoxes[0, 6] = int(points[5]) + resBoxes[0, 3] = int(points[6]) + resBoxes[0, 7] = int(points[7]) + pointMat = resBoxes[0].reshape([2, 4]).T + return plg.Polygon(pointMat) + + def rectangle_to_polygon(rect): + resBoxes = np.empty([1, 8], dtype='int32') + resBoxes[0, 0] = int(rect.xmin) + resBoxes[0, 4] = int(rect.ymax) + resBoxes[0, 1] = int(rect.xmin) + resBoxes[0, 5] = int(rect.ymin) + resBoxes[0, 2] = int(rect.xmax) + resBoxes[0, 6] = int(rect.ymin) + resBoxes[0, 3] = int(rect.xmax) + resBoxes[0, 7] = int(rect.ymax) + + pointMat = resBoxes[0].reshape([2, 4]).T + + return plg.Polygon(pointMat) + + def rectangle_to_points(rect): + points = [int(rect.xmin), int(rect.ymax), int(rect.xmax), int(rect.ymax), int(rect.xmax), int(rect.ymin), + int(rect.xmin), int(rect.ymin)] + return points + + def get_union(pD, pG): + areaA = pD.area(); + areaB = pG.area(); + return areaA + areaB - get_intersection(pD, pG); + + def get_intersection_over_union(pD, pG): + try: + return get_intersection(pD, pG) / get_union(pD, pG); + except: + return 0 + + def get_intersection(pD, pG): + pInt = pD & pG + if len(pInt) == 0: + return 0 + return pInt.area() + + def compute_ap(confList, matchList, numGtCare): + correct = 0 + AP = 0 + if len(confList) > 0: + confList = np.array(confList) + matchList = np.array(matchList) + sorted_ind = np.argsort(-confList) + confList = confList[sorted_ind] + matchList = matchList[sorted_ind] + for n in range(len(confList)): + match = matchList[n] + if match: + correct += 1 + AP += float(correct) / (n + 1) + + if numGtCare > 0: + AP /= numGtCare + + return AP + + perSampleMetrics = {} + + matchedSum = 0 + + Rectangle = namedtuple('Rectangle', 'xmin ymin xmax ymax') + + gt = rrc_evaluation_funcs.load_folder_file(gtFilePath, evaluationParams['GT_SAMPLE_NAME_2_ID']) + subm = rrc_evaluation_funcs.load_folder_file(submFilePath, evaluationParams['DET_SAMPLE_NAME_2_ID'], True) + + numGlobalCareGt = 0; + numGlobalCareDet = 0; + + arrGlobalConfidences = []; + arrGlobalMatches = []; + + for resFile in gt: + + gtFile = gt[resFile] # rrc_evaluation_funcs.decode_utf8(gt[resFile]) + recall = 0 + precision = 0 + hmean = 0 + + detMatched = 0 + + iouMat = np.empty([1, 1]) + + gtPols = [] + detPols = [] + + gtPolPoints = [] + detPolPoints = [] + + # Array of Ground Truth Polygons' keys marked as don't Care + gtDontCarePolsNum = [] + # Array of Detected Polygons' matched with a don't Care GT + detDontCarePolsNum = [] + + pairs = [] + detMatchedNums = [] + + arrSampleConfidences = []; + arrSampleMatch = []; + sampleAP = 0; + + evaluationLog = "" + + pointsList, _, transcriptionsList = rrc_evaluation_funcs.get_tl_line_values_from_file_contents(gtFile, + evaluationParams[ + 'CRLF'], + evaluationParams[ + 'LTRB'], + True, False) + for n in range(len(pointsList)): + points = pointsList[n] + transcription = transcriptionsList[n] + dontCare = transcription == "###" + if evaluationParams['LTRB']: + gtRect = Rectangle(*points) + gtPol = rectangle_to_polygon(gtRect) + else: + gtPol = polygon_from_points(points) + gtPols.append(gtPol) + gtPolPoints.append(points) + if dontCare: + gtDontCarePolsNum.append(len(gtPols) - 1) + + evaluationLog += "GT polygons: " + str(len(gtPols)) + ( + " (" + str(len(gtDontCarePolsNum)) + " don't care)\n" if len(gtDontCarePolsNum) > 0 else "\n") + + if resFile in subm: + + detFile = subm[resFile] # rrc_evaluation_funcs.decode_utf8(subm[resFile]) + + pointsList, confidencesList, _ = rrc_evaluation_funcs.get_tl_line_values_from_file_contents(detFile, + evaluationParams[ + 'CRLF'], + evaluationParams[ + 'LTRB'], + False, + evaluationParams[ + 'CONFIDENCES']) + for n in range(len(pointsList)): + points = pointsList[n] + + if evaluationParams['LTRB']: + detRect = Rectangle(*points) + detPol = rectangle_to_polygon(detRect) + else: + detPol = polygon_from_points(points) + detPols.append(detPol) + detPolPoints.append(points) + if len(gtDontCarePolsNum) > 0: + for dontCarePol in gtDontCarePolsNum: + dontCarePol = gtPols[dontCarePol] + intersected_area = get_intersection(dontCarePol, detPol) + pdDimensions = detPol.area() + precision = 0 if pdDimensions == 0 else intersected_area / pdDimensions + if (precision > evaluationParams['AREA_PRECISION_CONSTRAINT']): + detDontCarePolsNum.append(len(detPols) - 1) + break + + evaluationLog += "DET polygons: " + str(len(detPols)) + ( + " (" + str(len(detDontCarePolsNum)) + " don't care)\n" if len(detDontCarePolsNum) > 0 else "\n") + + if len(gtPols) > 0 and len(detPols) > 0: + # Calculate IoU and precision matrixs + outputShape = [len(gtPols), len(detPols)] + iouMat = np.empty(outputShape) + gtRectMat = np.zeros(len(gtPols), np.int8) + detRectMat = np.zeros(len(detPols), np.int8) + for gtNum in range(len(gtPols)): + for detNum in range(len(detPols)): + pG = gtPols[gtNum] + pD = detPols[detNum] + iouMat[gtNum, detNum] = get_intersection_over_union(pD, pG) + + for gtNum in range(len(gtPols)): + for detNum in range(len(detPols)): + if gtRectMat[gtNum] == 0 and detRectMat[ + detNum] == 0 and gtNum not in gtDontCarePolsNum and detNum not in detDontCarePolsNum: + if iouMat[gtNum, detNum] > evaluationParams['IOU_CONSTRAINT']: + gtRectMat[gtNum] = 1 + detRectMat[detNum] = 1 + detMatched += 1 + pairs.append({'gt': gtNum, 'det': detNum}) + detMatchedNums.append(detNum) + evaluationLog += "Match GT #" + str(gtNum) + " with Det #" + str(detNum) + "\n" + + if evaluationParams['CONFIDENCES']: + for detNum in range(len(detPols)): + if detNum not in detDontCarePolsNum: + # we exclude the don't care detections + match = detNum in detMatchedNums + + arrSampleConfidences.append(confidencesList[detNum]) + arrSampleMatch.append(match) + + arrGlobalConfidences.append(confidencesList[detNum]); + arrGlobalMatches.append(match); + + numGtCare = (len(gtPols) - len(gtDontCarePolsNum)) + numDetCare = (len(detPols) - len(detDontCarePolsNum)) + if numGtCare == 0: + recall = float(1) + precision = float(0) if numDetCare > 0 else float(1) + sampleAP = precision + else: + recall = float(detMatched) / numGtCare + precision = 0 if numDetCare == 0 else float(detMatched) / numDetCare + if evaluationParams['CONFIDENCES'] and evaluationParams['PER_SAMPLE_RESULTS']: + sampleAP = compute_ap(arrSampleConfidences, arrSampleMatch, numGtCare) + + hmean = 0 if (precision + recall) == 0 else 2.0 * precision * recall / (precision + recall) + + matchedSum += detMatched + numGlobalCareGt += numGtCare + numGlobalCareDet += numDetCare + + if evaluationParams['PER_SAMPLE_RESULTS']: + perSampleMetrics[resFile] = { + 'precision': precision, + 'recall': recall, + 'hmean': hmean, + 'pairs': pairs, + 'AP': sampleAP, + 'iouMat': [] if len(detPols) > 100 else iouMat.tolist(), + 'gtPolPoints': gtPolPoints, + 'detPolPoints': detPolPoints, + 'gtDontCare': gtDontCarePolsNum, + 'detDontCare': detDontCarePolsNum, + 'evaluationParams': evaluationParams, + 'evaluationLog': evaluationLog + } + + # Compute MAP and MAR + AP = 0 + if evaluationParams['CONFIDENCES']: + AP = compute_ap(arrGlobalConfidences, arrGlobalMatches, numGlobalCareGt) + + methodRecall = 0 if numGlobalCareGt == 0 else float(matchedSum) / numGlobalCareGt + methodPrecision = 0 if numGlobalCareDet == 0 else float(matchedSum) / numGlobalCareDet + methodHmean = 0 if methodRecall + methodPrecision == 0 else 2 * methodRecall * methodPrecision / ( + methodRecall + methodPrecision) + + methodMetrics = {'precision': methodPrecision, 'recall': methodRecall, 'hmean': methodHmean, 'AP': AP} + + resDict = {'calculated': True, 'Message': '', 'method': methodMetrics, 'per_sample': perSampleMetrics} + + return resDict; + + +def cal_recall_precison_f1(gt_path, result_path, show_result=False): + p = {'g': gt_path, 's': result_path} + result = rrc_evaluation_funcs.main_evaluation(p, default_evaluation_params, validate_data, evaluate_method, + show_result) + return result['method'] \ No newline at end of file diff --git a/tools/det_infer.py b/tools/det_infer.py new file mode 100644 index 0000000..8f9ec30 --- /dev/null +++ b/tools/det_infer.py @@ -0,0 +1,192 @@ +#-*- coding:utf-8 _*- +""" +@author:fxw +@file: det_infer.py +@time: 2020/08/20 +""" +import os +import sys +sys.path.append('./') + +import cv2 +import torch +import argparse +import yaml +from PIL import Image +import numpy as np +from tqdm import tqdm +import onnxruntime +import torchvision.transforms as transforms +from ptocr.utils.util_function import create_module,resize_image_batch +from ptocr.utils.util_function import create_process_obj,create_dir,load_model +from script.onnx_to_tensorrt import build_engine,allocate_buffers,do_inference + +def config_load(args): + stream = open(args.config, 'r', encoding='utf-8') + config = yaml.load(stream,Loader=yaml.FullLoader) + config['infer']['model_path'] = args.model_path + config['infer']['path'] = args.img_path + config['infer']['save_path'] = args.result_save_path + config['infer']['onnx_path'] = args.onnx_path + config['infer']['trt_path'] = args.trt_path + config['testload']['add_padding'] = args.add_padding + config['testload']['test_size'] = args.max_size + config['testload']['batch_size'] = args.batch_size + return config + + +def get_batch_files(path,img_files,batch_size=3): + img_files = np.array(img_files) + num = len(img_files)//batch_size + batch_imgs = [] + batch_img_names = [] + for i in range(num): + files = img_files[batch_size*i:batch_size*(i+1)] + img = [cv2.imread(os.path.join(path,img_file)) for img_file in files] + img_names = [img_file.split('.')[0] for img_file in files] + batch_imgs.append(img) + batch_img_names.append(img_names) + files = img_files[batch_size*(num):len(img_files)] + if(len(files)!=0): + img = [cv2.imread(os.path.join(path, img_file)) for img_file in files] + img_names = [img_file.split('.')[0] for img_file in files] + batch_imgs.append(img) + batch_img_names.append(img_names) + return batch_imgs,batch_img_names + + +def get_img(ori_imgs,config): + imgs = [] + scales = [] + for ori_img in ori_imgs: + img,scale = resize_image_batch(ori_img,config['base']['algorithm'],config['testload']['test_size'],add_padding = config['testload']['add_padding']) + img = Image.fromarray(img).convert('RGB') + img = transforms.ToTensor()(img) + img = transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])(img).unsqueeze(0) + imgs.append(img) + scales.append(scale) + return torch.cat(imgs,0),scales + +class TestProgram(): + def __init__(self,config): + super(TestProgram,self).__init__() + self.config = config + if(config['infer']['trt_path'] is not None): + engine = build_engine(config['infer']['onnx_path'],config['infer']['trt_path'],config['testload']['batch_size']) + self.inputs, self.outputs, self.bindings, self.stream = allocate_buffers(engine) + self.context = engine.create_execution_context() + self.infer_type = 'trt_infer' + + elif(config['infer']['onnx_path'] is not None): + self.model = onnxruntime.InferenceSession(config['infer']['onnx_path']) + self.infer_type = 'onnx_infer' + else: + model = create_module(config['architectures']['model_function'])(config) + model = load_model(model,config['infer']['model_path']) + if torch.cuda.is_available(): + model = model.cuda() + self.model = model + self.model.eval() + self.infer_type = 'torch_infer' + + img_process = create_module(config['postprocess']['function'])(config) + self.img_process = img_process + + + def infer_img(self,ori_imgs): + img,scales = get_img(ori_imgs,self.config) + if(self.infer_type == 'torch_infer'): + if torch.cuda.is_available(): + img = img.cuda() + with torch.no_grad(): + out = self.model(img) + elif(self.infer_type == 'onnx_infer'): + out = self.model.run(['out'], {'input': img.numpy()}) + out = torch.Tensor(out[0]) + else: + self.inputs[0].host = img.numpy() + output = do_inference(self.context, bindings=self.bindings, inputs=self.inputs, outputs=self.outputs, stream=self.stream,batch_size=int(self.config['testload']['batch_size'])) + test_size = self.config['testload']['test_size'] + if self.config['base']['algorithm']=='DB': + out = output[0].reshape(int(args.batch_size),1,test_size,test_size) + elif self.config['base']['algorithm']=='PAN': + out = output[0].reshape(int(args.batch_size),6,test_size,test_size) + elif self.config['base']['algorithm']=='PSE': + out = output[0].reshape(int(args.batch_size),7,test_size,test_size) + + out = torch.Tensor(out) + +# import pdb +# pdb.set_trace() + + if isinstance(out,dict): + img_num = out['f_score'].shape[0] + else: + img_num = out.shape[0] + + if(self.config['base']['algorithm']=='SAST'): + scales = [(1.0/scales[i][1],1.0/scales[i][0],ori_imgs[i].shape[0],ori_imgs[i].shape[1]) for i in range(len(scales))] + + out = create_process_obj(self.config['base']['algorithm'],out) + bbox_batch, score_batch = self.img_process(out, scales) + return bbox_batch,score_batch + +def InferOneImg(bin,img,image_name,save_path): + bbox_batch, score_batch = bin.infer_img(img) + for i in range(len(bbox_batch)): + img_show = img[i].copy() + with open(os.path.join(save_path, 'result_txt', 'res_' + image_name[i] + '.txt'), 'w+', encoding='utf-8') as fid_res: + bboxes = bbox_batch[i] + for bbox in bboxes: + bbox = bbox.reshape(-1, 2).astype(np.int) + img_show = cv2.drawContours(img_show, [bbox], -1, (0, 255, 0), 1) + bbox_str = [str(x) for x in bbox.reshape(-1)] + bbox_str = ','.join(bbox_str) + '\n' + fid_res.write(bbox_str) + cv2.imwrite(os.path.join(save_path, 'result_img', image_name[i] + '.jpg'), img_show) + +def InferImage(config): + path = config['infer']['path'] + save_path = config['infer']['save_path'] + test_bin = TestProgram(config) + create_dir(save_path) + create_dir(os.path.join(save_path,'result_img')) + create_dir(os.path.join(save_path,'result_txt')) + if os.path.isdir(path): + files = os.listdir(path) + batch_imgs,batch_img_names = get_batch_files(path,files,batch_size=config['testload']['batch_size']) +# print(batch_img_names) + bar = tqdm(total=len(batch_imgs)) + for i in range(len(batch_imgs)): + bar.update(1) + InferOneImg(test_bin, batch_imgs[i],batch_img_names[i], save_path) + bar.close() + + else: + image_name = path.split('/')[-1].split('.')[0] + img = cv2.imread(path) + InferOneImg(test_bin, [img], [image_name], save_path) + + +if __name__ == "__main__": + +# stream = open('./config/det_PSE_mobilev3.yaml', 'r', encoding='utf-8') +# stream = open('./config/det_PSE_resnet50.yaml', 'r', encoding='utf-8') +# stream = open('./config/det_PAN_mobilev3.yaml', 'r', encoding='utf-8') +# stream = open('./config/det_DB_mobilev3.yaml', 'r', encoding='utf-8') +# stream = open('./config/det_SAST_resnet50.yaml', 'r', encoding='utf-8') +# stream = open('./config/det_DB_resnet50.yaml', 'r', encoding='utf-8') + + parser = argparse.ArgumentParser(description='Hyperparams') + parser.add_argument('--config', help='config file path') + parser.add_argument('--model_path', nargs='?', type=str, default=None) + parser.add_argument('--onnx_path', nargs='?', type=str, default=None) + parser.add_argument('--trt_path', nargs='?', type=str, default=None) + parser.add_argument('--img_path', nargs='?', type=str, default=None) + parser.add_argument('--result_save_path', nargs='?', type=str, default=None) + parser.add_argument('--max_size', nargs='?', type=int, default=None) + parser.add_argument('--batch_size', nargs='?', type=int, default=None) + parser.add_argument('--add_padding', action='store_true', default=False) + args = parser.parse_args() + config = config_load(args) + InferImage(config) \ No newline at end of file diff --git a/tools/det_sast.py b/tools/det_sast.py new file mode 100644 index 0000000..7bd8eb1 --- /dev/null +++ b/tools/det_sast.py @@ -0,0 +1,238 @@ +# -*- coding:utf-8 _*- +""" +@author:fxw +@file: det_train.py +@time: 2020/08/07 +""" +import sys +sys.path.append('./') +import cv2 +import torch +import os +import numpy as np +from tqdm import tqdm +np.seterr(divide='ignore', invalid='ignore') +import yaml +import torch.utils.data +from ptocr.utils.util_function import create_module, create_loss_bin, \ +set_seed,save_checkpoint,create_dir +from ptocr.utils.metrics import runningScore +from ptocr.utils.logger import Logger +from ptocr.utils.cal_iou_acc import cal_DB,cal_PAN_PSE +from tools.cal_rescall.script import cal_recall_precison_f1 +from tools.cal_rescall.cal_det import cal_det_metrics +from ptocr.dataloader.DetLoad.SASTProcess_ori import alignCollate +from ptocr.utils.util_function import create_process_obj +torch.backends.cudnn.enabled = False + + +GLOBAL_WORKER_ID = None +GLOBAL_SEED = 2020 + +def worker_init_fn(worker_id): + global GLOBAL_WORKER_ID + GLOBAL_WORKER_ID = worker_id + set_seed(GLOBAL_SEED + worker_id) + +def ModelTrain(train_data_loader, model, criterion, optimizer, loss_bin, config, epoch): + if(config['base']['algorithm']=='DB' or config['base']['algorithm']=='SAST'): + running_metric_text = runningScore(2) + else: + running_metric_text = runningScore(2) + running_metric_kernel = runningScore(2) + for batch_idx, data in enumerate(train_data_loader): + if(data is None): + continue + pre_batch, gt_batch = model(data) + loss, metrics = criterion(pre_batch, gt_batch) + optimizer.zero_grad() + loss.backward() + optimizer.step() + cv2.imwrite('pre.jpg',pre_batch['f_score'][0,0].cpu().detach().numpy()*255) + for key in loss_bin.keys(): + if (key in metrics.keys()): + loss_bin[key].loss_add(metrics[key].item()) + else: + loss_bin[key].loss_add(loss.item()) + if(config['base']['algorithm']=='DB'): + iou,acc = cal_DB(pre_batch['binary'], gt_batch['gt'], gt_batch['mask'], running_metric_text) + elif(config['base']['algorithm']=='SAST'): + iou, acc = cal_DB(pre_batch['f_score'], gt_batch['input_score'], gt_batch['input_mask'], running_metric_text) + else: + iou,acc = cal_PAN_PSE(pre_batch['pre_kernel'], gt_batch['gt_kernel'], pre_batch['pre_text'], gt_batch['gt_text'], + gt_batch['train_mask'], running_metric_text,running_metric_kernel) + + if (batch_idx % config['base']['show_step'] == 0): + log = '({}/{}/{}/{}) | ' \ + .format(epoch, config['base']['n_epoch'], batch_idx, len(train_data_loader)) + bin_keys = list(loss_bin.keys()) + + for i in range(len(bin_keys)): + log += bin_keys[i] + ':{:.4f}'.format(loss_bin[bin_keys[i]].loss_mean()) + ' | ' + + log += 'ACC:{:.4f}'.format(acc) + ' | ' + log += 'IOU:{:.4f}'.format(iou) + ' | ' + log += 'lr:{:.8f}'.format(optimizer.param_groups[0]['lr']) + print(log) + loss_write = [] + for key in list(loss_bin.keys()): + loss_write.append(loss_bin[key].loss_mean()) + loss_write.extend([acc,iou]) + return loss_write + + +def ModelEval(test_dataset, test_data_loader, model, imgprocess, checkpoints,config): + bar = tqdm(total=len(test_data_loader)) + for batch_idx, (imgs, ori_imgs) in enumerate(test_data_loader): + bar.update(1) + if torch.cuda.is_available(): + imgs = imgs.cuda() + with torch.no_grad(): + out = model(imgs) + cv2.imwrite('pre_score.jpg',out['f_score'][0,0].cpu().detach().numpy()*255) + scales = [] + for i in range(out['f_score'].shape[0]): + if(config['base']['algorithm']=='SAST'): + scale = ((out['f_score'].shape[2]*4)/ori_imgs[i].shape[0],(out['f_score'].shape[3]*4)/ori_imgs[i].shape[1] ,ori_imgs[i].shape[0],ori_imgs[i].shape[1]) + else: + scale = (ori_imgs[i].shape[1] * 1.0 / out.shape[3], ori_imgs[i].shape[0] * 1.0 / out.shape[2]) + scales.append(scale) + out = create_process_obj(config['base']['algorithm'], out) + bbox_batch, score_batch = imgprocess(out, scales) + + if(config['base']['algorithm']=='SAST'): + out = out['f_score'] + + for i in range(len(bbox_batch)): + bboxes = bbox_batch[i] + img_show = ori_imgs[i].numpy().copy() + idx = i + out.shape[0] * batch_idx + image_name = test_dataset.img_list[idx].split('/')[-1].split('.')[0] # windows use \\ not / + with open(os.path.join(checkpoints, 'val', 'res_txt', 'res_' + image_name + '.txt'), 'w+', + encoding='utf-8') as fid_res: + for bbox in bboxes: + bbox = bbox.reshape(-1, 2).astype(np.int) + img_show = cv2.drawContours(img_show, [bbox], -1, (0, 255, 0), 1) + bbox_str = [str(x) for x in bbox.reshape(-1)] + bbox_str = ','.join(bbox_str) + '\n' + fid_res.write(bbox_str) + cv2.imwrite(os.path.join(checkpoints, 'val', 'res_img', image_name + '.jpg'), img_show) + bar.close() +# result_dict = cal_recall_precison_f1(config['testload']['test_gt_path'],os.path.join(checkpoints, 'val', 'res_txt')) + result_dict = cal_det_metrics(config['testload']['test_gt_path'],os.path.join(checkpoints, 'val', 'res_txt')) + return result_dict['recall'],result_dict['precision'],result_dict['hmean'] + +def TrainValProgram(config): + os.environ["CUDA_VISIBLE_DEVICES"] = config['base']['gpu_id'] + + create_dir(config['base']['checkpoints']) + checkpoints = os.path.join(config['base']['checkpoints'], + "ag_%s_bb_%s_he_%s_bs_%d_ep_%d" % (config['base']['algorithm'], + config['backbone']['function'].split(',')[-1], + config['head']['function'].split(',')[-1], + config['trainload']['batch_size'], + config['base']['n_epoch'])) + create_dir(checkpoints) + + model = create_module(config['architectures']['model_function'])(config) + criterion = create_module(config['architectures']['loss_function'])(config) + train_dataset = create_module(config['trainload']['function'])(config) + test_dataset = create_module(config['testload']['function'])(config) + optimizer = create_module(config['optimizer']['function'])(config, model) + optimizer_decay = create_module(config['optimizer_decay']['function']) + img_process = create_module(config['postprocess']['function'])(config) + + train_data_loader = torch.utils.data.DataLoader( + train_dataset, + batch_size=config['trainload']['batch_size'], + shuffle=True, + num_workers=config['trainload']['num_workers'], + worker_init_fn = worker_init_fn, + collate_fn=alignCollate(train_dataset), + drop_last=True, + pin_memory=True) + + test_data_loader = torch.utils.data.DataLoader( + test_dataset, + batch_size=config['testload']['batch_size'], + shuffle=False, + num_workers=config['testload']['num_workers'], + drop_last=True, + pin_memory=True) + + loss_bin = create_loss_bin(config['base']['algorithm']) + + if torch.cuda.is_available(): + if (len(config['base']['gpu_id'].split(',')) > 1): + model = torch.nn.DataParallel(model).cuda() + else: + model = model.cuda() + criterion = criterion.cuda() + + start_epoch = 0 + rescall, precision, hmean = 0,0,0 + best_rescall,best_precision ,best_hmean= 0,0,0 + + if config['base']['restore']: + print('Resuming from checkpoint.') + assert os.path.isfile(config['base']['restore_file']), 'Error: no checkpoint file found!' + checkpoint = torch.load(config['base']['restore_file']) + start_epoch = checkpoint['epoch'] + model.load_state_dict(checkpoint['state_dict']) + optimizer.load_state_dict(checkpoint['optimizer']) + best_rescall = checkpoint['rescall'] + best_precision = checkpoint['precision'] + best_hmean = checkpoint['hmean'] + log_write = Logger(os.path.join(checkpoints, 'log.txt'), title=config['base']['algorithm'], resume=True) + else: + print('Training from scratch.') + log_write = Logger(os.path.join(checkpoints, 'log.txt'), title=config['base']['algorithm']) + title = list(loss_bin.keys()) + title.extend(['piexl_acc','piexl_iou','t_rescall','t_precision','t_hmean','b_rescall','b_precision','b_hmean']) + log_write.set_names(title) + + for epoch in range(start_epoch,config['base']['n_epoch']): +# train_dataset.gen_train_img() + model.train() + optimizer_decay(config, optimizer, epoch) + loss_write = ModelTrain(train_data_loader, model, criterion, optimizer, loss_bin, config, epoch) + if(epoch >= config['base']['start_val']): + create_dir(os.path.join(checkpoints,'val')) + create_dir(os.path.join(checkpoints,'val','res_img')) + create_dir(os.path.join(checkpoints,'val','res_txt')) + model.eval() + rescall,precision,hmean = ModelEval(test_dataset, test_data_loader, model, img_process, checkpoints,config) + print('rescall:',rescall,'precision',precision,'hmean',hmean) + if (hmean > best_hmean): + save_checkpoint({ + 'epoch': epoch + 1, + 'state_dict': model.state_dict(), + 'lr': config['optimizer']['base_lr'], + 'optimizer': optimizer.state_dict(), + 'hmean': hmean, + 'rescall': rescall, + 'precision': precision + }, checkpoints, config['base']['algorithm'] + '_best' + '.pth.tar') + best_hmean = hmean + best_precision = precision + best_rescall = rescall + + loss_write.extend([rescall,precision,hmean,best_rescall,best_precision,best_hmean]) + log_write.append(loss_write) + for key in loss_bin.keys(): + loss_bin[key].loss_clear() + if epoch%config['base']['save_epoch'] ==0: + save_checkpoint({ + 'epoch': epoch + 1, + 'state_dict': model.state_dict(), + 'lr': config['optimizer']['base_lr'], + 'optimizer': optimizer.state_dict(), + },checkpoints,config['base']['algorithm']+'_'+str(epoch)+'.pth.tar') + + +if __name__ == "__main__": +# stream = open('./config/det_SAST_resnet50_ori_dataload.yaml', 'r', encoding='utf-8') + stream = open('./config/det_SAST_resnet50_3_3_ori_dataload.yaml', 'r', encoding='utf-8') + + config = yaml.load(stream,Loader=yaml.FullLoader) + TrainValProgram(config) \ No newline at end of file diff --git a/tools/det_train.py b/tools/det_train.py new file mode 100644 index 0000000..d22ad67 --- /dev/null +++ b/tools/det_train.py @@ -0,0 +1,307 @@ +# -*- coding:utf-8 _*- +""" +@author:fxw +@file: det_train.py +@time: 2020/08/07 +""" +import sys +sys.path.append('./') +import cv2 +import torch +import os +import random +import numpy as np +from tqdm import tqdm +import argparse +np.seterr(divide='ignore', invalid='ignore') +import yaml +import torch.utils.data +from ptocr.utils.util_function import create_module, create_loss_bin, \ +set_seed,save_checkpoint,create_dir +from ptocr.utils.metrics import runningScore +from ptocr.utils.logger import Logger +from ptocr.utils.cal_iou_acc import cal_DB,cal_PAN_PSE +from tools.cal_rescall.script import cal_recall_precison_f1 +# from ptocr.dataloader.DetLoad.SASTProcess import alignCollate +from ptocr.utils.util_function import create_process_obj,merge_config,load_model +from ptocr.utils.prune_script import updateBN,load_prune_model +from ptocr.utils.gen_teacher_model import GetTeacherModel,DistilLoss + + +GLOBAL_WORKER_ID = None +GLOBAL_SEED = 123456 + +torch.manual_seed(GLOBAL_SEED) +torch.cuda.manual_seed(GLOBAL_SEED) +torch.cuda.manual_seed_all(GLOBAL_SEED) +np.random.seed(GLOBAL_SEED) +random.seed(GLOBAL_SEED) + +def worker_init_fn(worker_id): + global GLOBAL_WORKER_ID + GLOBAL_WORKER_ID = worker_id + set_seed(GLOBAL_SEED + worker_id) + +def ModelTrain(train_data_loader,t_model,t_criterion, model, criterion, optimizer, loss_bin, args,config, epoch): + if(config['base']['algorithm']=='DB' or config['base']['algorithm']=='SAST'): + running_metric_text = runningScore(2) + else: + running_metric_text = runningScore(2) + running_metric_kernel = runningScore(2) + for batch_idx, data in enumerate(train_data_loader): + if(data is None): + continue + pre_batch, gt_batch = model(data) + + if(t_model is not None): + with torch.no_grad(): + t_pre_batch,_ = t_model(data) + distil_loss = t_criterion(pre_batch,t_pre_batch) + + loss, metrics = criterion(pre_batch, gt_batch) + + if(t_model is not None): + loss = args.t_ratio*loss+(1-args.t_ratio)*distil_loss + metrics['loss_distil'] = distil_loss + + optimizer.zero_grad() + loss.backward() + if(args.sr_lr is not None): + updateBN(model,args) + optimizer.step() + + for key in loss_bin.keys(): + if (key in metrics.keys()): + loss_bin[key].loss_add(metrics[key].item()) + else: + loss_bin[key].loss_add(loss.item()) + if(config['base']['algorithm']=='DB'): + iou,acc = cal_DB(pre_batch['binary'], gt_batch['gt'], gt_batch['mask'], running_metric_text) + elif(config['base']['algorithm']=='SAST'): + iou, acc = cal_DB(pre_batch['f_score'], gt_batch['input_score'], gt_batch['input_mask'], running_metric_text) + else: + iou,acc = cal_PAN_PSE(pre_batch['pre_kernel'], gt_batch['gt_kernel'], pre_batch['pre_text'], gt_batch['gt_text'], + gt_batch['train_mask'], running_metric_text,running_metric_kernel) + + if (batch_idx % config['base']['show_step'] == 0): + log = '({}/{}/{}/{}) | ' \ + .format(epoch, config['base']['n_epoch'], batch_idx, len(train_data_loader)) + bin_keys = list(loss_bin.keys()) + + for i in range(len(bin_keys)): + log += bin_keys[i] + ':{:.4f}'.format(loss_bin[bin_keys[i]].loss_mean()) + ' | ' + + log += 'ACC:{:.4f}'.format(acc) + ' | ' + log += 'IOU:{:.4f}'.format(iou) + ' | ' + log += 'lr:{:.8f}'.format(optimizer.param_groups[0]['lr']) + print(log) + loss_write = [] + for key in list(loss_bin.keys()): + loss_write.append(loss_bin[key].loss_mean()) + loss_write.extend([acc,iou]) + return loss_write + + +def ModelEval(test_dataset, test_data_loader, model, imgprocess, checkpoints,config): + bar = tqdm(total=len(test_data_loader)) + for batch_idx, (imgs, ori_imgs) in enumerate(test_data_loader): + bar.update(1) + if torch.cuda.is_available(): + imgs = imgs.cuda() + with torch.no_grad(): + out = model(imgs) + scales = [] + if isinstance(out,dict): + img_num = out['f_score'].shape[0] + else: + img_num = out.shape[0] + for i in range(img_num): + if(config['base']['algorithm']=='SAST'): + scale = ((out['f_score'].shape[2]*4)/ori_imgs[i].shape[0],(out['f_score'].shape[3]*4)/ori_imgs[i].shape[1] ,ori_imgs[i].shape[0],ori_imgs[i].shape[1]) + else: + scale = (ori_imgs[i].shape[1] * 1.0 / out.shape[3], ori_imgs[i].shape[0] * 1.0 / out.shape[2]) + scales.append(scale) + out = create_process_obj(config['base']['algorithm'], out) + bbox_batch, score_batch = imgprocess(out, scales) + + if(config['base']['algorithm']=='SAST'): + out = out['f_score'] + + for i in range(len(bbox_batch)): + bboxes = bbox_batch[i] + img_show = ori_imgs[i].numpy().copy() + idx = i + out.shape[0] * batch_idx + image_name = test_dataset.img_list[idx].split('/')[-1].split('.')[0] # windows use \\ not / + with open(os.path.join(checkpoints, 'val', 'res_txt', 'res_' + image_name + '.txt'), 'w+', + encoding='utf-8') as fid_res: + for bbox in bboxes: + bbox = bbox.reshape(-1, 2).astype(np.int) + img_show = cv2.drawContours(img_show, [bbox], -1, (0, 255, 0), 1) + bbox_str = [str(x) for x in bbox.reshape(-1)] + bbox_str = ','.join(bbox_str) + '\n' + fid_res.write(bbox_str) + cv2.imwrite(os.path.join(checkpoints, 'val', 'res_img', image_name + '.jpg'), img_show) + bar.close() + result_dict = cal_recall_precison_f1(config['testload']['test_gt_path'],os.path.join(checkpoints, 'val', 'res_txt')) + return result_dict['recall'],result_dict['precision'],result_dict['hmean'] + +def TrainValProgram(args): + + config = yaml.load(open(args.config, 'r', encoding='utf-8'),Loader=yaml.FullLoader) + config = merge_config(config,args) + + os.environ["CUDA_VISIBLE_DEVICES"] = config['base']['gpu_id'] + create_dir(config['base']['checkpoints']) + checkpoints = os.path.join(config['base']['checkpoints'], + "ag_%s_bb_%s_he_%s_bs_%d_ep_%d_%s" % (config['base']['algorithm'], + config['backbone']['function'].split(',')[-1], + config['head']['function'].split(',')[-1], + config['trainload']['batch_size'], + config['base']['n_epoch'], + args.log_str)) + create_dir(checkpoints) + + model = create_module(config['architectures']['model_function'])(config) + criterion = create_module(config['architectures']['loss_function'])(config) + train_dataset = create_module(config['trainload']['function'])(config) + test_dataset = create_module(config['testload']['function'])(config) + optimizer = create_module(config['optimizer']['function'])(config, model) + optimizer_decay = create_module(config['optimizer_decay']['function']) + img_process = create_module(config['postprocess']['function'])(config) + + if args.t_config is not None: + t_model = GetTeacherModel(args) + distil_loss = DistilLoss() + if torch.cuda.is_available(): + distil_loss = distil_loss.cuda() + + train_data_loader = torch.utils.data.DataLoader( + train_dataset, + batch_size=config['trainload']['batch_size'], + shuffle=True, + num_workers=config['trainload']['num_workers'], + worker_init_fn = worker_init_fn, + drop_last=True, + pin_memory=True) + + test_data_loader = torch.utils.data.DataLoader( + test_dataset, + batch_size=config['testload']['batch_size'], + shuffle=False, + num_workers=config['testload']['num_workers'], + drop_last=True, + pin_memory=True) + + use_distil = False + if args.t_config is not None: + use_distil = True + loss_bin = create_loss_bin(config['base']['algorithm'],use_distil) + + if torch.cuda.is_available(): + if (len(config['base']['gpu_id'].split(',')) > 1): + model = torch.nn.DataParallel(model).cuda() + else: + model = model.cuda() + criterion = criterion.cuda() + + start_epoch = 0 + rescall, precision, hmean = 0,0,0 + best_rescall,best_precision ,best_hmean= 0,0,0 + + if args.pruned_model_dict_path is not None: + print('finetune the pruend model.') + model = load_prune_model(model,args.prune_model_path,args.pruned_model_dict_path,args.prune_type) + log_write = Logger(os.path.join(checkpoints, 'log.txt'), title=config['base']['algorithm']) + title = list(loss_bin.keys()) + title.extend(['piexl_acc','piexl_iou','t_rescall','t_precision','t_hmean','b_rescall','b_precision','b_hmean']) + log_write.set_names(title) + + elif config['base']['restore']: + print('Resuming from checkpoint.') + assert os.path.isfile(config['base']['restore_file']), 'Error: no checkpoint file found!' + checkpoint = torch.load(config['base']['restore_file']) + start_epoch = checkpoint['epoch'] + model.load_state_dict(checkpoint['state_dict']) + optimizer.load_state_dict(checkpoint['optimizer']) + best_rescall = checkpoint['rescall'] + best_precision = checkpoint['precision'] + best_hmean = checkpoint['hmean'] + log_write = Logger(os.path.join(checkpoints, 'log.txt'), title=config['base']['algorithm'], resume=True) + else: + print('Training from scratch.') + log_write = Logger(os.path.join(checkpoints, 'log.txt'), title=config['base']['algorithm']) + title = list(loss_bin.keys()) + title.extend(['piexl_acc','piexl_iou','t_rescall','t_precision','t_hmean','b_rescall','b_precision','b_hmean']) + log_write.set_names(title) + + if args.start_epoch is not None: + start_epoch = args.start_epoch + + for epoch in range(start_epoch,config['base']['n_epoch']): + model.train() + if args.t_config is not None: + t_model.train() + else: + t_model = None + distil_loss = None + optimizer_decay(config, optimizer, epoch) + loss_write = ModelTrain(train_data_loader,t_model,distil_loss,model, criterion, optimizer, loss_bin, args,config, epoch) + + if(epoch >= config['base']['start_val']): + create_dir(os.path.join(checkpoints,'val')) + create_dir(os.path.join(checkpoints,'val','res_img')) + create_dir(os.path.join(checkpoints,'val','res_txt')) + model.eval() + rescall,precision,hmean = ModelEval(test_dataset, test_data_loader, model, img_process, checkpoints,config) + print('rescall:',rescall,'precision',precision,'hmean',hmean) + if (hmean > best_hmean): + save_checkpoint({ + 'epoch': epoch + 1, + 'state_dict': model.state_dict(), + 'lr': config['optimizer']['base_lr'], + 'optimizer': optimizer.state_dict(), + 'hmean': hmean, + 'rescall': rescall, + 'precision': precision + }, checkpoints, config['base']['algorithm'] + '_best' + '.pth.tar') + best_hmean = hmean + best_precision = precision + best_rescall = rescall + + loss_write.extend([rescall,precision,hmean,best_rescall,best_precision,best_hmean]) + log_write.append(loss_write) + for key in loss_bin.keys(): + loss_bin[key].loss_clear() + if epoch%config['base']['save_epoch'] ==0: + save_checkpoint({ + 'epoch': epoch + 1, + 'state_dict': model.state_dict(), + 'lr': config['optimizer']['base_lr'], + 'optimizer': optimizer.state_dict(), + 'hmean': 0, + 'rescall': 0, + 'precision': 0 + },checkpoints,config['base']['algorithm']+'_'+str(epoch)+'.pth.tar') + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description='Hyperparams') + parser.add_argument('--config', help='config file path') + parser.add_argument('--t_config',default=None, help='config file path') + parser.add_argument('--t_model_path',default=None, help='teacher model path') + parser.add_argument('--t_ratio', nargs='?', type=float, default=0.5) + parser.add_argument('--log_str', help='log title') + parser.add_argument('--sr_lr', nargs='?', type=float, default=None) + + parser.add_argument('--pruned_model_dict_path', help='config file path',default=None) + parser.add_argument('--prune_type',type=str, help='prune type,total or backbone') + parser.add_argument('--prune_model_path', help='model file path') + + parser.add_argument('--n_epoch', nargs='?', type=int, default=600) + parser.add_argument('--start_epoch', nargs='?', type=int, default=None) + parser.add_argument('--start_val', nargs='?', type=int, default=400) + parser.add_argument('--base_lr', nargs='?', type=float, default=0.001) + parser.add_argument('--gpu_id', help='config file path') + + args = parser.parse_args() + TrainValProgram(args) \ No newline at end of file diff --git a/tools/det_train_qua.py b/tools/det_train_qua.py new file mode 100644 index 0000000..df15ab8 --- /dev/null +++ b/tools/det_train_qua.py @@ -0,0 +1,333 @@ +# -*- coding:utf-8 _*- +""" +@author:fxw +@file: det_train.py +@time: 2020/08/07 +""" +import sys +sys.path.append('./') +import cv2 +import torch +import os +import random +import numpy as np +from tqdm import tqdm +import argparse +np.seterr(divide='ignore', invalid='ignore') +import yaml +import torch.utils.data +from ptocr.utils.util_function import create_module, create_loss_bin, \ +set_seed,save_checkpoint,create_dir +from ptocr.utils.metrics import runningScore +from ptocr.utils.logger import Logger +from ptocr.utils.cal_iou_acc import cal_DB,cal_PAN_PSE +from tools.cal_rescall.script import cal_recall_precison_f1 +# from ptocr.dataloader.DetLoad.SASTProcess import alignCollate +from ptocr.utils.util_function import create_process_obj,merge_config,load_model +from ptocr.utils.prune_script import updateBN,load_prune_model +from ptocr.utils.gen_teacher_model import GetTeacherModel,DistilLoss + + +GLOBAL_WORKER_ID = None +GLOBAL_SEED = 123456 + +torch.manual_seed(GLOBAL_SEED) +torch.cuda.manual_seed(GLOBAL_SEED) +torch.cuda.manual_seed_all(GLOBAL_SEED) +np.random.seed(GLOBAL_SEED) +random.seed(GLOBAL_SEED) + +def worker_init_fn(worker_id): + global GLOBAL_WORKER_ID + GLOBAL_WORKER_ID = worker_id + set_seed(GLOBAL_SEED + worker_id) + +def quantize_model(model, backend,convert = False): + if backend not in torch.backends.quantized.supported_engines: + raise RuntimeError("Quantized backend not supported ") + torch.backends.quantized.engine = backend + # Make sure that weight qconfig matches that of the serialized models + if backend == 'fbgemm': + model.qconfig = torch.quantization.QConfig( + activation=torch.quantization.default_observer, + weight=torch.quantization.default_per_channel_weight_observer) + elif backend == 'qnnpack': + model.qconfig = torch.quantization.QConfig( + activation=torch.quantization.default_observer, + weight=torch.quantization.default_weight_observer) + + model = torch.quantization.prepare_qat(model, inplace=True) + if(convert): + model = torch.quantization.convert(model, inplace=False) + return model + +def ModelTrain(train_data_loader,t_model,t_criterion, model, criterion, optimizer, loss_bin, args,config, epoch): + if(config['base']['algorithm']=='DB' or config['base']['algorithm']=='SAST'): + running_metric_text = runningScore(2) + else: + running_metric_text = runningScore(2) + running_metric_kernel = runningScore(2) + for batch_idx, data in enumerate(train_data_loader): + if(data is None): + continue + pre_batch, gt_batch = model(data) + + if(t_model is not None): + with torch.no_grad(): + t_pre_batch,_ = t_model(data) + distil_loss = t_criterion(pre_batch,t_pre_batch) + + loss, metrics = criterion(pre_batch, gt_batch) + + if(t_model is not None): + loss = args.t_ratio*loss+(1-args.t_ratio)*distil_loss + metrics['loss_distil'] = distil_loss + + optimizer.zero_grad() + loss.backward() + if(args.sr_lr is not None): + updateBN(model,args) + optimizer.step() + + for key in loss_bin.keys(): + if (key in metrics.keys()): + loss_bin[key].loss_add(metrics[key].item()) + else: + loss_bin[key].loss_add(loss.item()) + if(config['base']['algorithm']=='DB'): + iou,acc = cal_DB(pre_batch['binary'], gt_batch['gt'], gt_batch['mask'], running_metric_text) + elif(config['base']['algorithm']=='SAST'): + iou, acc = cal_DB(pre_batch['f_score'], gt_batch['input_score'], gt_batch['input_mask'], running_metric_text) + else: + iou,acc = cal_PAN_PSE(pre_batch['pre_kernel'], gt_batch['gt_kernel'], pre_batch['pre_text'], gt_batch['gt_text'], + gt_batch['train_mask'], running_metric_text,running_metric_kernel) + + if (batch_idx % config['base']['show_step'] == 0): + log = '({}/{}/{}/{}) | ' \ + .format(epoch, config['base']['n_epoch'], batch_idx, len(train_data_loader)) + bin_keys = list(loss_bin.keys()) + + for i in range(len(bin_keys)): + log += bin_keys[i] + ':{:.4f}'.format(loss_bin[bin_keys[i]].loss_mean()) + ' | ' + + log += 'ACC:{:.4f}'.format(acc) + ' | ' + log += 'IOU:{:.4f}'.format(iou) + ' | ' + log += 'lr:{:.8f}'.format(optimizer.param_groups[0]['lr']) + print(log) + loss_write = [] + for key in list(loss_bin.keys()): + loss_write.append(loss_bin[key].loss_mean()) + loss_write.extend([acc,iou]) + return loss_write + + +def ModelEval(test_dataset, test_data_loader, model, imgprocess, checkpoints,config): + bar = tqdm(total=len(test_data_loader)) + for batch_idx, (imgs, ori_imgs) in enumerate(test_data_loader): + bar.update(1) +# if torch.cuda.is_available(): +# imgs = imgs.cuda() + with torch.no_grad(): + out = model(imgs) + out = out['binary'] + scales = [] + if isinstance(out,dict): + img_num = out['f_score'].shape[0] + else: + img_num = out.shape[0] + for i in range(img_num): + if(config['base']['algorithm']=='SAST'): + scale = ((out['f_score'].shape[2]*4)/ori_imgs[i].shape[0],(out['f_score'].shape[3]*4)/ori_imgs[i].shape[1] ,ori_imgs[i].shape[0],ori_imgs[i].shape[1]) + else: + scale = (ori_imgs[i].shape[1] * 1.0 / out.shape[3], ori_imgs[i].shape[0] * 1.0 / out.shape[2]) + scales.append(scale) + out = create_process_obj(config['base']['algorithm'], out) + bbox_batch, score_batch = imgprocess(out, scales) + + if(config['base']['algorithm']=='SAST'): + out = out['f_score'] + + for i in range(len(bbox_batch)): + bboxes = bbox_batch[i] + img_show = ori_imgs[i].numpy().copy() + idx = i + out.shape[0] * batch_idx + image_name = test_dataset.img_list[idx].split('/')[-1].split('.')[0] # windows use \\ not / + with open(os.path.join(checkpoints, 'val', 'res_txt', 'res_' + image_name + '.txt'), 'w+', + encoding='utf-8') as fid_res: + for bbox in bboxes: + bbox = bbox.reshape(-1, 2).astype(np.int) + img_show = cv2.drawContours(img_show, [bbox], -1, (0, 255, 0), 1) + bbox_str = [str(x) for x in bbox.reshape(-1)] + bbox_str = ','.join(bbox_str) + '\n' + fid_res.write(bbox_str) + cv2.imwrite(os.path.join(checkpoints, 'val', 'res_img', image_name + '.jpg'), img_show) + bar.close() + result_dict = cal_recall_precison_f1(config['testload']['test_gt_path'],os.path.join(checkpoints, 'val', 'res_txt')) + return result_dict['recall'],result_dict['precision'],result_dict['hmean'] + +def TrainValProgram(args): + + config = yaml.load(open(args.config, 'r', encoding='utf-8'),Loader=yaml.FullLoader) + config = merge_config(config,args) + + os.environ["CUDA_VISIBLE_DEVICES"] = config['base']['gpu_id'] + create_dir(config['base']['checkpoints']) + checkpoints = os.path.join(config['base']['checkpoints'], + "ag_%s_bb_%s_he_%s_bs_%d_ep_%d_%s" % (config['base']['algorithm'], + config['backbone']['function'].split(',')[-1], + config['head']['function'].split(',')[-1], + config['trainload']['batch_size'], + config['base']['n_epoch'], + args.log_str)) + create_dir(checkpoints) + + model = create_module(config['architectures']['model_function'])(config) + criterion = create_module(config['architectures']['loss_function'])(config) + train_dataset = create_module(config['trainload']['function'])(config) + test_dataset = create_module(config['testload']['function'])(config) + optimizer = create_module(config['optimizer']['function'])(config, model) + optimizer_decay = create_module(config['optimizer_decay']['function']) + img_process = create_module(config['postprocess']['function'])(config) + + if args.t_config is not None: + t_model = GetTeacherModel(args) + distil_loss = DistilLoss() + if torch.cuda.is_available(): + distil_loss = distil_loss.cuda() + + train_data_loader = torch.utils.data.DataLoader( + train_dataset, + batch_size=config['trainload']['batch_size'], + shuffle=True, + num_workers=config['trainload']['num_workers'], + worker_init_fn = worker_init_fn, + drop_last=True, + pin_memory=True) + + test_data_loader = torch.utils.data.DataLoader( + test_dataset, + batch_size=config['testload']['batch_size'], + shuffle=False, + num_workers=config['testload']['num_workers'], + drop_last=True, + pin_memory=True) + + use_distil = False + if args.t_config is not None: + use_distil = True + loss_bin = create_loss_bin(config['base']['algorithm'],use_distil) + + if torch.cuda.is_available(): + if (len(config['base']['gpu_id'].split(',')) > 1): + model = torch.nn.DataParallel(model).cuda() + else: + model = model.cuda() + criterion = criterion.cuda() + + start_epoch = 0 + rescall, precision, hmean = 0,0,0 + best_rescall,best_precision ,best_hmean= 0,0,0 + + if args.pruned_model_dict_path is not None: + print('finetune the pruend model.') + model = load_prune_model(model,args.prune_model_path,args.pruned_model_dict_path,args.prune_type) + log_write = Logger(os.path.join(checkpoints, 'log.txt'), title=config['base']['algorithm']) + title = list(loss_bin.keys()) + title.extend(['piexl_acc','piexl_iou','t_rescall','t_precision','t_hmean','b_rescall','b_precision','b_hmean']) + log_write.set_names(title) + + elif config['base']['restore']: + print('Resuming from checkpoint.') + assert os.path.isfile(config['base']['restore_file']), 'Error: no checkpoint file found!' + checkpoint = torch.load(config['base']['restore_file']) + start_epoch = checkpoint['epoch'] + model.load_state_dict(checkpoint['state_dict']) + optimizer.load_state_dict(checkpoint['optimizer']) + best_rescall = checkpoint['rescall'] + best_precision = checkpoint['precision'] + best_hmean = checkpoint['hmean'] + log_write = Logger(os.path.join(checkpoints, 'log.txt'), title=config['base']['algorithm'], resume=True) + else: + print('Training from scratch.') + log_write = Logger(os.path.join(checkpoints, 'log.txt'), title=config['base']['algorithm']) + title = list(loss_bin.keys()) + title.extend(['piexl_acc','piexl_iou','t_rescall','t_precision','t_hmean','b_rescall','b_precision','b_hmean']) + log_write.set_names(title) + + if args.start_epoch is not None: + start_epoch = args.start_epoch + + + model = quantize_model(model, config['base']['backend'],False) + for epoch in range(start_epoch,config['base']['n_epoch']): + + model.train() + if args.t_config is not None: + t_model.train() + else: + t_model = None + distil_loss = None + optimizer_decay(config, optimizer, epoch) + loss_write = ModelTrain(train_data_loader,t_model,distil_loss,model, criterion, optimizer, loss_bin, args,config, epoch) + + if(epoch >= config['base']['start_val']): + create_dir(os.path.join(checkpoints,'val')) + create_dir(os.path.join(checkpoints,'val','res_img')) + create_dir(os.path.join(checkpoints,'val','res_txt')) + + qua_model = quantize_model(model.cpu(), config['base']['backend'],True) + qua_model.eval() + + rescall,precision,hmean = ModelEval(test_dataset, test_data_loader, qua_model, img_process, checkpoints,config) + print('rescall:',rescall,'precision',precision,'hmean',hmean) + if (hmean > best_hmean): + save_checkpoint({ + 'epoch': epoch + 1, + 'state_dict': model.state_dict(), + 'lr': config['optimizer']['base_lr'], + 'optimizer': optimizer.state_dict(), + 'hmean': hmean, + 'rescall': rescall, + 'precision': precision + }, checkpoints, config['base']['algorithm'] + '_best' + '.pth.tar') + best_hmean = hmean + best_precision = precision + best_rescall = rescall + + loss_write.extend([rescall,precision,hmean,best_rescall,best_precision,best_hmean]) + log_write.append(loss_write) + for key in loss_bin.keys(): + loss_bin[key].loss_clear() + if epoch%config['base']['save_epoch'] ==0: + save_checkpoint({ + 'epoch': epoch + 1, + 'state_dict': model.state_dict(), + 'lr': config['optimizer']['base_lr'], + 'optimizer': optimizer.state_dict(), + 'hmean': 0, + 'rescall': 0, + 'precision': 0 + },checkpoints,config['base']['algorithm']+'_'+str(epoch)+'.pth.tar') + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description='Hyperparams') + parser.add_argument('--config', help='config file path') + parser.add_argument('--t_config',default=None, help='config file path') + parser.add_argument('--t_model_path',default=None, help='teacher model path') + parser.add_argument('--t_ratio', nargs='?', type=float, default=0.2) + parser.add_argument('--log_str', help='log title') + parser.add_argument('--sr_lr', nargs='?', type=float, default=None) + + parser.add_argument('--pruned_model_dict_path', help='config file path',default=None) + parser.add_argument('--prune_type',type=str, help='prune type,total or backbone') + parser.add_argument('--prune_model_path', help='model file path') + + parser.add_argument('--n_epoch', nargs='?', type=int, default=600) + parser.add_argument('--start_epoch', nargs='?', type=int, default=None) + parser.add_argument('--start_val', nargs='?', type=int, default=400) + parser.add_argument('--base_lr', nargs='?', type=float, default=0.001) + parser.add_argument('--gpu_id', help='config file path') + + args = parser.parse_args() + TrainValProgram(args) \ No newline at end of file diff --git a/tools/pruned/__init__.py b/tools/pruned/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tools/pruned/prune_model_all.py b/tools/pruned/prune_model_all.py new file mode 100644 index 0000000..0b96e79 --- /dev/null +++ b/tools/pruned/prune_model_all.py @@ -0,0 +1,477 @@ +""" +#!-*- coding=utf-8 -*- +@author: BADBADBADBADBOY +@contact: 2441124901@qq.com +@software: PyCharm Community Edition +@file: prune.py +@time: 2020/6/27 10:23 + +""" +import os +os.environ["CUDA_VISIBLE_DEVICES"] = '1' +import sys +sys.path.append('./') +import yaml +from ptocr.utils.util_function import create_module,load_model,resize_image +import ptocr +import torch +import torch.nn as nn +import numpy as np +import collections +import torchvision.transforms as transforms +import cv2 + +import argparse +import math +from PIL import Image +from torch.autograd import Variable + +def prune(args): + + stream = open(args.config, 'r', encoding='utf-8') + config = yaml.load(stream,Loader=yaml.FullLoader) + + img = cv2.imread(args.img_file) + img = resize_image(img,config['base']['algorithm'],config['testload']['test_size'],stride=config['testload']['stride']) + img = Image.fromarray(img) + img = img.convert('RGB') + img = transforms.ToTensor()(img) + img = transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])(img).cuda() + img = Variable(img).unsqueeze(0) + + model = create_module(config['architectures']['model_function'])(config).cuda() + model = load_model(model,args.checkpoint) + + model.eval() + print(model) + with torch.no_grad(): + out = model(img) + cv2.imwrite('ori_model_result.jpg',out[0,0].cpu().numpy()*255) + cut_percent = args.cut_percent + base_num = args.base_num + + bn_weights = [] + for m in model.modules(): + if (isinstance(m, nn.BatchNorm2d)): + bn_weights.append(m.weight.data.abs().clone()) + bn_weights = torch.cat(bn_weights, 0) + + sort_result, sort_index = torch.sort(bn_weights) + + thresh_index = int(cut_percent * bn_weights.shape[0]) + + if (thresh_index == bn_weights.shape[0]): + thresh_index = bn_weights.shape[0] - 1 + + prued = 0 + prued_mask = [] + bn_index = [] + conv_index = [] + remain_channel_nums = [] + for k, m in enumerate(model.modules()): + if (isinstance(m, nn.BatchNorm2d)): + bn_weight = m.weight.data.clone() + mask = bn_weight.abs().gt(sort_result[thresh_index]) + remain_channel = mask.sum() + + if (remain_channel == 0): + remain_channel = 1 + mask[int(torch.argmax(bn_weight))] = 1 + + v = 0 + n = 1 + if (remain_channel % base_num != 0): + if (remain_channel > base_num): + while (v < remain_channel): + n += 1 + v = base_num * n + if (remain_channel - (v - base_num) < v - remain_channel): + remain_channel = v - base_num + else: + remain_channel = v + if (remain_channel > bn_weight.size()[0]): + remain_channel = bn_weight.size()[0] + remain_channel = torch.tensor(remain_channel) + result, index = torch.sort(bn_weight) + mask = bn_weight.abs().ge(result[-remain_channel]) + + remain_channel_nums.append(int(mask.sum())) + prued_mask.append(mask) + bn_index.append(k) + prued += mask.shape[0] - mask.sum() + elif (isinstance(m, nn.Conv2d) or isinstance(m, nn.ConvTranspose2d)): + conv_index.append(k) + conv_index.remove(227) + conv_index.remove(236) + print('remain_channel_nums',remain_channel_nums) + print('total_prune_ratio:', float(prued) / bn_weights.shape[0]) + print('bn_index',bn_index) + print('conv_index',conv_index) + +# import pdb +# pdb.set_trace() + + new_model = create_module(config['architectures']['model_function'])(config).cuda() + + keys = {} + tag = 0 + for k, m in enumerate(new_model.modules()): + if(isinstance(m,ptocr.model.backbone.det_mobilev3.Block)): + keys[tag]=k + tag+=1 + print(keys) + #### step 1 + mg_1 = np.array([-3,7,16]) + block_idx = keys[0] + tag = 0 + for idx in mg_1+block_idx: + if (tag == 0): + msk = prued_mask[bn_index.index(idx)] + else: + msk = msk | prued_mask[bn_index.index(idx)] + tag+=1 + print('step1',idx) + print(msk.sum()) + for idx in mg_1+block_idx: + prued_mask[bn_index.index(idx)] = msk + msk_1 = msk.clone() + + #### step 2 + block_idx2 = np.array([keys[1],keys[2]]) + mg_2 = 7 + tag = 0 + for idx in mg_2+block_idx2: + print('step2',idx) + if(tag==0): + msk = prued_mask[bn_index.index(idx)] + else: + msk = msk|prued_mask[bn_index.index(idx)] + tag += 1 + for idx in mg_2+block_idx2: + prued_mask[bn_index.index(idx)] = msk + print(msk.sum()) + msk_2 = msk.clone() + + ####step 3 + block_idx3s = [keys[3],keys[4],keys[5]] + mg_3 = np.array([7,16]) + tag = 0 + for block_idx3 in block_idx3s: + for idx in block_idx3+mg_3: + print('step3',idx) + if (tag == 0): + msk = prued_mask[bn_index.index(idx)] + else: + msk = msk | prued_mask[bn_index.index(idx)] + tag += 1 + for block_idx3 in block_idx3s: + for idx in block_idx3+mg_3: + prued_mask[bn_index.index(idx)] = msk + print(msk.sum()) + msk_3 = msk.clone() + + ####step 4_1 + block_idx4_all = [] + + block_idx4 = keys[6] + + mg_4 = np.array([7,16]) + block_idx4_all.extend((block_idx4+mg_4).tolist()) + + ####step 4_2 + block_idx4 = keys[7] + mg_4 = np.array([7,16]) + block_idx4_all.extend((block_idx4+mg_4).tolist()) + tag = 0 + + for idx in block_idx4_all: + print('step4',idx) + if(tag==0): + msk = prued_mask[bn_index.index(idx)] + else: + msk = msk | prued_mask[bn_index.index(idx)] + tag += 1 + + for idx in block_idx4_all: + prued_mask[bn_index.index(idx)] = msk + print(msk.sum()) + msk_4 = msk.clone() + + ####step 5 + block_idx5s = [keys[8],keys[9],keys[10]] + mg_5 = np.array([7,16]) + tag = 0 + for block_idx5 in block_idx5s: + for idx in block_idx5+mg_5: + if (tag == 0): + msk = prued_mask[bn_index.index(idx)] + else: + msk = msk | prued_mask[bn_index.index(idx)] + tag += 1 + + for block_idx5 in block_idx5s: + for idx in block_idx5+mg_5: + prued_mask[bn_index.index(idx)] = msk + print(msk.sum()) + msk_5 = msk.clone() + + group_index = [] + spl_index = [] + for i in range(11): + block_idx6 = keys[i] + tag = 0 + mg_6 = np.array([2,5]) + for idx in mg_6+block_idx6: + if(tag==0): + msk = prued_mask[bn_index.index(idx)] + else: + msk = msk | prued_mask[bn_index.index(idx)] + tag+=1 + for idx in mg_6 + block_idx6: + prued_mask[bn_index.index(idx)] = msk + if(i==6): + spl_index.extend([block_idx6+9,block_idx6-2]) + group_index.append(block_idx6+4) + + + count_conv = 0 + count_bn = 0 + conv_in_mask = [torch.ones(3)] + conv_out_mask = [] + bn_mask = [] + tag = 0 + for k, m in enumerate(new_model.modules()): + if(tag>187 ): + continue + else: + if isinstance(m,nn.Conv2d): + + if(tag in group_index): + m.groups = int(prued_mask[bn_index.index(tag+1)].sum()) + m.out_channels = int(prued_mask[count_conv].sum()) + conv_out_mask.append(prued_mask[count_conv]) + if(count_conv>0): + if (tag == spl_index[0]): + m.in_channels = int(prued_mask[bn_index.index(spl_index[1])].sum()) + conv_in_mask.append(prued_mask[bn_index.index(spl_index[1])]) + else: + m.in_channels = int(prued_mask[count_conv-1].sum()) + conv_in_mask.append(prued_mask[count_conv-1]) + + count_conv+=1 + elif isinstance(m,nn.BatchNorm2d): + m.num_features = int(prued_mask[count_bn].sum()) + bn_mask.append(prued_mask[count_bn]) + count_bn+=1 + tag+=1 + + + head_bn_merge_idx1 = np.array([189,193,197,201]) + tag = 0 + for idx in head_bn_merge_idx1: + if(tag==0): + _msk1 = prued_mask[bn_index.index(idx)] + else: + _msk1 = _msk1 | prued_mask[bn_index.index(idx)] + tag += 1 + + head_bn_merge_idx2 = np.array([205,209,213,217]) + num_ch = 0 + head_mask_1 = [] + for idx in head_bn_merge_idx2: + num_ch += int(prued_mask[bn_index.index(idx)].sum()) + head_mask_1.append(prued_mask[bn_index.index(idx)]) + + + print(new_model) + + head_bn_merge_idx2 = head_bn_merge_idx2 - 1 + + bn_i = 0 + conv_i = 0 + model_i = 0 + scale = [188,192,196,200] + scale_mask = [msk_5,msk_4,msk_3,msk_2] + import copy + prued_mask_bk = copy.deepcopy(prued_mask) + for [m0, m1] in zip(model.modules(), new_model.modules()): + if(model_i>187): + if isinstance(m0, nn.Conv2d) or isinstance(m0, nn.ConvTranspose2d): + if(model_i in scale): + index = scale.index(model_i) + m1.in_channels = int(scale_mask[index].sum()) + m1.out_channels = int(_msk1.sum()) + idx0 = np.squeeze(np.argwhere(np.asarray(scale_mask[index].cpu().numpy()))) + idx1 = np.squeeze(np.argwhere(np.asarray(_msk1.cpu().numpy()))) + if idx0.size == 1: + idx0 = np.resize(idx0, (1,)) + if idx1.size == 1: + idx1 = np.resize(idx1, (1,)) + w = m0.weight.data[:, idx0, :, :].clone() + m1.weight.data = w[idx1, :, :, :].clone() + if m1.bias is not None: + m1.bias.data = m0.bias.data[idx0].clone() + elif(model_i in head_bn_merge_idx2.tolist()): + + m1.in_channels = int(_msk1.sum()) + index = head_bn_merge_idx2.tolist().index(model_i) + m1.out_channels = int(head_mask_1[index].sum()) + + idx1 = np.squeeze(np.argwhere(np.asarray(head_mask_1[index].cpu().numpy()))) + idx0 = np.squeeze(np.argwhere(np.asarray(_msk1.cpu().numpy()))) + if idx0.size == 1: + idx0 = np.resize(idx0, (1,)) + if idx1.size == 1: + idx1 = np.resize(idx1, (1,)) + w = m0.weight.data[:, idx0, :, :].clone() + m1.weight.data = w[idx1, :, :, :].clone() +# merge_weight_mask.append(m1.weight.data) + if m1.bias is not None: + m1.bias.data = m0.bias.data[idx0].clone() + elif(model_i==221 or model_i==230): + + merge_mask = torch.cat(head_mask_1,0) + m1.in_channels = num_ch + index = bn_index.index(model_i+1) + m1.out_channels = int(prued_mask[index].sum()) + + idx1 = np.squeeze(np.argwhere(np.asarray(prued_mask[index].cpu().numpy()))) + idx0 = np.squeeze(np.argwhere(np.asarray(merge_mask.cpu().numpy()))) + if idx0.size == 1: + idx0 = np.resize(idx0, (1,)) + if idx1.size == 1: + idx1 = np.resize(idx1, (1,)) + w = m0.weight.data[:, idx0, :, :].clone() + m1.weight.data = w[idx1, :, :, :].clone() + + if m1.bias is not None: + m1.bias.data = m0.bias.data[idx0].clone() + + elif(model_i==224 or model_i==233): + + m1.in_channels = int(prued_mask[bn_index.index(model_i-2)].sum()) + m1.out_channels = int(prued_mask[bn_index.index(model_i+1)].sum()) + idx0 = np.squeeze(np.argwhere(np.asarray(prued_mask[bn_index.index(model_i+1)].cpu().numpy()))) + idx1 = np.squeeze(np.argwhere(np.asarray(prued_mask[bn_index.index(model_i-2)].cpu().numpy()))) + if idx0.size == 1: + idx0 = np.resize(idx0, (1,)) + if idx1.size == 1: + idx1 = np.resize(idx1, (1,)) + w = m0.weight.data[:, idx0, :, :].clone() + m1.weight.data = w[idx1, :, :, :].clone() + if m1.bias is not None: + m1.bias.data = m0.bias.data[idx0].clone() + elif(model_i==227 or model_i==236): +# import pdb +# pdb.set_trace() + m1.in_channels = int(prued_mask[bn_index.index(model_i-2)].sum()) + idx1 = np.squeeze(np.argwhere(np.asarray(prued_mask[bn_index.index(model_i-2)].cpu().numpy()))) + idx0 = np.squeeze(np.argwhere(np.asarray(torch.ones(1).cpu().numpy()))) + if idx0.size == 1: + idx0 = np.resize(idx0, (1,)) + if idx1.size == 1: + idx1 = np.resize(idx1, (1,)) + w = m0.weight.data[:, idx0, :, :].clone() + m1.weight.data = w[idx1, :, :, :].clone() + if m1.bias is not None: + m1.bias.data = m0.bias.data[idx0].clone() + else: + m1.weight.data = m0.weight.data.clone() + if m1.bias is not None: + m1.bias.data = m0.bias.data.clone() + + elif isinstance(m0, nn.BatchNorm2d): + + if(model_i in [189,193,197,201]): + m1.num_features = int(_msk1.sum()) + idx1 = np.squeeze(np.argwhere(np.asarray(_msk1.cpu().numpy()))) + if idx1.size == 1: + idx1 = np.resize(idx1, (1,)) + m1.weight.data = m0.weight.data[idx1].clone() + if m1.bias is not None: + m1.bias.data = m0.bias.data[idx1].clone() + m1.running_mean = m0.running_mean[idx1].clone() + m1.running_var = m0.running_var[idx1].clone() + + else: + + index = bn_index.index(model_i) + m1.num_features = prued_mask[index].sum() + idx1 = np.squeeze(np.argwhere(np.asarray(prued_mask[index].cpu().numpy()))) + + if idx1.size == 1: + idx1 = np.resize(idx1, (1,)) + m1.weight.data = m0.weight.data[idx1].clone() + if m1.bias is not None: + m1.bias.data = m0.bias.data[idx1].clone() + m1.running_mean = m0.running_mean[idx1].clone() + m1.running_var = m0.running_var[idx1].clone() + + + else: + if isinstance(m0, nn.BatchNorm2d): + idx1 = np.squeeze(np.argwhere(np.asarray(bn_mask[bn_i].cpu().numpy()))) + if idx1.size == 1: + idx1 = np.resize(idx1, (1,)) + m1.weight.data = m0.weight.data[idx1].clone() + if m1.bias is not None: + m1.bias.data = m0.bias.data[idx1].clone() + m1.running_mean = m0.running_mean[idx1].clone() + m1.running_var = m0.running_var[idx1].clone() + bn_i += 1 + elif isinstance(m0, nn.Conv2d): + if (isinstance(conv_in_mask[conv_i], list)): + idx0 = np.squeeze(np.argwhere(np.asarray(torch.cat(conv_in_mask[conv_i], 0).cpu().numpy()))) + else: + idx0 = np.squeeze(np.argwhere(np.asarray(conv_in_mask[conv_i].cpu().numpy()))) + idx1 = np.squeeze(np.argwhere(np.asarray(conv_out_mask[conv_i].cpu().numpy()))) + if idx0.size == 1: + idx0 = np.resize(idx0, (1,)) + if idx1.size == 1: + idx1 = np.resize(idx1, (1,)) + if(model_i in group_index): + m1.weight.data = m0.weight.data[idx1, :, :, :].clone() + if m1.bias is not None: + m1.bias.data = m0.bias.clone() + else: + w = m0.weight.data[:, idx0, :, :].clone() + m1.weight.data = w[idx1, :, :, :].clone() + if m1.bias is not None: + m1.bias.data = m0.bias.data[idx0].clone() + conv_i += 1 + model_i+=1 + + print('model after pruned') + print(new_model) + + new_model.eval() + with torch.no_grad(): + out = new_model(img) + + cv2.imwrite('pruned_model_result.jpg',out[0,0].cpu().numpy()*255) + + save_obj = {'prued_mask': prued_mask, 'bn_index': bn_index} + torch.save(save_obj, os.path.join(args.save_prune_model_path, 'pruned_dict.dict')) + torch.save(new_model.state_dict(), os.path.join(args.save_prune_model_path, 'pruned_dict.pth')) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description='Hyperparams') + parser.add_argument('--base_num', nargs='?', type=int, default = 2, + help='Base after Model Channel Clipping') + parser.add_argument('--cut_percent', nargs='?', type=float, default=0.5, + help='Model channel clipping scale') + parser.add_argument('--config', default='./config/det_DB_mobilev3.yaml', + type=str, metavar='PATH', + help='config path') + parser.add_argument('--checkpoint', default='./checkpoint/ag_DB_bb_mobilenet_v3_small_he_DB_Head_bs_16_ep_1200_mobile_slim_all/DB_best.pth.tar', + type=str, metavar='PATH', + help='ori model path') + parser.add_argument('--save_prune_model_path', default='./checkpoint/ag_DB_bb_mobilenet_v3_small_he_DB_Head_bs_16_ep_1200_mobile_slim_all/pruned/', type=str, metavar='PATH', + help='pruned model path') + parser.add_argument('--img_file', + default='/src/notebooks/detect_text/icdar2015/ch4_test_images/img_108.jpg', + type=str, + help='') + args = parser.parse_args() + prune(args) diff --git a/tools/pruned/prune_model_backbone.py b/tools/pruned/prune_model_backbone.py new file mode 100644 index 0000000..3558695 --- /dev/null +++ b/tools/pruned/prune_model_backbone.py @@ -0,0 +1,365 @@ +""" +#!-*- coding=utf-8 -*- +@author: BADBADBADBADBOY +@contact: 2441124901@qq.com +@software: PyCharm Community Edition +@file: prune.py +@time: 2020/6/27 10:23 + +""" +import os +import sys +sys.path.append('./') +import yaml +from ptocr.utils.util_function import create_module,load_model,resize_image +import ptocr +import torch +import torch.nn as nn +import numpy as np +import collections +import torchvision.transforms as transforms +import cv2 + +import argparse +import math +from PIL import Image +from torch.autograd import Variable + +def prune(args): + + stream = open(args.config, 'r', encoding='utf-8') + config = yaml.load(stream,Loader=yaml.FullLoader) + + img = cv2.imread(args.img_file) + img = resize_image(img,config['base']['algorithm'],config['testload']['test_size'],stride=config['testload']['stride']) + img = Image.fromarray(img) + img = img.convert('RGB') + img = transforms.ToTensor()(img) + img = transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])(img) + img = Variable(img.cuda()).unsqueeze(0) + + model = create_module(config['architectures']['model_function'])(config).cuda() + model = load_model(model,args.checkpoint) + + model.eval() + print(model) + + cut_percent = 0.5 + base_num = 4 + + bn_weights = [] + for m in model.modules(): + if (isinstance(m, nn.BatchNorm2d)): + bn_weights.append(m.weight.data.abs().clone()) + bn_weights = torch.cat(bn_weights, 0) + + sort_result, sort_index = torch.sort(bn_weights) + + thresh_index = int(cut_percent * bn_weights.shape[0]) + + if (thresh_index == bn_weights.shape[0]): + thresh_index = bn_weights.shape[0] - 1 + + prued = 0 + prued_mask = [] + bn_index = [] + conv_index = [] + remain_channel_nums = [] + tag = 0 + for k, m in enumerate(model.modules()): + if(tag>187): + break + tag+=1 + if (isinstance(m, nn.BatchNorm2d)): + bn_weight = m.weight.data.clone() + mask = bn_weight.abs().gt(sort_result[thresh_index]) + remain_channel = mask.sum() + + if (remain_channel == 0): + remain_channel = 1 + mask[int(torch.argmax(bn_weight))] = 1 + + v = 0 + n = 1 + if (remain_channel % base_num != 0): + if (remain_channel > base_num): + while (v < remain_channel): + n += 1 + v = base_num * n + if (remain_channel - (v - base_num) < v - remain_channel): + remain_channel = v - base_num + else: + remain_channel = v + if (remain_channel > bn_weight.size()[0]): + remain_channel = bn_weight.size()[0] + remain_channel = torch.tensor(remain_channel) + result, index = torch.sort(bn_weight) + mask = bn_weight.abs().ge(result[-remain_channel]) + + remain_channel_nums.append(int(mask.sum())) + prued_mask.append(mask) + bn_index.append(k) + prued += mask.shape[0] - mask.sum() + elif (isinstance(m, nn.Conv2d)): + conv_index.append(k) + + print('remain_channel_nums',remain_channel_nums) + print('total_prune_ratio:', float(prued) / bn_weights.shape[0]) + print('bn_index',bn_index) + print('conv_index',conv_index) + + + + new_model = create_module(config['architectures']['model_function'])(config).cuda() + + keys = {} + tag = 0 + for k, m in enumerate(new_model.modules()): + if(isinstance(m,ptocr.model.backbone.det_mobilev3.Block)): + keys[tag]=k + tag+=1 + print(keys) + #### step 1 + mg_1 = np.array([-3,7,16]) + block_idx = keys[0] + tag = 0 + for idx in mg_1+block_idx: + if (tag == 0): + msk = prued_mask[bn_index.index(idx)] + else: + msk = msk | prued_mask[bn_index.index(idx)] + tag+=1 + print('step1',idx) + print(msk.sum()) + for idx in mg_1+block_idx: + prued_mask[bn_index.index(idx)] = msk + msk_1 = msk.clone() + + #### step 2 + block_idx2 = np.array([keys[1],keys[2]]) + mg_2 = 7 + tag = 0 + for idx in mg_2+block_idx2: + print('step2',idx) + if(tag==0): + msk = prued_mask[bn_index.index(idx)] + else: + msk = msk|prued_mask[bn_index.index(idx)] + tag += 1 + for idx in mg_2+block_idx2: + prued_mask[bn_index.index(idx)] = msk + print(msk.sum()) + msk_2 = msk.clone() + + ####step 3 + block_idx3s = [keys[3],keys[4],keys[5]] + mg_3 = np.array([7,16]) + tag = 0 + for block_idx3 in block_idx3s: + for idx in block_idx3+mg_3: + print('step3',idx) + if (tag == 0): + msk = prued_mask[bn_index.index(idx)] + else: + msk = msk | prued_mask[bn_index.index(idx)] + tag += 1 + for block_idx3 in block_idx3s: + for idx in block_idx3+mg_3: + prued_mask[bn_index.index(idx)] = msk + print(msk.sum()) + msk_3 = msk.clone() + + ####step 4_1 + block_idx4_all = [] + + block_idx4 = keys[6] + + mg_4 = np.array([7,16]) + block_idx4_all.extend((block_idx4+mg_4).tolist()) + + ####step 4_2 + block_idx4 = keys[7] + mg_4 = np.array([7,16]) + block_idx4_all.extend((block_idx4+mg_4).tolist()) + tag = 0 + + for idx in block_idx4_all: + print('step4',idx) + if(tag==0): + msk = prued_mask[bn_index.index(idx)] + else: + msk = msk | prued_mask[bn_index.index(idx)] + tag += 1 + + for idx in block_idx4_all: + prued_mask[bn_index.index(idx)] = msk + print(msk.sum()) + msk_4 = msk.clone() + + ####step 5 + block_idx5s = [keys[8],keys[9],keys[10]] + mg_5 = np.array([7,16]) + tag = 0 + for block_idx5 in block_idx5s: + for idx in block_idx5+mg_5: + if (tag == 0): + msk = prued_mask[bn_index.index(idx)] + else: + msk = msk | prued_mask[bn_index.index(idx)] + tag += 1 + + for block_idx5 in block_idx5s: + for idx in block_idx5+mg_5: + prued_mask[bn_index.index(idx)] = msk + print(msk.sum()) + msk_5 = msk.clone() + + group_index = [] + spl_index = [] + for i in range(11): + block_idx6 = keys[i] + tag = 0 + mg_6 = np.array([2,5]) + for idx in mg_6+block_idx6: + if(tag==0): + msk = prued_mask[bn_index.index(idx)] + else: + msk = msk | prued_mask[bn_index.index(idx)] + tag+=1 + for idx in mg_6 + block_idx6: + prued_mask[bn_index.index(idx)] = msk + if(i==6): + spl_index.extend([block_idx6+9,block_idx6-2]) + group_index.append(block_idx6+4) + import pdb + pdb.set_trace() + count_conv = 0 + count_bn = 0 + conv_in_mask = [torch.ones(3)] + conv_out_mask = [] + bn_mask = [] + tag = 0 + for k, m in enumerate(new_model.modules()): + if(tag>187): + break + if isinstance(m,nn.Conv2d): + + if(tag in group_index): + m.groups = int(prued_mask[bn_index.index(tag+1)].sum()) + m.out_channels = int(prued_mask[count_conv].sum()) + conv_out_mask.append(prued_mask[count_conv]) + if(count_conv>0): + if (tag == spl_index[0]): + m.in_channels = int(prued_mask[bn_index.index(spl_index[1])].sum()) + conv_in_mask.append(prued_mask[bn_index.index(spl_index[1])]) + else: + m.in_channels = int(prued_mask[count_conv-1].sum()) + conv_in_mask.append(prued_mask[count_conv-1]) + + count_conv+=1 + elif isinstance(m,nn.BatchNorm2d): + m.num_features = prued_mask[count_bn].sum() + bn_mask.append(prued_mask[count_bn]) + count_bn+=1 + tag+=1 + + bn_i = 0 + conv_i = 0 + model_i = 0 + scale = [188,192,196,200] + scale_mask = [msk_5,msk_4,msk_3,msk_2] + for [m0, m1] in zip(model.modules(), new_model.modules()): + if(model_i>187): + if isinstance(m0, nn.Conv2d): + if(model_i in scale): + index = scale.index(model_i) + m1.in_channels = int(scale_mask[index].sum()) + idx0 = np.squeeze(np.argwhere(np.asarray(scale_mask[index].cpu().numpy()))) + idx1 = np.squeeze(np.argwhere(np.asarray(torch.ones(96).cpu().numpy()))) + if idx0.size == 1: + idx0 = np.resize(idx0, (1,)) + if idx1.size == 1: + idx1 = np.resize(idx1, (1,)) + w = m0.weight.data[:, idx0, :, :].clone() + m1.weight.data = w[idx1, :, :, :].clone() + if m1.bias is not None: + m1.bias.data = m0.bias.data[idx1].clone() + + else: + m1.weight.data = m0.weight.data.clone() + if m1.bias is not None: + m1.bias.data = m0.bias.data.clone() + + elif isinstance(m0, nn.BatchNorm2d): + m1.weight.data = m0.weight.data.clone() + if m1.bias is not None: + m1.bias.data = m0.bias.data.clone() + m1.running_mean = m0.running_mean.clone() + m1.running_var = m0.running_var.clone() + else: + if isinstance(m0, nn.BatchNorm2d): + idx1 = np.squeeze(np.argwhere(np.asarray(bn_mask[bn_i].cpu().numpy()))) + if idx1.size == 1: + idx1 = np.resize(idx1, (1,)) + m1.weight.data = m0.weight.data[idx1].clone() + if m1.bias is not None: + m1.bias.data = m0.bias.data[idx1].clone() + m1.running_mean = m0.running_mean[idx1].clone() + m1.running_var = m0.running_var[idx1].clone() + bn_i += 1 + elif isinstance(m0, nn.Conv2d): + if (isinstance(conv_in_mask[conv_i], list)): + idx0 = np.squeeze(np.argwhere(np.asarray(torch.cat(conv_in_mask[conv_i], 0).cpu().numpy()))) + else: + idx0 = np.squeeze(np.argwhere(np.asarray(conv_in_mask[conv_i].cpu().numpy()))) + idx1 = np.squeeze(np.argwhere(np.asarray(conv_out_mask[conv_i].cpu().numpy()))) + if idx0.size == 1: + idx0 = np.resize(idx0, (1,)) + if idx1.size == 1: + idx1 = np.resize(idx1, (1,)) + if(model_i in group_index): + m1.weight.data = m0.weight.data[idx1, :, :, :].clone() + if m1.bias is not None: + m1.bias.data = m0.bias.clone() + else: + w = m0.weight.data[:, idx0, :, :].clone() + m1.weight.data = w[idx1, :, :, :].clone() + if m1.bias is not None: + m1.bias.data = m0.bias.data[idx1].clone() + conv_i += 1 + model_i+=1 + + + + print(new_model) + new_model.eval() + with torch.no_grad(): + out = new_model(img) + print(out.shape) + cv2.imwrite('re1.jpg',out[0,0].cpu().numpy()*255) + + save_obj = {'prued_mask': prued_mask, 'bn_index': bn_index} + torch.save(save_obj, os.path.join(args.save_prune_model_path, 'pruned_dict.dict')) + torch.save(new_model.state_dict(), os.path.join(args.save_prune_model_path, 'pruned_dict.pth.tar')) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description='Hyperparams') + parser.add_argument('--base_num', nargs='?', type=int, default = 4, + help='Base after Model Channel Clipping') + parser.add_argument('--cut_percent', nargs='?', type=float, default=0.5, + help='Model channel clipping scale') + parser.add_argument('--config', default='./config/det_DB_mobilev3.yaml', + type=str, metavar='PATH', + help='config path') + parser.add_argument('--checkpoint', default='./checkpoint/ag_DB_bb_mobilenet_v3_small_he_DB_Head_bs_16_ep_1200/DB_best.pth.tar', + type=str, metavar='PATH', + help='ori model path') + parser.add_argument('--save_prune_model_path', default='./checkpoint/ag_DB_bb_mobilenet_v3_small_he_DB_Head_bs_16_ep_1200/pruned/', type=str, metavar='PATH', + help='pruned model path') + parser.add_argument('--img_file', + default='/src/notebooks/detect_text/icdar2015/ch4_test_images/img_108.jpg', + type=str, + help='') + args = parser.parse_args() + prune(args) diff --git a/tools/rec_infer.py b/tools/rec_infer.py new file mode 100644 index 0000000..31a505e --- /dev/null +++ b/tools/rec_infer.py @@ -0,0 +1,199 @@ +#-*- coding:utf-8 _*- +""" +@author:fxw +@file: det_infer.py +@time: 2020/08/20 +""" +import os +os.environ["CUDA_VISIBLE_DEVICES"] = '2' +import sys +sys.path.append('./') +import cv2 +import torch +import yaml +from PIL import Image +import numpy as np +from tqdm import tqdm +import torchvision.transforms as transforms +from ptocr.utils.util_function import create_module,resize_image,resize_image_crnn +from ptocr.utils.util_function import create_process_obj,create_dir,load_model +from get_acc_english import get_test_acc + +def is_chinese(check_str): + flag = False + for ch in check_str.decode('utf-8'): + if u'\u4e00' >= ch or ch >= u'\u9fff': + flag = True + return flag + +def is_number_letter(check_str): + if(check_str in '0123456789' or check_str in 'abcdefghijklmnopgrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'): + return True + else: + return False + + + + +class TestProgram(): + def __init__(self,config): + super(TestProgram,self).__init__() + + self.converter = create_module(config['label_transform']['function'])(config) + config['base']['classes'] = len(self.converter.alphabet) + model = create_module(config['architectures']['model_function'])(config) + model = load_model(model,config['infer']['model_path']) + + if torch.cuda.is_available(): + model = model.cuda() + self.model = model + self.congig = config + self.model.eval() + + def infer_img(self,ori_img): + +# img = resize_image(ori_img,self.congig['base']['algorithm'],32,4) + img = resize_image_crnn(ori_img) +# cv2.imwrite('result.jpg',img) + if(img.shape[0]!=self.congig['base']['img_shape'][0]): + return '' + + img = Image.fromarray(img).convert('RGB') + if(self.congig['base']['is_gray']): + img = img.convert('L') +# img = img.resize((100, 32),Image.ANTIALIAS) + image_for_show = np.array(img.convert('RGB')).copy() + img = transforms.ToTensor()(img) + img.sub_(0.5).div_(0.5) + img = img.unsqueeze(0) + + if torch.cuda.is_available(): + img = img.cuda() + + with torch.no_grad(): + preds,feau= self.model(img) + preds = preds.permute(1, 0, 2) + + time_step = preds.shape[0] + image_width = img.shape[3] + step_ = image_width//time_step + + ### 输出相似字结果 +# k_num = 3 +# p,idx = torch.softmax(preds,-1).squeeze().topk(k_num,-1) +# p = p[idx[:,0]>0].cpu().numpy() +# for i in range(k_num): +# index = idx[:,i][idx[:,0]>0] +# result = np.array(list(self.converter.alphabet))[index.cpu().numpy()-1].tolist() +# if(i==0): +# print('识别结果:',result,p[:,i]) +# else: +# print('相似字:',result,p[:,i]) + + preds_size = torch.IntTensor([preds.size(0)]) + _, preds = preds.max(2) + preds = preds.squeeze(1) + preds = preds.contiguous().view(-1) + + sim_preds = self.converter.decode(preds.data, preds_size.data, raw=False) + +# raw_pred = self.converter.decode(preds.data, preds_size.data, raw=True) +# start_step = 0 +# word_po = [] +# for i in range(len(raw_pred)-1): +# if(raw_pred[i]!='-' and raw_pred[i]!=raw_pred[i+1]): +# # image_for_show = cv2.rectangle(image_for_show,(start_step+step_,0),(start_step+step_, img.shape[2]-1),(0,0,255)) +# start_step+=step_ +# word_po.append(start_step+step_) +# else: +# start_step+=step_ + +# # word_po = np.array(word_po) +# # word_po_flag = np.zeros(len(word_po)) +# # word_po_flag[1:] = word_po[:-1] +# # word_len = sorted(word_po-word_po_flag)[len(word_po)//2] +# word_s_e = [] +# for i in range(len(word_po)): +# if(is_number_letter(sim_preds[i])): +# word_len = self.congig['base']['img_shape'][0]//4 +# else: +# word_len = self.congig['base']['img_shape'][0]//2 +# word_s_e.append([word_po[i]-word_len,word_po[i]+word_len]) +# # image_for_show = cv2.rectangle(image_for_show,(int(word_po[i]-word_len),1),(int(word_po[i]+word_len), img.shape[2]-1),(255,0,255)) +# image_for_show = cv2.line(image_for_show,(int(word_po[i]),1),(int(word_po[i]), img.shape[2]-1),(255,0,255)) +# cv2.imwrite('result.jpg',image_for_show) + + return sim_preds + +def InferImage(config): + path = config['infer']['path'] + save_path = config['infer']['save_path'] + test_bin = TestProgram(config) + fid = open('re_result.txt','w+',encoding='utf-8') + if os.path.isdir(path): + files = os.listdir(path) + bar = tqdm(total=len(files)) + for file in files: + print(file) + bar.update(1) + image_name = file.split('.')[0] + img_path = os.path.join(path,file) + img = cv2.imread(img_path) + rec_char = test_bin.infer_img(img) + fid.write(file+'\t'+rec_char+'\n') + print(rec_char) + bar.close() + + else: + image_name = path.split('/')[-1].split('.')[0] + img = cv2.imread(path) + rec_char = test_bin.infer_img(img) + print(rec_char) + +def InferImageAcc(config): + root_path = config['infer']['path'] + save_path = config['infer']['save_path'] + test_bin = TestProgram(config) + acc_fid = open('acc.txt','w+',encoding='utf-8') + acc_all = 0 + for _dir in os.listdir(root_path): + path = os.path.join(root_path,_dir,'image') + gt_file = os.path.join(root_path,_dir,'val.txt') +# print(gt_file) + fid = open(_dir+'.txt','w+',encoding='utf-8') + if os.path.isdir(path): + files = os.listdir(path) + bar = tqdm(total=len(files)) + for file in files: +# print(file) + bar.update(1) + image_name = file.split('.')[0] + img_path = os.path.join(path,file) + img = cv2.imread(img_path) + rec_char = test_bin.infer_img(img) + fid.write(file+'\t'+rec_char+'\n') +# print(rec_char) + bar.close() + + else: + image_name = path.split('/')[-1].split('.')[0] + img = cv2.imread(path) + rec_char = test_bin.infer_img(img) + print(rec_char) + fid.close() + acc = get_test_acc(_dir+'.txt',gt_file) + acc_all+=acc + acc_fid.write(_dir+':'+'\t'+str(acc)+'\n') + print('mean acc:',acc_all/9.0) + acc_fid.close() + + +if __name__ == "__main__": + + stream = open('./config/rec_CRNN_mobilev3_small_english_all.yaml', 'r', encoding='utf-8') + config = yaml.load(stream,Loader=yaml.FullLoader) + InferImageAcc(config) + +# stream = open('./config/rec_CRNN_resnet_english.yaml', 'r', encoding='utf-8') +# config = yaml.load(stream,Loader=yaml.FullLoader) +# InferImage(config) \ No newline at end of file diff --git a/tools/rec_infer1.py b/tools/rec_infer1.py new file mode 100644 index 0000000..b1cd2a7 --- /dev/null +++ b/tools/rec_infer1.py @@ -0,0 +1,159 @@ +#-*- coding:utf-8 _*- +""" +@author:fxw +@file: det_infer.py +@time: 2020/08/20 +""" +import os +os.environ["CUDA_VISIBLE_DEVICES"] = '2' +import sys +sys.path.append('./') +import cv2 +import torch +import yaml +from PIL import Image +import numpy as np +from tqdm import tqdm +import torchvision.transforms as transforms +from ptocr.utils.util_function import create_module,resize_image,resize_image_crnn +from ptocr.utils.util_function import create_process_obj,create_dir,load_model +from get_acc_english import get_test_acc + +def is_chinese(check_str): + flag = False + for ch in check_str.decode('utf-8'): + if u'\u4e00' >= ch or ch >= u'\u9fff': + flag = True + return flag + +def is_number_letter(check_str): + if(check_str in '0123456789' or check_str in 'abcdefghijklmnopgrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'): + return True + else: + return False + + + + +class TestProgram(): + def __init__(self,config): + super(TestProgram,self).__init__() + + self.converter = create_module(config['label_transform']['function'])(config) + model = create_module(config['architectures']['model_function'])(config) + model = load_model(model,config['infer']['model_path']) + + if torch.cuda.is_available(): + model = model.cuda() + self.model = model + self.congig = config + self.model.eval() + + def infer_img(self,ori_img): + +# img = resize_image(ori_img,self.congig['base']['algorithm'],32,4) + img = resize_image_crnn(ori_img) +# cv2.imwrite('result.jpg',img) + if(img.shape[0]!=self.congig['base']['img_shape'][0]): + return '' + + img = Image.fromarray(img).convert('RGB') + if(self.congig['base']['is_gray']): + img = img.convert('L') +# img = img.resize((100, 32),Image.ANTIALIAS) + image_for_show = np.array(img.convert('RGB')).copy() + img = transforms.ToTensor()(img) + img.sub_(0.5).div_(0.5) + img = img.unsqueeze(0) + + if torch.cuda.is_available(): + img = img.cuda() + + with torch.no_grad(): + preds,feau= self.model(img) + + time_step = preds.shape[0] + image_width = img.shape[3] + step_ = image_width//time_step + + + _, preds = preds.max(2) + + + sim_preds = self.converter.decode(preds.data) + print(preds) + print(sim_preds) + return sim_preds[0] + +def InferImage(config): + path = config['infer']['path'] + save_path = config['infer']['save_path'] + test_bin = TestProgram(config) + fid = open('re_result.txt','w+',encoding='utf-8') + if os.path.isdir(path): + files = os.listdir(path) + bar = tqdm(total=len(files)) + for file in files: + print(file) + bar.update(1) + image_name = file.split('.')[0] + img_path = os.path.join(path,file) + img = cv2.imread(img_path) + rec_char = test_bin.infer_img(img) + fid.write(file+'\t'+rec_char+'\n') + print(rec_char) + bar.close() + + else: + image_name = path.split('/')[-1].split('.')[0] + img = cv2.imread(path) + rec_char = test_bin.infer_img(img) + print(rec_char) + +def InferImageAcc(config): + root_path = config['infer']['path'] + save_path = config['infer']['save_path'] + test_bin = TestProgram(config) + acc_fid = open('acc.txt','w+',encoding='utf-8') + acc_all = 0 + for _dir in os.listdir(root_path): + path = os.path.join(root_path,_dir,'image') + gt_file = os.path.join(root_path,_dir,'val.txt') +# print(gt_file) + fid = open(_dir+'.txt','w+',encoding='utf-8') + if os.path.isdir(path): + files = os.listdir(path) + bar = tqdm(total=len(files)) + for file in files: +# print(file) + bar.update(1) + image_name = file.split('.')[0] + img_path = os.path.join(path,file) + img = cv2.imread(img_path) + rec_char = test_bin.infer_img(img) + fid.write(file+'\t'+rec_char+'\n') +# print(rec_char) + bar.close() + + else: + image_name = path.split('/')[-1].split('.')[0] + img = cv2.imread(path) + rec_char = test_bin.infer_img(img) + print(rec_char) + fid.close() + acc = get_test_acc(_dir+'.txt',gt_file) + acc_all+=acc + acc_fid.write(_dir+':'+'\t'+str(acc)+'\n') + print('mean acc:',acc_all/9.0) + acc_fid.close() + + +if __name__ == "__main__": + + stream = open('./config/rec_FC_resnet_english_all.yaml', 'r', encoding='utf-8') + config = yaml.load(stream,Loader=yaml.FullLoader) + InferImageAcc(config) + +# stream = open('./config/rec_CRNN_resnet_english.yaml', 'r', encoding='utf-8') +# config = yaml.load(stream,Loader=yaml.FullLoader) +# InferImage(config) \ No newline at end of file diff --git a/tools/rec_train.py b/tools/rec_train.py new file mode 100644 index 0000000..b11ea09 --- /dev/null +++ b/tools/rec_train.py @@ -0,0 +1,229 @@ +import sys +sys.path.append('./') +import cv2 +import torch +import time +import os +import argparse +import random +import numpy as np +from tqdm import tqdm +from torch.autograd import Variable +np.seterr(divide='ignore', invalid='ignore') +import yaml +import torch.utils.data +from ptocr.utils.util_function import create_module, create_loss_bin, \ +set_seed,save_checkpoint,create_dir +from ptocr.utils.metrics import runningScore +from ptocr.utils.logger import Logger,TrainLog +from ptocr.utils.util_function import create_process_obj,merge_config,AverageMeter,restore_training +from ptocr.dataloader.RecLoad.CRNNProcess import alignCollate +import copy + +### 设置随机种子 +GLOBAL_SEED = 2020 +torch.manual_seed(GLOBAL_SEED) +torch.cuda.manual_seed(GLOBAL_SEED) +torch.cuda.manual_seed_all(GLOBAL_SEED) +np.random.seed(GLOBAL_SEED) +random.seed(GLOBAL_SEED) + +def ModelTrain(train_data_loader,LabelConverter, model,criterion,optimizer, train_log,loss_dict, config, epoch): + for batch_idx, ( imgs,labels) in enumerate(train_data_loader): + pre_batch = {} + gt_batch = {} + + if torch.cuda.is_available(): + imgs = imgs.cuda() + preds,feau = model(imgs) + preds = preds.permute(1, 0, 2) + + ######### + labels,labels_len = LabelConverter.encode(labels,preds.size(0)) + preds_size = Variable(torch.IntTensor([preds.size(0)] * config['trainload']['batch_size'])) + pre_batch['preds'],pre_batch['preds_size'] = preds,preds_size + gt_batch['labels'],gt_batch['labels_len'] = labels,labels_len + ######### + + if config['loss']['use_ctc_weight']: + len_index = torch.softmax(preds,-1).max(2)[1].transpose(0,1)>0 + len_flag = torch.cat([labels_len.cuda().long().unsqueeze(0),len_index.sum(1).unsqueeze(0)],0) + ctc_loss_weight = len_flag.max(0)[0].float()/len_flag.min(0)[0].float() + ctc_loss_weight[ctc_loss_weight==torch.tensor(np.inf).cuda()]=2.0 + gt_batch['ctc_loss_weight'] = ctc_loss_weight + + loss = criterion(pre_batch, gt_batch).cuda() + optimizer.zero_grad() + loss.backward() + optimizer.step() + + metrics = {} + metrics['ctc_loss'] = loss.item() + + for key in loss_dict.keys(): + loss_dict[key].update(metrics[key]) + + if (batch_idx % config['base']['show_step'] == 0): + log = '({}/{}/{}/{}) | ' \ + .format(epoch, config['base']['n_epoch'], batch_idx, len(train_data_loader)) + keys = list(loss_dict.keys()) + for i in range(len(keys)): + log += keys[i] + ':{:.4f}'.format(loss_dict[keys[i]].avg) + ' | ' + log += 'lr:{:.8f}'.format(optimizer.param_groups[0]['lr']) + train_log.info(log) + +def ModelEval(val_data_loader,LabelConverter, model,criterion,train_log,loss_dict,config): + + bar = tqdm(total=len(val_data_loader)) + val_loss = AverageMeter() + n_correct = AverageMeter() + + for batch_idx, (imgs, labels) in enumerate(val_data_loader): + bar.update(1) + + pre_batch = {} + gt_batch = {} + + if torch.cuda.is_available(): + imgs = imgs.cuda() + + with torch.no_grad(): + preds,feau = model(imgs) + preds = preds.permute(1, 0, 2) + + labels_class, labels_len = LabelConverter.encode(labels,preds.size(0)) + preds_size = Variable(torch.IntTensor([preds.size(0)] * config['valload']['batch_size'])) + pre_batch['preds'],pre_batch['preds_size'] = preds,preds_size + gt_batch['labels'],gt_batch['labels_len'] = labels_class,labels_len + + if config['loss']['use_ctc_weight']: + len_index = torch.softmax(preds,-1).max(2)[1].transpose(0,1)>0 + len_flag = torch.cat([labels_len.cuda().long().unsqueeze(0),len_index.sum(1).unsqueeze(0)],0) + ctc_loss_weight = len_flag.max(0)[0].float()/len_flag.min(0)[0].float() + ctc_loss_weight[ctc_loss_weight==torch.tensor(np.inf).cuda()]=2.0 + gt_batch['ctc_loss_weight'] = ctc_loss_weight + + cost = criterion(pre_batch, gt_batch) + val_loss.update(cost.item()) + + _, preds = preds.max(2) + preds = preds.squeeze(1).transpose(1, 0).contiguous().view(-1) + + sim_preds = LabelConverter.decode(preds.data, preds_size.data, raw=False) + for pred, target in zip(sim_preds, labels): + if pred == target: + n_correct.update(1) + raw_preds = LabelConverter.decode(preds.data, preds_size.data, raw=True)[:config['base']['show_num']] + for raw_pred, pred, gt in zip(raw_preds, sim_preds, labels): + train_log.info('recog example %-20s => %-20s, gt: %-20s' % (raw_pred, pred, gt)) + + val_acc = n_correct.sum / float(len(val_data_loader) * config['valload']['batch_size']) + train_log.info('val loss: %f, val accuray: %f' % (val_loss.avg, val_acc)) + return val_acc + + +def TrainValProgram(args): + + config = yaml.load(open(args.config, 'r', encoding='utf-8'),Loader=yaml.FullLoader) + config = merge_config(config,args) + + os.environ["CUDA_VISIBLE_DEVICES"] = config['base']['gpu_id'] + + create_dir(config['base']['checkpoints']) + checkpoints = os.path.join(config['base']['checkpoints'],"ag_%s_bb_%s_he_%s_bs_%d_ep_%d_%s" % (config['base']['algorithm'], + config['backbone']['function'].split(',')[-1], + config['head']['function'].split(',')[-1], + config['trainload']['batch_size'], + config['base']['n_epoch'], + args.log_str)) + create_dir(checkpoints) + + LabelConverter = create_module(config['label_transform']['function'])(config) + config['base']['classes'] = len(LabelConverter.alphabet) + model = create_module(config['architectures']['model_function'])(config) + criterion = create_module(config['architectures']['loss_function'])(config) + train_dataset = create_module(config['trainload']['function'])(config,'train') + val_dataset = create_module(config['valload']['function'])(config,'val') + optimizer = create_module(config['optimizer']['function'])(config, model.parameters()) + optimizer_decay = create_module(config['optimizer_decay']['function']) + if os.path.exists(os.path.join(checkpoints,'train_log.txt')): + os.remove(os.path.join(checkpoints,'train_log.txt')) + train_log = TrainLog(os.path.join(checkpoints,'train_log.txt')) + train_log.info(model) + + train_data_loader = torch.utils.data.DataLoader( + train_dataset, + batch_size=config['trainload']['batch_size'], + shuffle=True, + num_workers=config['trainload']['num_workers'], + collate_fn = alignCollate(), + drop_last=True, + pin_memory=True) + + val_data_loader = torch.utils.data.DataLoader( + val_dataset, + batch_size=config['valload']['batch_size'], + shuffle=False, + num_workers=config['valload']['num_workers'], + collate_fn = alignCollate(), + drop_last=True, + pin_memory=True) + + loss_dict = {} + for title in config['loss']['loss_title']: + loss_dict[title] = AverageMeter() + + if torch.cuda.is_available(): + if (len(config['base']['gpu_id'].split(',')) > 1): + model = torch.nn.DataParallel(model).cuda() + else: + model = model.cuda() + criterion = criterion.cuda() + + start_epoch = 0 + val_acc = 0 + val_loss = 0 + best_acc = 0 + + if config['base']['restore']: + train_log.info('Resuming from checkpoint.') + assert os.path.isfile(config['base']['restore_file']), 'Error: no checkpoint file found!' + model,optimizer,start_epoch,best_acc = restore_training(config['base']['restore_file'],model,optimizer) + + for epoch in range(start_epoch,config['base']['n_epoch']): + model.train() + optimizer_decay(config, optimizer, epoch) + ModelTrain(train_data_loader,LabelConverter, model,criterion,optimizer, train_log,loss_dict, config, epoch) + if(epoch >= config['base']['start_val']): + model.eval() + val_acc = ModelEval(val_data_loader,LabelConverter, model,criterion,train_log,loss_dict,config) + if (val_acc > best_acc): + save_checkpoint({ + 'epoch': epoch + 1, + 'state_dict': model.state_dict(), + 'lr': config['optimizer']['base_lr'], + 'optimizer': optimizer.state_dict(), + 'best_acc': val_acc + }, checkpoints, config['base']['algorithm'] + '_best' + '.pth.tar') + best_acc = val_acc + train_log.info('best_acc:' + str(best_acc)) + for key in loss_dict.keys(): + loss_dict[key].reset() + + if epoch % config['base']['save_epoch'] == 0: + save_checkpoint({ + 'epoch': epoch + 1, + 'state_dict': model.state_dict(), + 'lr': config['optimizer']['base_lr'], + 'optimizer': optimizer.state_dict(), + 'best_acc': 0 + },checkpoints,config['base']['algorithm']+'_'+str(epoch)+'.pth.tar') + + +if __name__ == "__main__": + + parser = argparse.ArgumentParser(description='Hyperparams') + parser.add_argument('--config', help='config file path') + parser.add_argument('--log_str', help='log title') + args = parser.parse_args() + TrainValProgram(args) \ No newline at end of file diff --git a/tools/rec_train1.py b/tools/rec_train1.py new file mode 100644 index 0000000..811d27c --- /dev/null +++ b/tools/rec_train1.py @@ -0,0 +1,396 @@ +# -*- coding:utf-8 _*- +""" +@author:fxw +@file: det_train.py +@time: 2020/08/07 +""" +import sys +sys.path.append('./') +import cv2 +import torch +import time +import os +import argparse +import random +import numpy as np +from tqdm import tqdm +from torch.autograd import Variable +np.seterr(divide='ignore', invalid='ignore') +import yaml +import torch.utils.data +from ptocr.utils.util_function import create_module, create_loss_bin, \ +set_seed,save_checkpoint,create_dir +from ptocr.utils.metrics import runningScore +from ptocr.utils.logger import Logger +from ptocr.utils.util_function import create_process_obj,merge_config,AverageMeter +from ptocr.dataloader.RecLoad.CRNNProcess import alignCollate +import copy +import re + +GLOBAL_WORKER_ID = None +GLOBAL_SEED = 2020 + + +torch.manual_seed(GLOBAL_SEED) +torch.cuda.manual_seed(GLOBAL_SEED) +torch.cuda.manual_seed_all(GLOBAL_SEED) +np.random.seed(GLOBAL_SEED) +random.seed(GLOBAL_SEED) + + + +def worker_init_fn(worker_id): + global GLOBAL_WORKER_ID + GLOBAL_WORKER_ID = worker_id + set_seed(GLOBAL_SEED + worker_id) + +# def backward_hook(self,grad_input, grad_output): +# for g in grad_input: +# g[g != g] = 0 # replace all nan/inf in gradients to zero + + +def ModelEval(test_data_loaders,LabelConverter,model,criterion,config): + loss_all= [] + acc_all = [] + for test_data_loader in test_data_loaders: + bar = tqdm(total=len(test_data_loader)) + loss_avg = [] + n_correct = 0 + for batch_idx, (imgs, labels) in enumerate(test_data_loader): + bar.update(1) + pre_batch = {} + gt_batch = {} + if torch.cuda.is_available(): + imgs = imgs.cuda() + with torch.no_grad(): + preds,feau = model(imgs) + preds = preds.permute(1, 0, 2) + + labels_class, labels_len = LabelConverter.encode(labels,preds.size(0)) + preds_size = Variable(torch.IntTensor([preds.size(0)] * config['valload']['batch_size'])) + pre_batch['preds'],pre_batch['preds_size'] = preds,preds_size + gt_batch['labels'],gt_batch['labels_len'] = labels_class,labels_len + + if config['loss']['use_ctc_weight']: + len_index = torch.softmax(preds,-1).max(2)[1].transpose(0,1)>0 + len_flag = torch.cat([labels_len.cuda().long().unsqueeze(0),len_index.sum(1).unsqueeze(0)],0) + ctc_loss_weight = len_flag.max(0)[0].float()/len_flag.min(0)[0].float() + ctc_loss_weight[ctc_loss_weight==torch.tensor(np.inf).cuda()]=2.0 + gt_batch['ctc_loss_weight'] = ctc_loss_weight + + cost = criterion(pre_batch, gt_batch) + loss_avg.append(cost.item()) + _, preds = preds.max(2) + preds = preds.squeeze(1) + preds = preds.contiguous().view(-1) + sim_preds = LabelConverter.decode(preds.data, preds_size.data, raw=False) + for pred, target in zip([sim_preds], labels): + target = ''.join(re.findall('[0-9a-zA-Z]+',target)).lower() + if pred == target: + n_correct += 1 + val_acc = n_correct / float(len(test_data_loader) * config['valload']['batch_size']) + val_loss = np.mean(loss_avg) + loss_all.append(val_loss) + acc_all.append(val_acc) + print('Test acc:' ,acc_all) + return loss_all,acc_all + +def ModelTrain(train_data_loader,test_data_loader,LabelConverter,model,center_model, criterion, optimizer,center_criterion,optimizer_center,center_flag,loss_bin, config,optimizer_decay,optimizer_decay_center,log_write,checkpoints): + batch_time = AverageMeter() + end = time.time() + best_acc = 0 + dataloader_bin = [] + fid = open('test.txt','w+') + for i in range(len(train_data_loader)): + dataloader_bin.append(enumerate(train_data_loader[i])) + + for iters in range(config['base']['start_iters'],config['base']['max_iters']): + model.train() + optimizer_decay(config, optimizer, iters) + if config['loss']['use_center']: + optimizer_decay_center(config, optimizer_center, iters) + + imgs = [] + labels = [] + try: + for i in range(len(train_data_loader)): + index,(img,label) = next(dataloader_bin[i]) + imgs.append(img) + labels.extend(label) + imgs = torch.cat(imgs,0) + except: + for i in range(len(train_data_loader)): + dataloader_bin[i] = enumerate(train_data_loader[i]) + continue + + pre_batch = {} + gt_batch = {} + + if torch.cuda.is_available(): + imgs = imgs.cuda() + preds,feau = model(imgs) + + preds = preds.permute(1, 0, 2) + + labels,labels_len = LabelConverter.encode(labels,preds.size(0)) + preds_size = Variable(torch.IntTensor([preds.size(0)] * config['trainload']['batch_size'])) + pre_batch['preds'],pre_batch['preds_size'] = preds,preds_size + gt_batch['labels'],gt_batch['labels_len'] = labels,labels_len + ######### + +# import pdb +# pdb.set_trace() + + if config['loss']['use_ctc_weight']: +# print('use') + + len_index = torch.softmax(preds,-1).max(2)[1].transpose(0,1)>0 + len_flag = torch.cat([labels_len.cuda().long().unsqueeze(0),len_index.sum(1).unsqueeze(0)],0) + ctc_loss_weight = len_flag.max(0)[0].float()/len_flag.min(0)[0].float() + + ctc_loss_weight[ctc_loss_weight==torch.tensor(np.inf).cuda()]=2.0 + gt_batch['ctc_loss_weight'] = ctc_loss_weight + + ctc_loss = criterion(pre_batch, gt_batch).cuda() + metrics = {} + metrics['loss_total'] = 0.0 + metrics['loss_center'] = 0.0 + if center_criterion is not None and center_flag is True: + center_model.eval() + ##### + feautures = preds.clone() + with torch.no_grad(): + center_preds,center_feau = center_model(imgs) + center_preds = center_preds.permute(1, 0, 2) + + center_preds = torch.softmax(center_preds,-1) + confs, center_preds = center_preds.max(2) + center_preds = center_preds.squeeze(1).transpose(1, 0).contiguous() + confs = confs.transpose(1, 0).contiguous() + +# confs = [] +# for i in range(center_preds.shape[0]): +# conf = [] +# for j in range(len(center_preds[i])): +# conf.append(probs[i,j,center_preds[i][j]]) +# confs.append(conf) +# confs = torch.Tensor(confs).cuda() + + b,t = center_preds.shape + +# feautures = feautures.transpose(1, 0).contiguous() + feautures = center_feau[0].transpose(1, 0).contiguous() + +# import pdb +# pdb.set_trace() + ### 去重复 + repeat_index = (center_preds[:,:-1] == center_preds[:,1:]) + center_preds[:,:-1][repeat_index] = 0 + + confs = confs.view(-1) + center_preds = center_preds.view(-1) + feautures = feautures.view(b*t,-1) + + + index = (center_preds>0) & (confs>config['loss']['label_score']) + center_preds = center_preds[index] + feautures = feautures[index] + + center_loss = center_criterion(feautures,center_preds)*config['loss']['weight_center'] + + loss = ctc_loss + center_loss + + + metrics['loss_total'] = loss.item() + metrics['loss_ctc'] = ctc_loss.item() + metrics['loss_center'] = center_loss.item() + + ##### + optimizer_center.zero_grad() + optimizer.zero_grad() + + loss.backward() + optimizer.step() + + for param in center_criterion.parameters(): + param.grad.data *= (1. / config['loss']['weight_center']) + optimizer_center.step() + else: + loss = ctc_loss + metrics['loss_ctc'] = ctc_loss.item() + optimizer.zero_grad() + loss.backward() + optimizer.step() + + for key in loss_bin.keys(): + loss_bin[key].loss_add(metrics[key]) + batch_time.update(time.time() - end) + end = time.time() + if (iters % config['base']['show_step'] == 0): + log = '({}/{}) | ' \ + .format(iters,config['base']['max_iters']) + bin_keys = list(loss_bin.keys()) + + for i in range(len(bin_keys)): + log += bin_keys[i] + ':{:.4f}'.format(loss_bin[bin_keys[i]].loss_mean()) + ' | ' + log += 'lr:{:.8f}'.format(optimizer.param_groups[0]['lr'])+ ' | ' + log+='batch_time:{:.2f} s'.format(batch_time.avg)+ ' | ' + log+='total_time:{:.2f} min'.format(batch_time.avg * iters / 60.0)+ ' | ' + log+='ETA:{:.2f} min'.format(batch_time.avg*(config['base']['max_iters']-iters)/60.0) + print(log) + + + if(iters % config['base']['eval_iter']==0 and iters!=0): + + loss_write = [] + for key in list(loss_bin.keys()): + loss_write.append(loss_bin[key].loss_mean()) + + model.eval() + val_loss,acc_all = ModelEval(test_data_loader,LabelConverter, model,criterion ,config) + val_acc = np.mean(acc_all) + if (val_acc > best_acc): + save_checkpoint({ + 'iters': iters + 1, + 'state_dict': model.state_dict(), + 'lr': config['optimizer']['base_lr'], + 'optimizer': optimizer.state_dict(), + 'best_acc': val_acc + }, checkpoints, config['base']['algorithm'] + '_best' + '.pth.tar') + best_acc = val_acc + acc_all.append(val_acc) + acc_all = [str(x) for x in acc_all] + fid.write(str(','.join(acc_all))+'\n') + fid.flush() + loss_write.extend([0,0,0]) + log_write.append(loss_write) + for key in loss_bin.keys(): + loss_bin[key].loss_clear() + if iters %config['base']['eval_iter'] ==0: + save_checkpoint({ + 'iters': iters + 1, + 'state_dict': model.state_dict(), + 'lr': config['optimizer']['base_lr'], + 'optimizer': optimizer.state_dict(), + 'best_acc': 0 + },checkpoints,config['base']['algorithm']+'_'+str(iters)+'.pth.tar') + + + + + + + + + +def TrainValProgram(config): + + config = yaml.load(open(args.config, 'r', encoding='utf-8'),Loader=yaml.FullLoader) + config = merge_config(config,args) + + os.environ["CUDA_VISIBLE_DEVICES"] = config['base']['gpu_id'] + + create_dir(config['base']['checkpoints']) + checkpoints = os.path.join(config['base']['checkpoints'], + "ag_%s_bb_%s_he_%s_bs_%d_ep_%d_%s" % (config['base']['algorithm'], + config['backbone']['function'].split(',')[-1], + config['head']['function'].split(',')[-1], + config['trainload']['batch_size'], + config['base']['max_iters'], + args.log_str)) + create_dir(checkpoints) + + LabelConverter = create_module(config['label_transform']['function'])(config) + config['base']['classes'] = len(LabelConverter.alphabet) + model = create_module(config['architectures']['model_function'])(config) + criterion = create_module(config['architectures']['loss_function'])(config) + train_data_loader = create_module(config['trainload']['function'])(config) + test_data_loader = create_module(config['valload']['function'])(config) + optimizer = create_module(config['optimizer']['function'])(config, model) + optimizer_decay = create_module(config['optimizer_decay']['function']) + + if config['loss']['use_center']: +# center_criterion = create_module(config['loss']['center_function'])(config['base']['classes'],config['base']['classes']) + center_criterion = create_module(config['loss']['center_function'])(config['base']['classes'],config['base']['hiddenchannel']) + + optimizer_center = torch.optim.Adam(center_criterion.parameters(), lr= config['loss']['center_lr']) + optimizer_decay_center = create_module(config['optimizer_decay_center']['function']) + else: + center_criterion = None + optimizer_center=None + optimizer_decay_center = None + + + + + loss_bin = create_loss_bin(config['base']['algorithm'],use_center=config['loss']['use_center']) + + if torch.cuda.is_available(): + if (len(config['base']['gpu_id'].split(',')) > 1): + model = torch.nn.DataParallel(model).cuda() + else: + model = model.cuda() + criterion = criterion.cuda() + + + + print(model) + + ### model.head.lstm_2.embedding = nn.Linear(in_features=512, out_features=2000, bias=True) + + if config['base']['restore']: + print('Resuming from checkpoint.') + assert os.path.isfile(config['base']['restore_file']), 'Error: no checkpoint file found!' + checkpoint = torch.load(config['base']['restore_file']) + start_epoch = checkpoint['epoch'] +# model.load_state_dict(checkpoint['state_dict']) + try: + model.load_state_dict(checkpoint['state_dict']) + except: + state = model.state_dict() + for key in state.keys(): + state[key] = checkpoint['state_dict'][key[7:]] + model.load_state_dict(state) + + optimizer.load_state_dict(checkpoint['optimizer']) + best_acc = checkpoint['best_acc'] + if not config['loss']['use_center']: + log_write = Logger(os.path.join(checkpoints, 'log.txt'), title=config['base']['algorithm'], resume=True) + if config['loss']['use_center']: + if os.path.exists(os.path.join(checkpoints, 'log_center.txt')): + log_write = Logger(os.path.join(checkpoints, 'log_center.txt'), title=config['base']['algorithm'], resume=True) + else: + log_write = Logger(os.path.join(checkpoints, 'log_center.txt'), title=config['base']['algorithm']) + title = list(loss_bin.keys()) + title.extend(['val_loss','test_acc','best_acc']) + log_write.set_names(title) + else: + print('Training from scratch.') + log_write = Logger(os.path.join(checkpoints, 'log.txt'), title=config['base']['algorithm']) + title = list(loss_bin.keys()) + title.extend(['val_loss','test_acc','best_acc']) + log_write.set_names(title) + center_flag = False + center_model = None + if config['base']['finetune']: + start_epoch = 0 + optimizer.param_groups[0]['lr'] = 0.0001 + center_flag = True + center_model = copy.deepcopy(model) + + + loss_write,loss_flag = ModelTrain(train_data_loader,test_data_loader,LabelConverter, model,center_model, criterion, optimizer, center_criterion,optimizer_center,center_flag,loss_bin, config,optimizer_decay,optimizer_decay_center,log_write,checkpoints) + + + + +if __name__ == "__main__": + + + parser = argparse.ArgumentParser(description='Hyperparams') + parser.add_argument('--config', help='config file path') + parser.add_argument('--log_str', help='log title') + args = parser.parse_args() + + + TrainValProgram(args) \ No newline at end of file diff --git a/tools/rec_train2.py b/tools/rec_train2.py new file mode 100644 index 0000000..ce6a9eb --- /dev/null +++ b/tools/rec_train2.py @@ -0,0 +1,332 @@ +# -*- coding:utf-8 _*- +""" +@author:fxw +@file: det_train.py +@time: 2020/08/07 +""" +import sys +sys.path.append('./') +import cv2 +import torch +import time +import os +import argparse +import random +import numpy as np +from tqdm import tqdm +from torch.autograd import Variable +np.seterr(divide='ignore', invalid='ignore') +import yaml +import torch.utils.data +from ptocr.utils.util_function import create_module, create_loss_bin, \ +set_seed,save_checkpoint,create_dir +from ptocr.utils.metrics import runningScore +from ptocr.utils.logger import Logger +from ptocr.utils.util_function import create_process_obj,merge_config,AverageMeter +from ptocr.dataloader.RecLoad.CRNNProcess import alignCollate +import copy +import re + +GLOBAL_WORKER_ID = None +GLOBAL_SEED = 2020 + + +torch.manual_seed(GLOBAL_SEED) +torch.cuda.manual_seed(GLOBAL_SEED) +torch.cuda.manual_seed_all(GLOBAL_SEED) +np.random.seed(GLOBAL_SEED) +random.seed(GLOBAL_SEED) + + + +def worker_init_fn(worker_id): + global GLOBAL_WORKER_ID + GLOBAL_WORKER_ID = worker_id + set_seed(GLOBAL_SEED + worker_id) + +# def backward_hook(self,grad_input, grad_output): +# for g in grad_input: +# g[g != g] = 0 # replace all nan/inf in gradients to zero + + +def ModelEval(test_data_loaders,LabelConverter,model,criterion,config): + loss_all= [] + acc_all = [] + for test_data_loader in test_data_loaders: + bar = tqdm(total=len(test_data_loader)) + loss_avg = [] + n_correct = 0 + for batch_idx, (imgs, labels) in enumerate(test_data_loader): + bar.update(1) + pre_batch = {} + gt_batch = {} + if torch.cuda.is_available(): + imgs = imgs.cuda() + with torch.no_grad(): + preds,feau = model(imgs) + + + _,_, labels_class = LabelConverter.test_encode(labels) + + pre_batch['pred'],gt_batch['gt'] = preds,labels_class + + + if config['loss']['use_ctc_weight']: + len_index = torch.softmax(preds,-1).max(2)[1].transpose(0,1)>0 + len_flag = torch.cat([labels_len.cuda().long().unsqueeze(0),len_index.sum(1).unsqueeze(0)],0) + ctc_loss_weight = len_flag.max(0)[0].float()/len_flag.min(0)[0].float() + ctc_loss_weight[ctc_loss_weight==torch.tensor(np.inf).cuda()]=2.0 + gt_batch['ctc_loss_weight'] = ctc_loss_weight + + cost,_ = criterion(pre_batch, gt_batch) + loss_avg.append(cost.item()) + _, preds = preds.max(2) + + sim_preds = LabelConverter.decode(preds.data) + for pred, target in zip(sim_preds, labels): + target = ''.join(re.findall('[0-9a-zA-Z]+',target)).lower() + if pred == target: + n_correct += 1 + val_acc = n_correct / float(len(test_data_loader) * config['valload']['batch_size']) + val_loss = np.mean(loss_avg) + loss_all.append(val_loss) + acc_all.append(val_acc) + print('Test acc:' ,acc_all) + return loss_all,acc_all + +def ModelTrain(train_data_loader,test_data_loader,LabelConverter,model,center_model, criterion, optimizer,center_criterion,optimizer_center,center_flag,loss_bin, config,optimizer_decay,optimizer_decay_center,log_write,checkpoints): + batch_time = AverageMeter() + end = time.time() + best_acc = 0 + dataloader_bin = [] + fid = open('test.txt','w+') + for i in range(len(train_data_loader)): + dataloader_bin.append(enumerate(train_data_loader[i])) + + for iters in range(config['base']['start_iters'],config['base']['max_iters']): + model.train() + optimizer_decay(config, optimizer, iters) + if config['loss']['use_center']: + optimizer_decay_center(config, optimizer_center, iters) + + imgs = [] + labels = [] + try: + for i in range(len(train_data_loader)): + index,(img,label) = next(dataloader_bin[i]) + imgs.append(img) + labels.extend(label) + imgs = torch.cat(imgs,0) + except: + for i in range(len(train_data_loader)): + dataloader_bin[i] = enumerate(train_data_loader[i]) + continue + + pre_batch = {} + gt_batch = {} + + _, _, labels = LabelConverter.train_encode(labels) + + if torch.cuda.is_available(): + imgs = imgs.cuda() + preds,feau = model(imgs) + + + pre_batch['pred'],gt_batch['gt'] = preds,labels.long().cuda() + ######### + + + + if config['loss']['use_ctc_weight']: +# print('use') + + len_index = torch.softmax(preds,-1).max(2)[1].transpose(0,1)>0 + len_flag = torch.cat([labels_len.cuda().long().unsqueeze(0),len_index.sum(1).unsqueeze(0)],0) + ctc_loss_weight = len_flag.max(0)[0].float()/len_flag.min(0)[0].float() + + ctc_loss_weight[ctc_loss_weight==torch.tensor(np.inf).cuda()]=2.0 + gt_batch['ctc_loss_weight'] = ctc_loss_weight + + loss,_ = criterion(pre_batch, gt_batch) + +# import pdb +# pdb.set_trace() + + metrics = {} + metrics['loss_fc'] = loss.item() + optimizer.zero_grad() + loss.backward() + optimizer.step() + + for key in loss_bin.keys(): + loss_bin[key].loss_add(metrics[key]) + batch_time.update(time.time() - end) + end = time.time() + if (iters % config['base']['show_step'] == 0): + log = '({}/{}) | ' \ + .format(iters,config['base']['max_iters']) + bin_keys = list(loss_bin.keys()) + + for i in range(len(bin_keys)): + log += bin_keys[i] + ':{:.4f}'.format(loss_bin[bin_keys[i]].loss_mean()) + ' | ' + log += 'lr:{:.8f}'.format(optimizer.param_groups[0]['lr'])+ ' | ' + log+='batch_time:{:.2f} s'.format(batch_time.avg)+ ' | ' + log+='total_time:{:.2f} min'.format(batch_time.avg * iters / 60.0)+ ' | ' + log+='ETA:{:.2f} min'.format(batch_time.avg*(config['base']['max_iters']-iters)/60.0) + print(log) + + + if(iters % config['base']['eval_iter']==0 and iters!=0): + + loss_write = [] + for key in list(loss_bin.keys()): + loss_write.append(loss_bin[key].loss_mean()) + + model.eval() + val_loss,acc_all = ModelEval(test_data_loader,LabelConverter, model,criterion ,config) + val_acc = np.mean(acc_all) + if (val_acc > best_acc): + save_checkpoint({ + 'iters': iters + 1, + 'state_dict': model.state_dict(), + 'lr': config['optimizer']['base_lr'], + 'optimizer': optimizer.state_dict(), + 'best_acc': val_acc + }, checkpoints, config['base']['algorithm'] + '_best' + '.pth.tar') + best_acc = val_acc + acc_all.append(val_acc) + acc_all = [str(x) for x in acc_all] + fid.write(str(','.join(acc_all))+'\n') + fid.flush() + loss_write.extend([0,0,0]) + log_write.append(loss_write) + for key in loss_bin.keys(): + loss_bin[key].loss_clear() + if iters %config['base']['eval_iter'] ==0: + save_checkpoint({ + 'iters': iters + 1, + 'state_dict': model.state_dict(), + 'lr': config['optimizer']['base_lr'], + 'optimizer': optimizer.state_dict(), + 'best_acc': 0 + },checkpoints,config['base']['algorithm']+'_'+str(iters)+'.pth.tar') + + + + + + + + + +def TrainValProgram(config): + + config = yaml.load(open(args.config, 'r', encoding='utf-8'),Loader=yaml.FullLoader) + config = merge_config(config,args) + + os.environ["CUDA_VISIBLE_DEVICES"] = config['base']['gpu_id'] + + create_dir(config['base']['checkpoints']) + checkpoints = os.path.join(config['base']['checkpoints'], + "ag_%s_bb_%s_he_%s_bs_%d_ep_%d_%s" % (config['base']['algorithm'], + config['backbone']['function'].split(',')[-1], + config['head']['function'].split(',')[-1], + config['trainload']['batch_size'], + config['base']['max_iters'], + args.log_str)) + create_dir(checkpoints) + + LabelConverter = create_module(config['label_transform']['function'])(config) + + model = create_module(config['architectures']['model_function'])(config) + criterion = create_module(config['architectures']['loss_function'])(config) + train_data_loader = create_module(config['trainload']['function'])(config) + test_data_loader = create_module(config['valload']['function'])(config) + optimizer = create_module(config['optimizer']['function'])(config, model) + optimizer_decay = create_module(config['optimizer_decay']['function']) + + if config['loss']['use_center']: +# center_criterion = create_module(config['loss']['center_function'])(config['base']['classes'],config['base']['classes']) + center_criterion = create_module(config['loss']['center_function'])(config['base']['classes'],config['base']['hiddenchannel']) + + optimizer_center = torch.optim.Adam(center_criterion.parameters(), lr= config['loss']['center_lr']) + optimizer_decay_center = create_module(config['optimizer_decay_center']['function']) + else: + center_criterion = None + optimizer_center=None + optimizer_decay_center = None + + + + + loss_bin = create_loss_bin(config['base']['algorithm'],use_center=config['loss']['use_center']) + + if torch.cuda.is_available(): + if (len(config['base']['gpu_id'].split(',')) > 1): + model = torch.nn.DataParallel(model).cuda() + else: + model = model.cuda() + criterion = criterion.cuda() + + + + print(model) + + ### model.head.lstm_2.embedding = nn.Linear(in_features=512, out_features=2000, bias=True) + + if config['base']['restore']: + print('Resuming from checkpoint.') + assert os.path.isfile(config['base']['restore_file']), 'Error: no checkpoint file found!' + checkpoint = torch.load(config['base']['restore_file']) + start_epoch = checkpoint['epoch'] +# model.load_state_dict(checkpoint['state_dict']) + try: + model.load_state_dict(checkpoint['state_dict']) + except: + state = model.state_dict() + for key in state.keys(): + state[key] = checkpoint['state_dict'][key[7:]] + model.load_state_dict(state) + + optimizer.load_state_dict(checkpoint['optimizer']) + best_acc = checkpoint['best_acc'] + if not config['loss']['use_center']: + log_write = Logger(os.path.join(checkpoints, 'log.txt'), title=config['base']['algorithm'], resume=True) + if config['loss']['use_center']: + if os.path.exists(os.path.join(checkpoints, 'log_center.txt')): + log_write = Logger(os.path.join(checkpoints, 'log_center.txt'), title=config['base']['algorithm'], resume=True) + else: + log_write = Logger(os.path.join(checkpoints, 'log_center.txt'), title=config['base']['algorithm']) + title = list(loss_bin.keys()) + title.extend(['val_loss','test_acc','best_acc']) + log_write.set_names(title) + else: + print('Training from scratch.') + log_write = Logger(os.path.join(checkpoints, 'log.txt'), title=config['base']['algorithm']) + title = list(loss_bin.keys()) + title.extend(['val_loss','test_acc','best_acc']) + log_write.set_names(title) + center_flag = False + center_model = None + if config['base']['finetune']: + start_epoch = 0 + optimizer.param_groups[0]['lr'] = 0.0001 + center_flag = True + center_model = copy.deepcopy(model) + + + loss_write,loss_flag = ModelTrain(train_data_loader,test_data_loader,LabelConverter, model,center_model, criterion, optimizer, center_criterion,optimizer_center,center_flag,loss_bin, config,optimizer_decay,optimizer_decay_center,log_write,checkpoints) + + + + +if __name__ == "__main__": + + + parser = argparse.ArgumentParser(description='Hyperparams') + parser.add_argument('--config', help='config file path') + parser.add_argument('--log_str', help='log title') + args = parser.parse_args() + + + TrainValProgram(args) \ No newline at end of file diff --git a/tools/rec_train3.py b/tools/rec_train3.py new file mode 100644 index 0000000..67d6c11 --- /dev/null +++ b/tools/rec_train3.py @@ -0,0 +1,376 @@ +# -*- coding:utf-8 _*- +""" +@author:fxw +@file: det_train.py +@time: 2020/08/07 +""" +import sys +sys.path.append('./') +import cv2 +import torch +import time +import os +import argparse +import random +import numpy as np +from tqdm import tqdm +from torch.autograd import Variable +np.seterr(divide='ignore', invalid='ignore') +import yaml +import torch.utils.data +from ptocr.utils.util_function import create_module, create_loss_bin, \ +set_seed,save_checkpoint,create_dir +from ptocr.utils.metrics import runningScore +from ptocr.utils.logger import Logger +from ptocr.utils.util_function import create_process_obj,merge_config,AverageMeter +from ptocr.dataloader.RecLoad.CRNNProcess import alignCollate +import copy + +GLOBAL_WORKER_ID = None +GLOBAL_SEED = 2020 + + +torch.manual_seed(GLOBAL_SEED) +torch.cuda.manual_seed(GLOBAL_SEED) +torch.cuda.manual_seed_all(GLOBAL_SEED) +np.random.seed(GLOBAL_SEED) +random.seed(GLOBAL_SEED) + + + +def worker_init_fn(worker_id): + global GLOBAL_WORKER_ID + GLOBAL_WORKER_ID = worker_id + set_seed(GLOBAL_SEED + worker_id) + +# def backward_hook(self,grad_input, grad_output): +# for g in grad_input: +# g[g != g] = 0 # replace all nan/inf in gradients to zero + + +def ModelTrain(train_data_loader,LabelConverter,model,center_model, criterion, optimizer,center_criterion,optimizer_center,center_flag,loss_bin, config, epoch): + batch_time = AverageMeter() + end = time.time() + for batch_idx, data in enumerate(train_data_loader): +# model.register_backward_hook(backward_hook) + if(data is None): + continue + imgs,labels = data + pre_batch = {} + gt_batch = {} + + if torch.cuda.is_available(): + imgs = imgs.cuda() + preds,feau = model(imgs) + + preds = preds.permute(1, 0, 2) + + labels,labels_len = LabelConverter.encode(labels,preds.size(0)) + preds_size = Variable(torch.IntTensor([preds.size(0)] * config['trainload']['batch_size'])) + pre_batch['preds'],pre_batch['preds_size'] = preds,preds_size + gt_batch['labels'],gt_batch['labels_len'] = labels,labels_len + ######### + if config['loss']['use_ctc_weight']: + + len_index = torch.softmax(preds,-1).max(2)[1].transpose(0,1)>0 + len_flag = torch.cat([labels_len.cuda().long().unsqueeze(0),len_index.sum(1).unsqueeze(0)],0) + ctc_loss_weight = len_flag.max(0)[0].float()/len_flag.min(0)[0].float() + + ctc_loss_weight[ctc_loss_weight==torch.tensor(np.inf).cuda()]=2.0 + gt_batch['ctc_loss_weight'] = ctc_loss_weight + + ctc_loss = criterion(pre_batch, gt_batch).cuda() + metrics = {} + metrics['loss_total'] = 0.0 + metrics['loss_center'] = 0.0 + if center_criterion is not None and center_flag is True: + center_model.eval() + ##### + feautures = preds.clone() + with torch.no_grad(): + center_preds,center_feau = center_model(imgs) + center_preds = center_preds.permute(1, 0, 2) + + center_preds = torch.softmax(center_preds,-1) + confs, center_preds = center_preds.max(2) + center_preds = center_preds.squeeze(1).transpose(1, 0).contiguous() + confs = confs.transpose(1, 0).contiguous() + +# confs = [] +# for i in range(center_preds.shape[0]): +# conf = [] +# for j in range(len(center_preds[i])): +# conf.append(probs[i,j,center_preds[i][j]]) +# confs.append(conf) +# confs = torch.Tensor(confs).cuda() + + b,t = center_preds.shape + +# feautures = feautures.transpose(1, 0).contiguous() + feautures = center_feau[0].transpose(1, 0).contiguous() + +# import pdb +# pdb.set_trace() + ### 去重复 + repeat_index = (center_preds[:,:-1] == center_preds[:,1:]) + center_preds[:,:-1][repeat_index] = 0 + + confs = confs.view(-1) + center_preds = center_preds.view(-1) + feautures = feautures.view(b*t,-1) + + + index = (center_preds>0) & (confs>config['loss']['label_score']) + center_preds = center_preds[index] + feautures = feautures[index] + + center_loss = center_criterion(feautures,center_preds)*config['loss']['weight_center'] + + loss = ctc_loss + center_loss + + + metrics['loss_total'] = loss.item() + metrics['loss_ctc'] = ctc_loss.item() + metrics['loss_center'] = center_loss.item() + + ##### + optimizer_center.zero_grad() + optimizer.zero_grad() + + loss.backward() + optimizer.step() + + for param in center_criterion.parameters(): + param.grad.data *= (1. / config['loss']['weight_center']) + optimizer_center.step() + else: + loss = ctc_loss + metrics['loss_ctc'] = ctc_loss.item() + optimizer.zero_grad() + loss.backward() + optimizer.step() + + for key in loss_bin.keys(): + loss_bin[key].loss_add(metrics[key]) + batch_time.update(time.time() - end) + end = time.time() + if (batch_idx % config['base']['show_step'] == 0): + log = '({}/{}/{}/{}) | ' \ + .format(epoch, config['base']['n_epoch'], batch_idx, len(train_data_loader)) + bin_keys = list(loss_bin.keys()) + + for i in range(len(bin_keys)): + log += bin_keys[i] + ':{:.4f}'.format(loss_bin[bin_keys[i]].loss_mean()) + ' | ' + log += 'lr:{:.8f}'.format(optimizer.param_groups[0]['lr'])+ ' | ' + log+='batch_time:{:.2f} s'.format(batch_time.avg)+ ' | ' + log+='total_time:{:.2f} min'.format(batch_time.avg * batch_idx / 60.0)+ ' | ' + log+='ETA:{:.2f} min'.format(batch_time.avg*(len(train_data_loader)-batch_idx)/60.0) + print(log) + loss_write = [] + for key in list(loss_bin.keys()): + loss_write.append(loss_bin[key].loss_mean()) + return loss_write,loss_bin['loss_ctc'].loss_mean() + + +def ModelEval(test_data_loader,LabelConverter,model,criterion,config): + bar = tqdm(total=len(test_data_loader)) + loss_avg = [] + n_correct = 0 + for batch_idx, (imgs, labels) in enumerate(test_data_loader): + bar.update(1) + pre_batch = {} + gt_batch = {} + if torch.cuda.is_available(): + imgs = imgs.cuda() + with torch.no_grad(): + preds,feau = model(imgs) + preds = preds.permute(1, 0, 2) + + labels_class, labels_len = LabelConverter.encode(labels,preds.size(0)) + preds_size = Variable(torch.IntTensor([preds.size(0)] * config['testload']['batch_size'])) + pre_batch['preds'],pre_batch['preds_size'] = preds,preds_size + gt_batch['labels'],gt_batch['labels_len'] = labels_class,labels_len + + if config['loss']['use_ctc_weight']: + len_index = torch.softmax(preds,-1).max(2)[1].transpose(0,1)>0 + len_flag = torch.cat([labels_len.cuda().long().unsqueeze(0),len_index.sum(1).unsqueeze(0)],0) + ctc_loss_weight = len_flag.max(0)[0].float()/len_flag.min(0)[0].float() + ctc_loss_weight[ctc_loss_weight==torch.tensor(np.inf).cuda()]=2.0 + gt_batch['ctc_loss_weight'] = ctc_loss_weight + + cost = criterion(pre_batch, gt_batch) + loss_avg.append(cost.item()) + _, preds = preds.max(2) + preds = preds.squeeze(1) + preds = preds.transpose(1, 0).contiguous().view(-1) + sim_preds = LabelConverter.decode(preds.data, preds_size.data, raw=False) + for pred, target in zip(sim_preds, labels): + if pred == target: + n_correct += 1 + raw_preds = LabelConverter.decode(preds.data, preds_size.data, raw=True)[:config['base']['show_num']] + for raw_pred, pred, gt in zip(raw_preds, sim_preds, labels): + print('%-20s => %-20s, gt: %-20s' % (raw_pred, pred, gt)) + + val_acc = n_correct / float(len(test_data_loader) * config['testload']['batch_size']) + val_loss = np.mean(loss_avg) + print('Test loss: %f, accuray: %f' % (val_loss, val_acc)) + return val_acc,val_loss + +def TrainValProgram(config): + + config = yaml.load(open(args.config, 'r', encoding='utf-8'),Loader=yaml.FullLoader) + config = merge_config(config,args) + + os.environ["CUDA_VISIBLE_DEVICES"] = config['base']['gpu_id'] + + create_dir(config['base']['checkpoints']) + checkpoints = os.path.join(config['base']['checkpoints'], + "ag_%s_bb_%s_he_%s_bs_%d_ep_%d_%s" % (config['base']['algorithm'], + config['backbone']['function'].split(',')[-1], + config['head']['function'].split(',')[-1], + config['trainload']['batch_size'], + config['base']['n_epoch'], + args.log_str)) + create_dir(checkpoints) + + LabelConverter = create_module(config['label_transform']['function'])(config) + config['base']['classes'] = len(LabelConverter.alphabet) + model = create_module(config['architectures']['model_function'])(config) + criterion = create_module(config['architectures']['loss_function'])(config) + train_dataset = create_module(config['trainload']['function'])(config) + test_dataset = create_module(config['testload']['function'])(config) + optimizer = create_module(config['optimizer']['function'])(config, model) + optimizer_decay = create_module(config['optimizer_decay']['function']) + + if config['loss']['use_center']: +# center_criterion = create_module(config['loss']['center_function'])(config['base']['classes'],config['base']['classes']) + center_criterion = create_module(config['loss']['center_function'])(config['base']['classes'],config['base']['hiddenchannel']) + + optimizer_center = torch.optim.Adam(center_criterion.parameters(), lr= config['loss']['center_lr']) + optimizer_decay_center = create_module(config['optimizer_decay_center']['function']) + else: + center_criterion = None + optimizer_center=None + + + train_data_loader = torch.utils.data.DataLoader( + train_dataset, + batch_size=config['trainload']['batch_size'], + shuffle=True, + num_workers=config['trainload']['num_workers'], + worker_init_fn = worker_init_fn, + collate_fn = alignCollate(), + drop_last=True, + pin_memory=True) + + test_data_loader = torch.utils.data.DataLoader( + test_dataset, + batch_size=config['testload']['batch_size'], + shuffle=False, + num_workers=config['testload']['num_workers'], + collate_fn = alignCollate(), + drop_last=True, + pin_memory=True) + + loss_bin = create_loss_bin(config['base']['algorithm'],use_center=config['loss']['use_center']) + + if torch.cuda.is_available(): + if (len(config['base']['gpu_id'].split(',')) > 1): + model = torch.nn.DataParallel(model).cuda() + else: + model = model.cuda() + criterion = criterion.cuda() + + start_epoch = 0 + val_acc = 0 + val_loss = 0 + best_acc = 0 + + print(model) + + if config['base']['restore']: + print('Resuming from checkpoint.') + assert os.path.isfile(config['base']['restore_file']), 'Error: no checkpoint file found!' + checkpoint = torch.load(config['base']['restore_file']) + start_epoch = checkpoint['epoch'] +# model.load_state_dict(checkpoint['state_dict']) + try: + model.load_state_dict(checkpoint['state_dict']) + except: + state = model.state_dict() + for key in state.keys(): + state[key] = checkpoint['state_dict'][key[7:]] + model.load_state_dict(state) + + optimizer.load_state_dict(checkpoint['optimizer']) + best_acc = checkpoint['best_acc'] + if not config['loss']['use_center']: + log_write = Logger(os.path.join(checkpoints, 'log.txt'), title=config['base']['algorithm'], resume=True) + if config['loss']['use_center']: + if os.path.exists(os.path.join(checkpoints, 'log_center.txt')): + log_write = Logger(os.path.join(checkpoints, 'log_center.txt'), title=config['base']['algorithm'], resume=True) + else: + log_write = Logger(os.path.join(checkpoints, 'log_center.txt'), title=config['base']['algorithm']) + title = list(loss_bin.keys()) + title.extend(['val_loss','test_acc','best_acc']) + log_write.set_names(title) + else: + print('Training from scratch.') + log_write = Logger(os.path.join(checkpoints, 'log.txt'), title=config['base']['algorithm']) + title = list(loss_bin.keys()) + title.extend(['val_loss','test_acc','best_acc']) + log_write.set_names(title) + center_flag = False + center_model = None + if config['base']['finetune']: + start_epoch = 0 + optimizer.param_groups[0]['lr'] = 0.0001 + center_flag = True + center_model = copy.deepcopy(model) + for epoch in range(start_epoch,config['base']['n_epoch']): + model.train() + optimizer_decay(config, optimizer, epoch) + if config['loss']['use_center']: + optimizer_decay_center(config, optimizer_center, epoch) + loss_write,loss_flag = ModelTrain(train_data_loader,LabelConverter, model,center_model, criterion, optimizer, center_criterion,optimizer_center,center_flag,loss_bin, config, epoch) +# if loss_flag < config['loss']['min_score']: +# center_flag = True + if(epoch >= config['base']['start_val']): + model.eval() + val_acc,val_loss = ModelEval(test_data_loader,LabelConverter, model,criterion ,config) + print('val_acc:',val_acc,'val_loss',val_loss) + if (val_acc > best_acc): + save_checkpoint({ + 'epoch': epoch + 1, + 'state_dict': model.state_dict(), + 'lr': config['optimizer']['base_lr'], + 'optimizer': optimizer.state_dict(), + 'best_acc': val_acc + }, checkpoints, config['base']['algorithm'] + '_best' + '.pth.tar') + best_acc = val_acc + + + loss_write.extend([val_loss,val_acc,best_acc]) + log_write.append(loss_write) + for key in loss_bin.keys(): + loss_bin[key].loss_clear() + if epoch%config['base']['save_epoch'] ==0: + save_checkpoint({ + 'epoch': epoch + 1, + 'state_dict': model.state_dict(), + 'lr': config['optimizer']['base_lr'], + 'optimizer': optimizer.state_dict(), + 'best_acc': 0 + },checkpoints,config['base']['algorithm']+'_'+str(epoch)+'.pth.tar') + + +if __name__ == "__main__": + + + parser = argparse.ArgumentParser(description='Hyperparams') + parser.add_argument('--config', help='config file path') + parser.add_argument('--log_str', help='log title') + args = parser.parse_args() + + + TrainValProgram(args) \ No newline at end of file From 222df1fd2ba0a0185e1a5589873b96c22018842b Mon Sep 17 00:00:00 2001 From: BADBADBADBOY <15671628547@163.COM> Date: Sun, 2 May 2021 16:27:49 +0800 Subject: [PATCH 3/4] change yaml --- config/det_DB_mobilev3.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/det_DB_mobilev3.yaml b/config/det_DB_mobilev3.yaml index 043d391..b7bfe32 100644 --- a/config/det_DB_mobilev3.yaml +++ b/config/det_DB_mobilev3.yaml @@ -20,8 +20,8 @@ backbone: function: ptocr.model.backbone.det_mobilev3,mobilenet_v3_small head: -# function: ptocr.model.head.det_DBHead,DB_Head - function: ptocr.model.head.det_FPEM_FFM_Head,FPEM_FFM_Head + function: ptocr.model.head.det_DBHead,DB_Head +# function: ptocr.model.head.det_FPEM_FFM_Head,FPEM_FFM_Head # function: ptocr.model.head.det_FPNHead,FPN_Head segout: From efae5454651e96794dd7a460bd305dc9ccd95359 Mon Sep 17 00:00:00 2001 From: BADBADBADBOY <15671628547@163.COM> Date: Wed, 19 May 2021 20:11:42 +0800 Subject: [PATCH 4/4] add dbnet mul detect --- README.md | 13 ++ config/det_DB_mobilev3.yaml | 4 +- config/det_DB_resnet50_mul.yaml | 89 ++++++++ config/rec_CRNN_resnet34_english_lmdb.yaml | 8 +- doc/show/1.jpg | Bin 0 -> 94850 bytes doc/show/2.jpg | Bin 0 -> 273265 bytes doc/show/3.jpg | Bin 0 -> 39258 bytes ptocr/__pycache__/__init__.cpython-36.pyc | Bin 0 -> 167 bytes ptocr/__pycache__/optimizer.cpython-36.pyc | Bin 0 -> 2618 bytes ptocr/dataloader/DetLoad/DBProcess.py | 81 +++++++ ptocr/dataloader/DetLoad/MakeSegMap.py | 55 +++++ .../__pycache__/DBProcess.cpython-36.pyc | Bin 4664 -> 7448 bytes .../__pycache__/MakeBorderMap.cpython-36.pyc | Bin 4034 -> 4034 bytes .../__pycache__/MakeSegMap.cpython-36.pyc | Bin 5294 -> 6825 bytes .../__pycache__/__init__.cpython-36.pyc | Bin 186 -> 186 bytes .../__pycache__/transform_img.cpython-36.pyc | Bin 10578 -> 11542 bytes ptocr/dataloader/DetLoad/transform_img.py | 36 +++ .../__pycache__/CommonFunction.cpython-36.pyc | Bin 3102 -> 3102 bytes .../__pycache__/det_model.cpython-36.pyc | Bin 3302 -> 3624 bytes ptocr/model/architectures/det_model.py | 27 ++- .../__pycache__/det_mobilev3.cpython-36.pyc | Bin 7813 -> 7813 bytes .../__pycache__/det_resnet.cpython-36.pyc | Bin 9459 -> 9459 bytes .../__pycache__/det_DBHead.cpython-36.pyc | Bin 1770 -> 1770 bytes .../det_FPEM_FFM_Head.cpython-36.pyc | Bin 3585 -> 3585 bytes .../__pycache__/basical_loss.cpython-36.pyc | Bin 10364 -> 11086 bytes .../loss/__pycache__/db_loss.cpython-36.pyc | Bin 1371 -> 2445 bytes ptocr/model/loss/basical_loss.py | 18 ++ ptocr/model/loss/db_loss.py | 33 ++- .../__pycache__/__init__.cpython-36.pyc | Bin 180 -> 180 bytes .../__pycache__/det_DB_segout.cpython-36.pyc | Bin 2929 -> 5573 bytes ptocr/model/segout/det_DB_segout.py | 101 +++++++++ ptocr/postprocess/DBpostprocess.py | 213 +++++++++++++++++- .../__pycache__/DBpostprocess.cpython-36.pyc | Bin 5942 -> 11418 bytes .../__pycache__/__init__.cpython-36.pyc | Bin 179 -> 179 bytes .../__pycache__/__init__.cpython-36.pyc | Bin 692 -> 692 bytes .../__pycache__/cal_iou_acc.cpython-36.pyc | Bin 2126 -> 2126 bytes .../gen_teacher_model.cpython-36.pyc | Bin 2191 -> 2191 bytes .../__pycache__/prune_script.cpython-36.pyc | Bin 10396 -> 10396 bytes .../__pycache__/util_function.cpython-36.pyc | Bin 6559 -> 7740 bytes ptocr/utils/util_function.py | 16 +- script/__pycache__/__init__.cpython-36.pyc | Bin 0 -> 168 bytes .../onnx_to_tensorrt.cpython-36.pyc | Bin 0 -> 6367 bytes script/warp_polar.py | 67 ++++++ tools/__pycache__/MarginLoss.cpython-36.pyc | Bin 0 -> 2086 bytes tools/__pycache__/__init__.cpython-36.pyc | Bin 0 -> 167 bytes .../__pycache__/__init__.cpython-36.pyc | Bin 179 -> 179 bytes .../rrc_evaluation_funcs.cpython-36.pyc | Bin 13315 -> 13315 bytes .../__pycache__/script.cpython-36.pyc | Bin 7714 -> 7714 bytes tools/det_infer.py | 47 +++- tools/det_train.py | 4 +- tools/{rec_infer1.py => rec_infer_bk1.py} | 0 tools/{rec_train1.py => rec_train_bk1.py} | 0 tools/{rec_train2.py => rec_train_bk2.py} | 0 tools/{rec_train3.py => rec_train_bk3.py} | 0 54 files changed, 788 insertions(+), 24 deletions(-) create mode 100644 config/det_DB_resnet50_mul.yaml create mode 100644 doc/show/1.jpg create mode 100644 doc/show/2.jpg create mode 100644 doc/show/3.jpg create mode 100644 ptocr/__pycache__/__init__.cpython-36.pyc create mode 100644 ptocr/__pycache__/optimizer.cpython-36.pyc create mode 100644 script/__pycache__/__init__.cpython-36.pyc create mode 100644 script/__pycache__/onnx_to_tensorrt.cpython-36.pyc create mode 100644 script/warp_polar.py create mode 100644 tools/__pycache__/MarginLoss.cpython-36.pyc create mode 100644 tools/__pycache__/__init__.cpython-36.pyc rename tools/{rec_infer1.py => rec_infer_bk1.py} (100%) rename tools/{rec_train1.py => rec_train_bk1.py} (100%) rename tools/{rec_train2.py => rec_train_bk2.py} (100%) rename tools/{rec_train3.py => rec_train_bk3.py} (100%) diff --git a/README.md b/README.md index 2e2b73d..21f6bc1 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,7 @@ *** 最近跟新: +- 2021.05.19 更新基于DBnet的多语种文本检测。 - 2021.05.01 更新CRNN 训练,解决了多gpu训练问题,更换成lmdb训练,需要将图片先转成lmdb(在script文件夹中有多进程将图片转成lmdb的代码),做了一些训练优化,模型结构更改(训练时使用名字中带lmdb的yaml文件),实际训练效果如下表。 - 2021.03.26 更新CRNN 训练效果,代码整理后上传 - 2021.03.06 更新CRNN backbone resnet 和 mobilev3 以及配置文件 @@ -107,6 +108,18 @@ +*** + +### Dbnet多语种文本检测效果 + +#### 生成数据集: + + +#### 公开数据集: + + + + *** ### 有问题及交流加微信 diff --git a/config/det_DB_mobilev3.yaml b/config/det_DB_mobilev3.yaml index b7bfe32..bee1d35 100644 --- a/config/det_DB_mobilev3.yaml +++ b/config/det_DB_mobilev3.yaml @@ -14,7 +14,7 @@ base: checkpoints: ./checkpoint save_epoch: 100 restore: False - restore_file : ./checkpoint/ag_DB_bb_mobilenet_v3_small_he_DB_Head_bs_16_ep_1200_mobile_slim_all/DB_best.pth.tar + restore_file : ./checkpoint/DB_best.pth.tar backbone: function: ptocr.model.backbone.det_mobilev3,mobilenet_v3_small @@ -82,6 +82,6 @@ postprocess: min_size: 3 infer: - model_path: './checkpoint/ag_DB_bb_mobilenet_v3_small_he_DB_Head_bs_16_ep_1200/DB_best.pth.tar' + model_path: './checkpoint/DB_best.pth.tar' path: '/src/notebooks/detect_text/icdar2015/ch4_test_images' save_path: './result' diff --git a/config/det_DB_resnet50_mul.yaml b/config/det_DB_resnet50_mul.yaml new file mode 100644 index 0000000..e37dcc4 --- /dev/null +++ b/config/det_DB_resnet50_mul.yaml @@ -0,0 +1,89 @@ +base: + gpu_id: '1' # 设置训练的gpu id,多卡训练设置为 '0,1,2' + algorithm: DB # 算法名称 + pretrained: True # 是否加载预训练 + in_channels: [256, 512, 1024, 2048] # + inner_channels: 256 # + k: 50 + n_class: 3 + adaptive: True + crop_shape: [640,640] #训练时crop图片的大小 + shrink_ratio: 0.4 # kernel向内收缩比率 + n_epoch: 1200 # 训练的epoch + start_val: 400 #开始验证的epoch,如果不想验证直接设置数值大于n_epoch + show_step: 20 #设置迭代多少次输出一次loss + checkpoints: ./checkpoint #保存模型地址 + save_epoch: 100 #设置每多少个epoch保存一次模型 + restore: False #是否恢复训练 + restore_file : ./DB.pth.tar #恢复训练所需加载模型的地址 + +backbone: + function: ptocr.model.backbone.det_resnet,resnet50 + +head: + function: ptocr.model.head.det_DBHead,DB_Head +# function: ptocr.model.head.det_FPEM_FFM_Head,FPEM_FFM_Head +# function: ptocr.model.head.det_FPNHead,FPN_Head + +segout: + function: ptocr.model.segout.det_DB_segout,SegDetectorMul + +architectures: + model_function: ptocr.model.architectures.det_model,DetModel + loss_function: ptocr.model.architectures.det_model,DetLoss + +loss: + function: ptocr.model.loss.db_loss,DBLossMul + l1_scale: 10 + bce_scale: 1 + class_scale: 1 + +#optimizer: +# function: ptocr.optimizer,AdamDecay +# base_lr: 0.002 +# beta1: 0.9 +# beta2: 0.999 + +optimizer: + function: ptocr.optimizer,SGDDecay + base_lr: 0.002 + momentum: 0.99 + weight_decay: 0.0005 + +optimizer_decay: + function: ptocr.optimizer,adjust_learning_rate_poly + factor: 0.9 + +#optimizer_decay: +# function: ptocr.optimizer,adjust_learning_rate +# schedule: [1,2] +# gama: 0.1 + +trainload: + function: ptocr.dataloader.DetLoad.DBProcess,DBProcessTrainMul + train_file: /src/notebooks/fangxuwei_96/TextGenerator-master/output/train/train_list.txt + num_workers: 10 + batch_size: 8 + +testload: + function: ptocr.dataloader.DetLoad.DBProcess,DBProcessTest + test_file: /src/notebooks/detect_text/icdar2015/test_list.txt + test_gt_path: /src/notebooks/detect_text/icdar2015/ch4_test_gts/ + test_size: 736 + stride: 32 + num_workers: 5 + batch_size: 4 + +postprocess: + function: ptocr.postprocess.DBpostprocess,DBPostProcessMul + is_poly: False #测试时,检测弯曲文本设置成 True,否则就是输出矩形框 + thresh: 0.5 + box_thresh: 0.6 + max_candidates: 1000 + unclip_ratio: 2 + min_size: 3 + +infer: + model_path: './checkpoint/ag_DB_bb_resnet50_he_DB_Head_bs_8_ep_601_train_mul/DB_best.pth.tar' + path: '/src/notebooks/fangxuwei_96/TextGenerator-master/output/img/' + save_path: './result' diff --git a/config/rec_CRNN_resnet34_english_lmdb.yaml b/config/rec_CRNN_resnet34_english_lmdb.yaml index 54a56d8..f5da317 100644 --- a/config/rec_CRNN_resnet34_english_lmdb.yaml +++ b/config/rec_CRNN_resnet34_english_lmdb.yaml @@ -1,5 +1,5 @@ base: - gpu_id: '1' + gpu_id: '0' algorithm: CRNN pretrained: False inchannel: 512 @@ -15,9 +15,9 @@ base: checkpoints: ./checkpoint save_epoch: 1 show_num: 10 - restore: False + restore: True finetune: False - restore_file : ./checkpoint/ + restore_file : ./checkpoint/ag_CRNN_bb_resnet34_he_CRNN_Head_bs_512_ep_8_no_attention_no_weight/CRNN_best.pth.tar backbone: function: ptocr.model.backbone.reg_resnet_bd,resnet34 @@ -54,7 +54,7 @@ trainload: train_file: '/src/notebooks/MyworkData/EnglishCrnnData/train_lmdb/SynthText/' key_file: /src/notebooks/MyworkData/EnglishCrnnData/key_new.txt bg_path: ./bg_img/ - num_workers: 10 + num_workers: 16 batch_size: 512 valload: diff --git a/doc/show/1.jpg b/doc/show/1.jpg new file mode 100644 index 0000000000000000000000000000000000000000..b8a957bf151dda11715acaa7d6014d8c75c11c8a GIT binary patch literal 94850 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

6%b?q-3uUkL!j>hYHLo}kCfEKnTGC_;k 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 z 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�(p|YiYo`Lr*Re>J3C7{v`$0LEUL9rwBBJk7le9 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 z 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 b?wDzP@dNwU@G}`4v*2g6O85!&?~nfjDaX-i literal 0 HcmV?d00001 diff --git a/doc/show/3.jpg b/doc/show/3.jpg new file mode 100644 index 0000000000000000000000000000000000000000..3e8090068a971e4e62e5f15819ca17cc155a9618 GIT binary patch literal 39258 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 H=FihF?oJFO literal 0 HcmV?d00001 diff --git a/ptocr/__pycache__/__init__.cpython-36.pyc b/ptocr/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..04928da39ceab263b25f2c4fb87f998e4910d05c GIT binary patch literal 167 zcmXr!<>fLBZA%DbU|@I*#Bjg}WH|tFF$a)HVTfW#VGL%_WU4ada!4#K$;dCVN~}zQ`1q9! mMNB|5!Nf0NJ^g}`{Ny5iBqMHd*yQG?l;)(`v4I=_#0&tvP$**n literal 0 HcmV?d00001 diff --git a/ptocr/__pycache__/optimizer.cpython-36.pyc b/ptocr/__pycache__/optimizer.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c85c005a014dda672a015b4c20c240839a8b9c03 GIT binary patch literal 2618 zcmcgu-HRJl6u);ql1Z9&w{6_rbX9_i8m!%~A|)<%t3^R@k#0d)D&utSY?5t~iFYRS zg9#M2P!(zss}IEo3q=tW_rV8!@ITNeAB6Emu-WZ`e}PZ_&YkQuyRO(LZ-}kpb65R#@PM zm=OC!;Tm5yMM)eGBj{zss5pqVtT-eNgIl5~9s$pZF)`~v!zo1LDWo|--}H8Ydnf?90R)rg^yx*2@0;0xwK zLbl3RwTMShyu^f7VuPNZ_KXr+(FU_7WHL95o)(cx*~K`Q-XCYrJAr36Wo(}JLT4sn zvvJ{~*QnJ)TX?Q>xuVC~wnQLF6U>;QFWq`VVS7$MZv`8O=Viz=7jx=x`9I8e}5i0het}rm-6HrxX zO5(UPT!S0!6jF@!8e>-7ov!d~xrG z&o@83zFVZxPDIvk?kRHCcDu4#?*I64|NR@Ax34LqY`phr1CMU_sIzvfTHd;StAF=) zfBmET>uVdIdXxt&)5tOCVch z>KZ<@-%lbH7%eBP$GYnWvDRr+GI1uso$y9GHWh+chk~)GE>B{W62;J)GVrF(Jb}67 zJ>`nLz>jkIEM{Da3~CfwsV7fhfY5r78z~PWGYaHH^i(!1u{EQoM|#9pxyWM8nnxjk zw{pYz!Jyb9NBKPYc|Z`sVJVomVlr!et=JK`8;|lYzpKwjhw+?2kgWER1&reL1F+Z z88E_{c~0x;J!4>m3}&KN;^E^8W7BRNNLDcJqq_H7R+c z{0M?u&iy}Nm&bO+v+bf7dQwi|{s+({)!BbTE-60cERkKlo!krS;D%F>-wF9R-J!{G zI*7^x?xXx&*%xPRTllVRCl!nS4X9G8ij8`2Dif%8p`t`tPttiq8Y$h93O(QQ$+a)h Yn+m8b8r;&XjFq*lypbQVO#Cu`18%M~X#fBK literal 0 HcmV?d00001 diff --git a/ptocr/dataloader/DetLoad/DBProcess.py b/ptocr/dataloader/DetLoad/DBProcess.py index 7fbe42b..749baed 100644 --- a/ptocr/dataloader/DetLoad/DBProcess.py +++ b/ptocr/dataloader/DetLoad/DBProcess.py @@ -94,7 +94,88 @@ def __getitem__(self, index): return img,gt,gt_mask,thresh_map,thresh_mask +class DBProcessTrainMul(data.Dataset): + def __init__(self,config): + super(DBProcessTrainMul,self).__init__() + self.crop_shape = config['base']['crop_shape'] + self.MBM = MakeBorderMap() + self.TSM = Random_Augment(self.crop_shape) + self.MSM = MakeSegMap(shrink_ratio = config['base']['shrink_ratio']) + img_list, label_list = self.get_base_information(config['trainload']['train_file']) + self.img_list = img_list + self.label_list = label_list + + def order_points(self, pts): + rect = np.zeros((4, 2), dtype="float32") + s = pts.sum(axis=1) + rect[0] = pts[np.argmin(s)] + rect[2] = pts[np.argmax(s)] + diff = np.diff(pts, axis=1) + rect[1] = pts[np.argmin(diff)] + rect[3] = pts[np.argmax(diff)] + return rect + + def get_bboxes(self,gt_path): + polys = [] + tags = [] + classes = [] + with open(gt_path, 'r', encoding='utf-8') as fid: + lines = fid.readlines() + for line in lines: + line = line.replace('\ufeff', '').replace('\xef\xbb\xbf', '') + gt = line.split(',') + if "#" in gt[-1]: + tags.append(True) + classes.append(-2) + else: + tags.append(False) + classes.append(int(gt[-1])) + # box = [int(gt[i]) for i in range(len(gt)//2*2)] + box = [int(gt[i]) for i in range(8)] + polys.append(box) + return np.array(polys), tags, classes + + def get_base_information(self,train_txt_file): + label_list = [] + img_list = [] + with open(train_txt_file,'r',encoding='utf-8') as fid: + lines = fid.readlines() + for line in lines: + line = line.strip('\n').split('\t') + img_list.append(line[0]) + result = self.get_bboxes(line[1]) + label_list.append(result) + return img_list,label_list + + def __len__(self): + return len(self.img_list) + + def __getitem__(self, index): + + img = Image.open(self.img_list[index]).convert('RGB') + img = np.array(img)[:,:,::-1] + + polys, dontcare, classes = self.label_list[index] + + img, polys = self.TSM.random_scale(img, polys, self.crop_shape[0]) + img, polys = self.TSM.random_rotate(img, polys) + img, polys = self.TSM.random_flip(img, polys) + img, polys, classes,dontcare = self.TSM.random_crop_db_mul(img, polys,classes, dontcare) + img, gt, classes,gt_mask = self.MSM.process_mul(img, polys, classes,dontcare) + img, thresh_map, thresh_mask = self.MBM.process(img, polys, dontcare) + + img = Image.fromarray(img).convert('RGB') + img = transforms.ColorJitter(brightness=32.0 / 255, saturation=0.5)(img) + img = self.TSM.normalize_img(img) + + + gt = torch.from_numpy(gt).float() + gt_classes = torch.from_numpy(classes).long() + gt_mask = torch.from_numpy(gt_mask).float() + thresh_map = torch.from_numpy(thresh_map).float() + thresh_mask = torch.from_numpy(thresh_mask).float() + return img,gt,gt_classes,gt_mask,thresh_map,thresh_mask class DBProcessTest(data.Dataset): def __init__(self,config): diff --git a/ptocr/dataloader/DetLoad/MakeSegMap.py b/ptocr/dataloader/DetLoad/MakeSegMap.py index 3a72c7d..02a8bff 100644 --- a/ptocr/dataloader/DetLoad/MakeSegMap.py +++ b/ptocr/dataloader/DetLoad/MakeSegMap.py @@ -27,6 +27,7 @@ def __init__(self, algorithm='DB',min_text_size = 8,shrink_ratio = 0.4,is_traini self.shrink_ratio = shrink_ratio self.is_training = is_training self.algorithm = algorithm + def process(self, img,polys,dontcare): ''' requied keys: @@ -77,7 +78,61 @@ def process(self, img,polys,dontcare): if self.algorithm == 'PAN': return img,gt_text,gt_text_key,gt,gt_kernel_key,mask return img,gt,mask + + def process_mul(self, img,polys,classes,dontcare): + ''' + requied keys: + image, polygons, ignore_tags, filename + adding keys: + mask + ''' + h, w = img.shape[:2] + if self.is_training: + polys, dontcare = self.validate_polygons( + polys, dontcare, h, w) + gt = np.zeros((h, w), dtype=np.float32) + gt_class = np.zeros((h, w), dtype=np.float32) + mask = np.ones((h, w), dtype=np.float32) + + if self.algorithm =='PAN': + gt_text = np.zeros((h, w), dtype=np.float32) + gt_text_key = np.zeros((h, w), dtype=np.float32) + gt_kernel_key = np.zeros((h, w), dtype=np.float32) + for i in range(len(polys)): + polygon = polys[i] + height = max(polygon[:, 1]) - min(polygon[:, 1]) + width = max(polygon[:, 0]) - min(polygon[:, 0]) + if dontcare[i] or min(height, width) < self.min_text_size: + cv2.fillPoly(mask, polygon.astype( + np.int32)[np.newaxis, :, :], 0) + dontcare[i] = True + else: + if self.algorithm == 'PAN': + cv2.fillPoly(gt_text, [polygon.astype(np.int32)], 1) + cv2.fillPoly(gt_text_key, [polygon.astype(np.int32)], i + 1) + polygon_shape = Polygon(polygon) + distance = polygon_shape.area * \ + (1 - np.power(self.shrink_ratio, 2)) / polygon_shape.length + subject = [tuple(l) for l in polys[i]] + padding = pyclipper.PyclipperOffset() + padding.AddPath(subject, pyclipper.JT_ROUND, + pyclipper.ET_CLOSEDPOLYGON) + shrinked = padding.Execute(-distance) + if shrinked == []: + cv2.fillPoly(mask, polygon.astype( + np.int32)[np.newaxis, :, :], 0) + dontcare[i] = True + continue + shrinked = np.array(shrinked[0]).reshape(-1, 2) + cv2.fillPoly(gt, [shrinked.astype(np.int32)], 1) + cv2.fillPoly(gt_class, polygon.astype(np.int32)[np.newaxis, :, :], 1+classes[i]) + if self.algorithm == 'PAN': + cv2.fillPoly(gt_kernel_key, [shrinked.astype(np.int32)], i + 1) + if self.algorithm == 'PAN': + return img,gt_text,gt_text_key,gt,gt_kernel_key,mask + return img,gt,gt_class,mask + def validate_polygons(self, polygons, ignore_tags, h, w): ''' polygons (numpy.array, required): of shape (num_instances, num_points, 2) diff --git a/ptocr/dataloader/DetLoad/__pycache__/DBProcess.cpython-36.pyc b/ptocr/dataloader/DetLoad/__pycache__/DBProcess.cpython-36.pyc index 69d33da8e94a23810b830d78902054a1f1a9b4a5..2035a232dbedeb1e52ebe548bbea5fd021ca97db 100644 GIT binary patch delta 3321 zcma)8-H#hr6`wmZ9*@WNuGiV@XSQvUHZa*{2}yx%yKI(pNlDwJX%k4gq_|$s*tKhW zmNVmMyBcF8M{Pl>P&I%`UU=Z8yns+aLIkx{D?a{)d7+3W)CZn;K^5Wr?l^X`n@Wsz ze{=3R=U(4?KIiujzWvzYlli=La{CuA-C9tTe<*i6I@4=B{CD0u{F9j{6=%#Dzpgmr zuGyIAYa5e}dQq_pzj^Pb5OK7l_Kp3lOjD7jt%yRjrhRH00AnFKtuGy(W-`{c0zcNt ztpu^~>2r|I4PpM>NbiV)VAvwo>g~W*V|D3xEb2U0YeAwrAKAuh$_a0_)C)Ek6$Cg=e3@e58ACVye9o*73Xc_dQkfr>9 z>Ra?U7%PA?4EPfW#{w$X+ofPTC{^oCm!_Z~?|mB76M%In8sz$&CeA#@EP2a$2q&Y4 z8BAjZp9NbcvP9wCf1AG&z1Z5MdhO+)?fQOf`Q@NP<)H4h=_{;WE6C@AQ}#5eSS}Ba z{o-`RYhQCIAQ*>M0rGf72y;|!J6@~gSIT_sELiygi@cy5xN&wcjSgk^AKeFq^cHy) zJ_Jo*mZ_50k`|9GwnE#lderjcsGMCsC~cFfw;gwzJ=dzy8djK&*A6OWa_N!i57{Hx zJA55|lKtMP0xQuX04AnMz&U`!5a&y!O0(?y6}QG*d#e* z66id&^~8rW5RJIlu$UrQ623~?MH8S zQ(>wh`byX6ZNCv3>&lDY@210aLv?f~)mQm#;4OKYlkSTjD31At;uL0;R}5;0T0?iN zz7R^NGc^IKeL!+^x4A)z^^3DgUx4$vQWZ|#8M|%&)+(q1x!_p$UcW9=)tw zRvhIy<$2|b5~Ld}?2Awf^{U1daNl`)m~V7Fy{AKv`%efk2E zbQ}OTULr2K%9#q>hzIsU{2|o*=#$h!FDZxgMP{&zPw-rU^^AVMwtDG%vVMeNM(*=MO# zYPxMMlyEv8R4_yvZ0ui(9?z_&wXW9HyQ%1U=4>w&>V5sH-c^~G>gu7=$T!A9m1B7% zF<6pj$#|n6C3;`Gs__c5n+cT01Y*CTIh;COH7GXrIT=pK^qVU9U6t3R8~Bnk z25V0vn=$K!DW1;o)RIEZUMWn=87G^ZnR1pH%rblkBecSFXoi`p!3k%br}KP0nLyH$ zHJYEH3#X+mA zkhfKGHcPEeQ+9`q?4V;xj_e`d!lKK7D~i%n=mn4)fNuk?0$yZ@^`_UZ+1Vrzz5{j% zum*Su@Cra?Ng328pu(_W)q2Zos-3TPRxlZwZL>v zLqC!~b!XvTLB^G%9=NSi$+pPlou~?crIW_F2nzYKtBKRg;0+J8$bp{pZm`jJaTOn( zfxDq)TIQsgGc_rRE{Lh<=jeQwgJqnO@0iS~0Sh49wK5+eD^bsf<|E|(DyfewqzAaw% zM;<;tJW$e-a%M$~<4-;G|I~p$MK{KO*=zIS*uV#>KW z^>=w5-ux{OA4g=*r_GG161evMI9)0^UWLE-e4fiJiBs)PYwJ~+p0Re}>@yT~CeDc~ n(MJ;}jvX~&7YPj delta 1209 zcmZ`%OHUI~6z;TTS}3JxDUY@rf)WeSD83L0;3Gy1ASf;v#BrDzps}4H=iU-9MiwTn z+?XrIg^4Q{ZcGdt9}E2fE?7@ujPVz+_7U%QOQ112Nxz1t@qMAt_fX9jmG+4Iv8Ru_HgqR^Jyd9Vv-LsK4Dl%ZKyQ&D%7{#4P)mtxk)L`F z2YE3aJ<`=GO(U|*Sr8@H=7Gkj8_|wk$Zo!jRy3_cr&yjxe{3y1qm1;`gg_^kRZ6_JbHvi(UYxh#f_xbjYZEqO z^{=&cqhX$ki%Z;d88aG~#Vcg-qVLg3nZDSpzE8u2HVWgq>KpZfnC*KjE^z`CGzd`L zNqIs>BUzr~mrZ6{vg-tnk~>G_bBnu1V#gIH5}BU;G9id9lW)r=s2N`U0{LadZ_pq~ z@YsS+Wo8JL=v+|?jbEHBa!9*8@>$8C#T>Jy(6-H93z#jnKu;gn3G0sstC z8ZZi&kq{(J(=J(z*>0(1ntL=!-rA9r74IAS#k;nzGfi-B0Z=hWF1p;5O(=m_RU92r z5J9&AZ4wIMD*wvfsaf48WT28Gw%ebS{#q6UFXDQ%N%f3@7xkJ#NwTmD5tTy!r4c`Lq diff --git a/ptocr/dataloader/DetLoad/__pycache__/MakeBorderMap.cpython-36.pyc b/ptocr/dataloader/DetLoad/__pycache__/MakeBorderMap.cpython-36.pyc index 65ed1460e2d27ed1e4dc015173deaaad8faf591c..014c97a68f2cc8fcb6032ded4e460550de7394a3 100644 GIT binary patch delta 17 YcmX>ke@LFgn3tE!G_);YBgZa&04<0G6aWAK delta 17 ZcmX>ke@LFgn3tF9?$rJ98##9I0{}JI1~dQw diff --git a/ptocr/dataloader/DetLoad/__pycache__/MakeSegMap.cpython-36.pyc b/ptocr/dataloader/DetLoad/__pycache__/MakeSegMap.cpython-36.pyc index 2a80220a8a226ef1e5829066050afc9b47abd079..aaafb5a668d843fb65a34938d14b00166871be85 100644 GIT binary patch delta 1624 zcmZvc&u<%55Xav<@7mAn^?Lm)iIe6hZNU|4+J=Ns(zLWes;DX`E=43wQ#Wf{IIfbu zbpTaw<%3%U2SloO1aSc&wSr!JP7WM6fVjXd`~lpMxb^_dY+B+V%X*)k{m#tWnc25~ zTzoifO(m1o54XO)xis;>`Z;r&F;-!V;IT#S!D7TqKlo$9_EPuQqVU`jD`q-LxMsq+ zju-t2U{_CyaTwKC#Ad4xQw6RIxcn!aF$HzzfyeLj0=v!wGl(iy2l*nBE3OP>s%YCl z4-brHq#oW^Ia;O{`HTm#9-2TcN_kyzaojKF8Lc%gANNSHdT5Crn!Uw88f%JXT=6!v zVS#P&WhY2tp~(UZoTjB5YzFRg&#YoYZD=MAC?%8zSdi)p#47(6_*B6-7yHMJx4@sp zjZ(^ObL_)jLq#^4$-1NB%F#Q&umA4g*ukC zudAeI_OsBJQBE6zv`W{ro{4!m+%fO*HXmR)hHBps3o@SY;`i8I(+k*9kezMz*9Y1x z$f;btpt5*3h--+pMCFjmBq%Je@->FVae1-3_%lz7$Hxt;yceZ$)JHsv9vkGt$e7CS zm)q5b2-_cCHdTZMwb#V6A;_ID*Y3{UZ}ra3yX;vcR`jO=WvPUUDO<&>(Gng^v<2FR zu9~f^OyP+wf^_jB(}awEV$Qc@mUJ$2B;^VAe@K!=Cw>!`kB6N_+1+E8%WIWVrFOZz zaePCruU342<@RRd_*`RswcPOM(Rkl)uCCwSn3w&SQXG{7c#&~JfiOrIA{=5@k;7zK zgb~6hVT>?Nh!G|T&k+t2ju4I#s3`e7;ROPHnzE<|-Qm_GnI{M*5yd{4Ao~(vvnkLK;UM8ogHCIBvoF(lQ0{v^qR|#{3GlY4_)w)h=Ktw1?!wkyq6aV;nXkb_v~?piV=nZR#Z%h8U3-nJ#vjg!oeM^R66MRR+p2X!z2Ay z@+(-=vSEcfE#H zeKIJia9i z`39Fy2TX9w5D^iz1|{i5OVS(IJ6*@n+I4;XMs7TDlhg>Iq(8_V&Cy3=2mu1$29i51 Gmi`wY5QcA(yRhu#F7Xm$E*LR3a+QfkOeFycq|lfzFlPOT$k{2v1uaAfSO``U zhFIF$XmO2=q&NB#tOb97rMQz~o}GD@_uZYR;KV7NJHk2_1qUOv5X3k@R9+&S$dv(~Ux0WnmWK8J~2A``hSd3i>{o!i2gs U&Eah(W>KWd7%+&+Vj;8p4^JvjqW}N^ diff --git a/ptocr/dataloader/DetLoad/__pycache__/__init__.cpython-36.pyc b/ptocr/dataloader/DetLoad/__pycache__/__init__.cpython-36.pyc index 76e8f4c40ffafa5a0059adea8b3f8e62a9a99dae..1c817717efbac6a526f8bbc9d1cfd9b79a9d637b 100644 GIT binary patch delta 16 XcmdnRxQmg)n3tE!G_);YBF82GBh&b%7 diff --git a/ptocr/dataloader/DetLoad/__pycache__/transform_img.cpython-36.pyc b/ptocr/dataloader/DetLoad/__pycache__/transform_img.cpython-36.pyc index 21ae535ee2232f291726419b2034f2580df3c965..333ac1ead571ca6eaa745f5f12e1a2fd7d3732ec 100644 GIT binary patch delta 2002 zcmaKs>u(!H5WsgX=kxg)`{E?d!*%KsJMJTWr)?VA&_?M4il7xmn6|F#M;>(?XV0yZ z>aHnr(~44{bOjPZNJ#Jv2?-gA4}3sKz$c(kRRtN4KnPL(0o01_dvQliN%QGqa<Xj%y4uu(`AjCaOA|1yJYJzT!yZOMw|0N|r~gQ45Zmmi%!IW+e0o7=EM0&~ma!Dbab}hwkMOLYzQe=PF?} zAwXC|Xd|=}9E7!qcq{KEIY@9KYW8$KrR!OpuOn4IfntNNCu|^WBy1vVCLG;D+)7vu z13o}Q4`Cak<|<^%$tA;XQa3NdXDul5>2y}tlT&B%PoY8|5U<)}{j#Kiik}w(;3P`t zIuEA`ndzy$7^x$vQmX%KzyopE_EVN9mAVqUKl$v(@{K0Kf&|cinCo zq9f&Wexevx>h~WOf4g?V6*1txrBJl;5#jR$HCh;V)|Wg+>QOQ1S?e1naUWqn;ea^n zSqImv?|E)WQOmZiJf7uQF4OEDf9KE55n6b?Z2Qrk8^Ri_|v~S zyq%WqB8Th%QcB8_af9@gT3Nz z`wl3GO~EPnx%y7q}`!c~Fz5Ql;^ltk3Wg50UnqI2js5D{qAc;DNXqy0@Qp)MSnk#b;;*M35Q{u_%R%TRpLQ-?Q%_|#H9L2 zN9$1pmIF2knA4Q!az(}}2d2$bOgmLQLow_*D%|iSK7wVNq9U&TCvW)<)Yfy>O1nWC zQtmaL%PIg?s%doH>8Yeu>ocb)<6>BvbqZDnop5fY_%_z1)Mu*ko<9JFg+H>Q({is> zSF9(pYRzFfp(qYS`tbF5WIL>>z8_iDqzse(lDO5o8zw{`8iAbH7A^gsi7%p0z-6Ju zBB3j^_a%Z=c&?(9PzuS3sWF|uES`=H!ai{|Hu=m+oS67)ad9yY+(bFv%#UHmj}uN1 zVuUe*^^9ebD17(}gbLvzVS(@p;dL?E7hazxk(|Yoh_A5eR6DT_*;JdV_*`n6GpKr0 z8NY6x6xaK9`>gmGr#+UR&Wm6Bx?6KJ_7kiCTtQM&Xz^$_1&?giHm60*V@cIiw|AcP F`~#^|w%q^# delta 1171 zcmaKrO>7fK6vy{D>-Dn!hy(Vc=6Sx487y!~EvIZ~pUk z=e^k*`ojatMz2@-`TD)g7o)e7J6*ny1VP9PIv9fPFjQRbni3BRTCWuqpEy#wFIz5W za#@zo)NAFI$$Z?cOF34nXU=!#R%dQ@=A!O0bJ=n})6{XND-1dwb6v$HGE@e=haujz5hkSMZ z_@=%gYzCv@umN+90vM|dWdU@_a2uWlL3bGuTnw&LE(@ZddwCD~@M-WOq_LnLhDB>h zbpYJ9?)UZpe1i`|pY3^FLY`i8uoLwD459lC=tI~^j=OxA4h~7sB)unlcCM1IHZrI4 zt$M9gZJ6vhejc8IEItdz;0}5sZJ5BPku8wJfxZeXS=ag=0Po}e=!Ze7AyWyxgib@O z=roMtU(o?49rs%+*1rN{6*9O8tNyc+(m2{;CEF;rMe_`6uqZ{VxGy%zvs{gBhF|bO z?9W+B-WFN5YOn%1<~Vd|n%|8wV?~PO2mwNgKrj3E+Jq>e5Ka@k9JoVKF&1|_UI{o7 zZ%RjKVGO^FuZ4H<`*<9>@OeCGJsJEJU>tuK>W|pRlgVbEn?9V)5b`J{*71^zBqqRR z6%vD8l6~`9cuh;e30&6VFonNsjsHtAKfD({!5@d?(N8Jo1p@tG>>`IQRWqf^oXIYs zXZ<7$;>`L}vxoRX>D9{0iU#Zq*P4&*rws=P2MO;GJ|@_;DU+g}u(O0F;XGlUa2ciK z+L5Cq?aPwp>MK4>Dya-Ur|gs^zgt$7knEL3`7M^lbTYNm?#6N2W*3NY)m9rdmMfks jnWb8Fw85KZ7HX_wvSVcHA=q8+CzZxW$ponQcQX7BO1l!O diff --git a/ptocr/dataloader/DetLoad/transform_img.py b/ptocr/dataloader/DetLoad/transform_img.py index ce02ead..adbb77a 100644 --- a/ptocr/dataloader/DetLoad/transform_img.py +++ b/ptocr/dataloader/DetLoad/transform_img.py @@ -76,6 +76,38 @@ def process(self, img, polys, dont_care): new_dotcare.append(dont_care[i]) return img, new_polys, new_dotcare + + def process_mul(self, img, polys, classes, dont_care): + all_care_polys = [] + for i in range(len(dont_care)): + if (dont_care[i] is False): + all_care_polys.append(polys[i]) + crop_x, crop_y, crop_w, crop_h = self.crop_area(img, all_care_polys) + scale_w = self.size[0] / crop_w + scale_h = self.size[1] / crop_h + scale = min(scale_w, scale_h) + h = int(crop_h * scale) + w = int(crop_w * scale) + padimg = np.zeros( + (self.size[1], self.size[0], img.shape[2]), img.dtype) + padimg[:h, :w] = cv2.resize( + img[crop_y:crop_y + crop_h, crop_x:crop_x + crop_w], (w, h)) + img = padimg + + new_polys = [] + new_dotcare = [] + new_classes = [] + + for i in range(len(polys)): + poly = polys[i] + poly = ((np.array(poly) - + (crop_x, crop_y)) * scale) + if not self.is_poly_outside_rect(poly, 0, 0, w, h): + new_polys.append(poly) + new_dotcare.append(dont_care[i]) + new_classes.append(classes[i]) + + return img, new_polys,new_classes, new_dotcare def is_poly_in_rect(self, poly, x, y, w, h): poly = np.array(poly) @@ -259,6 +291,10 @@ def random_flip(self, img, polys): def random_crop_db(self, img, polys, dont_care): img, new_polys, new_dotcare = self.random_crop_data.process(img, polys, dont_care) return img, new_polys, new_dotcare + + def random_crop_db_mul(self, img, polys,classes, dont_care): + img, new_polys, new_classes,new_dotcare = self.random_crop_data.process_mul(img, polys, classes,dont_care) + return img, new_polys,new_classes, new_dotcare def random_crop_pse(self, imgs, ): h, w = imgs[0].shape[0:2] diff --git a/ptocr/model/__pycache__/CommonFunction.cpython-36.pyc b/ptocr/model/__pycache__/CommonFunction.cpython-36.pyc index 7e8b2445fa5582bdd5a374e8916cb304a3e6037a..cb2c691448be397335c3c990258000c0d667d7ae 100644 GIT binary patch delta 17 YcmbOyF;9ZSn3tE!G_);YBZn9d04D1M7XSbN delta 17 YcmbOyF;9ZSn3tF9?$rJ98#%;y053cRHvj+t diff --git a/ptocr/model/architectures/__pycache__/det_model.cpython-36.pyc b/ptocr/model/architectures/__pycache__/det_model.cpython-36.pyc index 6be6cdfffadb65f4c965a046fbffa4c77c550dd7..1f3381f4f6235eb99f76cdf942d056bd9503da5a 100644 GIT binary patch literal 3624 zcmb7H&2JmW72hxJ7e7Q%vSYQ0A-QgArh()*X_Fd8VK*NIidG0Ul3MO6 zvqQ-ib}yw-p9192V{gr+Ko3R#j-r3S9CBz+`3H)ir~cmTilPhwZb|I>ntAiy4Cnp! z@ttnhf8*p&pMCKiP5YO&^06R4#8dndglU1sbY`S_pet@n&A?dnsynb=792{?fLNNEUp(?B12$$mB{N zQ~CUDC9{FbZ2XEZbo@8M>j0m+{6~&XlE|SB1E~I`W z-H1iUVses5^OHyW(or*#0@~w`&TeM8*e>SB$2k|_oyfmPJ8%>^PCS2X%$I2l6IVD zqsbxa&kO6s!|>1Af9~w=9g95Xd#Zf*A|8(?B8kPEC&k`sS@TVV?cynZ0;1_Xd~fQ$ zzHRhC`?{?U6ukv)VB8>z?+wU`LcVUCt)JKG?)m1HMSBg4(ndePQ+$dNM(Jr^8vHgm zN(d$RS4akbv(!JeE2A_(HGTvQ@gwe4v#!xCQQjx4E-6n$`c=sZc?DuM~J?z!scbYubs(!Uz4XTav{UVQV zn>^N3)V`wYiVhUrAUdk^xbU~jHMXWsppUqH#9aqp2OoeBz&F4*9%vb$y8@_Aozht7 z)g~mj+-xMT+=SIoR=P*Of$om*4l6eS7n}RMgNuf0Fb;*ZW>Ik@eKE$RABVH(Shklk z2HZnYR}3H2J`M~bc`Bp_;V9t%33wB#z_~c$eN2<>W)dbmlilS+Qn^>J1CTkMi?E1u zo&=p*ImkInxb$m{mJ7UE7AN^qjPs!{T@`YY9o7gz6(n0CM+r+Sp0h}HCPf&XM3Xc+ zNE2zXNh~fPgug~fT;p5hX0b>_(wxi=`Az8j7DzzbRQuT`(+-F_ii1eRWBxr-{pGd_ z{(YMC10wYD=05}pJhe4C(ZH`arsVE<;%fQ8!OoQ35wQ}a{*=E?izxC2BioMH=d>+_^mGvm zf_zu+>?lhNW%(iCr_cY>6FJEKS9wt^(e+YGbn=RpXysLG0(ZY+)FPo}qH0BJawb~P z(IPQhwLf)(gZ8PhqDg8bv`3AEMo2X!)F{2|O)FjUsT-y2AMkA&HRdCaudnoMFJp9? zM#Rqku;STOh}^%@x2N()Q?pO#<&AP8zYTUiG!2&?RWsuoH50 z(m^LWJPa9%oiqhfflv4piU^H>LYu&*B1=!fQDfk4U7C=*rZUshCm+AEM{)TEW&Kkk z)DhG>xlR&YRDPex4UnOskcH!)31GIq^2>5ot{FzurLLQaK1%eK{<`s|(WkZn-)+@8 zP*Dy{RgiS0FFJ?w!XQr12eclYDc%G4Z3z8=^!h%cQ&vN>hB^-&U|3_=+b~}gW?hj~ zyD#t*R5F@=w$_x=?h*(1k1@ch3*zq)QO7`T{Cy%<7djo-49|I?Z>b|3deR9)md5~y zA7K=bPVl{(WzxV?pQ*I*_Xhuz6g1Q)zl^bhknV%}57p18iz=u|v=l^k%t7>DDaS~^ delta 1695 zcmZux&u<$=6rP#=z23Fgb`ok@k~EP*O(~R?f)oXTC9M6=`v@aY&rH>8uql zyRy_kZ%7d%B#`(6xWYd`g*bvh94(b2M-Dx3=9}k@cO#+IeDBT7d+&QQ`{sRr;qQ)r zJq-OPU;h+;zVL$|ao=K$85rp!^}4^LZt}y0j3>Oq5`j@02>Hjz#7dwBqH`teuwu8~ zVaG^AGptC}$|C*qyA)fdJk^?tC*J@vbBS^4JH#Ws!_>f^ouV7!iR zk^6jE)Pa$Rxcb(-HlD+tjd2qrT@wt1`wR#B2NIWqOUW|6Y2_j}ax=GbdnERaee;;f zI~g=2Xb_r&7NM=NZ=Lb7G9wXR^4!VH%t?$5mRXSI7>W(W+5qW;r$maIkbi;H1gV>Q zxj&&1N%uO7k@(|etgAeId7<{_xJ~+lUk<{`a4@)APfBg(qji>^` zutIBZp7k%OYoM%wIw(>IA%ilN;UhX~IHpXVA`=~`o;UI+Z|1Gj6C;tIi$v-~+aQbx zn}jWm{b?elzm?UKI^977#0?NfkP&1PvI*IOY;C;3hA8p}$jgy4l#2v}r-f(ZYD{WPGU%smgtUD(c zASPzg8}%l}-o0IU9`n1-?kK+3O9%2f%;cgraj~@38*P6om-N)fbg(0bI|r$}sEt-M zENfU*-??wz=xCMTS!ffde_wvqlgTAa(hmTPyC`>*ym%4P#TN))UGR?H((glF1x&&? z-ahE1>GsaSK^#w`>5yG6&uJqaS(<75%I z=+enhcVhXPhIv3&P=&~6v|e2M?5~nn4?F2HVA{$eevY?zf$B+c>#B~`HKv8;9m<`e zjILn0{H4=>ssH+s`Z!$t=oP(tO~Z8!vzm~v>glYwPE(>v&1bVV;>2-s Ze;XC_y!tI%o2LWMLKH=+-1-v#@INxRVlMyy diff --git a/ptocr/model/architectures/det_model.py b/ptocr/model/architectures/det_model.py index 0f91513..78f7337 100644 --- a/ptocr/model/architectures/det_model.py +++ b/ptocr/model/architectures/det_model.py @@ -20,9 +20,15 @@ def __init__(self, config): self.head = create_module(config['head']['function']) \ (config['base']['in_channels'], config['base']['inner_channels']) - + self.mulclass = False if (config['base']['algorithm']) == 'DB': - self.seg_out = create_module(config['segout']['function'])(config['base']['inner_channels'], + if 'n_class' in config['base'].keys(): + self.mulclass = True + self.seg_out = create_module(config['segout']['function'])(config['base']['n_class'],config['base']['inner_channels'], + config['base']['k'], + config['base']['adaptive']) + else: + self.seg_out = create_module(config['segout']['function'])(config['base']['inner_channels'], config['base']['k'], config['base']['adaptive']) elif (config['base']['algorithm']) == 'PAN': @@ -40,14 +46,21 @@ def __init__(self, config): def forward(self, data): if self.training: if self.algorithm == "DB": - img, gt, gt_mask, thresh_map, thresh_mask = data + if self.mulclass: + img, gt,gt_class, gt_mask, thresh_map, thresh_mask = data + else: + img, gt, gt_mask, thresh_map, thresh_mask = data if torch.cuda.is_available(): + if self.mulclass: + gt_class = gt_class.cuda() img, gt, gt_mask, thresh_map, thresh_mask = \ img.cuda(), gt.cuda(), gt_mask.cuda(), thresh_map.cuda(), thresh_mask.cuda() gt_batch = dict(gt=gt) gt_batch['mask'] = gt_mask gt_batch['thresh_map'] = thresh_map gt_batch['thresh_mask'] = thresh_mask + if self.mulclass: + gt_batch['gt_class'] = gt_class elif self.algorithm == "PSE": img, gt_text, gt_kernels, train_mask = data @@ -98,8 +111,12 @@ def __init__(self, config): super(DetLoss, self).__init__() self.algorithm = config['base']['algorithm'] if (config['base']['algorithm']) == 'DB': - self.loss = create_module(config['loss']['function'])(config['loss']['l1_scale'], - config['loss']['bce_scale']) + if 'n_class' in config['base'].keys(): + self.loss = create_module(config['loss']['function'])(config['base']['n_class'],config['loss']['l1_scale'], + config['loss']['bce_scale'],config['loss']['class_scale']) + else: + self.loss = create_module(config['loss']['function'])(config['loss']['l1_scale'],config['loss']['bce_scale']) + elif (config['base']['algorithm']) == 'PAN': self.loss = create_module(config['loss']['function'])(config['loss']['kernel_rate'], config['loss']['agg_dis_rate']) diff --git a/ptocr/model/backbone/__pycache__/det_mobilev3.cpython-36.pyc b/ptocr/model/backbone/__pycache__/det_mobilev3.cpython-36.pyc index 9f24b70b51991145c8215657b2f0cd2c0c89bc83..1a840d568d208cad41b88e8dc76aa93add21f07c 100644 GIT binary patch delta 17 YcmZp*ZMEev=H=xw4Q)%<$Wbc?04V_kr~m)} delta 17 YcmZp*ZMEev=H=zOJ9U5jMvhuJ05MVp$N&HU diff --git a/ptocr/model/backbone/__pycache__/det_resnet.cpython-36.pyc b/ptocr/model/backbone/__pycache__/det_resnet.cpython-36.pyc index 58ed83d48f9dc631ded50352c54e190a24bbaefe..cd01ea5aed2b074a9907b4379e910f7c526c4c12 100644 GIT binary patch delta 465 zcmezD`Pq}hn3tE!G_);YBZr45ez~yKMYKkf>^+0_ zP*e^gDnNuFkkDi)s)UPHO`E=>Px# diff --git a/ptocr/model/loss/__pycache__/basical_loss.cpython-36.pyc b/ptocr/model/loss/__pycache__/basical_loss.cpython-36.pyc index baf47e500c746c835ef710570d271da83bb702cc..ca9da33362666bf110fdab5465a5425d68667b86 100644 GIT binary patch delta 4605 zcmbVP>u(%a72i9L-P!f-I^Ou%IEkI4akfbvh*P8hN%LyjB#vWfUM!U9cxJrW_3q5- z&a4}UwKj^~hLlzmT~&OcpZc{D5=H`q!Uz5U_s?C_h?W)32*QT2q#fWILuLMtiZK%=QzhfYgrhpza>CJEwVGa1OBvAUP7*Z9SkncXlw*L#aNAL%EU3~> z22`22Q4Ta+P8KxTSd#}$&dGx&A8WduolcKaSZ1YdP`BObg}UCjt_L)IPCsb+V@&}x z1I{3524l^3XQx*Cmk*QG zl#^UyRbwf!sJAfG%+x~$!*di5(f5Z47lf9rls%uj5Ubojdwrs+Sd_+C-xh$bIJf!(CydD5_XC3HDdM`81QSp75#w8QtK7z5E6p8Uc1$i|3x zM`;c6G=8s6L^K;oUKIP(gG?9G>R|6`7?JVF_Z+$8+=sU=^ql^!#QzYtA2^Xo^a_#J)~qZ?}7x+T?y|C&^5ewp0l=dtyRC@z2~Zi{F!iHfhG_&Nxg z0ysnWRw&akuHu8NJ@hixZ*-Qw1iDwmfn58<6)1Hw_Y$@|i((1|T9*c{Oc>W@6PQ)= z;^W+?eF93w^p>gj0@}eLXvD{6BGT#}o7)IAGRN5>U=k8Wl60m>?I{ z2wX~&f3&q;#$MAPqz3aEa0kD)iYvbw7v=H$;(T9l>?X91d9h}GeSQEAn}|VN|C#tl z-)I%#02g=Skejw9ijsn+P;s2cCJFNXJMacs5RfFQk%2VnH+s1VY4WR!v_MzU5+z68 zbIVp(nRiVNe}1%KsS#jxY=xW6);rh(aerWf9bS1b@C$YfSt((a@?2;mR$NrNP>&{6EMFd{;~gg3x5fT84*8q*j@?Vo1p`SbI%(b$M5KDs!) z4=d~v#g)-yYUg@GRp{a zc=`g(+Y&UQdE;#?-9bSUq(w8fNxS6+`?2`f@J}w2qshn&EevFf@`#p*^;XwrU;zi* z7zZKwiTL-?wJ<+umqQCOt?%nLzeKfllL?^M=wZN)JA@NT!F*8)mz}w+rig@@2k>kVBTh9_CZ=xlH1aeQ0{*tJonL+|nrOw2iJ)`SG6(8(* z>qTaS0If=KQ^$leu4_RG>q>_Hp%IV|A_(SG_amfis&yKLXH zEx7Z+8^M{G<$m4tn)Sv4t%7jrPLcxw-Tm-X7DzEWHqypTXj6vC47oxZ&Ot<^0BztJ zf3JQ_}N?!7vX3eELV06Emzy`-r&^Zg&MSd3rW+JB)B~lV7=-9fLR#_>Huhz-% o31n+90%Amidus;nu!d476uJw&g)bLQ7Dh8hp{vkW$Qwid1-z7nng9R* delta 3942 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 F@joF#3FrU- diff --git a/ptocr/model/loss/__pycache__/db_loss.cpython-36.pyc b/ptocr/model/loss/__pycache__/db_loss.cpython-36.pyc index e2d0de398035a8a7b80ccbc8b38cf3a5be6b340c..d51b19848cf59f92b7a0d03bca715bf7db23641e 100644 GIT binary patch literal 2445 zcmb7FOOM<{5bpNdo<~T?YaxLUAm9KyyWtW=QM5_|TsC{*W+54`+moG%=Rw=6BrDHp zgK|oK0}>~0T;KfG(D%*J_fM`HHbxR#UYW4#dE z9pr`9&U`+}v+|fj``&(er_8E~oN_u~@lYn{sjfg23>jdFC4#4{&xIr0Cm}N73I7R8 zoTtoTiA!Ilyt}t)rxy`|qy_QEvp;|O?yp~eyp=CDq|u(g)paN}t9io?t)X?uIIHc( zKEynAYWI*~^lD#^K||KCu^=PCa2(%>Tgt8Wr-hUOHc~;F4#tBzO{GiIP~huzLM0uj z{BqOG=n5*>oiC~Y_6pf4R7f_h#*T8TqU_7p;44p3fL%=m~z~H;#yIGK_5KH1273jx{!Pt<8eQ8G3!zc+WX!W2doUfvr#7-r7;# z?qHnB$CA2NS@&z@jL^SGXFxeik$2_Qd$OqZRAk0Aivq0wmgL-H_2L!VEjuqOy`R+7QYtvh8LmWf9`)ah;p+KaGH=JhPi?RS|4U2&um1pkcyR#O)xZYe zF}FYp4sogQK?<$_dx8{x!-@k>fd5})0`P-UqB$veGXl8_&(<-|qohC(cSRed074KQ zi~-`j##@quuQk+d7?3s#8+RCjCj2E$gbi$(Cb~P45TrRmo+oV$LbVOvO+7SPb)k^? zH^}Er5*J8ZB=HuBw;_^9^Mmn?O!e1kQJ3-(X|Lc3Q`vX08b_w`m&x;862vf4Z}A>! zI+`kNx{dxPR~R(lBbd^06@3kn!ucb#5b(=}7*4J_V!;j03R8w4K0!Qs@k0zUCUKR7 zZg@IGenjHc3_#GJ3ojU;rNI(+l%J+zl7nE}dLoGk^{_r3D{DMX+Phgbz`x?^4^(Ox qQGZH;wZW2iPt%lU6@$X%Esii`aMQ5aEefot%^eV9#4os~&i(_Pcn+Tc delta 405 zcmYk2y-EW?5XW~ed*5DEq9C@hIhznHY=Q<9Q(4)>G|PE=CXovXyBC6$ptct0;*015 z2)=^diIp$n&ZbBf=Ffcm=f|)wjh~=-6h*=B+b4Ucg#3}Ur)$#HA4NHcv57sFSd0#@Mj|9>?TP^l{YMOQlukc5bh)^#5k3PGVt>@TQ&@^ zqWM%xv4d0u9#6bERG7)$d+$~4P!##WdZL<3Do v6Rws(hs1C4pzfJY94g!Ib}Hv`%IA4zdMm)%FUl1h5DH0yI;o diff --git a/ptocr/model/loss/basical_loss.py b/ptocr/model/loss/basical_loss.py index d1655f8..0c74cd3 100644 --- a/ptocr/model/loss/basical_loss.py +++ b/ptocr/model/loss/basical_loss.py @@ -6,8 +6,26 @@ """ import torch import torch.nn as nn +import torch.nn.functional as F import numpy as np +class MulClassLoss(nn.Module): + def __init__(self, ): + super(MulClassLoss, self).__init__() + + def forward(self,pre_score,gt_score,n_class): + gt_score = gt_score.reshape(-1) + index = gt_score>0 + if index.sum()>0: + pre_score = pre_score.permute(0,2,3,1).reshape(-1,n_class) + gt_score = gt_score[index] + gt_score = gt_score - 1 + pre_score = pre_score[index] + class_loss = F.cross_entropy(pre_score,gt_score.long(), ignore_index=-1) + else: + class_loss = torch.tensor(0.0).cuda() + return class_loss + class CrossEntropyLoss(nn.Module): diff --git a/ptocr/model/loss/db_loss.py b/ptocr/model/loss/db_loss.py index d8ed9e7..38dc872 100644 --- a/ptocr/model/loss/db_loss.py +++ b/ptocr/model/loss/db_loss.py @@ -6,7 +6,11 @@ """ import torch import torch.nn as nn -from .basical_loss import MaskL1Loss,BalanceCrossEntropyLoss,DiceLoss,FocalCrossEntropyLoss +from .basical_loss import MaskL1Loss,BalanceCrossEntropyLoss,DiceLoss,FocalCrossEntropyLoss,MulClassLoss + + + + class DBLoss(nn.Module): def __init__(self, l1_scale=10, bce_scale=1,eps=1e-6): super(DBLoss, self).__init__() @@ -27,4 +31,31 @@ def forward(self, pred_bach, gt_batch): metrics.update(**l1_metric) else: loss = bce_loss + return loss, metrics + +class DBLossMul(nn.Module): + def __init__(self, n_class,l1_scale=10, bce_scale=1, class_scale=1,eps=1e-6): + super(DBLossMul, self).__init__() + self.dice_loss = DiceLoss(eps) + self.l1_loss = MaskL1Loss() + self.bce_loss = BalanceCrossEntropyLoss() + self.class_loss = MulClassLoss() + self.l1_scale = l1_scale + self.bce_scale = bce_scale + self.class_scale = class_scale + self.n_class = n_class + + def forward(self, pred_bach, gt_batch): + bce_loss = self.bce_loss(pred_bach['binary'][:,0], gt_batch['gt'], gt_batch['mask']) + class_loss = self.class_loss(pred_bach['binary_class'] ,gt_batch['gt_class'],self.n_class) + metrics = dict(loss_bce=bce_loss) + if 'thresh' in pred_bach: + l1_loss, l1_metric = self.l1_loss(pred_bach['thresh'][:,0], gt_batch['thresh_map'], gt_batch['thresh_mask']) + dice_loss = self.dice_loss(pred_bach['thresh_binary'][:,0], gt_batch['gt'], gt_batch['mask']) + metrics['loss_thresh'] = dice_loss + metrics['loss_class'] = class_loss + loss = dice_loss + self.l1_scale * l1_loss + bce_loss * self.bce_scale + class_loss * self.class_scale + metrics.update(**l1_metric) + else: + loss = bce_loss return loss, metrics \ No newline at end of file diff --git a/ptocr/model/segout/__pycache__/__init__.cpython-36.pyc b/ptocr/model/segout/__pycache__/__init__.cpython-36.pyc index cda4725a473e7364c95acb962af6d2e4b518376a..876605de43c087bb89457cb11fb692c0c9a9e089 100644 GIT binary patch delta 16 XcmdnOxP_6!n3tE!G_);YBF7p4BN_yM delta 16 YcmdnOxP_6!n3tF9?$rJ96FJrZ04hrbp8x;= diff --git a/ptocr/model/segout/__pycache__/det_DB_segout.cpython-36.pyc b/ptocr/model/segout/__pycache__/det_DB_segout.cpython-36.pyc index 4bd465740bd3885db9178f44e59efe7983867e9a..844eabc46e3dd95f7a2165634446fdc51df4b226 100644 GIT binary patch delta 2018 zcmb_d&2QXP5P#!ud%aGw5owa9kT#^P8xm*=357sfQqr{0s3JYB1lzFB`7q0Fcy>Uo z99d|CdSNAyd+5Cai6hF13+e?9Nc{&a5E2|YQ2qmE-fq%ng?eIF^YgrU?6Gl`QFdEp}=(Z&^!)j_x?5;(El>|7$S(aYq2KOTv$2g&7o`U!x&e&EEQW+Nl~8FiV2PTrikj$%T8s%pF%u;@OG` z3*FE|>}j3{JT?o9ogs}hbm>jBuRvVKnLbQ_jt93|l$;V;N^RdgR~d!tg6>pMer48u zMNp1~gK)zKQJ?{394CtV6U%3Xoe7^R8h2pHfA(#Hs%C!k0<85u72IAb5dbl3R< z673OP1_KM@ETLeuN4ugmj7PelGR_QhbXZKI*0q?-`rT~F9PjCV`Ifno$~Vsa$7{6c zw=0p5ZeyV4p_hSHQ|@3SCf8dEmW__B$XNCv71S+#$lVV&>#O2cn~$;x z_DiJQLCLE}5?owGT@d30rAE9#@R+o-G<=mn65%{)uMxaXAQziM&9@{6Qt>94=Lskh z@h-syf`fg#FO(YgcPJ6n5K|m*kj)Nq+EQG}Y6}K&>=IuFx^8W)AwF9b{NTE>hxS}5 zLw*a_5_D^_3il~`i41QONOnq)wMS?85oMnqBV8Lsaj;cc+pbsR%|_ifp6K$j$uBPY z3W_7JZc-)|X@|=MvcbrfA-e^!u=9l?jXp(>BiZi=G9YSC@1sN|1Pky3fq!qpvYkFl zwVsjf%eQm0QsIqirIHt_jT-V6!#h)t+BZpg$SwRVRX3*Ub$Nfeajn6(Yk^R4PF%_6 R3nx7)VGYx3Vd6{g{R;q#cfSAt delta 207 zcmX@A{ZUNWn3tF9?$rJ9`*;`_9y1^T79iUJh>I;IDx0&VFb6YevTZE7!@?8A;+&sX zR>T36i<%t8s>G-|xt2AGF>3P-RyHO^{mJ*)6&dw5GjilJ@)-iv++r(AEiTO|DUzMs zD=0pBDrXy`$z&NWGe*nF!Cdx?YLh2(>2m7<6&Bfn2+PT*xxD2yIc~AX$EV~c$H(8| oPR`HCNlh-v%+D(>Vgji#nOq>MFnOw|5)a4`EQ~yi9E@B{05mQ#2LJ#7 diff --git a/ptocr/model/segout/det_DB_segout.py b/ptocr/model/segout/det_DB_segout.py index 895472e..2d9ee69 100644 --- a/ptocr/model/segout/det_DB_segout.py +++ b/ptocr/model/segout/det_DB_segout.py @@ -88,3 +88,104 @@ def forward(self, fuse,img): def step_function(self, x, y): return torch.reciprocal(1 + torch.exp(-self.k * (x - y))) + + +class SegDetectorMul(nn.Module): + def __init__(self,n_classes = 1, + inner_channels=256, k=10, + adaptive=False, + serial=False, bias=False, + *args, **kwargs): + ''' + bias: Whether conv layers have bias or not. + adaptive: Whether to use adaptive threshold training or not. + smooth: If true, use bilinear instead of deconv. + serial: If true, thresh prediction will combine segmentation result as input. + ''' + super(SegDetectorMul, self).__init__() + self.k = k + self.serial = serial + + self.binarize = nn.Sequential( + nn.Conv2d(inner_channels, inner_channels // 4, 3, padding=1, bias=bias), + nn.BatchNorm2d(inner_channels // 4), + nn.ReLU(inplace=True), + nn.ConvTranspose2d(inner_channels // 4, inner_channels // 4, 2, 2), + nn.BatchNorm2d(inner_channels // 4), + nn.ReLU(inplace=True), + nn.ConvTranspose2d(inner_channels // 4, 1, 2, 2), + nn.Sigmoid() + ) + + self.classhead = nn.Sequential( + nn.Conv2d(inner_channels, inner_channels // 4, 3, padding=1, bias=bias), + nn.BatchNorm2d(inner_channels // 4), + nn.ReLU(inplace=True), + nn.ConvTranspose2d(inner_channels // 4, inner_channels // 4, 2, 2), + nn.BatchNorm2d(inner_channels // 4), + nn.ReLU(inplace=True), + nn.ConvTranspose2d(inner_channels // 4, n_classes, 2, 2) + ) + + + self.binarize.apply(self.weights_init) + self.classhead.apply(self.weights_init) + + self.adaptive = adaptive + if adaptive: + self.thresh = self._init_thresh( + inner_channels, serial=serial,bias=bias) + self.thresh.apply(self.weights_init) + + def weights_init(self, m): + classname = m.__class__.__name__ + if classname.find('Conv') != -1: + nn.init.kaiming_normal_(m.weight.data) + elif classname.find('BatchNorm') != -1: + m.weight.data.fill_(1.) + m.bias.data.fill_(1e-4) + + def _init_thresh(self, inner_channels, + serial=False, bias=False): + in_channels = inner_channels + if serial: + in_channels += 1 + self.thresh = nn.Sequential( + nn.Conv2d(in_channels, inner_channels // + 4, 3, padding=1, bias=bias), + nn.BatchNorm2d(inner_channels // 4), + nn.ReLU(inplace=True), + self._init_upsample(inner_channels // 4, inner_channels // 4), + nn.BatchNorm2d(inner_channels // 4), + nn.ReLU(inplace=True), + self._init_upsample(inner_channels // 4, 1), + nn.Sigmoid() + ) + return self.thresh + + def _init_upsample(self, in_channels, out_channels): + return nn.ConvTranspose2d(in_channels, out_channels, 2, 2) + + def forward(self, fuse,img): + + binary = self.binarize(fuse) + binary_class = self.classhead(fuse) + + if self.training: + result = OrderedDict(binary=binary) + result.update(binary_class = binary_class) + else: + return binary,binary_class + + if self.adaptive and self.training: + if self.serial: + fuse = torch.cat( + (fuse, nn.functional.interpolate( + binary, fuse.shape[2:])), 1) + thresh = self.thresh(fuse) + thresh_binary = self.step_function(binary, thresh) + result.update(thresh=thresh, thresh_binary=thresh_binary) + return result + + def step_function(self, x, y): + return torch.reciprocal(1 + torch.exp(-self.k * (x - y))) diff --git a/ptocr/postprocess/DBpostprocess.py b/ptocr/postprocess/DBpostprocess.py index e483e89..959c270 100644 --- a/ptocr/postprocess/DBpostprocess.py +++ b/ptocr/postprocess/DBpostprocess.py @@ -216,4 +216,215 @@ def __call__(self, pred, ratio_list): boxes_batch.append(boxes) score_batch.append(score) - return boxes_batch, score_batch \ No newline at end of file + return boxes_batch, score_batch + +class DBPostProcessMul(object): + """ + The post process for Differentiable Binarization (DB). + """ + + def __init__(self, config): + self.thresh = config['postprocess']['thresh'] + self.box_thresh = config['postprocess']['box_thresh'] + self.max_candidates = config['postprocess']['max_candidates'] + self.is_poly = config['postprocess']['is_poly'] + self.unclip_ratio = config['postprocess']['unclip_ratio'] + self.min_size = config['postprocess']['min_size'] + + def polygons_from_bitmap(self, pred,classes, _bitmap, dest_width, dest_height): + ''' + _bitmap: single map with shape (1, H, W), + whose values are binarized as {0, 1} + ''' + + + bitmap = _bitmap + pred = pred + height, width = bitmap.shape + boxes = [] + scores = [] + + contours, _ = cv2.findContours( + (bitmap*255).astype(np.uint8), + cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE) + + for contour in contours[:self.max_candidates]: + epsilon = 0.001 * cv2.arcLength(contour, True) + approx = cv2.approxPolyDP(contour, epsilon, True) + points = approx.reshape((-1, 2)) + if points.shape[0] < 4: + continue + # _, sside = self.get_mini_boxes(contour) + # if sside < self.min_size: + # continue + score = self.box_score_fast(pred,classes, points.reshape(-1, 2)) + if self.box_thresh > score: + continue + + if points.shape[0] > 2: + box = self.unclip(points, self.unclip_ratio) + if len(box) > 1: + continue + else: + continue + box ,type_class = box.reshape(-1, 2) + _, sside = self.get_mini_boxes(box.reshape((-1, 1, 2))) + if sside < self.min_size + 2: + continue + + if not isinstance(dest_width, int): + dest_width = dest_width.item() + dest_height = dest_height.item() + + box[:, 0] = np.clip( + np.round(box[:, 0] / width * dest_width), 0, dest_width) + box[:, 1] = np.clip( + np.round(box[:, 1] / height * dest_height), 0, dest_height) + boxes.append(box.tolist()) + scores.append(score) + return boxes, scores + + def boxes_from_bitmap(self, pred,classes, _bitmap, dest_width, dest_height): + ''' + _bitmap: single map with shape (1, H, W), + whose values are binarized as {0, 1} + ''' + + bitmap = _bitmap + height, width = bitmap.shape + + outs = cv2.findContours((bitmap * 255).astype(np.uint8), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE) + if len(outs) == 3: + img, contours, _ = outs[0], outs[1], outs[2] + elif len(outs) == 2: + contours, _ = outs[0], outs[1] + + num_contours = min(len(contours), self.max_candidates) + boxes = np.zeros((num_contours, 4, 2), dtype=np.int16) + scores = np.zeros((num_contours, ), dtype=np.float32) + type_classes = np.zeros((num_contours, ), dtype=np.float32) + + for index in range(num_contours): + contour = contours[index] + points, sside = self.get_mini_boxes(contour) + if sside < self.min_size: + continue + points = np.array(points) + score,type_class = self.box_score_fast(pred,classes, points.reshape(-1, 2)) + if self.box_thresh > score: + continue + box = self.unclip(points,self.unclip_ratio).reshape(-1, 1, 2) + box, sside = self.get_mini_boxes(box) + if sside < self.min_size + 2: + continue + box = np.array(box) + if not isinstance(dest_width, int): + dest_width = dest_width.item() + dest_height = dest_height.item() + + box[:, 0] = np.clip( + np.round(box[:, 0] / width * dest_width), 0, dest_width) + box[:, 1] = np.clip( + np.round(box[:, 1] / height * dest_height), 0, dest_height) + boxes[index, :, :] = box.astype(np.int16) + scores[index] = score + type_classes[index] = type_class + return boxes, scores,type_classes + + def unclip(self, box, unclip_ratio=2): + poly = Polygon(box) + distance = poly.area * unclip_ratio / poly.length + offset = pyclipper.PyclipperOffset() + offset.AddPath(box, pyclipper.JT_ROUND, pyclipper.ET_CLOSEDPOLYGON) + expanded = np.array(offset.Execute(distance)) + return expanded + + def get_mini_boxes(self, contour): + bounding_box = cv2.minAreaRect(contour) + points = sorted(list(cv2.boxPoints(bounding_box)), key=lambda x: x[0]) + + index_1, index_2, index_3, index_4 = 0, 1, 2, 3 + if points[1][1] > points[0][1]: + index_1 = 0 + index_4 = 1 + else: + index_1 = 1 + index_4 = 0 + if points[3][1] > points[2][1]: + index_2 = 2 + index_3 = 3 + else: + index_2 = 3 + index_3 = 2 + + box = [ + points[index_1], points[index_2], points[index_3], points[index_4] + ] + return box, min(bounding_box[1]) + + def box_score_fast(self, bitmap,classes,_box): + h, w = bitmap.shape[:2] + box = _box.copy() + xmin = np.clip(np.floor(box[:, 0].min()).astype(np.int), 0, w - 1) + xmax = np.clip(np.ceil(box[:, 0].max()).astype(np.int), 0, w - 1) + ymin = np.clip(np.floor(box[:, 1].min()).astype(np.int), 0, h - 1) + ymax = np.clip(np.ceil(box[:, 1].max()).astype(np.int), 0, h - 1) + + mask = np.zeros((ymax - ymin + 1, xmax - xmin + 1), dtype=np.uint8) + box[:, 0] = box[:, 0] - xmin + box[:, 1] = box[:, 1] - ymin + cv2.fillPoly(mask, box.reshape(1, -1, 2).astype(np.int32), 1) + classes = classes[ymin:ymax + 1, xmin:xmax + 1] + return cv2.mean(bitmap[ymin:ymax + 1, xmin:xmax + 1], mask)[0],np.argmax(np.bincount(classes.reshape(-1).astype(np.int32))) + + def __call__(self, pred,pred_class, ratio_list): + pred = pred[:, 0, :, :] + segmentation = pred > self.thresh + classes = pred_class[:, 0, :, :] + + boxes_batch = [] + score_batch = [] + type_classes_batch = [] + for batch_index in range(pred.shape[0]): + height, width = pred.shape[-2:] + if(self.is_poly): + tmp_boxes, tmp_scores = self.polygons_from_bitmap( + pred[batch_index], classes[batch_index],segmentation[batch_index], width, height) + + boxes = [] + score = [] + for k in range(len(tmp_boxes)): + if tmp_scores[k] > self.box_thresh: + boxes.append(tmp_boxes[k]) + score.append(tmp_scores[k]) + if len(boxes) > 0: + ratio_w, ratio_h = ratio_list[batch_index] + for i in range(len(boxes)): + boxes[i] = np.array(boxes[i]) + boxes[i][:, 0] = boxes[i][:, 0] * ratio_w + boxes[i][:, 1] = boxes[i][:, 1] * ratio_h + + boxes_batch.append(boxes) + score_batch.append(score) + else: + tmp_boxes, tmp_scores,type_classes = self.boxes_from_bitmap( + pred[batch_index], classes[batch_index],segmentation[batch_index], width, height) + + boxes = [] + score = [] + _classes = [] + for k in range(len(tmp_boxes)): + if tmp_scores[k] > self.box_thresh: + boxes.append(tmp_boxes[k]) + score.append(tmp_scores[k]) + _classes.append(type_classes[k]) + if len(boxes) > 0: + boxes = np.array(boxes) + + ratio_w, ratio_h = ratio_list[batch_index] + boxes[:, :, 0] = boxes[:, :, 0] * ratio_w + boxes[:, :, 1] = boxes[:, :, 1] * ratio_h + type_classes_batch.append(_classes) + boxes_batch.append(boxes) + score_batch.append(score) + return boxes_batch,score_batch,type_classes_batch \ No newline at end of file diff --git a/ptocr/postprocess/__pycache__/DBpostprocess.cpython-36.pyc b/ptocr/postprocess/__pycache__/DBpostprocess.cpython-36.pyc index 2b23210239e68bcd305668adb6f59cf6064f0fd3..e6bd8b598d923fcba5e8274ddcc0ca5d943001ad 100644 GIT binary patch delta 4141 zcmaJ^O^_Q$74Ghtkw&Ax(Z6J`|Ki}Xz$~^m=4Y`JlNb!a-Yg{k#kRb=Bdu5a-x>#b z>4}oaCKsr(8BQFyaN?4JauuW~ih>)M1697E;xBOHLWMI2zSq)fW!WHAf8F!m>+YF1 z@9X#GXOlm;a&Rh{bRKN}=;nK;8T$tt`I#6)##%QY9DJ>KmU*@pyUV;--RT-v6P|dT zRh^%uWwq)_UP&p>nVnpP%y=Zq@X1@)&MG9tZSd{^~|;e!X39^}=T1qrz87^BlqR z1P2H@;YM-#!QU1$?wOZo*IWLqTwkcSTIX-CgctbFEg6APcvI{PYvMphCTU8FAUy<^ zMj620(OL-rCR&|1IXj430CC?0SXX|ih~hz9l| z3u22g7C1g{3Ka>D7?n;uNZjXbsp6hctZCm96?TT*=htrpNzZ^?OeHI7p>q`Ei?I@%^9qdP+SSf<6hj0bOr1)kS0}T*_7|YVtYoU z0|8A)DHoXTiXiK|D%<6r1f+gcyeX$X3ESnY#&v4 zN;RTA$%d1*rIwx(#T;ue#~PUf?MbF$%`$Ga(#4vbDF;^>8g0&8f=)T%FHOHHvv_AS zi~srYM~~0X&%X4AG;l3*1bB~3<_YNihF7e~aNem-#YnQ3U?0JL0v(AqjdXOzX>^c4 z$7v$`i!-rLew+lK08|V8I6Q)JFS@WY*J{;UVI;n{Bd5sb1%eXVL*f2fCesj`0vd}SSF)#U?>qLs~MSV<~%$EF%tHLpa@M~6_!m<=K0M>xGE^J zE_MYh9nXZV5r8JNNl9-~+TO&o23)%~*b4ffyQqvyOUV-7d7Ys1+;_zmJ}I{ZV-+b>^2MD-cDLr~&omCMuU!VAvyC*>E&Nb~b$ z8c_twGX!5G=yQ=qUjl@`b*7T#9U{tmp%F)hI7?L+6S8vHzNvS!i znW;Ib>G^F^5J$%;Noi~!PM1C@HP(Lw{aR`y)$isejphjE2^Ik1sdPnL zm%;hxLw7OhMC7Hg@fE?U@4=?!Mlhr zKnKw@k@EkT-jlODqs&0-=s<2m9&`8X-j>@t_E?V*Ps0x}FScVplxN&M9S3qpIVx6W zo>4*)Rnh*aA)twSCQiAyiVWt6K7Gq-%Q{)*6-I97v#iz$(p@XLy#!y9N$y zfr^1QJ)>(Nw+hOqbOZNn|DnebjT)fBuv`W$s{}TQVBx_ARAe-$ z@QjA1G7}x0iK{tTg!83GphNdCjp_vS@@0kK5J7`rkw6~^yJ=J+xJ9s^V2Pj!=tbt_ z;_6(xXU{LKEv(;O^Q(z|6dfg#dw{cYnT9I_s|1=NYcyIX*dW*>kOX5CIRM!zf~8ds zGug`Y1mAi(;85(=S_|v4UTe&?{9(Y+^A26IbtS{hA_apiO^UGXIs zStm$g@pSt|pZk`=X-^AZpF~BPR4yj6W&}m#2P)N!ssu@N4VK20*6TX#gG&r^QAm1R zXTi6WK^tLG{{zaW?T+i z8#h$YUV(omo^}v9j@aKY!k^^xQAuZbH6H#xKh>d(S1s-&+pErotb2p1)rV5vCLoT< zHwnH8xSAc!vo-yRDBGkRej&;`bh$+^MWFNUw`imY0)n>~>oEBqhCRE#Ba-$QH9OEM zq9)}e#wUBm>>`ytiU9!`XwAG~ShVK`T^O^9&;sJgaU(ThlP)Zl+8_1-R0)Hwk62k4U+5ZB7 CWn(e` delta 254 zcmbOgxlK>mn3tF9?$rJ9Tjd!T9y1^T79iUJh>HUzD*LmgFaEj> zJvTM41SFCdHQ7#7QaOq{DX}CuBOYvYks;9fA|oIX#aWVD5TBG^ky;!zxk~h-s24~^ z2}G!X2t^Q4GPyy_iba#BsA4jwjuE5pWE&mD$t_~SlS_5@h4_FfSQt6LkOxTeFaZFt CD?^I_ diff --git a/ptocr/postprocess/__pycache__/__init__.cpython-36.pyc b/ptocr/postprocess/__pycache__/__init__.cpython-36.pyc index c97c3c681ed8c725455b7f843babc6adf7d1bd1c..7936da5e364c580078e05f04730dd7dec7d90b49 100644 GIT binary patch delta 16 XcmdnYxS5f|n3tE!G_);YBFAa~BKrh< delta 16 YcmdnYxS5f|n3tF9?$rJ96FF7`04gj6oB#j- diff --git a/ptocr/postprocess/dbprocess/__pycache__/__init__.cpython-36.pyc b/ptocr/postprocess/dbprocess/__pycache__/__init__.cpython-36.pyc index 53b601c544faf6ff285e274d296181a88d60be36..a00defcfcad5fa56d8474acf17dd4f68882fe07b 100644 GIT binary patch delta 17 YcmdnOx`ma)n3tE!G_);YBgYyh04S3LsQ>@~ delta 17 ZcmdnOx`ma)n3tF9?$rJ98#&f60RS-M1<3#a diff --git a/ptocr/utils/__pycache__/cal_iou_acc.cpython-36.pyc b/ptocr/utils/__pycache__/cal_iou_acc.cpython-36.pyc index 8a42d2c91101339a80376b3a2ccce3d6614edc44..43a58c4a2c47c738a9aefcb9cdcb963d06aa3419 100644 GIT binary patch delta 89 zcmX>na87{3n3tE!G_);YBZnm`mlkLM delta 89 zcmX>na87{3n3tF9?$rJ98#ydl8KWjUv6eC_PTs+K7D%?S=`kuy-pb~|s5$u`+gvtv m1_p*AjmZnybr`iLpJTTbPy`8Rf(UI8!9Tf>Rd})=)!R=H=xw4Q)&K$+nRzkd;w!a||mlBexa<14A(fkYHe%T*CH>*AT?#0r44G c7$;}5hx2NJL|A|X2O|&r=)!R=H=zOJ9U4&DEmgPKvqVj%`vRJjNIA`3=G8_K!SmBatYfjUIP%H2h3+- co}A4d&Z`9yVF3~xj6AH9&$I7k7UAFk0H%Ttf&c&j diff --git a/ptocr/utils/__pycache__/prune_script.cpython-36.pyc b/ptocr/utils/__pycache__/prune_script.cpython-36.pyc index 832c49504086bb8f499a233d3c6cc6421b920b20..8f331e1422f0af1970349fa52a1aa5c5fa4571d5 100644 GIT binary patch delta 17 YcmbOeI46+9n3tE!G_);YBgYgC04{L_9{>OV delta 17 ZcmbOeI46+9n3tF9?$rJ98#$(E001_^20s7* diff --git a/ptocr/utils/__pycache__/util_function.cpython-36.pyc b/ptocr/utils/__pycache__/util_function.cpython-36.pyc index e1cb139ee9a3bb43e650620b2251bba056203603..3e865399fd073d2aadaa1a652599d1ead1cc17e3 100644 GIT binary patch delta 3334 zcma)8ZEPGz8J^j%+gqRS&X@DYC64pqy3Muuu$!bwzzu;WPEt}6;)*10((C$W2{NfMKyLY&W zRUv$yXWxBh-r1RV=6z^W~JSO@?0ajKV5dC5^tghos z42UhOOl(=vCUat|*aq(n;-J_r23PrwcQ@`94~s{@^@*Kg7oG*NTkOGeBRq%22s}r; zO)L6jztARF@z5gCIk;|Ffw zl|-c++(}@pQL^3$Dsz=3Pe%5X=LaRXTrQfC8Pp}38`Ziiq6}f_1J9=1My|%Jx9At` zNoTGuylUxErRqgyY|BBacgmN&@@%7CsRffA@RmWTH3&2AD$?OEA3#g#tN{fw%1R8R zF1=D9-Ab)en;rt|_W>ApxXtq#&v6Szn)mZ8(B^X2?fu5@X?;xV3w)n?+1lT69PRJ? zNkjj`w|{3nz{mH3koySs6Ff#>5*z>&9Z4%GKT0r4aG2m@1V;!yPC$z+j}jbHN9?Wq zjB@Q_$0-nZ1hFIPru_zgR6U;TefEETP9CD>9|5#K93|)6h0?W(2rmDBpTCtH8k&Z` zp93&0#SLu$k;rLT1S!wuX|VEPfYO}{c}X#h4Bx%#6%F}FT&J!%2QpV-`jn9jft2Ue zFPtCo^QzKu$b1Ie^XiR`&txyaiY&L$@M@x{$&0EpwVi)f4W}k-sw#3&FQxkVbx_;X zZ&C+_r$9@pllgW7ve&J1vRe23(wyhkUQolGpZcJ$H}gH}Tb(vvRX^x_s+-0kU4k+} z9A{72=`GD?>7BNWJP%kx@#zybPwBQsznCv$3&ejkOC5z4(f=_ZWR#zjmCIVF34KrXV?mxwd82XFKEk@%Y25fuo-QIh599^1La|xz>6kKIDbFn!YUyB+YRwHiiRR_ zAD4(Z`##Q&x#WFZk~sR%1QQHd?2Jdol?g^(}f$TwxD5PWSLVAz{pQK(Sr_E*a$~*CUGl5rQ*{rR? z&4Jq_Xz2g64z2;N6PyH>5^3-ma;=>Mp9TLV@LeKDXG6>%0pAV&hoL6Fi@F}sOa0(e zJ<}Y1TT$PK`nN?Pe$R@Hr^KdN&3`B0qJM?Iz!!Q#6JnfRwgOsmBbMKHNq*(s79%!e zeMnsg#^nrVzqTw)&Z;xn!2?dbz2oJX?1*iow&!1t^wSf^+RUscYhHD-l?j2j5XdLh zcd~tak9sRR!au40mfc}de#T_~PE#`)^^PnW9iwp>c z)LXg!O+SO_X8{bo(9c1SROF(%n|r+f3^knqM3%Qu@dF6y5T41=D-zsl$vuJrE0zG29LXSd*VyWAy-pmgS{TgI@hhxZEns&FQLG~={_cV5Smdoqv z{rvNMhpKgN>Y!wRSl01SuXT?!|3LmJ07+#UXHbf7IDosmQfZ&bwI%|(s-MJ~@9{wwJfWnrxk4ev4@=m+bo$~@u`nyLd@>TyZj87)2=}U;uOc+sk)1vERkbm#F$)bw0>OK^@I2`jgL-Y zhTziPl3#`WU%_cInmdVjtT=Q$FC1Kp?oLEkUpAmSCT>1@JHkeZBX>=qk(T0~6yKXD z=A^1cef;UjiihVHb@@ef*qUF)742CgsP*|q(W1#c$Krd9{4%xs3c<^O$@JR%TB*10 zkn1|*RXwQo>9tV52QNPjpjlEjwyrYrRrSa2UluNcUl%AzC*b?)r#;n&=v-e>x6Qk(cVevPcJ12EUv^QL;QcQgODGW&{65*cLKC2BKG za2XIKN+nS*mrC&nBy}xGOYRw8JlpsPR;aj(5-*@l6Vq)=5?kbB^smX?czGJn6L?C} zBaGxJvPYVPsgs2VamQ;E f`Zhau#u;$Bos46oJMEma#dd7N&e=)ZvXlP-tFGC3 delta 2230 zcma)7U2GIp6rMY?GdrFA?f&#<+odfng=wJ_s!+6q%1?=)1yclE!DTyl+HQBdvz*yh zS{6%eA9zti?mNCcy4+)uEbUzvD1&1n7yg7D7g}VW`Z97QaX4~1K8}*76{=Oc!c7@Yh>s(iSw-B5Oz5OC z8B*J#?W~Gamzav~O`Jj+NC(MB6F!S(8HhX46GM;IklJ_*A(T9$?6_{;D%%C9;n>AJ zL&ugf;+1Gx@Ywb=IRz60uM(UlI3qrYb@ZMjk&M7`gjwt^8P{1@(Pe5rFAA(_`4aMV ziL;UnsXCjLR-{?BB25q{BUi06uC$_wb)H5e5@O&fF5q+OQ=IB^+EbiH9NNat@f~Jx zd<=%s0q8);1*h_^<$#^jr}C2ya2s;!RKB+0_z%l5WlyoH^PXCnpQ|on@F^?3Kox8* zdKkMxlks9g>hBPw{!$KswBOyP1YBONFGdXcLk!AbD?}--HA++Q!_M8|_Zqpj^ z+aWHdk_ThB{WPF_F*WEHx07w@oIY8s=z0pG_#@fbxQf%(tUAvZdZJXafr5i_}U8H8< zUQ&k{$nrZbsb3Y0t7B#XIe0ALS!P{4C9RjUb2M1@n3CSoyr~0g!1} z7Wdm*mKRa{2D?jlluntb=o%1fvN8J0un7mJ@VOVC&`BBvMo8`=*iVEEqa?rJ%VT3^ z2CmZBPp~GE{Cm;A68*%b{02(qTuwRp;!8kv87PhrCCC!cEemu30^LGzjDU`oUx#3l zB+6ze5dZfLBZA%DbU|@I*#Bjg}WH|tFF$a)HVTfW#VGL%_WU4ada!4#K$;dCVN~}zQ`1q9! nMNB|5!Ne~SJ^kY3qRfI4eIzq(aoFVMrWC=O literal 0 HcmV?d00001 diff --git a/script/__pycache__/onnx_to_tensorrt.cpython-36.pyc b/script/__pycache__/onnx_to_tensorrt.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d7121cb99fe1fe77a78dc1b594dbb29639557d90 GIT binary patch literal 6367 zcmb_gTW=#t74GV8x7+PYoXkv;$z0aMWm$ug*^5}AWtsbJVaW_hV3%f>rsJwOcI>vN zx;-;7Z9vKbE1_r?3Gsx41W!o(1jIYi!YiUA1QJLnFFYWD(DDG^skWU=X5k52U0r?Z zRMn|dr@nJey-+TDpZwx?zV`RChVduk$j?Fj3ZD2SW*FSytYi4uoB5{xw%WFz(>2G> z>$mH>`d#n~`tA8e{Vw?>{Vw}u{jT^G{!wo8+*{0_;ZJjy7f`F-Hn_)&ZyCHOX7?h#6QeGvTx~e&+Qrhc`+}} z?=c3QtsC`c4oIo12L=?n(XiZ&Ww`ziPO%fM(U-7Es~{}T0FPo%D<+r{0-XNw8-=HPtDX@ zG(aiv0(bZGqtgAqP1?~`STu-Bw|iN8WT!=}s|4*7SCxHzZFTja1|2AS{p$M0!M9M| zxFunBMxCkLgRnCYbxVIA7Ecm9|3o6gDBkMHZqVvBgLpga3xB2|qbTTxkAjYfn#s1x zX}YpnQKIZ_2#Vbht&Xx951v3#n$V-CetJb1k@jcXfT^ z>gqdYHlYg_prq=CkvFl z)d`!*i^RiV+*Y7^)?Um6;j|J}qQL~!!>0I|AQ zz+Xu1J@!s@k}=AoK0`g$P!;e$I1|$sJHmfRYF0ms83~o&)_9 zFQJ@=oH&OVZMYcHh5Og45zDy*!KET#pl3^achyzSt%{G&u|YivUmmb zvy*Sq0wg|1{YGW2kD{I@hxMON%k8H~2S?{Q)WZkW(lX9bd3AVZO@w4|HJEuT616Sa z>(;hf9Z~!GNSSg|*05jl0*Z}W%7*oSN)j3kXCsk3?8%*4C)^b>uC-!)%9QzVvtxmQ zezsAsDJzkQa$fBY~p*RHOuzOs5-nY~!qeL6;tc+`rMSmpYX&Xz+5F~k|`^+n|8 z*9KA2>I#2>i&)53*ugOi8Ur2%nY_xDkY5%sn}2RK6X}N7YDKN2)r+p9k>0oH$W(5= ze(%*+uD`MoT)Vn){f^3C8?-t&rGBMBY$6!1$oKBzWZVlSCR7!IfXy&zYzK70{Q}u@ z(Cu+lNf}vnflg~936kADr0Yfa4AC0YfQjT7QR5|M0@x4wMPgde&vuV7^ zit{u^DU&^GTm~h>79dLrxUtC2TUDy#@z@;xj_H^swurW>Su(4jme`9-UPaFap6Sm- z?FydwBPfL7Gj1T%09?S;TO0=%Zv+k8!8^zEFnd>(@ATs2hIr6wh|h~|V`8xMrs1yB za8(p3OO3#QKQaOgi>_?7ZYp~ly_Lgt|GK5@Sai0OOVpq%y2{I1sD4}tZV-@d2Ei9- z3hmdK!-n&Z$+I$HY7mCfkKQDl77Q^Lf&_B;ZGhtf0A~R}O75M$Af&ZAbZEerykWu1 zR{4?#IW5?wq#@)D6m=$VQk~qwk!{Kc0oa1ziy+6O41*0%9T!h?JSH7UsHJmJfh@+} zSY1qh7L@-o8Pp*~AG5sqhpuB*N-T)RJs8SHzAB3H(Mc%_@}Y zxM930;gZa@g*Dr0j@;GA0SD0>(DE5Abr1}Z19H=Rn;_gxt+tmGQ#W&P;4BfROj8DW zInAR5`^k*^OR0IB@){YYnOdp6WoJfNK{x_bRL*(=Fzd>XLT2BI`h-aSEWycUD&?N| z7W~dvz!Wc`Fmyyhk%`IFXEG{w!dab~5FH(w>;z3j!Z&d&-kcxl zcn6zo;<4T~_N?!l_pyn3rFC201*bm?g*W=U!FJN)K^X5w4Sizu*#-WXE6-9O+Q*@K z4eW=9_kHi>4xDMD*X>`rPxEF%3~RiHje)*I)DSB#V(dZ4J(7(A|U2PO03apYDyWD60hu=?F#zNU<@-gaRO%GASLaGB*P!(S9G3n zaaanF@K9Q^((X5#XfljH_O3L&-~P1!8Sf95o4k~dCdp%|e5;!UQfAawxlqb*R~2+-*zX})D~d;b7(u{WT>`(D zKvd6znP*a>WoipE_#i>GEo?vm31R@f&jck#n|6^UY?46H{v#35cEHhwZ}LXurSg=d zkPqVazD0XU$<7=0$Qs#nIRI~ibc%cxhd)BUQc8&lrAuY9mv5V=jW=*zfHbLsbP9A_ zIxwU=luji8#$wr4RyDVc*8%aNB#o#~u~eFy1_k@2J@N_vpk80{0>(^oRjrezwX zeR9$RX_>cYjVj5h_B<~VC+xWdd#(^4FXMUwY1Ct_72P^>*!nJuYI=^Up5q6{=irq5 zd3*M;S?2UOrw_+@Y!=7?8I}%PqhIJbva6+kJ~79s|1rmz!%;q9j{+IWMHWl!|Hx zMM$DyPHt1JMFjzgvMIUNPA7xMJnnz!{!0U?B$%$xNb*zQ6bK8hpnAm9QMiz~yRV^! z>jCVHVz{Pyi0l~PiVhs_!WY zz2#+61C1~&EiWJD+%(b>7Zgn9R#wr3Rmro<@8U6_8+wJL=FG| literal 0 HcmV?d00001 diff --git a/script/warp_polar.py b/script/warp_polar.py new file mode 100644 index 0000000..00399a6 --- /dev/null +++ b/script/warp_polar.py @@ -0,0 +1,67 @@ +#-*- coding:utf-8 _*- +""" +@author:fxw +@file: tt.py +@time: 2020/12/25 +""" +import cv2 +import numpy as np +import sys + +#实现图像的极坐标的转换 center代表及坐标变换中心‘;r是一个二元元组,代表最大与最小的距离;theta代表角度范围 +#rstep代表步长; thetastap代表角度的变化步长 +def polar(image,center,r,theta=(70,360+70),rstep=0.8,thetastep=360.0/(360*2)): + #得到距离的最小值、最大值 + minr,maxr=r + #角度的最小范围 + mintheta,maxtheta=theta + #输出图像的高、宽 O:指定形状类型的数组float64 + H=int((maxr-minr)/rstep)+1 + W=int((maxtheta-mintheta)/thetastep)+1 + O=125*np.ones((H,W,3),image.dtype) + #极坐标转换 利用tile函数实现W*1铺成的r个矩阵 并对生成的矩阵进行转置 + r=np.linspace(minr,maxr,H) + r=np.tile(r,(W,1)) + r=np.transpose(r) + theta=np.linspace(mintheta,maxtheta,W) + theta=np.tile(theta,(H,1)) + x,y=cv2.polarToCart(r,theta,angleInDegrees=True) + #最近插值法 + for i in range(H): + for j in range(W): + px=int(round(x[i][j])+cx) + py=int(round(y[i][j])+cy) + if((px>=0 and px<=w-1) and (py>=0 and py<=h-1)): + O[i][j][0]=image[py][px][0] + O[i][j][1]=image[py][px][1] + O[i][j][2]=image[py][px][2] + + return O + +import time +if __name__=="__main__": + img = cv2.imread(r"C:\Users\fangxuwei\Desktop\111.jpg") + # 传入的图像宽:600 高:400 + h, w = img.shape[:2] + print("h:%s w:%s"%(h,w)) + # 极坐标的变换中心(300,200) + # cx, cy = h//2, w//2 + cx, cy = 204, 201 + # cx, cy = 131, 123 + # 圆的半径为10 颜色:灰 最小位数3 + cv2.circle(img, (int(cx), int(cy)), 10, (255, 0, 0, 0), 3) + s = time.time() + L = polar(img, (cx, cy), (h//3, w//2)) + # 旋转 + L = cv2.flip(L, 0) + print(time.time()-s) + + # 显示与输出 + cv2.imshow('img', img) + cv2.imshow('O', L) + cv2.waitKey(0) + + + + + diff --git a/tools/__pycache__/MarginLoss.cpython-36.pyc b/tools/__pycache__/MarginLoss.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..34f15590f87e21072bd1a6f318a8f6f11eed2b88 GIT binary patch literal 2086 zcmaJ>NpBoQ6t3QT+IAF&kd>tr3FJohgfSO1LQxRL2#EwKjySX;waZ;Ko*sIcRMm_Z zwI!r6y-Zu62UvzQ{{>93 z1w^8e)u6_@iW+-npSZBL8?X>o&WUGM*7?eB+{SDCt7zmj4z-_Kx6#O*A$Z>*f5LN* zn4IuR;H(yT#jwcByZ~$D5#D3+fUyc6_Asd^KHOY~OXYYX8O5Zit9?xKvKwI-pYwgu zH=JI9z}noSxR1AUE+oz?kZf<8JDV@v*;(H)b)y@-v1~%vjHvuF{jL{*i#Oh&I)NP=QQ?GG=`t;n{KzU*^W}IIK zQ5B|XUga`Pm0z03tB!Usl}BTy!iivMJ!GW{G)?yeG+jiE--0uJQ$#=zWXw0;@vYaq?aqz4XjNL|BAvg&qCq2QOyAr5j)b-ezP1gk zGOsoav0$^<-)`1^1}`GCY>%~jY|{S7>y7a+*G!kfJ>?K;i2kq*l&xl(C|i zV@bCOh$+@ebu}9NYTOA8kkGmmnw{7-Y2bP@UTzCJG;OL|Rm!fafLBZA%DZU|@I*#Bjg}WH|tFF$a)HVTfW#VGL%_WU4ada!4#K$;dCVN~}zQ`1q9! mMNB|5!Nf0NJ^hmW{G4KaBqMHd*yQG?l;)(`v4I=_#0&tx87OT4 literal 0 HcmV?d00001 diff --git a/tools/cal_rescall/__pycache__/__init__.cpython-36.pyc b/tools/cal_rescall/__pycache__/__init__.cpython-36.pyc index 358aeb286a65d7620e63b1a2c3321cead929b57b..22329dee39b8e51a7981ed89206403b0e208ccf4 100644 GIT binary patch delta 16 XcmdnYxS5f|n3tE!G_);YBFAa~BKrh< delta 16 YcmdnYxS5f|n3tF9?$rJ96FF7`04gj6oB#j- diff --git a/tools/cal_rescall/__pycache__/rrc_evaluation_funcs.cpython-36.pyc b/tools/cal_rescall/__pycache__/rrc_evaluation_funcs.cpython-36.pyc index 1aadcb4f5dbcf611522ab4347b9572330293fd0d..e52d2ade26752671239a5a7469a5ea3915a9e180 100644 GIT binary patch delta 220 zcmZq9XwKj;=H=xw4Q)%<$PuT^cx!XIas>-_3IhW}Q7VW?oBT>Wo-uc`m&R=t_5z@Y zTT%XG9bHw%qRBzJ4vdA9C+qHH%$w}17YYiNolcz`(B*FqB*eBZ=iR%^B0NJ-#3yL!HN{To@;=&-p8c1le7PW!c?H~ed pT<7FEBM-(&laCuEFji02Fji-*og857#hnXM(F-CbOzZm|{=W#*L>aexGbL4-As&}1!Y v1+m*e1lWR($#q5^j1wmxH%efvnyg{0&R8=!z}SmB2c)70L` 1): diff --git a/tools/rec_infer1.py b/tools/rec_infer_bk1.py similarity index 100% rename from tools/rec_infer1.py rename to tools/rec_infer_bk1.py diff --git a/tools/rec_train1.py b/tools/rec_train_bk1.py similarity index 100% rename from tools/rec_train1.py rename to tools/rec_train_bk1.py diff --git a/tools/rec_train2.py b/tools/rec_train_bk2.py similarity index 100% rename from tools/rec_train2.py rename to tools/rec_train_bk2.py diff --git a/tools/rec_train3.py b/tools/rec_train_bk3.py similarity index 100% rename from tools/rec_train3.py rename to tools/rec_train_bk3.py

D&b&R=ZR_{T}~A-s~{>?DB! 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

b?YYcc8qmX98jZG;nxPC9H9zQZw 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 z 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