{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"## Purpose\n\nThis notebook uses RDKit to generate images and bounding boxes for given InChI strings. It's based on [this notebook](https://www.kaggle.com/stainsby/extract-molecule-structure-with-rdkit) by [@stainsby](https://www.kaggle.com/stainsby) and aims to accomplish step 2 of [this outline](https://www.kaggle.com/c/bms-molecular-translation/discussion/229984) by [@CPMP](https://www.kaggle.com/cpmpml).","metadata":{}},{"cell_type":"code","source":"print('Installing required packages. This can take a while ...')\n# We use a specific version of RDKit to ensure consistent results.\n!conda install -q -y -c rdkit rdkit=2020_03_6\n!pip -q install cairosvg==2.5.2\nprint('DONE.')","metadata":{"execution":{"iopub.status.busy":"2021-05-19T23:08:28.392465Z","iopub.execute_input":"2021-05-19T23:08:28.39314Z","iopub.status.idle":"2021-05-19T23:10:59.234814Z","shell.execute_reply.started":"2021-05-19T23:08:28.393035Z","shell.execute_reply":"2021-05-19T23:10:59.233691Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Imports","metadata":{}},{"cell_type":"code","source":"from io import BytesIO\n\nimport numpy as np\nfrom PIL import Image\n\nfrom rdkit import Chem\nfrom rdkit.Chem import AllChem, Draw\nfrom rdkit.Chem.Draw import rdMolDraw2D\n\nimport lxml.etree as et\nimport cairosvg\n\nfrom matplotlib import patches, pyplot as plt","metadata":{"execution":{"iopub.status.busy":"2021-05-19T23:10:59.236696Z","iopub.execute_input":"2021-05-19T23:10:59.236993Z","iopub.status.idle":"2021-05-19T23:10:59.56268Z","shell.execute_reply.started":"2021-05-19T23:10:59.236966Z","shell.execute_reply":"2021-05-19T23:10:59.561692Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Implementation\n\nWe will be extracting the bbox center coordinates, height and width as shown below.\n\n![hi](https://raw.githubusercontent.com/sgrvinod/a-PyTorch-Tutorial-to-Object-Detection/master/img/cs.PNG)","metadata":{}},{"cell_type":"code","source":"def svg_to_image(svg, convert_to_greyscale=True):\n    svg_str = et.tostring(svg)\n    # TODO: would prefer to convert SVG dirrectly to a numpy array.\n    png = cairosvg.svg2png(bytestring=svg_str)\n    image = np.array(Image.open(BytesIO(png)), dtype=np.float32)\n    # Naive greyscale conversion.\n    if convert_to_greyscale:\n        image = image.mean(axis=-1)\n    return image\n\n\ndef drawmol(mol, size, add_hs=False):\n    if add_hs:\n        mol = Chem.AddHs(mol)\n    h_size = 2*size\n    v_size = size\n    atom_height = 15 / v_size\n    atom_width = 15 / h_size\n    min_bond_height, min_bond_width = 20 / v_size, 20 / h_size\n    \n    d = rdMolDraw2D.MolDraw2DSVG(2*size, size)\n    AllChem.Compute2DCoords(mol)\n    mol = rdMolDraw2D.PrepareMolForDrawing(mol)\n    d.DrawMolecule(mol)\n    d.FinishDrawing()\n    drawing =  d.GetDrawingText()\n    svg = et.fromstring(drawing.encode('iso-8859-1'))\n    img = 1 - svg_to_image(svg)/255\n    height, width = img.shape\n    bbox_info = []\n    atoms = mol.GetAtoms()\n    for iatom in range(mol.GetNumAtoms()):\n        p = d.GetDrawCoords(iatom)\n        x, y = p.x, p.y\n        atom = atoms[iatom]\n        num_h = atom.GetNumExplicitHs() + atom.GetNumImplicitHs()\n        chiral_tag = atom.GetChiralTag()\n        chiral_label = chiral_tag != Chem.rdchem.ChiralType.CHI_UNSPECIFIED and f' {chiral_tag}' or ''\n        ring_atom = atom.IsInRing()\n        bonds = []\n        for ibond, bond in enumerate(atom.GetBonds()):\n            stereo = bond.GetStereo()\n            stereo_label = stereo != Chem.rdchem.BondStereo.STEREONONE and f' {stereo}' or ''\n            bond_direction = bond.GetBondDir()\n            bond_direction_label = Chem.rdchem.BondDir.NONE != bond_direction and f' {bond_direction} →' or ' →'\n            in_ring_label = bond.IsInRing() and ' ⭕' or ''\n            bond_type = bond.GetBondType()\n            bond_strength = bond.GetBondTypeAsDouble()\n            from_atom = bond.GetBeginAtomIdx()\n            to_atom = bond.GetEndAtomIdx()\n            bonds.append({\n                'from': from_atom,\n                'to': to_atom,\n                'type': bond_type\n            })\n        bbox_info.append({\n            'x': p.x / h_size,\n            'y': p.y / v_size,\n            'width': atom_width,\n            'height': atom_height,\n            'id': iatom,\n            'name': atom.GetSymbol(),\n            'bonds': bonds\n        })\n\n    \n    # build bond bboxes\n    for atom in bbox_info:\n        for bond in atom['bonds']:\n            if bond['from'] == atom['id']:\n                to_atom = bbox_info[bond['to']]\n                bond['x'] = (min(atom['x'], to_atom['x']) + abs(atom['x'] - to_atom['x']) / 2)\n                bond['y'] = (min(atom['y'], to_atom['y']) + abs(atom['y'] - to_atom['y']) / 2)\n                bond['width'] = max(abs(atom['x'] - to_atom['x']), min_bond_width)\n                bond['height'] = max(abs(atom['y'] - to_atom['y']), min_bond_height)\n    \n    \n    def get_matplotlib_coors(box):\n        x = (box['x'] - box['width'] / 2) * h_size\n        y = (box['y'] - box['height'] / 2) * v_size\n        return (x, y)\n    \n    fig, ax = plt.subplots()\n    fig.set_figheight(15)\n    fig.set_figwidth(15)\n    ax.imshow(img, cmap='gray_r', interpolation='bilinear')\n    for atom in bbox_info:\n        # ax.text(atom['x'], atom['y'], f\"{atom['name']}: {atom['id']}\")\n        ax.add_patch(patches.Rectangle(get_matplotlib_coors(atom), atom['width'] * h_size, atom['height'] * v_size, fill=False, edgecolor='red'))\n        for bond in atom['bonds']:\n            if bond['from'] == atom['id']:\n                ax.add_patch(patches.Rectangle(get_matplotlib_coors(bond), bond['width'] * h_size, bond['height'] * v_size, fill=False, edgecolor='blue'))\n    plt.show()\n    return bbox_info","metadata":{"execution":{"iopub.status.busy":"2021-05-19T23:10:59.563943Z","iopub.execute_input":"2021-05-19T23:10:59.564351Z","iopub.status.idle":"2021-05-19T23:10:59.589943Z","shell.execute_reply.started":"2021-05-19T23:10:59.564321Z","shell.execute_reply":"2021-05-19T23:10:59.588751Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"INCHI = [\n    'InChI=1S/C19H18ClN3O5S/c1-8-11(12(22-28-8)9-6-4-5-7-10(9)20)15(24)21-13-16(25)23-14(18(26)27)19(2,3)29-17(13)23/h4-7,13-14,17H,1-3H3,(H,21,24)(H,26,27)/t13-,14+,17-/m1/s1',\n    \"InChI=1S/C31H25NO3/c1-34-31-17-9-23(8-16-30(33)25-10-13-28(14-11-25)32-18-4-5-19-32)20-27(31)22-35-29-15-12-24-6-2-3-7-26(24)21-29/h2-21H,22H2,1H3/b16-8+\",\n    \"InChI=1S/C18H10Br2N2O4S/c19-11-1-3-13-9(5-11)7-15(17(23)21-13)27(25,26)16-8-10-6-12(20)2-4-14(10)22-18(16)24/h1-8H,(H,21,23)(H,22,24)\", \n    \"InChI=1S/C20H24N2O4S/c1-10-5-6-12-15(7-10)27-20-16(12)19(23)21-18(22-20)11-8-13(24-2)17(26-4)14(9-11)25-3/h8-10,18,22H,5-7H2,1-4H3,(H,21,23)/t10-,18-/m1/s1\", \n    \"InChI=1S/C36H39FN2O7/c1-2-43-34(35(40)41)26-27-10-16-30(17-11-27)45-25-23-39(22-6-7-24-44-31-18-12-28(37)13-19-31)36(42)38-29-14-20-33(21-15-29)46-32-8-4-3-5-9-32/h3-5,8-21,34H,2,6-7,22-26H2,1H3,(H,38,42)(H,40,41)\"\n]\n\nfor _inch in INCHI:\n    mol = Chem.inchi.MolFromInchi(_inch)\n    bbox_info = drawmol(mol, size=200)\n    # display(bbox_info)\n","metadata":{"execution":{"iopub.status.busy":"2021-05-19T23:10:59.591246Z","iopub.execute_input":"2021-05-19T23:10:59.591624Z","iopub.status.idle":"2021-05-19T23:11:02.10206Z","shell.execute_reply.started":"2021-05-19T23:10:59.591593Z","shell.execute_reply":"2021-05-19T23:11:02.10119Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"I'll be using this bbox info to train an object detection model in [detectron2](https://github.com/facebookresearch/detectron2)","metadata":{}},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}