{"id":801,"date":"2021-12-07T18:30:25","date_gmt":"2021-12-07T09:30:25","guid":{"rendered":"https:\/\/rfsec.ddns.net\/db\/?p=801"},"modified":"2021-12-08T16:12:37","modified_gmt":"2021-12-08T07:12:37","slug":"solver%e3%81%a8%e7%b5%84%e3%81%bf%e5%90%88%e3%82%8f%e3%81%9b%e3%81%a6%e6%95%b0%e7%8b%ac%e3%82%92%e8%a7%a3%e3%81%8f","status":"publish","type":"post","link":"https:\/\/rfsec.ddns.net\/db\/?p=801","title":{"rendered":"solver\u3068\u7d44\u307f\u5408\u308f\u305b\u3066\u6570\u72ec\u3092\u89e3\u304f"},"content":{"rendered":"\n<p>solver\u3092\u5916\u90e8\u306eClass\u30d5\u30a1\u30a4\u30eb\u5316<\/p>\n\n\n\n<div class=\"hcb_wrap\"><pre class=\"prism line-numbers lang-python\" data-lang=\"Python\"><code>from __future__ import annotations\nfrom pprint import pformat\nfrom typing import List, Set, Tuple\n\nROWS = COLS = 9\nNUMBERS = [x for x in range(1, 9 + 1)]\n\nclass Grid:\n    &quot;&quot;&quot;\u6570\u72ec\u306e\u30af\u30a4\u30ba\u3092\u8868\u3059\u30b0\u30ea\u30c3\u30c9&quot;&quot;&quot;\n    _values: List[List[int]]\n    def __init__(self, values: List[List[int]]):\n        assert isinstance(values, list)\n        assert len(values) == ROWS\n        for row in values:\n            assert isinstance(row, list)\n            assert len(row) == COLS\n\n        self._values = values\n\n    def __hash__(self):\n        &quot;&quot;&quot;hashable \u5316\u3059\u308b\u305f\u3081\u306e __hash__ \u5b9a\u7fa9\n        - set() \u3067\u5229\u7528\u3059\u308b\u305f\u3081\n        &quot;&quot;&quot;\n        return hash(&#39;&#39;.join(str(x) for row in self._values for x in row))\n\n    def __str__(self):\n        &quot;&quot;&quot;`print()` \u3067\u51fa\u529b\u3055\u308c\u305f\u3068\u304d\u306e\u8868\u73fe\u3092\u5b9a\u7fa9\u3059\u308b&quot;&quot;&quot;\n        return &#39;{}(\\n{}\\n)&#39;.format(type(self).__name__, pformat(self._values))\n\n    def solved(self) -&gt; bool:\n        &quot;&quot;&quot;\u7a7a\u30bb\u30eb\u304c\u306a\u304f\u306a\u3063\u305f\u304b\u3069\u3046\u304b\u3092\u5224\u5b9a\u3059\u308b&quot;&quot;&quot;\n        all_values = [x for row in self._values for x in row]\n        return 0 not in all_values\n\n    def possible_numbers(self) -&gt; List[Tuple[int, int, List[int]]]:\n        &quot;&quot;&quot;\u3059\u3079\u3066\u306e\u7a7a\u30bb\u30eb\u3068\u5165\u308a\u3046\u308b\u6570\u5b57\u306e\u7d44\u307f\u5408\u308f\u305b\u3092\u5168\u4ef6\u6d17\u3044\u51fa\u3059&quot;&quot;&quot;\n        return [\n            (row, col, self._possible_numbers_for_cell(row, col))\n            for row, values in enumerate(self._values)\n            for col, x in enumerate(values)\n            if x == 0\n        ]\n\n    def clone_filled(self, row, col, number) -&gt; Grid:\n        &quot;&quot;&quot;\u7279\u5b9a\u306e\u30bb\u30eb\u306b\u6307\u5b9a\u3055\u308c\u305f\u5024\u304c\u5165\u3063\u305f\u65b0\u3057\u3044 grid \u3092\u8fd4\u3059&quot;&quot;&quot;\n        values = [[x for x in row] for row in self._values]\n        values[row][col] = number\n        return type(self)(values)\n\n    def _possible_numbers_for_cell(self, row, col) -&gt; List[int]:\n        row_numbers = [x for x in self._values[row]]\n        col_numbers = [row[col] for row in self._values]\n        block_numbers = self._block_numbers(row, col)\n\n        return [\n            x\n            for x in NUMBERS\n            if (x not in row_numbers)\n            and (x not in col_numbers)\n            and (x not in block_numbers)\n        ]\n\n    def _block_numbers(self, row, col) -&gt; List[int]:\n        row_start = (row \/\/ 3) * 3\n        col_start = (col \/\/ 3) * 3\n        return [\n            x\n            for row in self._values[row_start : row_start + 3]\n            for x in row[col_start : col_start + 3]\n        ]\n\ndef solve_all(grid: Grid) -&gt; Set[Grid]:\n    &quot;&quot;&quot;\u6307\u5b9a\u3055\u308c\u305f\u6570\u72ec\u306b\u5bfe\u3059\u308b\u89e3\u3092\u5168\u4ef6\u8fd4\u3059&quot;&quot;&quot;\n    solutions = set()\n\n    def _solve(grid: Grid):\n        # S4. \u7a7a\u306e\u30bb\u30eb\u304c\u306a\u304f\u306a\u3063\u305f\u3089\u6b63\u89e3\u3068\u3057\u3066\u8ffd\u52a0\n        if grid.solved():\n            solutions.add(grid)\n            return\n\n        # S1. \u3059\u3079\u3066\u306e\u30bb\u30eb\u306b\u5bfe\u3057\u3066\u5165\u308a\u3046\u308b\u6570\u5b57\u3092\u30ea\u30b9\u30c8\u30a2\u30c3\u30d7\u3059\u308b\n        possible_numbers = grid.possible_numbers()\n\n        # S2 + S3. \u5165\u308a\u3046\u3061\u6570\u5b57\u304c\u6700\u3082\u5c11\u306a\u3044\u30bb\u30eb\u306b\u4eee\u306b\u6570\u5b57\u3092\u5165\u308c\u3066\u518d\u5e30\n        row, col, numbers = min(possible_numbers, key=lambda x: len(x[-1]))\n\n        # S5. \u5165\u308a\u3046\u308b\u6570\u5b57\u304c\u3072\u3068\u3064\u3082\u7121\u3044\u7a7a\u306e\u30bb\u30eb\u304c\u3042\u308b\u5834\u5408\u306f\u305d\u306e\u30eb\u30fc\u30c8\u306f\u9593\u9055\u3044\u306a\u306e\u3067\u7d42\u4e86\n        if not numbers:\n            return\n\n        for number in numbers:\n            next_grid = grid.clone_filled(row, col, number)\n            _solve(next_grid)\n\n    _solve(grid)\n\n    return solutions<\/code><\/pre><\/div>\n\n\n\n<p>OCR\u304b\u3089solver\u3092\u547c\u3073\u51fa\u3057\u3066\u3001\u6570\u72ec\u306e\u56de\u7b54\u3092\u8868\u793a\u3059\u308b\u3002<\/p>\n\n\n\n<div class=\"hcb_wrap\"><pre class=\"prism line-numbers lang-python\" data-lang=\"Python\"><code>import streamlit as st\nimport cv2\nfrom PIL import Image           # \u753b\u50cf\u51e6\u7406\u30e9\u30a4\u30d6\u30e9\u30ea\nimport numpy as np              # \u30c7\u30fc\u30bf\u5206\u6790\u7528\u30e9\u30a4\u30d6\u30e9\u30ea\n#import os                       # os \u306e\u60c5\u5831\u3092\u6271\u3046\u30e9\u30a4\u30d6\u30e9\u30ea\nimport pytesseract              # tesseract \u306e python \u7528\u30e9\u30a4\u30d6\u30e9\u30ea\nimport unicodedata\nimport pprint\nfrom typing import List, Set, Tuple\nimport solver\n\nROWS = COLS = 9\nNUMBERS = [x for x in range(1, 9 + 1)]\n\ndef disp(ans):\n        m1=&quot;&lt;span style=\\&quot;color: red; \\&quot;&gt;&quot;\n        m2=&quot;&lt;\/span&gt;&quot;\n        msg=&quot;### &quot;\n        for x in range(9):\n            for y in range(9):\n                c=ans[x][y]\n                c=str(c)\n                if c != &quot;0&quot;:\n                    msg=msg+m1+c+m2\n                else:\n                    msg=msg+c\n            msg=msg+&#39;&lt;br&gt;&#39;\n\n        msg=msg+&#39;&lt;br&gt;&#39;\n        st.markdown(msg,unsafe_allow_html=True)\n\ndef remove_control_characters(s):\n    return &quot;&quot;.join(ch for ch in s if unicodedata.category(ch)[0]!=&quot;C&quot;)\n\ndef erase_lines(img,img_thresh,th1):\n    # OpenCV\u3067\u76f4\u7dda\u306e\u691c\u51fa\n    # https:\/\/qiita.com\/tifa2chan\/items\/d2b6c476d9f527785414\n    img2 = img.copy()\n    img3 = img.copy()\n    gray = cv2.cvtColor(img_thresh, cv2.COLOR_BGR2GRAY)\n    gray_list = np.array(gray)\n    gray2 = cv2.bitwise_not(gray)\n    gray2_list = np.array(gray2)\n    #lines = cv2.HoughLinesP(gray2, rho=1, theta=np.pi\/360, threshold=th1, minLineLength=80, maxLineGap=5)\n    lines = cv2.HoughLinesP(gray2, rho=1, theta=np.pi\/360, threshold=th1, minLineLength=150, maxLineGap=5)\n    xmin,ymin=500,500\n    xmax,ymax=0,0\n    if lines is not None:\n\n        for line in lines:\n            x1, y1, x2, y2 = line[0]\n            if x1&lt;xmin:\n                xmin=x1\n            if y1&lt;ymin:\n                ymin=y1\n            if x1&gt;xmax:\n                xmax=x1\n            if y1&gt;ymax:\n                ymax=y1\n\n            # \u7dd1\u8272\u306e\u7dda\u3092\u5f15\u304f\n            red_lines_img = cv2.line(img2, (x1,y1), (x2,y2), (0,255,0), 3)\n            red_lines_np=np.array( red_lines_img)\n            #cv2.imwrite(&quot;calendar_mod3.png&quot;, red_lines_img)\n\n            # \u7dda\u3092\u6d88\u3059(\u767d\u3067\u7dda\u3092\u5f15\u304f)\n            no_lines_img = cv2.line(img_thresh, (x1,y1), (x2,y2), (255,255,255), 3)\n            no_lines=np.array( no_lines_img)\n        dx=int(0.5+(xmax-xmin)\/9)\n        dy=int(0.5+(ymax-ymin)\/9)\n        sx=int(0.5+dx*0.05)\n        sy=int(0.5+dy*0.05)\n        st.write(xmin,ymin,xmax,ymax,dx,dy)\n        peaces=[]\n        for y in range(9):\n            for x in range(9):\n                p = xmin + x*dx + sx\n                q = ymin + y*dy + sy\n                cv2.rectangle(no_lines,(p,q),(p+dx-sx,q+dy-sy),(0,0,255),1)\n                peaces.append(cv2.cvtColor(no_lines_img[q:q+dy-sy,p:p+dx-sx],cv2.COLOR_BGR2RGB))\n                #st.image(peace,caption=str(x)+&#39;,&#39;+str(y))\n        im_h= cv2.hconcat([red_lines_img, no_lines])\n    else:\n        im_h = None\n        no_lines = img_thresh\n    return im_h, no_lines,peaces\n\ndef main():\n    st.title(&#39;\u6587\u5b57\u8a8d\u8b58\u306e\u5b9f\u9a13&#39;)\n    col1, col2 ,col3, col4 = st.columns([3,1,1,1])\n    KEI = None\n    with col1:\n        uploaded_file = st.file_uploader(&quot;\u753b\u50cf\u30d5\u30a1\u30a4\u30eb\u3092\u9078\u629e\u3057\u3066\u30a2\u30c3\u30d7\u30ed\u30fc\u30c9&quot;)\n    if uploaded_file is not None:\n        img = Image.open(uploaded_file)\n        img = np.array(img)\n        th2 = st.slider(label=&#39;\uff12\u5024\u5316\u306e\u95be\u5024&#39;,min_value=0, max_value=255, value=100)\n        th1 = st.slider(label=&#39;\u7dda\u6d88\u53bb\u306e\u95be\u5024&#39;,min_value=0, max_value=255, value=100)\n        with col2:\n            LNG =  st.selectbox(&quot;\u8a00\u8a9e\u9078\u629e&quot;,[&#39;eng&#39;,&#39;jpn&#39;])\n        with col3:\n            KEI = st.checkbox(&#39;\u7dda\u524a\u9664&#39;)\n        with col4:\n            OCR = st.checkbox(&#39;OCR\u5b9f\u884c&#39;)\n\n        ret, img_thresh = cv2.threshold(img, th2, 255, cv2.THRESH_BINARY)\n        im_h = cv2.hconcat([img, img_thresh])\n        st.image(im_h, caption=&#39;\u5143\u753b\u50cf&lt;---&gt;\uff12\u5024\u5316\u753b\u50cf&#39;)\n        if KEI:\n            im_h, no_lines, peaces = erase_lines(img,img_thresh,th1)\n            if im_h is None:\n                st.warning(&#39;No line detectd&#39;)\n            else:\n                new_image = cv2.cvtColor(im_h, cv2.COLOR_BGR2RGB)\n                st.image(new_image,caption=&#39;\u7dda\u3092\u524a\u9664\u3057\u305f\u753b\u50cf&#39;)\n        else:\n            no_lines=img_thresh\n\n        if OCR:\n            my_bar = st.progress(0)\n            st.subheader(&#39;[OCR\u7d50\u679c]&#39;)\n            #txt = pytesseract.image_to_string(no_lines, lang=&quot;eng&quot;,config=&#39;--psm 11&#39;)\n            conf=&#39;-l &#39; + LNG + &#39; --psm 6  outputbase digits&#39;\n            n=0\n            row=[]\n            for peace in peaces:\n                txt=pytesseract.image_to_string(peace, config=conf)\n                txt=remove_control_characters(txt)\n                if txt.isdigit():\n                    ans=int(txt)\n                else:\n                    ans=0\n                row.append(ans)\n                my_bar.progress(int(100*n\/80))\n                n=n+1\n\n            row2=np.array(row).reshape(-1,9).tolist()\n            st.success(row2)\n            #st.write(row2)\n\n            grid = solver.Grid(row2)\n            results = solver.solve_all(grid)\n            st.subheader(&#39;[\u6570\u72ec\u56de\u7b54]&#39;)\n            m1=&quot;&lt;span style=\\&quot;color: darkgray; \\&quot;&gt;&quot;\n            m2=&quot;&lt;\/span&gt;&quot;\n            msg=&quot;### &quot;\n            for r in results:\n                buf=[]\n                for y in range(9):\n                    for x in range(9):\n                        buf.append(r._values[y][x])\n                        c = r._values[y][x]\n                        c = str(c)\n                        d = row2[y][x]\n                        if d != 0:\n                            msg=msg + m1 + c + m2\n                        else:\n                            msg=msg + c\n                    msg=msg + &#39;&lt;br&gt;&#39;\n                msg=msg + &quot;&lt;br&gt;&quot;\n                st.markdown(msg,unsafe_allow_html=True)\n#\n#                b = np.array(buf)\n#                disp(b)\n#                c=b.reshape(-1,9)\n#                st.write(c)\nif __name__ == &#39;__main__&#39;:\n    main()<\/code><\/pre><\/div>\n\n\n\n<p>\u5b9f<strong>\u884c\u4f8b\uff1a<\/strong><\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"712\" height=\"785\" src=\"https:\/\/rfsec.ddns.net\/db\/wp-content\/uploads\/2021\/12\/sudoku.png\" alt=\"\" class=\"wp-image-802\" srcset=\"https:\/\/rfsec.ddns.net\/db\/wp-content\/uploads\/2021\/12\/sudoku.png 712w, https:\/\/rfsec.ddns.net\/db\/wp-content\/uploads\/2021\/12\/sudoku-272x300.png 272w, https:\/\/rfsec.ddns.net\/db\/wp-content\/uploads\/2021\/12\/sudoku-624x688.png 624w\" sizes=\"auto, (max-width: 712px) 100vw, 712px\" \/><\/figure>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>solver\u3092\u5916\u90e8\u306eClass\u30d5\u30a1\u30a4\u30eb\u5316 OCR\u304b\u3089solver\u3092\u547c\u3073\u51fa\u3057\u3066\u3001\u6570\u72ec\u306e\u56de\u7b54\u3092\u8868\u793a\u3059\u308b\u3002 \u5b9f\u884c\u4f8b\uff1a<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[1],"tags":[],"class_list":["post-801","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"featured_image_src":null,"author_info":{"display_name":"mars","author_link":"https:\/\/rfsec.ddns.net\/db\/?author=1"},"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/rfsec.ddns.net\/db\/index.php?rest_route=\/wp\/v2\/posts\/801","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/rfsec.ddns.net\/db\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/rfsec.ddns.net\/db\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/rfsec.ddns.net\/db\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/rfsec.ddns.net\/db\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=801"}],"version-history":[{"count":4,"href":"https:\/\/rfsec.ddns.net\/db\/index.php?rest_route=\/wp\/v2\/posts\/801\/revisions"}],"predecessor-version":[{"id":810,"href":"https:\/\/rfsec.ddns.net\/db\/index.php?rest_route=\/wp\/v2\/posts\/801\/revisions\/810"}],"wp:attachment":[{"href":"https:\/\/rfsec.ddns.net\/db\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=801"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/rfsec.ddns.net\/db\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=801"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/rfsec.ddns.net\/db\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=801"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}