A differentiable 3D mesh renderer
Project description
DEODR
DEODR (for DiscontinuityEdgeOverdraw based Differentiable Renderer) is a differentiable 3D mesh renderer written in C with Python and Matlab bindings. The python code provides interfaces with Pytorch and Tensorflow. It provides a differentiable rendering function and its associated reverse mode differentiation function (a.k.a adjoint function) that provides derivatives of a loss defined on the rendered image with respect to the lightning, the 3D vertices positions and the vertices colors. The core triangle rasterization procedures and their adjoint are written in C for speed, while the vertices normals computation and camera projections are computed in either Python (numpy, pytorch or tensorflow) or Matlab in order to gain flexibility and improve the integration with automatic differentiation libraries. The core C++ differentiable renderer has been implemented in 2008 and described in [1,2]. Unlike most other differentiable renderers (except the recent SoftRas [8] and to some extend the differentiable ray/path tracing methods in [10] and [13]), the rendering is differentiable along the occlusion boundaries and no hadhoc approximation is needed in the backpropagation pass to deal with discontinuities along occlusion boundaries. This is achieved by using a differentiable antialiasing method called DiscontinuityEdgeOverdraw [3] that progressively blends the colour of the front triangle with the back triangle along occlusion boundaries.
Table of content
Features
 linearly interpolated color triangles with arbitrary number of color channels.
 textured triangles with Gouraud shading. The gradient backward pass is supported only for linear texture mapping (it is not implemented for perspectivecorrect texture mapping yet).
 derivatives with respect to triangles vertices positions, triangles colors and lights.
 derivatives with respect to the texture pixel intensities
 derivatives with respect to the texture UV coordinates
 derivatives along occlusion boundaries
 differentiability of the rendering function
 exact gradient of the rendering function
 classical camera projection representation used in computer vision
 camera distortion with OpenCV's 5 distortion parameters described here. It requires small triangles surface tesselations as the distortion is applied only at the vertices projection stage.
 possibility to render images corresponding to depth, normals, albedo, shading, xyz coordinates, object/background mask and faces ids. Example here
Some unsupported features:
 SIMD instructions acceleration
 multithreading
 GPU acceleration
 differentiable handling of seams at visible self intersections
 selfcollision detection to prevent interpenetrations (that lead to aliasing and non differentiability along the visible selfintersections)
 phong shading
 gradients backward pass for perspectivecorrect texture mapping
 gradients backward pass for perspectivecorrect vertex attributes/color interpolation
 texture mipmapping (would require trilinear filtering to make it smoother and differentiable)
 shadow casting (making it differentiable would be challenging)
Using texture triangles
Keeping the rendering differentiable everywhere when using texture is challenging: if you use textured triangles you will need to make sure there are no adjacent triangles in the 3D mesh that are simultaneously visible while being disconnected in the UV map, i.e. that there is no visible seam. Otherwise the rendering will not in general be continuous with respect to the 3D vertices positions due to the texture discontinuity along the seam. Depending on the shape of your object, you might not be able to define a continuous UV mapping over the entire mesh and you will need to define the UV texture coordinates in a very specific manner described in Figure 3 in [1], with some constraints on the texture intensities so that the continuity of the rendering is still guaranteed along edges between disconnected triangles in the UV map after texture bilinear interpolation. Note that an improved version of that approach is also described in [8].
Installation
Python
For python 3.6,3.7 and python 3.8 under Windows and Linux you can use
pip install deodr
Otherwise or if that does not work you can try
pip install git+https://github.com/martinResearch/DEODR.git
Matlab
Simply download the zip file, decompress it, run compile.m.
For the hand fitting example you will also need to download my Matlab automatic differentiation toolbox from here add the decompressed folder in your matlab path
Examples
Iterative Mesh fitting in Python
Example of fitting a hand mesh to a depth sensor image deodr/examples/depth_image_hand_fitting.py
Example of fitting a hand mesh to a RGB sensor image deodr/examples/rgb_image_hand_fitting.py
Example of fitting a hand mesh to several RGB sensor images deodr/examples/rgb_multiview_hand.py
iterative mesh fitting in Matlab
Example of a simple triangle soup fitting Matlab/examples/triangle_soup_fitting.m
Example of fitting a hand mesh to a RGB sensor image Matlab/examples/hand_fitting.m. For this example you will also need to download the automatic differentiation toolbox from https://github.com/martinResearch/MatlabAutoDiff
Equations
This code implements the core of the differentiable renderer described in [1,2] and has been mostly written in 20082009. It is anterior to OpenDR and is to my knowledge the first differentiable renderer that deals with vertices displacements to appear in the literature. It renders a set of triangles with either a texture that is bilinearly interpolated and shaded or with interpolated RGB colours. In contrast with most renderers, the intensity of each pixel in the rendered image is continuous and differentiable with respect to the vertices positions even along occlusion boundaries. This is achieved by using a differentiable antialiasing method called DiscontinuityEdgeOverdraw [3] that progressively blends the colour of the front triangle with the back triangle along occlusion boundaries, using a linear combination of the front and back triangles with a mixing coefficient that varies continuously as the reprojected vertices move in the image (see [1,2] for more details). This allows us to capture the effect of change of visibility along occlusion boundaries in the gradient of the loss in a principled manner by simply applying the chain rule of derivatives to our differentiable rendering function. Note that this code does not provide explicitly the sparse Jacobian of the rendering function (where each row would correspond to the color intensity of a pixel of the rendered image, like done in [4]) but it provides the vectorJacobian product operator, which corresponds to the backward function in PyTorch.
This can be used to do efficient analysisbysynthesis computer vision by minimizing the function E that corresponds to the sum or the squared pixel intensities differences between a rendered image and a reference observed image I_{o} with respect to the scene parameters we aim to estimate.
$$E(V)=\sum_{ij} (I(i,j,V)I_o(i,j))^2$$
Using D(i,j) as the adjoint i.e. derivative of the error (for example the squared residual) between observed pixel intensity and synthetized pixel intensity at location (i,j)
$$ D(i,j)=\partial E/\partial I(i,j))$$
We can use DEODR to obtain the gradient with respect to the 2D vertices locations and their colors i.e :
$$\partial E/\partial V_k =\sum_{ij} D(i,j)(\partial I(i,j)/\partial V_k)$$
and
$$\partial E/\partial C_k = \sum_{ij} D(i,j)(\partial I(i,j)/\partial C_k)$$
In combination with an automatic differentiation tool, this core function allows one to obtain the gradient of the error function with respect to the parameters of a complex 3D scene one aims to estimate.
The rendering function implemented in C++ can draw an image given a list of 2D projected triangles with the associated vertices depths (i.e 2.5D scene), where each triangle can have
 a linearly interpolated color between its three extremities
 a texture with linear texture mapping (no perspectivecorrect texture mapping yet but it will lead to noticeable bias only for large triangle that are no frontoparallel)
 a texture combined with shading interpolated linearly (gouraud shading)
We provide a functions in Matlab and Python to obtain the 2.5D representation of the scene from a textured 3D mesh, a camera and a simple lighting model. This function is used in the example in which we fit a 3D hand model to an image. We kept this function as minimalist as possible as we did not intend to rewrite an entire rendering pipeline but to focus on the part that is difficult to differentiate and that cannot be differentiated easily using automatic differentiation.
Our code provides two methods to handle discontinuities at the occlusion boundaries
 the first method consists in antialiasing the synthetic image before comparing it to the observed image.
 the second method consists in antialising the squared residual between the observed image and the synthesized one, and corresponds to the method described in [1]. Note that antialiasing the residual (instead of the squared residual) is equivalent to do antialiasing on the synthetized image and then subtract the observed image.
The choice of the method is done through the Boolean parameter antialiaseError. Both approaches lead to a differentiable error function after summation of the residuals over the pixels and both lead to similar gradients. The difference is subtle and is only noticeable at the borders after convergence on synthetic antialiased data. The first methods can potentially provide more flexibility for the design of the error function as one can for example use a nonlocal image loss by comparing image moments instead of comparing pixel per pixel.
Note: In order to keep the code minimal and well documented, I decided not to provide here the Matlab code to model the articulated hand and the code to update the texture image from observation used in [1]. The hand fitting example provided here does not relies on a underlying skeleton but on a regularization term that penalizes nonrigid deformations. Some Matlab code for Linear Blend Skinning can be found here. Using a Matlab implementation of the skinning equations would allow the use of the Matlab automatic differentiation toolbox provided here to compute the Jacobian of the vertices positions with respect to the hand pose parameters.
Conventions
Pixel coordinates:
If integer_pixel_centers is True (default) then pixel centers are at integer coordinates with
 upper left at (0, 0)
 upper right at (width  1, 0)
 lower left at (0, height  1)
 lower right at (width  1, height  1)
If integer_pixel_centers is False, then pixel centers are at halfinteger coordinates with
 upper left at (0.5, 0.5)
 upper right at (width  0.5, 0.5)
 lower left at (0.5, height  0.5)
 lower right at (width 0.5, height  0.5)
According to this page, OpengGL has always used upper left pixel center at (0.5, 0.5) while Direct3D was using pixel center at (0,0) before version 10 and switched to (0.5,0.5) at version 10.
Texel coordinates:
Unlike in OpenGL Texel (texture pixel) center are at integer coordinates and origin in in the upper left corner of the texture image. The coordinate of the upper left texture pixel center (texel) is (0, 0). The color of the texture bilinearly sampled at float position (0.0,0.0) is texture[0, 0]. The value of the texture bilinearly sampled at float position (0.5,0.5) is equal to the average (texture[0, 0] + texture[0, 1] + texture[1, 0] + texture[1, 1])/4
TO DO
 add support for multiple meshes in the scene
 add support for multiple lights in the scene
 write more unit tests
 add possibility to provide the camera parameters using OpenGL parameterization
 write pure C++ only rendering example
 write pure C++ only mesh fitting example
 accelerate C++ code using SIMD instruction and multithreading
 add automatic texture reparameterization and resampling to avoid texture discontinuities (see section on texture)
 add phong shading
License
BSD 2clause "Simplified" license.
If you use any part of this work please cite the following:
Modelbased 3D Hand Pose Estimation from Monocular Video. M. de la Gorce, N. Paragios and David Fleet. PAMI 2011 pdf
@article{deLaGorce:2011:MHP:2006854.2007005,
author = {de La Gorce, Martin and Fleet, David J. and Paragios, Nikos},
title = {ModelBased 3D Hand Pose Estimation from Monocular Video},
journal = {IEEE Trans. Pattern Anal. Mach. Intell.},
issue_date = {September 2011},
volume = {33},
number = {9},
month = sep,
year = {2011},
issn = {01628828},
pages = {17931805},
numpages = {13},
url = {http://dx.doi.org/10.1109/TPAMI.2011.33},
doi = {10.1109/TPAMI.2011.33},
acmid = {2007005},
publisher = {IEEE Computer Society},
address = {Washington, DC, USA},
}
Projects that use DEODR
 TraceArmature. Set of python scripts that allow for high fidelity motion capture through the use of AI pose estimation (using METRABS), fiducial markers, VR body trackers, and optional hand annotations.
Please let me know if you found DEODR useful by adding a comment in here.
Alternatives

SoftRas (MIT Licence). Method published in [9]. This method consists in a differentiable render with a differentiable forward pass. To my knowledge, this is at the moment the only method besides ours that has a differentiable forward pass and that computes the exact gradient of the forward pass in the backward pass.

OpenDR [4] (MIT Licence) is an open source differentiable renderer written in python and make publicly available in 2014. OpenDR calls OpenGL and relies an a python automatic differentiation toolbox by the same author called chumpy. Like in our code OpenDR uses a intermediate 2.5D representation of the scene using a set of 2D projected triangles. In contrast to our code OpenDR does not provide a continuous loss function as there is not continuous antialiasing formulation at the occlusion boundaries and the minimised function will have jumps when a pixel at the boundary switch between the front of back object. By providing a continuous differentiable error function using edgeoverdraw antialiasing and its exact gradient, our method can lead to better a convergence of continuous optimisation methods..

DIRT (MIT licence) is an open source differentiable renderer that uses approximations in the gradient computation similar OpenDR but that is interfaced with tensorflow. It makes considerable effort to return correctlybehaving derivatives even in cases of selfocclusion, where most other differentiable renderers can fail.

Neural 3D Mesh Renderer (MIT Licence). Method published in [6]. This method consists in a differentiable render whose gradients are designed to be used in neural networks.

tf_mesh_renderer (Apache License 2.0). A differentiable, 3D mesh renderer using TensorFlow. Unlike other differentiable renderer it does not provides suppport for occlusion boundaries in the gradient computation and thus is inadequate for many applications.

Code accompanying the paper [7] github. It renders only silhouettes.

redner Method published in [10]. It is a differentiable pathtracer that can propagate gradients through indirect illumination.

Differentiable Surface Splatting Method publihed in [11]. It is a differentiable implementation of the surface splatting method that allows to render point clouds. The advantage of this approach over mesh based methods is that there is no predefined connectivity associated to the set of points, which allows topological changes during minimization.

DIBRender Method published in [12]. This method removes discontinuies along the projected mesh and background boundary (external silhouette) by rendering background pixels colors using a weighted sum of the background colour and nearby projected faces. However, unlike our method, it does not remove discontinuities along selfocclusions.

Differentiable path tracing Method published in [13]

PyTorch3D's renderer. The method implemented keeps a list of the K nearest faces intersecting the ray corresponding to each pixel in the image (as opposed to traditional rasterization which returns only the index of the closest face in the mesh per pixel). The top K face properties can then be aggregated using different methods (such as the sigmoid/softmax approach proposed by Li et at in SoftRasterizer). Note however that the K face selection is not a continuous operation and thus may potentially lead to discontinuities in the rendered pixel intensities with respect to the shape parameters.

Tensorflow graphics. Library from google that allows differentiable rendering. At the date of march 2020 discontinuities along occlusion boundaries are not handled yet in a differentiable manner. As a result fitting using a loss defined with the rendered image may not converge well in some scenario.

SDFDiff.[15] Differentiable Implicit surface rendering using signed distance functions. This approach seems not to deal with depth discontinuity along the silhouette and self occlusions in a differentiable way, and thus the change in visibility in these locations is not captured in the backpropagated gradients which will hamper convergence, especially in scenarios where the silhouette is an important signal for the fitting..

DIST[16] Differentiable Implicit surface rendering using signed distance functions. The depth discontinuity along the object/background boundary is taken into account in the computation of the silhouette mask only, and is not taken into account in the rendered color, depth or normal images. This could hamper convergence of the surface fitting methods that uses these images in the loss for concave objects that exhibit self occlusion in the chosen camera view point(s).

Nvdiffrast[17] Fast and modular differentiable renderer implemented using OpenGL and cuda with Pytorch and tensoflow interfaces. The method uses deferred rendering which yields great flexibility for the user by allowing the user to write a custom pixel shader using pytorch . An antialiasing step is applied after deferred rendering in order to blend the colors along selfocclusion edges using weights that depend on the distance of the adjacent pixel centers to the line segment. The colors used for blending of along the self occlusion edges are extracted from the nearest points with integer coordinates in the adjacent triangles. This is not a continuous operation and thus may result in small temporal color jumps along the selfocclusion when the edges line segment crosses a pixel center. This may degrade the quality of the gradients as a first order approximations of the change in the loss function w.r.t scene parameters. Moreover the method used to detect self occlusion edges is done in the image space using faces indices which is not very reliable when triangles are small, which introduces noise in the gradient that might be biased. In contrast, our antialiasing method uses the color of the nearest point on the occlusing triangle edge (euclidean projection), which does not lead to discontinuity when edges cross pixel centers, and our silhouette edges detection is done in the object space and thus is reliable regardless of the triangles size during rasterization.
References
[1] Modelbased 3D Hand Pose Estimation from Monocular Video. M. de la Gorce, N. Paragios and David Fleet. PAMI 2011 paper
[2] Modelbased 3D Hand Pose Estimation from Monocular Video. M. de la Gorce. PhD thesis. Ecole centralde de Paris 2009. paper
[3] Discontinuity edge overdraw P.V. Sander and H. Hoppe, J.Snyder and S.J. Gortler. SI3D 2001 paper
[4] OpenDR: An Approximate Differentiable Renderer Loper, Matthew M. and Black, Michael J. ECCV 2014 paper code online documentation
[5] A Morphable Model For The Synthesis Of 3D Faces. Volker Blanz and Thomas Vetter. SIGGRAPH 99. paper
[6] Neural 3D Mesh Renderer. Hiroharu Kato and Yoshitaka Ushiku and Tatsuya Harada.CoRR 2017 paper
[7] Endtoend 6DoF Object Pose Estimation through Differentiable Rasterization Andrea Palazzi, Luca Bergamini, Simone Calderara, Rita Cucchiara. Second Workshop on 3D Reconstruction Meets Semantics (3DRMS) at ECCVW 2018. paper
[8] Mesh Color textures Cem Yuksel. Proceedings of High Performance Graphics 2017. paper
[9] Soft Rasterizer: A Differentiable Renderer for Imagebased 3D Reasoning. Shichen Liu, Tianye Li, Weikai Chen and Hao Li. ICCV 2019. paper
[10] Differentiable Monte Carlo Ray Tracing through Edge Sampling. zuMao Li, Miika Aittala, Frédo Durand, Jaakko Lehtinen. SIGGRAPH Asia 2018. project page
[11] Differentiable Surface Splatting for Pointbased Geometry Processing. Felice Yifan, Serena Wang, Shihao Wu, Cengiz Oztireli and Olga SorkineHornung. SIGGRAPH Asia 2019. paper and video
[12] Learning to Predict 3D Objects with an Interpolationbased Differentiable Renderer Wenzheng Chen, Jun Gao, Huan Ling, Edward J. Smith, Jaakko Lehtinen, Alec Jacobson, Sanja Fidler. NeurIPS 2019. paper
[13] Reparameterizing discontinuous integrands for differentiable rendering. Guillaume Loubet, Nicolas Holzschuch and Wenzel Jakob. SIGGRAPH Asia 2019. project page
[14] TensorFlow Graphics: Computer Graphics Meets Deep Learning. Valentin, Julien and Keskin, Cem and Pidlypenskyi, Pavel and Makadia, Ameesh and Sud, Avneesh and Bouaziz, Sofien. 2019
[15] SDFDiff: Differentiable Rendering of Signed Distance Fields for 3D Shape Optimization. Yue Jiang, Dantong Ji, Zhizhong Han, Matthias Zwicker. CVPR 2020. code
[16] DIST: Rendering Deep Implicit Signed Distance Function with Differentiable Sphere Tracing. Shaohui Liu1, Yinda Zhang, Songyou Peng, Boxin Shi, Marc Pollefeys, Zhaopeng Cui. CVPR 2020. code
[17] Modular Primitives for HighPerformance Differentiable Rendering. Samuli Laine, Janne Hellsten, Tero Karras, Yeongho Seol, Jaakko Lehtinen, Timo Aila. paper.
Project details
Release history Release notifications  RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distributions
Hashes for DEODR0.2.2pp39pypy39_pp73win_amd64.whl
Algorithm  Hash digest  

SHA256  e9f3d6a1ff65e6442cc9f073b33ada93373396a5d7cf0ba166abc1e91ad46bb7 

MD5  a77c0d4da5c8b7644b5e159a528f88b6 

BLAKE2b256  851c2bfd88286682656e1f21086a666ddd623f8adfedee0c192baf7d7e476932 
Hashes for DEODR0.2.2pp39pypy39_pp73manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm  Hash digest  

SHA256  5a68e5b0417e2d337d7a92c200af2e4ae353d075856769d268bb305ed340d980 

MD5  5725ec0405d2c611ecbf806b9af49752 

BLAKE2b256  efd1712cb76dbfe1ad762243d0627b93bde9f7b7d506ad04e11d165ca6254347 
Hashes for DEODR0.2.2pp39pypy39_pp73manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl
Algorithm  Hash digest  

SHA256  d90f3e3095db6430e3abe895c945d5ef8a5768e36f06553730f47c31cde18672 

MD5  3a48dcd0fc1171a0eff6f1f515c45cf8 

BLAKE2b256  d1e5a9f328482dc8ae0711dfa2a1bf73b79baff7f1e79a2e60423e7997305f0b 
Hashes for DEODR0.2.2pp39pypy39_pp73macosx_10_9_x86_64.whl
Algorithm  Hash digest  

SHA256  bfec6a1649143152944dd4f787c7f4bcf26b9d1150ded07f4e67a0f3056d12ce 

MD5  ff4edcb8f32ecf3b69828e2ed46de324 

BLAKE2b256  baa11c507ce7ebe43de3bf4177bdb653176d439039d15cdadbe3848a7291f85a 
Hashes for DEODR0.2.2cp312cp312win_amd64.whl
Algorithm  Hash digest  

SHA256  3e3e707d1ffd58c076b87b742510d5a5e2e8c2922706b2014b30f4c4b57d9629 

MD5  8806541f91dcf16f348c0d5b9c3cfbb2 

BLAKE2b256  6266a11c6ab2a0df8c7acd492fa656e1418a365c6d902e4a854c4999eb11e23c 
Hashes for DEODR0.2.2cp312cp312win32.whl
Algorithm  Hash digest  

SHA256  cb05097d1afb21cda7700bf9d9dae4025b3fc63ca63c9449a778ef0e6d3a00c5 

MD5  a41080024fcaba3ee44d43ea97b3f8de 

BLAKE2b256  44838323fae9620eb4c7c3c9f6cecee8499bf5bd2b1cb360fbe52181bbf5e6a7 
Hashes for DEODR0.2.2cp312cp312musllinux_1_1_x86_64.whl
Algorithm  Hash digest  

SHA256  a469c446b2cb6a73a500f2576d2de1d3ab0bf700ed50a282fbb38531f08c7781 

MD5  de77e6ca40d4728d774823710d7b0338 

BLAKE2b256  67685121129e879e5a66394ab9dfbe54c2477765db6502b0b3e7c391b0704204 
Hashes for DEODR0.2.2cp312cp312musllinux_1_1_i686.whl
Algorithm  Hash digest  

SHA256  d852b9d48c6ef4150c35894db0c264a5cee4d1edb04f4ffd83fd8b7eb4b4baeb 

MD5  ed6ebc9fae84824a794e0b3e4b8ef820 

BLAKE2b256  d024b5c1ed4085cb30109f342f1e5498df618d07d18373da737768e10a2c7dcb 
Hashes for DEODR0.2.2cp312cp312manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm  Hash digest  

SHA256  95119ea0556e50215ac950518a1781b151a5802f26b500413f019674f6c075e2 

MD5  4fe6ea63dbb2cb1729c70d9a122ff772 

BLAKE2b256  c2b27b4e49b4551339325870fea943be20523f646c4ef2b3a3d58261d7521807 
Hashes for DEODR0.2.2cp312cp312manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl
Algorithm  Hash digest  

SHA256  c94920fb7f8197e6be76ab610f0775fc5d28ee133a9bbc25cf9d35401fdb2ce2 

MD5  5a097d221c586c848bc60dee23c98183 

BLAKE2b256  54e59272abbb5430e60a4ca93f2d39780c7bbaaa5390c73e8417092fe3800c6c 
Hashes for DEODR0.2.2cp312cp312macosx_10_9_x86_64.whl
Algorithm  Hash digest  

SHA256  95f8ed70b95f3bae291db5a0cfa5adba4322f7b55c21616d5c6df30af270f6d9 

MD5  9351161c1cc17f3effc8692d5dc94ac4 

BLAKE2b256  14c14c3adda592307dcd27ac1a7ff8854af17405c8509d490f391ba5dd6a15df 
Hashes for DEODR0.2.2cp311cp311win_amd64.whl
Algorithm  Hash digest  

SHA256  915a1e71cc6a09bbc4a60c63b596c1dae711fd80fa226250052423549e7ecacf 

MD5  d1e3669d13521cea4eec08cfa1b2435b 

BLAKE2b256  dda930233508f8f1e72acd051cb7e3262802b390aab9ba32c6663ea50da07d83 
Hashes for DEODR0.2.2cp311cp311win32.whl
Algorithm  Hash digest  

SHA256  af84c78e93e6a7ef53d79f9b7b22748659d49de3ae57deab1b96756b931469a0 

MD5  8bd7b39b15e7317c8ffed23ae1906921 

BLAKE2b256  b7bf6ad11a4f352cd6733770d67ac8f17225f7fc3e50f4bb2309ba0a30d821d2 
Hashes for DEODR0.2.2cp311cp311musllinux_1_1_x86_64.whl
Algorithm  Hash digest  

SHA256  5933dceb1aad2000fff195ab23e240601be452ecd14d619becc6f7115a584397 

MD5  569125d21645c48a7f1935d68e817b37 

BLAKE2b256  09a2f3bdb61e025913722405e74cf068faacc2199a38785f3dd3a519d3caae99 
Hashes for DEODR0.2.2cp311cp311musllinux_1_1_i686.whl
Algorithm  Hash digest  

SHA256  9b8d2ccc73149e6556d6b459c8a26ebb4bdfcbdd5f94833acfb9027c0a99d179 

MD5  36beae4e60303e351b6bc227b8d2ed29 

BLAKE2b256  07629e0c6ab0344850a8b6764ef8cf85a0bcd58d7cc81c983991a1e62a8268e8 
Hashes for DEODR0.2.2cp311cp311manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm  Hash digest  

SHA256  7df0020152b5d5e43415a256cb012e086246fe35aeaba22fe61e59b2a533cde6 

MD5  00cdeab1ecf8a1a959471faf70f959ed 

BLAKE2b256  bfe7866561d4ddc8055df4a64bd4159a6c586f58d076e278a7e862d161860f8f 
Hashes for DEODR0.2.2cp311cp311manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl
Algorithm  Hash digest  

SHA256  7e72bc5bc1151f98f3c15cace71e46923557b99e845515dabbab04f7326e81a6 

MD5  84aa76fe9f210d966c1c731a48f4c595 

BLAKE2b256  1ddc36a6d04292d98393ca97bbe4145c567847343be3642acdce769e3a3d1edc 
Hashes for DEODR0.2.2cp311cp311macosx_10_9_x86_64.whl
Algorithm  Hash digest  

SHA256  16adea3d4d4c21722f8b99d434a1c3661054e03cb20539fb7056331763de9c5d 

MD5  2cd06405baec0e616f38c5946429bc4b 

BLAKE2b256  4c2636acc1cfd6e5a961fd5ec4eb31e457067eb77e2e38a3f6ffa4e041bc0425 
Hashes for DEODR0.2.2cp310cp310win_amd64.whl
Algorithm  Hash digest  

SHA256  9858ac904e243aec69379868bdbab199ae50383c8679e9ab2b1ebc1c739f93f7 

MD5  2064229eab9eb72926c91d0ad1d84c70 

BLAKE2b256  48ac2278421ebf2bcf5e163bd4f8a3fd8a3a982026be34a1fe7c2aa338e9eb27 
Hashes for DEODR0.2.2cp310cp310win32.whl
Algorithm  Hash digest  

SHA256  99fcf7af5b4146dcb32a32701494723a3aa9ab3be3d9d6abda21a9d3ffb836b8 

MD5  68903c14e32c9fbf013970493cc03682 

BLAKE2b256  56b2f4cf7736e136daa2830bb5864b04ad421fea8896236dbce84f639e66534d 
Hashes for DEODR0.2.2cp310cp310musllinux_1_1_x86_64.whl
Algorithm  Hash digest  

SHA256  35cb2cc9e18903f29898cd8a24828b5763f9cb4e0d0b89c64ea7a4cdcfed20d3 

MD5  aefa97050f2e20835fbfdf200f315627 

BLAKE2b256  1fb4967fc8d8675d24aad89fe915e424db3a5a4123ed0746c711d5a209303934 
Hashes for DEODR0.2.2cp310cp310musllinux_1_1_i686.whl
Algorithm  Hash digest  

SHA256  2009fd13b65a948ca7d91fd63f26293ab4623570bbc197b54df8ddc635e7830b 

MD5  5062c32d26c10014844e49f0b9687823 

BLAKE2b256  1bfbac7b71c4f61766d28a38cb11e3cc9c5eb16c3a4511ccef1e66bb5a6fcf38 
Hashes for DEODR0.2.2cp310cp310manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm  Hash digest  

SHA256  49750032b8795c74939f7a829122e5a832712cfc7b46740eb78e1ccaafaaec19 

MD5  85992638107dd896ad29fa4379354362 

BLAKE2b256  0a97e73e265d8e18ee7a1ca6e2edf074ba19713682730dc5f8254d20c49e02c3 
Hashes for DEODR0.2.2cp310cp310manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl
Algorithm  Hash digest  

SHA256  c9f241d268072c56a05004d8701b52dcac4e09bec988cb8cddc18fda6536eb67 

MD5  fee97da1dddee455c7887014e5e09ce2 

BLAKE2b256  3330768565ce0219c2476955e47431d4160a73eeebba10b48f29a7b6b49e811b 
Hashes for DEODR0.2.2cp310cp310macosx_10_9_x86_64.whl
Algorithm  Hash digest  

SHA256  2543b199da36789f2cd7e5a6445aeaafa9a2d363ccb16c1d07c82bc7d4d3c765 

MD5  6b063a3431a5bf92f980eaed0187cfd8 

BLAKE2b256  07b8f1dd4f94d525ff4380f92b92a0804b5dcd7804e1dadfea085ea9ae7da086 
Hashes for DEODR0.2.2cp39cp39win_amd64.whl
Algorithm  Hash digest  

SHA256  eaa5d5d95b108be044e79cfe21cada8930ad15702117af656c2fc0cd20dd11ea 

MD5  88b547828a097d8ab9a25f7bc22e9592 

BLAKE2b256  4ae01bf4c05376f0c1424534db700580d4613eefebb5bc71c57e995f2ad9a965 
Hashes for DEODR0.2.2cp39cp39musllinux_1_1_x86_64.whl
Algorithm  Hash digest  

SHA256  e7f9e82c50f25b911456d635e8ad2879f3998f781b1045ba7256ff8f612db025 

MD5  67aca55997a26aa1bd7d40399da356f9 

BLAKE2b256  9140a7ff1e5f71b0a0396f4fd000cabc2ca393253bcb60fbb11e40804f9a1c53 
Hashes for DEODR0.2.2cp39cp39musllinux_1_1_i686.whl
Algorithm  Hash digest  

SHA256  6ff8a25ab7cf633dd8610344ea10c369d3375efe57d5871fca9f1eaa66495bfc 

MD5  7d5a31675d81f079baf9ced51db0a21c 

BLAKE2b256  9a1665427e081a44059843409658ddb7baa84ed0226e15e0f9a7df1f2a661653 
Hashes for DEODR0.2.2cp39cp39manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm  Hash digest  

SHA256  44651268844994c5285a098f1e025baecd3a04f088fc424d7542907ced9818de 

MD5  299b15d8b6488057d6cf7455f499c6fa 

BLAKE2b256  9222db7e168d81b79701c103fa2a9b7526b120fcc1db102f229a0980d7905c82 
Hashes for DEODR0.2.2cp39cp39manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl
Algorithm  Hash digest  

SHA256  0efca30fef63749b8d1fe721afddbc9418a3efad099d484405c673b2861a4ca9 

MD5  d1eed7a9adaa552cf2c050660a896949 

BLAKE2b256  9978f3695ae7985c83c963ae0008c0aeb828751311b07963bab778100d397219 
Hashes for DEODR0.2.2cp39cp39macosx_10_9_x86_64.whl
Algorithm  Hash digest  

SHA256  6c92745d44384f2dabb4b182a582d14f4fbd9c9ed0f5cd42047222afb90a2d8d 

MD5  65d2d8b5984b7ed54b52f125c7ed8efc 

BLAKE2b256  7a4b4e4f57db13b57b117285b3821110c3582954996acc0f4298ea7c8fd65eb7 