├── LICENSE ├── README.md ├── book_dimension_detection.py ├── book_dimension_top_bottom_edge.py ├── book_dimensions.py ├── book_dimensions_new.py ├── images ├── Output_.png ├── book_final.jpg ├── recorded_lengths.png └── region_1.png ├── qr_code_detection.py └── utils.py /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Measurement-of-Book-Cover 2 | OpenCV based dimensional measurement of a book cover using Homography and Ratio comparison. 3 | 4 | ## Files Description 5 | 6 | 1) `book_dimensions.py` : Simpler, easy and gives lengths of all edges. 7 | 2) `book_dimension_detection.py` : Complex and gives only width and height of the book. 8 | 3) `book_dimension_top_bottom_edge.py` : Complex, gives length of top and bottom edge of the book (only width) 9 | 10 | Folder: `images/` : All output and input images are in this folder. Use `book_final.jpg` as sample image. 11 | 12 | Documentation of each file is in the code itself. 13 | 14 | ## What it does? 15 | Approximation of the dimensions of a cover page of a book using techniques: Homography Algorithms, [QR Code Detection using Zbar] 16 | 17 | ## How it does? 18 | 1) QR Code generation using any online web service. [Example: https://www.qr-code-generator.com/] 19 | 20 | 2) Detection of the QR Code and Text generation [encoded in the QR code - assuming text or any hyperlink etc.] using zbar module in Python. 21 | Credits: learnopencv.com 22 | 23 | 3) Printing out the QR Code on a page, assuming it to be on a book - take the snap of it, and determine the approximate dimensions 24 | of the book cover, using the measured (manually) dimensions of the QR code. 25 | 26 | Note: The QR Code detection has been shown in qr_code_detection.py file, although in the book dimension code - a text has been assumed 27 | instead of QR code because of some unavailability of the printing facilities. The version for QR code will be out soon. 28 | 29 | 4) Homography technique is used, feature detection, choosing the image of the QR code as the selected area. 30 | 31 | ## Challenges 32 | 33 | 1) What if a stack of several books is there and then the snap is clicked? [Challenge credits: Vishwesh Ravi Shrimali]. 34 | 35 | 2) Which angle should the snap be taken? Sideways? Which one is the better? 36 | 37 | 3) Can we measure the number of books in the stack based on the photo angle? 38 | 39 | ## Debugging 40 | 41 | In case of errors, import exceptions for following libraries: 42 | 43 | 1) video : No module named "video" 44 | 45 | Copy and paste video.py from [Video Module: OpenCV](https://github.com/opencv/opencv/blob/master/samples/python/video.py) to the current directory. 46 | 47 | 2) plane\_tracker: No module named 'plane\_tracker' 48 | 49 | Copy and paste plane\_tracker.py from [Plane Tracker: OpenCV](https://github.com/opencv/opencv/blob/master/samples/python/plane_tracker.py) to the current directory. 50 | 51 | 3) common : No module named 'common' 52 | 53 | Copy and paste common.py from [Common Module: OpenCV](https://github.com/opencv/opencv/blob/master/samples/python/common.py) to the current directory. 54 | 55 | 4) numpy : No module named 'numpy' 56 | 57 | Install numpy either using pip or build from source. `pip install numpy` [or `pip3` based on the pip and python versions] should do, if not, please follow the searches on google. 58 | 59 | 5) cv2 : No module named 'cv2' 60 | 61 | You need to install OpenCV2 then. `pip install opencv-python` should do, or follow searches from google for more information. 62 | 63 | Note: No function `ORB_create()` [most probably when you are using python3.5(+) version], if this error comes, then replace `ORB_create(nfeatures = 1000)` with `ORB(nfeatures = 1000)` in `plane_tracker.py` file. 64 | 65 | -------------------------------------------------------------------------------- 66 | Inspiration Credits: learnopencv.com latest blog post on QR Code Detection. 67 | -------------------------------------------------------------------------------- /book_dimension_detection.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | ''' 4 | Book Dimension Approximation 5 | ================== 6 | 7 | Approximate the height and width of the book, using Homography, Feature Detection and zbar 8 | for QR detection. Tracker used from plane_tracker.py in opencv modules. 9 | 10 | Instructions 11 | =================== 12 | 13 | 1) Select the book cover having all the four corners. 14 | 2) Select the text, to be used for finding ratio. [Here the text will be QR code later] 15 | [Note: Here, text has to be "AMBIGUITIES", if not then update the actual lengths in main method.] 16 | 17 | Usage 18 | ----- 19 | book_length_detection.py [] 20 | 21 | Select a plane representing book's corners, QR code scanners. Homography Feature Detection done, 22 | if QR code in the book. Ratio used to calculate the height and width of the book. 23 | ''' 24 | 25 | 26 | # Python 2/3 compatibility 27 | from __future__ import print_function 28 | 29 | import numpy as np 30 | import cv2 as cv 31 | 32 | # local modules 33 | import video 34 | from video import presets 35 | import common 36 | from common import getsize, draw_keypoints 37 | from plane_tracker import PlaneTracker 38 | 39 | class App: 40 | def __init__(self, src): 41 | image = cv.imread(src, 1) 42 | small = cv.resize(image, (0,0), fx=0.125, fy=0.125) # resizing the image, as high resolution image taken 43 | self.frame = small 44 | self.paused = False 45 | self.tracker = PlaneTracker() 46 | 47 | cv.namedWindow('Selected Region') 48 | self.rect_sel = common.RectSelector('Selected Region', self.on_rect) 49 | 50 | def on_rect(self, rect): 51 | self.tracker.clear() 52 | self.tracker.add_target(self.frame, rect) 53 | 54 | def run_original(self): 55 | while True: 56 | playing = not self.paused and not self.rect_sel.dragging 57 | # flag used to quit imaging, when the size detected. 58 | flag = 0 59 | # if playing or self.frame is None: 60 | # # ret, frame = self.cap.read() 61 | # if not ret: 62 | # break 63 | # self.frame = frame.copy() 64 | # size of the frame (image) - used to draw rectangle 65 | w, h = getsize(self.frame) 66 | vis = np.zeros((h, w*2, 3), np.uint8) 67 | # copy to the image (original) 68 | vis[:h,:w] = self.frame 69 | if len(self.tracker.targets) > 0: 70 | target = self.tracker.targets[0] 71 | vis[:,w:] = target.image 72 | draw_keypoints(vis[:,w:], target.keypoints) 73 | x0, y0, x1, y1 = target.rect # rectangle coordinates 74 | cv.rectangle(vis, (x0+w, y0), (x1+w, y1), (0, 255, 0), 2) # green line drawn 75 | width_region = x1 - x0 # width of the selected region 76 | height_region = y1 - y0 # height of the selected region 77 | print("Dimensions of the selected region: (width, height) ", (width_region, height_region)) 78 | flag = 1 # set flag = 1, quit the program! 79 | self.rect_sel.draw(vis) 80 | cv.imshow('Selected Region', vis) # show the image 81 | ch = cv.waitKey(1) 82 | if ch == 27 or flag == 1: 83 | print("Quitting") 84 | return [width_region, height_region] 85 | def run(self, Cover_Dimensions, Actual_Dimensions): 86 | ''' 87 | Runs the program, of selection of the image's region. Approximates the 88 | dimensions of the cover based on given dimensions. 89 | ''' 90 | while True: 91 | # flag used to quit imaging, when the size detected. 92 | flag = 0 93 | playing = not self.rect_sel.dragging 94 | 95 | # get size of the book cover 96 | w, h = getsize(self.frame) 97 | vis = np.zeros((h, w*2, 3), np.uint8) 98 | vis[:h,:w] = self.frame 99 | 100 | if len(self.tracker.targets) > 0: 101 | target = self.tracker.targets[0] 102 | vis[:,w:] = target.image 103 | draw_keypoints(vis[:,w:], target.keypoints) 104 | x0, y0, x1, y1 = target.rect # get the selected region coordinates 105 | cv.rectangle(vis, (x0+w, y0), (x1+w, y1), (0, 255, 0), 2) 106 | 107 | Region_Dimensions = [x1 - x0, y1 - y0] 108 | print("Region_Dimensions:", Region_Dimensions) 109 | # Ratio of the cover dimensions (width) to region dimensions (width) 110 | # similarly for height 111 | ratio_width = float(Cover_Dimensions[0])/(Region_Dimensions[0]) 112 | ratio_height = float(Cover_Dimensions[1])/(Region_Dimensions[1]) 113 | 114 | # print("Ratio of width : ", ratio_width) 115 | # print("Ratio of height : ", ratio_height) 116 | 117 | # Hard coded the actual width and height of the region. 118 | # Actual width (in cm) = 14 119 | # Actual height (in cm) = 1.1 120 | width_of_book = ratio_width * Actual_Dimensions[0] 121 | height_of_book = ratio_height * Actual_Dimensions[1] 122 | 123 | # Optional (useful in case of video source) 124 | if playing: 125 | tracked = self.tracker.track(self.frame) 126 | if len(tracked) > 0: 127 | tracked = tracked[0] 128 | cv.polylines(vis, [np.int32(tracked.quad)], True, (255, 255, 255), 2) 129 | for (x0, y0), (x1, y1) in zip(np.int32(tracked.p0), np.int32(tracked.p1)): 130 | cv.line(vis, (x0+w, y0), (x1, y1), (0, 255, 0)) 131 | print("Detected") 132 | # break when the region is detected. 133 | flag = 1 134 | break 135 | draw_keypoints(vis, self.tracker.frame_points) 136 | 137 | self.rect_sel.draw(vis) 138 | cv.imshow('Selected Region', vis) 139 | cv.waitKey(1) 140 | if flag == 1: 141 | print("Dimensions of the book : ", width_of_book, height_of_book) 142 | return 143 | 144 | if __name__ == '__main__': 145 | print(__doc__) 146 | 147 | import sys 148 | try: 149 | img_src = sys.argv[1] 150 | except: 151 | img_src = "book_new.jpg" 152 | Dimensions_cover = App(img_src).run_original() 153 | # print(Dimensions_cover, [14, 1.1]) 154 | App(img_src).run(Dimensions_cover, [14, 1.1]) 155 | -------------------------------------------------------------------------------- /book_dimension_top_bottom_edge.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | ''' 4 | Book Dimension Approximation 5 | ================== 6 | 7 | Approximate the height and width of the book, using Homography, Feature Detection and zbar 8 | for QR detection. Tracker used from plane_tracker.py in opencv modules. 9 | 10 | Instructions 11 | =================== 12 | 13 | 1) (For top edge) Select a small rectangle, starting point being the (initial) edge point and ending to the 14 | end point of the edge. [This is used to find the edge length] 15 | 2) Repeat step 1 for bottom edge. 16 | 3) Select the text, to be used for Step 1. [Here the text will be QR code later] 17 | 4) Repeat step 3, as it is. 18 | [Note: Here, text has to be "AMBIGUITIES", if not then update the actual lengths in main method.] 19 | 20 | Usage 21 | ----- 22 | book_length_detection.py [] 23 | 24 | Select a plane representing book's corners, QR code scanners. Homography Feature Detection done, 25 | if QR code in the book. Ratio used to calculate the height and width of the book. 26 | ''' 27 | 28 | # Python 2/3 compatibility 29 | from __future__ import print_function 30 | import matplotlib.pyplot as plt 31 | import numpy as np 32 | import cv2 as cv 33 | import math 34 | 35 | # local modules 36 | import video 37 | from video import presets 38 | import common 39 | from common import getsize, draw_keypoints 40 | from plane_tracker import PlaneTracker 41 | 42 | def distance(x0,x1,y0,y1): 43 | sq1 = (x0 - x1)**2 44 | sq2 = (y0 - y1)**2 45 | return math.sqrt(abs(sq1 + sq2)) 46 | 47 | class App: 48 | def __init__(self, src): 49 | image = cv.imread(src, 1) 50 | small = cv.resize(image, (0,0), fx=0.125, fy=0.125) # resizing the image, as high resolution image taken 51 | self.frame = small 52 | self.paused = False 53 | self.tracker = PlaneTracker() 54 | 55 | cv.namedWindow('Selected Region') 56 | # cv.setMouseCallback('Selected Region', self.draw_circle_corner) 57 | self.rect_sel = common.RectSelector('Selected Region', self.on_rect) 58 | 59 | def on_rect(self, rect): 60 | self.tracker.clear() 61 | self.tracker.add_target(self.frame, rect) 62 | 63 | def run_original(self): 64 | 65 | while True: 66 | playing = not self.paused and not self.rect_sel.dragging 67 | # flag used to quit imaging, when the size detected. 68 | flag = 0 69 | # if playing or self.frame is None: 70 | # # ret, frame = self.cap.read() 71 | # if not ret: 72 | # break 73 | # self.frame = frame.copy() 74 | # size of the frame (image) - used to draw rectangle 75 | w, h = getsize(self.frame) 76 | vis = np.zeros((h, w*2, 3), np.uint8) 77 | # copy to the image (original) 78 | vis[:h,:w] = self.frame 79 | if len(self.tracker.targets) > 0: 80 | target = self.tracker.targets[0] 81 | vis[:,w:] = target.image 82 | draw_keypoints(vis[:,w:], target.keypoints) 83 | x0, y0, x1, y1 = target.rect # rectangle coordinates 84 | cv.rectangle(vis, (x0+w, y0), (x1+w, y1), (0, 255, 0), 2) # green line drawn 85 | # finding the width of the region 86 | width_region = distance(x0, x1, y0, y1) 87 | flag = 1 # set flag = 1, quit the program! 88 | self.rect_sel.draw(vis) 89 | cv.imshow('Selected Region', vis) # show the image 90 | ch = cv.waitKey(1) 91 | if ch == 27 or flag == 1: 92 | print("Quitting") 93 | return width_region 94 | 95 | def run(self, Cover_Dimensions, Actual_Dimensions): 96 | ''' 97 | Runs the program, of selection of the image's region. Approximates the 98 | dimensions of the cover based on given dimensions. 99 | ''' 100 | while True: 101 | # flag used to quit imaging, when the size detected. 102 | flag = 0 103 | playing = not self.rect_sel.dragging 104 | 105 | # get size of the book cover 106 | w, h = getsize(self.frame) 107 | vis = np.zeros((h, w*2, 3), np.uint8) 108 | vis[:h,:w] = self.frame 109 | 110 | if len(self.tracker.targets) > 0: 111 | target = self.tracker.targets[0] 112 | vis[:,w:] = target.image 113 | draw_keypoints(vis[:,w:], target.keypoints) 114 | x0, y0, x1, y1 = target.rect # get the selected region coordinates 115 | cv.rectangle(vis, (x0+w, y0), (x1+w, y1), (0, 255, 0), 2) 116 | 117 | Region_Dimensions = [x1 - x0, y1 - y0] 118 | print("Region_Dimensions:", Region_Dimensions) 119 | # Ratio of the cover dimensions (width) to region dimensions (width) 120 | # similarly for height 121 | ratio_width = float(Cover_Dimensions)/(Region_Dimensions[0]) 122 | 123 | # Hard coded the actual width and height of the region. 124 | # Actual width (in cm) = 14 125 | # Actual height (in cm) = 1.1 126 | width_of_book = ratio_width * Actual_Dimensions[0] 127 | # height_of_book = ratio_height * Actual_Dimensions[1] 128 | 129 | # Optional (useful in case of video source) 130 | if playing: 131 | tracked = self.tracker.track(self.frame) 132 | if len(tracked) > 0: 133 | tracked = tracked[0] 134 | cv.polylines(vis, [np.int32(tracked.quad)], True, (255, 255, 255), 2) 135 | for (x0, y0), (x1, y1) in zip(np.int32(tracked.p0), np.int32(tracked.p1)): 136 | cv.line(vis, (x0+w, y0), (x1, y1), (0, 255, 0)) 137 | print("Detected") 138 | # break when the region is detected. 139 | flag = 1 140 | break 141 | draw_keypoints(vis, self.tracker.frame_points) 142 | 143 | self.rect_sel.draw(vis) 144 | cv.imshow('Selected Region', vis) 145 | cv.waitKey(1) 146 | if flag == 1: 147 | print("Dimensions of the book : ", width_of_book) 148 | return 149 | 150 | if __name__ == '__main__': 151 | print(__doc__) 152 | 153 | import sys 154 | try: 155 | img_src = sys.argv[1] 156 | except: 157 | img_src = "book_final.jpg" 158 | 159 | Dimensions_width_top = App(img_src).run_original() 160 | Dimensions_width_bottom = App(img_src).run_original() 161 | 162 | print(Dimensions_width_top) 163 | print(Dimensions_width_bottom) 164 | # App(img_src).cv2.setMouseCallback('image',draw_circle) 165 | 166 | # print(Dimensions_cover, [14, 1.1]) 167 | App(img_src).run(Dimensions_width_top, [14, 1.1]) 168 | App(img_src).run(Dimensions_width_bottom, [14, 1.1]) 169 | -------------------------------------------------------------------------------- /book_dimensions.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | ''' 4 | Book Dimension Approximation 5 | ================== 6 | 7 | Approximate the height and width of the book, using Homography, Feature Detection and zbar 8 | for QR detection. Tracker used from plane_tracker.py in opencv modules. 9 | 10 | Instructions 11 | =================== 12 | (Double left click to select points) 13 | 1) Select corner points of the book cover. Click on the points, starting in this order: 14 | top left -> top right -> bottom right -> bottom left 15 | 2) Select corner points of the relative shape. (test case - "AMBIGUITIES") in this order: 16 | top left -> top right -> bottom right -> bottom left 17 | 3) If the program quits, then it means that you haven't selected all 8 points. 18 | 4) The program will then show real width and height of all the edges. 19 | 20 | [Note: Here, text has to be "AMBIGUITIES", if not then update the actual lengths in main method.] 21 | 22 | Usage 23 | ----- 24 | book_length_detection.py --image [] --real_width [] --real_height [] 25 | 26 | real_height and real_weight : of the relative shape [for test, 1.1 and 14 respectively of "AMBIGUITIES"] 27 | 28 | Select a plane representing book's corners, QR code scanners. Homography Feature Detection done, 29 | if QR code in the book. Ratio used to calculate the height and width of the book. 30 | ''' 31 | # Python 2/3 compatibility 32 | from __future__ import print_function 33 | import argparse 34 | import sys 35 | import cv2 as cv 36 | import math 37 | 38 | def distance(x0,y0,x1,y1): 39 | sq1 = (x0 - x1)**2 40 | sq2 = (y0 - y1)**2 41 | return math.sqrt(abs(sq1 + sq2)) 42 | 43 | def avg(x, y): 44 | return (x + y)/2.0 45 | 46 | refPt = [] # list of reference points of the corners 47 | 48 | # callback function to select corners of the book cover and relative shape 49 | # responds if left button is clicked twice 50 | def click_and_select(event, x, y, flags, param): 51 | global refPt 52 | 53 | if event == cv2.EVENT_LBUTTONDBLCLK: 54 | print("Point selected: ", (x, y)) 55 | refPt.append((x, y)) 56 | 57 | if __name__ == '__main__': 58 | # Print documentation in the beginning 59 | print(__doc__) 60 | 61 | # Add arguments for command line intake 62 | ap = argparse.ArgumentParser() 63 | ap.add_argument("-i", "--image", required=True, help="Path to image") 64 | ap.add_argument("-w", "--real_width", required = True, help = "Real Width") 65 | ap.add_argument("-H", "--real_height", required = True, help = "Real Height") 66 | 67 | try: 68 | args = vars(ap.parse_args()) 69 | 70 | real_width = args["real_width"] 71 | real_height = args["real_height"] 72 | 73 | image = cv2.imread(args["image"]) 74 | except: 75 | print("Check if proper arguments are given.") 76 | print("Quitting") 77 | sys.exit() 78 | 79 | clone = cv.resize(image, (0,0), fx=0.125, fy=0.125) # resizing, reducing higher resolution 80 | 81 | cv.namedWindow("image") 82 | # set mouse call back event for left button double click 83 | cv.setMouseCallback("image", click_and_select) 84 | 85 | while True: 86 | cv.imshow("image", clone) 87 | # record the key pressed 88 | key = cv.waitKey(1) & 0xFF 89 | 90 | # exit if key 'c' is pressed 91 | if key == ord('c'): 92 | if(len(refPt) != 8): 93 | print("Select all 8 coordinates, for proper functioning.") 94 | print("Quitting") 95 | sys.exit(0) 96 | else: 97 | break 98 | 99 | cv.destroyAllWindows() 100 | 101 | # calculate edge lengths based on recorded points 102 | Dimensions_width_top = distance(refPt[0][0], refPt[0][1], refPt[1][0], refPt[1][1]) 103 | Dimensions_width_bottom = distance(refPt[2][0], refPt[2][1], refPt[3][0], refPt[3][1]) 104 | 105 | Actual_Dimensions_top_width = refPt[5][0] - refPt[4][0] 106 | Actual_Dimensions_bottom_width = refPt[6][0] - refPt[7][0] 107 | 108 | Dimensions_height_top = distance(refPt[0][0], refPt[0][1], refPt[3][0], refPt[3][1]) 109 | Dimensions_height_bottom = distance(refPt[2][0], refPt[2][1], refPt[1][0], refPt[1][1]) 110 | 111 | Actual_Dimensions_left_height = abs(refPt[4][1] - refPt[7][1]) 112 | Actual_Dimensions_right_height = abs(refPt[5][1] - refPt[6][1]) 113 | 114 | # find actual edge lengths 115 | width_top = Dimensions_width_top / float(Actual_Dimensions_top_width) * float(real_width) 116 | width_bottom = Dimensions_width_bottom / float(Actual_Dimensions_bottom_width) * float(real_width) 117 | 118 | height_left = Dimensions_height_top / float(Actual_Dimensions_left_height) * float(real_height) 119 | height_right = Dimensions_height_bottom / float(Actual_Dimensions_right_height) * float(real_height) 120 | 121 | print("Top Edge width: ", width_top) 122 | print("Bottom Edge width: ", width_bottom) 123 | print("Left Edge height: ", height_left) 124 | print("Right Edge height: ", height_right) 125 | -------------------------------------------------------------------------------- /book_dimensions_new.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | ''' 4 | Book Dimension Approximation 5 | ================== 6 | 7 | Approximate the height and width of the book, using Homography, Feature Detection and zbar 8 | for QR detection. Tracker used from plane_tracker.py in opencv modules. 9 | 10 | Instructions 11 | =================== 12 | (Double left click to select points) 13 | 1) Select corner points of the book cover. Click on the points, starting in this order: 14 | top left -> top right -> bottom right -> bottom left 15 | 2) Corrected image will be shown in front of you (perspective correction). 16 | 3) Select corner points of the relative shape. (test case - "AMBIGUITIES") in this order: 17 | top left -> top right -> bottom right -> bottom left 18 | 4) If the program quits, then it means that you haven't selected all 8 points. 19 | 5) The program will then show real width and height of all the edges. 20 | 21 | [Note: Here, text has to be "AMBIGUITIES", if not then update the actual lengths in main method.] 22 | 23 | Usage 24 | ----- 25 | book_dimensions_new.py --image [] --real_width [] --real_height [] 26 | 27 | real_height and real_weight : of the relative shape [for test, 1.1 and 14 respectively of "AMBIGUITIES"] 28 | 29 | Select a plane representing book's corners, QR code scanners. Homography Feature Detection done, 30 | if QR code in the book. Ratio used to calculate the height and width of the book. 31 | ''' 32 | # Python 2/3 compatibility 33 | from __future__ import print_function 34 | import argparse 35 | import sys 36 | # import cv2 as cv 37 | import cv2 38 | import math 39 | import numpy as np 40 | from utils import * 41 | 42 | if __name__ == '__main__': 43 | # Print documentation in the beginning 44 | print(__doc__) 45 | 46 | # Add arguments for command line intake 47 | ap = argparse.ArgumentParser() 48 | ap.add_argument("-i", "--image", required=True, help="Path to image") 49 | ap.add_argument("-w", "--real_width", required = True, help = "Real Width") 50 | ap.add_argument("-H", "--real_height", required = True, help = "Real Height") 51 | 52 | try: 53 | args = vars(ap.parse_args()) 54 | 55 | real_width = args["real_width"] 56 | real_height = args["real_height"] 57 | 58 | img = cv2.imread(args["image"]) 59 | 60 | except: 61 | print("Check if proper arguments are given.") 62 | print("Quitting") 63 | sys.exit() 64 | 65 | # Optional 66 | # resizing, reducing higher resolution 67 | clone = cv2.resize(img, (0,0), fx=0.125, fy=0.125) 68 | 69 | size = (300, 400, 3) 70 | 71 | # create numpy array (zero matrix) with given size 72 | img_dest = np.zeros(size, np.uint8) 73 | 74 | # reference points for destination image 75 | refPt_dst = np.array( 76 | [ 77 | [0,0], 78 | [size[0] - 1, 0], 79 | [size[0] - 1, size[1] -1], 80 | [0, size[1] - 1 ] 81 | ], dtype=float 82 | ) 83 | 84 | cv2.imshow("Image", clone) 85 | 86 | # get points of the corner of the books 87 | print('Select points of the corner of the books. (top left to bottom right). Once done, press any key') 88 | points = get_four_points(clone) 89 | 90 | if(len(points) != 4): 91 | print("You are required to enter only 4 points.") 92 | print("Quitting.") 93 | sys.exit() 94 | 95 | # Calculation of Homography matrix 96 | h, status = cv2.findHomography(points, refPt_dst) 97 | 98 | # Warping source image to destination image 99 | img_dest = cv2.warpPerspective(clone, h, size[0:2]) 100 | 101 | # Save the corrected image for future reference 102 | cv2.imwrite("images/book_perspective.jpg", img_dest) 103 | 104 | # set corner of the books to the corner of the image 105 | refPt = [[0, 0], [img_dest.shape[1], 0], [img_dest.shape[1], img_dest.shape[0]], [0, img_dest.shape[0]]] 106 | # refPt = get_four_points(img_dest) # book corners selected earlier 107 | # get reference image corners 108 | print('Select corners of the reference image. (top left to bottom left). Once done, press any key') 109 | refPt_reference = get_four_points(img_dest) 110 | 111 | if(len(refPt_reference) != 4): 112 | print("You are required to enter only 4 points.") 113 | print("Quitting.") 114 | sys.exit() 115 | # print(refPt) 116 | # calculate edge lengths based on recorded points 117 | Dimensions_width_top = distance(refPt[0], refPt[1]) 118 | Dimensions_width_bottom = distance(refPt[2], refPt[3]) 119 | 120 | Actual_Dimensions_top_width = distance(refPt_reference[1], refPt_reference[0]) 121 | Actual_Dimensions_bottom_width = distance(refPt_reference[2], refPt_reference[3]) 122 | 123 | Dimensions_height_left = distance(refPt[0], refPt[3]) 124 | Dimensions_height_right = distance(refPt[2], refPt[1]) 125 | 126 | Actual_Dimensions_left_height = distance(refPt_reference[0], refPt_reference[3]) 127 | Actual_Dimensions_right_height = distance(refPt_reference[1], refPt_reference[2]) 128 | 129 | # find actual edge lengths 130 | width_top = Dimensions_width_top / float(Actual_Dimensions_top_width) * float(real_width) 131 | width_bottom = Dimensions_width_bottom / float(Actual_Dimensions_bottom_width) * float(real_width) 132 | 133 | height_left = Dimensions_height_left / float(Actual_Dimensions_left_height) * float(real_height) 134 | height_right = Dimensions_height_right / float(Actual_Dimensions_right_height) * float(real_height) 135 | 136 | print("Top Edge width: ", width_top) 137 | print("Bottom Edge width: ", width_bottom) 138 | print("Left Edge height: ", height_left) 139 | print("Right Edge height: ", height_right) 140 | -------------------------------------------------------------------------------- /images/Output_.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krshrimali/Dimensionality-Calculation-using-Homography-Matrix-and-QR-Code-Detection/b4d482aa88808b120de18f47ea2d9699a608f436/images/Output_.png -------------------------------------------------------------------------------- /images/book_final.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krshrimali/Dimensionality-Calculation-using-Homography-Matrix-and-QR-Code-Detection/b4d482aa88808b120de18f47ea2d9699a608f436/images/book_final.jpg -------------------------------------------------------------------------------- /images/recorded_lengths.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krshrimali/Dimensionality-Calculation-using-Homography-Matrix-and-QR-Code-Detection/b4d482aa88808b120de18f47ea2d9699a608f436/images/recorded_lengths.png -------------------------------------------------------------------------------- /images/region_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krshrimali/Dimensionality-Calculation-using-Homography-Matrix-and-QR-Code-Detection/b4d482aa88808b120de18f47ea2d9699a608f436/images/region_1.png -------------------------------------------------------------------------------- /qr_code_detection.py: -------------------------------------------------------------------------------- 1 | # Reference: http://www.learnopencv.com/barcode-and-qr-code-scanner-using-zbar-and-opencv/ 2 | # Python 2/3 support 3 | from __future__ import print_function 4 | import pyzbar.pyzbar as pyzbar 5 | import numpy as np 6 | import cv2 7 | 8 | def decode(im) : 9 | # Find barcodes and QR codes 10 | decodedObjects = pyzbar.decode(im) 11 | 12 | # Print results 13 | for obj in decodedObjects: 14 | print('Type : ', obj.type) 15 | print('Data : ', obj.data,'\n') 16 | 17 | return decodedObjects 18 | 19 | 20 | # Display barcode and QR code location 21 | def display(im, decodedObjects): 22 | 23 | # Loop over all decoded objects 24 | for decodedObject in decodedObjects: 25 | points = decodedObject.location 26 | 27 | # If the points do not form a quad, find convex hull 28 | if len(points) > 4 : 29 | hull = cv2.convexHull(np.array([point for point in points], dtype=np.float32)) 30 | hull = list(map(tuple, np.squeeze(hull))) 31 | else : 32 | hull = points; 33 | 34 | # Number of points in the convex hull 35 | n = len(hull) 36 | 37 | # Draw the convext hull 38 | for j in range(0,n): 39 | cv2.line(im, hull[j], hull[ (j+1) % n], (255,0,0), 3) 40 | 41 | # Display results 42 | cv2.imshow("Results", im); 43 | cv2.waitKey(0); 44 | 45 | 46 | # Main 47 | if __name__ == '__main__': 48 | 49 | # Read image 50 | im = cv2.imread('zbar-test.jpg') 51 | 52 | decodedObjects = decode(im) 53 | display(im, decodedObjects) 54 | -------------------------------------------------------------------------------- /utils.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Utility functions for "Measurement of book cover" Project. 3 | Available functions: 4 | 1) Euclidean Distance Function 5 | 2) Average Calculation 6 | 3) Mouse Handling, (Left Button Clicked Once) 7 | 4) Get four points from the user 8 | ''' 9 | import math 10 | import cv2 11 | import numpy as np 12 | 13 | def distance(x, y): 14 | return math.sqrt((x[0] - y[0])**2 + (x[1] - y[1])**2) 15 | 16 | def avg(x, y): 17 | return (x + y)/2.0 18 | 19 | def mouse_handler(event, x, y, flags, data) : 20 | if event == cv2.EVENT_LBUTTONDOWN : 21 | cv2.circle(data['im'], (x,y), 3, (0,0,255), 1, 1); 22 | cv2.imshow("Image", data['im']); 23 | if len(data['points']) < 4 : 24 | data['points'].append([x,y]) 25 | 26 | def get_four_points(im): 27 | 28 | # Set up data to send to mouse handler 29 | data = {} 30 | data['im'] = im.copy() 31 | data['points'] = [] 32 | 33 | #Set the callback function for any mouse event 34 | cv2.imshow("Image",im) 35 | cv2.setMouseCallback("Image", mouse_handler, data) 36 | cv2.waitKey(0) 37 | 38 | # Convert array to np.array 39 | points = np.vstack(data['points']).astype(float) 40 | 41 | return points --------------------------------------------------------------------------------