├── src ├── .gitignore ├── Exception.cpp ├── Makefile ├── ComponentID.cpp ├── CircuitEntityConnection.cpp ├── Connection.cpp ├── DebugRow.cpp ├── ResultCircuits.cpp ├── ConnVector.cpp ├── Logger.cpp ├── SpaceIterator.cpp ├── PeakFinder.cpp ├── Transform.cpp ├── Point.cpp ├── WhiteoutPerimeter.cpp ├── PeakVector.cpp ├── WhiteoutPeakFinder.cpp ├── ConnFinder.cpp ├── SmartComponentID.cpp ├── Rectangle.cpp ├── WhiteoutConnFinder.cpp ├── Tesseract.cpp ├── Whiteout.cpp ├── AlgorithmDebug.cpp ├── Filter.cpp ├── PortMapper.cpp ├── main.cpp ├── OutsideRectangleIterator.cpp ├── CircuitEntity.cpp ├── AlgorithmBase.cpp ├── Circuit.cpp └── NetlistGenerator.cpp ├── Makefile ├── include ├── TerminatedException.h ├── Logger.h ├── Exception.h ├── ComponentID.h ├── Transform.h ├── Tesseract.h ├── ResultEnglish.h ├── DebugRow.h ├── ConnVector.h ├── CircuitEntityConnection.h ├── ResultCircuits.h ├── Mask.h ├── PeakVector.h ├── PortMapper.h ├── WhiteoutPerimeter.h ├── SpaceIterator.h ├── Space.h ├── PeakFinder.h ├── ConnFinder.h ├── Point.h ├── WhiteoutPeakFinder.h ├── SmartComponentID.h ├── AlgorithmDebug.h ├── Connection.h ├── Whiteout.h ├── Algorithm.h ├── OutsideRectangleIterator.h ├── WhiteoutConnFinder.h ├── Filter.h ├── Rectangle.h ├── AlgorithmResult.h ├── NetlistGenerator.h ├── CircuitEntity.h ├── AlgorithmBase.h └── Circuit.h ├── README.md └── LICENSE.txt /src/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | circuitsense 3 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | @cd src; $(MAKE) all 3 | 4 | clean: 5 | @cd src; $(MAKE) clean 6 | -------------------------------------------------------------------------------- /src/Exception.cpp: -------------------------------------------------------------------------------- 1 | // ----------------------------------------------------------------------------- 2 | // Copyright (c) 2011-2012 Matt Pillar. All rights reserved. 3 | // ----------------------------------------------------------------------------- 4 | 5 | #include "Exception.h" 6 | 7 | namespace cs 8 | { 9 | 10 | const std::string& Exception::what() const 11 | { 12 | return m_message; 13 | } 14 | 15 | } // end namespace cs 16 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | TOPDIR = .. 2 | 3 | SRCS = $(wildcard *.cpp) 4 | OBJS = $(addprefix ., ${SRCS:.cpp=.o}) 5 | 6 | BINARY = circuitsense 7 | 8 | all: ${BINARY} 9 | 10 | ${BINARY}: ${OBJS} 11 | @echo "$(shell date +%H:%M:%S) Linking..." 12 | @g++ ${OBJS} -o ${BINARY} \ 13 | -lopencv_core \ 14 | -lopencv_highgui \ 15 | -lopencv_imgproc \ 16 | -ltesseract 17 | 18 | .%.o: %.cpp 19 | @echo "$(shell date +%H:%M:%S) Building $?..." 20 | @g++ -c $? -I../include -o $@ 21 | 22 | clean: 23 | @rm -f ${OBJS} ${BINARY} 24 | -------------------------------------------------------------------------------- /src/ComponentID.cpp: -------------------------------------------------------------------------------- 1 | // ----------------------------------------------------------------------------- 2 | // Copyright (c) 2011-2012 Matt Pillar. All rights reserved. 3 | // ----------------------------------------------------------------------------- 4 | 5 | #include 6 | #include 7 | 8 | #include "ComponentID.h" 9 | 10 | namespace cs 11 | { 12 | 13 | std::string ComponentID::ToString() const 14 | { 15 | std::ostringstream os; 16 | os << GetID(); 17 | return os.str(); 18 | } 19 | 20 | } // end namespace cs 21 | -------------------------------------------------------------------------------- /src/CircuitEntityConnection.cpp: -------------------------------------------------------------------------------- 1 | // ----------------------------------------------------------------------------- 2 | // Copyright (c) 2011-2012 Matt Pillar. All rights reserved. 3 | // ----------------------------------------------------------------------------- 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "CircuitEntityConnection.h" 11 | 12 | namespace cs 13 | { 14 | 15 | std::string CircuitEntityConnection::ToString() const 16 | { 17 | std::ostringstream os; 18 | os << "port=" << GetPort() << " -> node=" << GetNode(); 19 | return os.str(); 20 | } 21 | 22 | } // end namespace cs 23 | -------------------------------------------------------------------------------- /src/Connection.cpp: -------------------------------------------------------------------------------- 1 | // ----------------------------------------------------------------------------- 2 | // Copyright (c) 2011-2012 Matt Pillar. All rights reserved. 3 | // ----------------------------------------------------------------------------- 4 | 5 | #include 6 | #include 7 | 8 | #include "Connection.h" 9 | 10 | namespace cs 11 | { 12 | 13 | std::string Connection::ToString() const 14 | { 15 | std::ostringstream os; 16 | 17 | os << "{" 18 | << m_peak1 << ":" << m_point1.ToString() 19 | << m_peak2 << ":" << m_point2.ToString() 20 | << "}"; 21 | 22 | return os.str(); 23 | } 24 | 25 | } // end namespace cs 26 | -------------------------------------------------------------------------------- /src/DebugRow.cpp: -------------------------------------------------------------------------------- 1 | // ----------------------------------------------------------------------------- 2 | // Copyright (c) 2011-2012 Matt Pillar. All rights reserved. 3 | // ----------------------------------------------------------------------------- 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include "DebugRow.h" 10 | 11 | namespace cs 12 | { 13 | 14 | std::string DebugRow::ToString() const 15 | { 16 | std::ostringstream os; 17 | os 18 | << m_threshold << "," 19 | << m_confidence << "," 20 | << m_components << "," 21 | << m_textConfidence << "," 22 | << m_textComponents; 23 | 24 | return os.str(); 25 | } 26 | 27 | } // end namespace cs 28 | -------------------------------------------------------------------------------- /src/ResultCircuits.cpp: -------------------------------------------------------------------------------- 1 | // ----------------------------------------------------------------------------- 2 | // Copyright (c) 2011-2012 Matt Pillar. All rights reserved. 3 | // ----------------------------------------------------------------------------- 4 | 5 | #include 6 | #include 7 | 8 | #include "ResultCircuits.h" 9 | #include "Tesseract.h" 10 | 11 | namespace cs 12 | { 13 | 14 | std::string ResultCircuits::VectorToString(const CircuitVector& vec) 15 | { 16 | if (vec.empty()) 17 | { 18 | return std::string(); 19 | } 20 | 21 | std::ostringstream os; 22 | 23 | for (CircuitVector::const_iterator it = vec.begin(); it != vec.end(); it++) 24 | { 25 | os << it->ToString() << ","; 26 | } 27 | 28 | return os.str().substr(0, os.str().size()-1); 29 | } 30 | 31 | } // end namespace cs 32 | -------------------------------------------------------------------------------- /src/ConnVector.cpp: -------------------------------------------------------------------------------- 1 | // ----------------------------------------------------------------------------- 2 | // Copyright (c) 2011-2012 Matt Pillar. All rights reserved. 3 | // ----------------------------------------------------------------------------- 4 | 5 | #include 6 | 7 | #include "ConnFinder.h" 8 | 9 | namespace cs 10 | { 11 | 12 | void ConnVector::Push(Connection c) 13 | { 14 | m_conns.push_back(c); 15 | } 16 | 17 | std::string ConnVector::ToString() const 18 | { 19 | if (m_conns.size() == 0) 20 | { 21 | return "{}"; 22 | } 23 | 24 | std::ostringstream os; 25 | 26 | for (ConnectionVector::const_iterator it = m_conns.begin(); 27 | it != m_conns.end(); it++) 28 | { 29 | os << it->ToString() << std::endl; 30 | } 31 | 32 | return os.str().substr(0, os.str().size()-1); 33 | } 34 | 35 | } // end namespace cs 36 | -------------------------------------------------------------------------------- /include/TerminatedException.h: -------------------------------------------------------------------------------- 1 | // ----------------------------------------------------------------------------- 2 | // Copyright (c) 2011-2012 Matt Pillar. All rights reserved. 3 | // ----------------------------------------------------------------------------- 4 | 5 | #ifndef API_EXCEPTION_TERMINATEDEXCEPTION_H 6 | #define API_EXCEPTION_TERMINATEDEXCEPTION_H 7 | 8 | #include 9 | #include "Exception.h" 10 | 11 | namespace cs 12 | { 13 | 14 | // ----------------------------------------------------------------------------- 15 | // class TerminatedException 16 | // ----------------------------------------------------------------------------- 17 | 18 | class TerminatedException : public Exception 19 | { 20 | public: 21 | // Constructor 22 | TerminatedException(const std::string& message) : 23 | Exception(message) 24 | { 25 | // Nothing to do. 26 | } 27 | }; 28 | 29 | } // end namespace cs 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /include/Logger.h: -------------------------------------------------------------------------------- 1 | // ----------------------------------------------------------------------------- 2 | // Copyright (c) 2011-2012 Matt Pillar. All rights reserved. 3 | // ----------------------------------------------------------------------------- 4 | 5 | #ifndef API_DEBUG_LOGGER_H 6 | #define API_DEBUG_LOGGER_H 7 | 8 | #include 9 | #include 10 | 11 | namespace cs 12 | { 13 | 14 | // ----------------------------------------------------------------------------- 15 | // class Logger 16 | // ----------------------------------------------------------------------------- 17 | 18 | class Logger 19 | { 20 | private: 21 | std::string GetTimeStamp(); 22 | 23 | public: 24 | // Constructor. 25 | Logger() 26 | { 27 | // Nothing to do. 28 | } 29 | 30 | // Log a message. 31 | void Log(const std::string& s); 32 | }; 33 | 34 | void operator<< (Logger& out, const std::string& s); 35 | 36 | } 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /src/Logger.cpp: -------------------------------------------------------------------------------- 1 | // ----------------------------------------------------------------------------- 2 | // This header contains the implmenetation for the general purpose logging class 3 | // as part of the debug library. 4 | // 5 | // Copyright (c) 2011-2012 Matt Pillar. All rights reserved. 6 | // ----------------------------------------------------------------------------- 7 | 8 | #include 9 | #include 10 | 11 | #include "Logger.h" 12 | 13 | namespace cs 14 | { 15 | 16 | std::string Logger::GetTimeStamp() 17 | { 18 | time_t t; 19 | time(&t); 20 | 21 | char buff[9]; 22 | strftime(buff, 9, "%H:%M:%S", localtime(&t)); 23 | 24 | return std::string(buff); 25 | } 26 | 27 | void Logger::Log(const std::string& s) 28 | { 29 | std::cout << GetTimeStamp() << " " << s << std::endl; 30 | } 31 | 32 | void operator<< (Logger& out, const std::string& s) 33 | { 34 | out.Log(s); 35 | } 36 | 37 | } // end namespace cs 38 | -------------------------------------------------------------------------------- /include/Exception.h: -------------------------------------------------------------------------------- 1 | // ----------------------------------------------------------------------------- 2 | // Copyright (c) 2011-2012 Matt Pillar. All rights reserved. 3 | // ----------------------------------------------------------------------------- 4 | 5 | #ifndef API_EXCEPTION_EXCEPTION_H 6 | #define API_EXCEPTION_EXCEPTION_H 7 | 8 | #include 9 | 10 | namespace cs 11 | { 12 | 13 | // ----------------------------------------------------------------------------- 14 | // class Exception 15 | // ----------------------------------------------------------------------------- 16 | 17 | class Exception 18 | { 19 | private: 20 | // The error message. 21 | std::string m_message; 22 | 23 | public: 24 | // Constructor 25 | Exception(const std::string& message) : m_message(message) 26 | { 27 | // no-op 28 | } 29 | 30 | // Get the error message. 31 | const std::string& what() const; 32 | }; 33 | 34 | } // end namespace cs 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /include/ComponentID.h: -------------------------------------------------------------------------------- 1 | // ----------------------------------------------------------------------------- 2 | // Copyright (c) 2011-2012 Matt Pillar. All rights reserved. 3 | // ----------------------------------------------------------------------------- 4 | 5 | #ifndef API_TESS_COMPONENTID_H 6 | #define API_TESS_COMPONENTID_H 7 | 8 | #include 9 | 10 | namespace cs 11 | { 12 | 13 | // ----------------------------------------------------------------------------- 14 | // class ComponentID 15 | // ----------------------------------------------------------------------------- 16 | 17 | class ComponentID 18 | { 19 | private: 20 | // Our id. 21 | char m_id; 22 | 23 | public: 24 | // Constructor. Intentionally do not use an explicit constructor. 25 | ComponentID(char id) : m_id(id) 26 | { 27 | // no-op 28 | } 29 | 30 | // Accessors 31 | char GetID() const {return m_id;} 32 | std::string ToString() const; 33 | 34 | // Mutators 35 | void SetID(char id) {m_id = id;} 36 | }; 37 | 38 | } 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /src/SpaceIterator.cpp: -------------------------------------------------------------------------------- 1 | // ----------------------------------------------------------------------------- 2 | // Copyright (c) 2011-2012 Matt Pillar. All rights reserved. 3 | // ----------------------------------------------------------------------------- 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | #include "Exception.h" 12 | #include "Whiteout.h" 13 | #include "SpaceIterator.h" 14 | 15 | namespace cs 16 | { 17 | 18 | SpaceIterator::SpaceIterator(Space space) : 19 | m_space(space), 20 | m_done(false), 21 | m_current(0,0), 22 | m_starting(0,0) 23 | { 24 | // Nothing to do. 25 | } 26 | 27 | void SpaceIterator::operator++(int) 28 | { 29 | m_current.IncrX(); 30 | 31 | if (m_current.GetX() >= m_space.GetWidth()) 32 | { 33 | m_current.SetX(0); 34 | 35 | m_current.IncrY(); 36 | m_current.SetY(m_current.GetY() % m_space.GetHeight()); 37 | } 38 | 39 | if (m_current == m_starting) 40 | { 41 | m_done = true; 42 | } 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /include/Transform.h: -------------------------------------------------------------------------------- 1 | // ----------------------------------------------------------------------------- 2 | // This library is used to transform rectangular spaces in one basis to another. 3 | // 4 | // Copyright (c) 2011-2012 Matt Pillar. All rights reserved. 5 | // ----------------------------------------------------------------------------- 6 | 7 | #ifndef API_TRANSFORM_TRANSFORM_H 8 | #define API_TRANSFORM_TRANSFORM_H 9 | 10 | #include "Space.h" 11 | #include "Rectangle.h" 12 | 13 | namespace cs 14 | { 15 | 16 | // ----------------------------------------------------------------------------- 17 | // class Transform 18 | // ----------------------------------------------------------------------------- 19 | 20 | class Transform 21 | { 22 | private: 23 | Space m_src; 24 | Space m_dst; 25 | 26 | public: 27 | Transform(Space src, Space dst) : 28 | m_src(src), 29 | m_dst(dst) 30 | { 31 | // no-op 32 | } 33 | 34 | // Map the source rectangle rect from the src space to the dst space. 35 | Rectangle Map(Rectangle rect); 36 | }; 37 | 38 | } // end namespace cs 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /include/Tesseract.h: -------------------------------------------------------------------------------- 1 | // ----------------------------------------------------------------------------- 2 | // Copyright (c) 2011-2012 Matt Pillar. All rights reserved. 3 | // ----------------------------------------------------------------------------- 4 | 5 | #ifndef API_TESS_TESSERACT_H 6 | #define API_TESS_TESSERACT_H 7 | 8 | #include 9 | #include 10 | 11 | #include "Rectangle.h" 12 | 13 | namespace cs 14 | { 15 | 16 | class ResultEnglish; 17 | class ResultCircuits; 18 | 19 | // ----------------------------------------------------------------------------- 20 | // class Tesseract 21 | // ----------------------------------------------------------------------------- 22 | 23 | class Tesseract 24 | { 25 | private: 26 | // Source image. 27 | IplImage& m_src; 28 | IplImage* m_gray; 29 | 30 | public: 31 | // Constructor. 32 | Tesseract(IplImage& src); 33 | 34 | // Destructor. 35 | ~Tesseract(); 36 | 37 | // Mutators 38 | ResultEnglish TesseractEnglish(Rectangle rect); 39 | ResultCircuits TesseractCircuits(Rectangle rect); 40 | }; 41 | 42 | } // end namespace cs 43 | 44 | #endif 45 | -------------------------------------------------------------------------------- /src/PeakFinder.cpp: -------------------------------------------------------------------------------- 1 | // ----------------------------------------------------------------------------- 2 | // Implementation of the peakfinder library. Used to map rectangular spaces 3 | // in one coordinate basis to another coordinate basis. Useful for manipulating 4 | // multiple instances of the same images that have been resized. 5 | // 6 | // Copyright (c) 2011-2012 Matt Pillar. All rights reserved. 7 | // ----------------------------------------------------------------------------- 8 | 9 | #include 10 | 11 | #include "SpaceIterator.h" 12 | #include "PeakFinder.h" 13 | 14 | namespace cs 15 | { 16 | 17 | PeakVector& PeakFinder::FindPeaks(unsigned int scaleFactor) 18 | { 19 | if (m_done) 20 | { 21 | return m_vector; 22 | } 23 | 24 | for (SpaceIterator it(Space(m_src.width, m_src.height)); 25 | it.More(); it++) 26 | { 27 | Rectangle peak = m_whiteout.WhiteoutPeak(it.GetCurrent()); 28 | 29 | if (peak.IsZero()) 30 | { 31 | continue; 32 | } 33 | 34 | m_vector.Push(peak); 35 | } 36 | 37 | return m_vector; 38 | } 39 | 40 | } // end namespace cs 41 | -------------------------------------------------------------------------------- /include/ResultEnglish.h: -------------------------------------------------------------------------------- 1 | // ----------------------------------------------------------------------------- 2 | // Copyright (c) 2011-2012 Matt Pillar. All rights reserved. 3 | // ----------------------------------------------------------------------------- 4 | 5 | #ifndef API_TESS_RESULTENGLISH_H 6 | #define API_TESSS_RESULTENGLISH_H 7 | 8 | namespace cs 9 | { 10 | 11 | class Tesseract; 12 | 13 | // ----------------------------------------------------------------------------- 14 | // class Tesseract 15 | // ----------------------------------------------------------------------------- 16 | 17 | class ResultEnglish 18 | { 19 | private: 20 | // Members 21 | std::string m_chars; 22 | int m_confidence; 23 | bool m_error; 24 | 25 | public: 26 | // Constructor 27 | ResultEnglish() : 28 | m_confidence(0), 29 | m_error(false) 30 | { 31 | // no-op 32 | } 33 | 34 | // Accessors 35 | bool GetError() const {return m_error;} 36 | int GetConfidence() const {return m_confidence;} 37 | const std::string& GetChars() const {return m_chars;} 38 | 39 | // Friends 40 | friend class Tesseract; 41 | }; 42 | 43 | } // end namespace cs 44 | 45 | #endif 46 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CircuitSense Project 2 | 3 | Welcome to the CircuitSense project. CircuitSense is an application that 4 | converts a picture of a circuit schematic to a netlist. CircuitSense was 5 | originally developed as an iPhone application, and has since been migrated here 6 | and transformed into a more general purpose tool. 7 | 8 | # CircuitSense Usage 9 | 10 | ## Installation 11 | 12 | On Linux, the following apt libraries are required: 13 | 14 | ``` 15 | python-opencv 16 | libcv-dev 17 | libopencv-highgui-dev 18 | libopencv-imgproc-dev 19 | libtesseract-dev 20 | ``` 21 | 22 | On mac, the following brew libraries are required: 23 | 24 | ``` 25 | tesseract 26 | opencv 27 | ``` 28 | 29 | ## Training 30 | 31 | To operate, CircuitSense requires a training data file, `cir.traindata`. Such 32 | training files are currently not provided in this project. To run this 33 | software, you will need to create this file as per the tesseract guidelines. 34 | 35 | Pull requests in this area are welcome. 36 | 37 | # Compatibility 38 | 39 | CircuitSense is only compatible with Tesseract 2.0.4, and will not work with 40 | the newest versions of the Tesseract library (3+). I may add support for this 41 | in the future, if time allows. 42 | -------------------------------------------------------------------------------- /src/Transform.cpp: -------------------------------------------------------------------------------- 1 | // ----------------------------------------------------------------------------- 2 | // Implementation of the transform library. Used to map rectangular spaces 3 | // in one coordinate basis to another coordinate basis. Useful for manipulating 4 | // multiple instances of the same images that have been resized. 5 | // 6 | // Copyright (c) 2011-2012 Matt Pillar. All rights reserved. 7 | // ----------------------------------------------------------------------------- 8 | 9 | #include "Transform.h" 10 | 11 | namespace cs 12 | { 13 | 14 | Rectangle Transform::Map(Rectangle rect) 15 | { 16 | // rect is the source, result is the dst. 17 | Rectangle result(0, 0, 0, 0); 18 | 19 | // src/dst = xsrc/xdst ==> xdst = xsrc * (dst/src) 20 | result.SetX(rect.GetX() * ((double) m_dst.GetWidth()/m_src.GetWidth())); 21 | 22 | // And the rest... 23 | result.SetY(rect.GetY() * ((double) m_dst.GetHeight()/m_src.GetHeight())); 24 | 25 | result.SetWidth( 26 | rect.GetWidth() * ((double) m_dst.GetWidth()/m_src.GetWidth())); 27 | result.SetHeight( 28 | rect.GetHeight() * ((double) m_dst.GetHeight()/m_src.GetHeight())); 29 | 30 | return result; 31 | } 32 | 33 | } // end namespace cs 34 | -------------------------------------------------------------------------------- /src/Point.cpp: -------------------------------------------------------------------------------- 1 | // ----------------------------------------------------------------------------- 2 | // Copyright (c) 2011-2012 Matt Pillar. All rights reserved. 3 | // ----------------------------------------------------------------------------- 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include "Whiteout.h" 10 | 11 | namespace cs 12 | { 13 | 14 | uint8_t* Point::GetDataByPoint(IplImage& img, const Point& p) 15 | { 16 | // Return a null pointer when the point is not in the image. 17 | if (p.GetX() >= img.width || p.GetY() >= img.height) 18 | { 19 | return 0; 20 | } 21 | 22 | // Very important to know how the data is indexed. 23 | uint8_t* data; 24 | data = (uint8_t*) img.imageData; 25 | 26 | int index = p.GetY()*img.widthStep + p.GetX()*img.nChannels; 27 | return &data[index]; 28 | } 29 | 30 | std::string Point::ToString() const 31 | { 32 | std::ostringstream os; 33 | os << "{" << m_x << "," << m_y << "}"; 34 | return os.str(); 35 | } 36 | 37 | bool Point::operator==(const Point& p) const 38 | { 39 | return p.GetX() == GetX() && p.GetY() == GetY(); 40 | } 41 | 42 | bool Point::operator!=(const Point& p) const 43 | { 44 | return !operator==(p); 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /include/DebugRow.h: -------------------------------------------------------------------------------- 1 | // ----------------------------------------------------------------------------- 2 | // This class serves as a general logging utility for this library. 3 | // 4 | // Copyright (c) 2011-2012 Matt Pillar. All rights reserved. 5 | // ----------------------------------------------------------------------------- 6 | 7 | #ifndef API_DEBUG_DEBUGROW_H 8 | #define API_DEBUG_DEBUGROW_H 9 | 10 | namespace cs 11 | { 12 | 13 | class AlgorithmDebug; 14 | 15 | // ----------------------------------------------------------------------------- 16 | // class DebugRow 17 | // ----------------------------------------------------------------------------- 18 | 19 | class DebugRow 20 | { 21 | private: 22 | unsigned int m_threshold; 23 | unsigned int m_confidence; 24 | unsigned int m_components; 25 | unsigned int m_textConfidence; 26 | unsigned int m_textComponents; 27 | 28 | public: 29 | // Constructor. 30 | DebugRow() : 31 | m_threshold(0), 32 | m_confidence(0), 33 | m_components(0), 34 | m_textConfidence(0), 35 | m_textComponents(0) 36 | { 37 | // no-op 38 | } 39 | 40 | std::string ToString() const; 41 | 42 | // Friends 43 | friend class AlgorithmDebug; 44 | }; 45 | 46 | } // end namespace cs 47 | 48 | #endif 49 | -------------------------------------------------------------------------------- /include/ConnVector.h: -------------------------------------------------------------------------------- 1 | // ----------------------------------------------------------------------------- 2 | // This class is pretty stupid, and is managed by the connection whiteout 3 | // object. 4 | // 5 | // Copyright (c) 2011-2012 Matt Pillar. All rights reserved. 6 | // ----------------------------------------------------------------------------- 7 | 8 | #ifndef API_CONNFINDER_CONNVECTOR_H 9 | #define API_CONNFINDER_CONNVECTOR_H 10 | 11 | #include 12 | #include 13 | 14 | #include "PeakFinder.h" 15 | #include "Connection.h" 16 | 17 | namespace cs 18 | { 19 | 20 | // ----------------------------------------------------------------------------- 21 | // class ConnVector 22 | // ----------------------------------------------------------------------------- 23 | 24 | class ConnVector 25 | { 26 | public: 27 | typedef std::vector ConnectionVector; 28 | 29 | private: 30 | // Our list of connections. 31 | ConnectionVector m_conns; 32 | 33 | public: 34 | // Constructor. 35 | ConnVector() 36 | { 37 | // no-op 38 | } 39 | 40 | // Mutators 41 | void Push(Connection c); 42 | 43 | // Accessors 44 | std::string ToString() const; 45 | const ConnectionVector& GetVector() const {return m_conns;} 46 | }; 47 | 48 | } // end namespace cs 49 | 50 | #endif 51 | -------------------------------------------------------------------------------- /include/CircuitEntityConnection.h: -------------------------------------------------------------------------------- 1 | // ----------------------------------------------------------------------------- 2 | // Copyright (c) 2011-2012 Matt Pillar. All rights reserved. 3 | // ----------------------------------------------------------------------------- 4 | 5 | #ifndef API_CIRCUIT_CIRCUITENTITYCONNECTION_H 6 | #define API_CIRCUIT_CIRCUITENTITYCONNECTION_H 7 | 8 | #include 9 | 10 | namespace cs 11 | { 12 | 13 | // ----------------------------------------------------------------------------- 14 | // class CircuitEntityConnection 15 | // ----------------------------------------------------------------------------- 16 | 17 | class CircuitEntityConnection 18 | { 19 | private: 20 | unsigned int m_port; 21 | unsigned int m_node; 22 | 23 | public: 24 | // Constructor. 25 | CircuitEntityConnection(unsigned int port, unsigned int node) : 26 | m_port(port), 27 | m_node(node) 28 | { 29 | // no-op 30 | } 31 | 32 | // Accessors 33 | unsigned int GetPort() const {return m_port;} 34 | unsigned int GetNode() const {return m_node;} 35 | 36 | std::string ToString() const; 37 | 38 | // Mutators 39 | void SetPort(unsigned int port) {m_port = port;} 40 | void SetNode(unsigned int node) {m_node = node;} 41 | }; 42 | 43 | } // end namespace cs 44 | 45 | #endif 46 | -------------------------------------------------------------------------------- /include/ResultCircuits.h: -------------------------------------------------------------------------------- 1 | // ----------------------------------------------------------------------------- 2 | // Copyright (c) 2011 Matt Pillar. All rights reserved. 3 | // ----------------------------------------------------------------------------- 4 | 5 | #ifndef API_TESS_RESULTCIRCUITS_H 6 | #define API_TESS_RESULTCIRCUITS_H 7 | 8 | #include 9 | #include "ComponentID.h" 10 | 11 | namespace cs 12 | { 13 | 14 | // Forward Declarations. 15 | class Tesseract; 16 | 17 | // ----------------------------------------------------------------------------- 18 | // class ResultCircuits 19 | // ----------------------------------------------------------------------------- 20 | 21 | class ResultCircuits 22 | { 23 | public: 24 | typedef std::vector CircuitVector; 25 | 26 | private: 27 | // Members 28 | CircuitVector m_vector; 29 | int m_confidence; 30 | 31 | public: 32 | // Constructor 33 | ResultCircuits() : m_confidence(0) 34 | { 35 | // no-op 36 | } 37 | 38 | // Accessors 39 | int GetConfidence() const {return m_confidence;} 40 | const CircuitVector& GetVector() const {return m_vector;} 41 | std::string ToString() const {return VectorToString(m_vector);} 42 | 43 | // Statics 44 | static std::string VectorToString(const CircuitVector& vec); 45 | 46 | // Friends 47 | friend class Tesseract; 48 | }; 49 | 50 | } // end namespace cs 51 | 52 | #endif 53 | -------------------------------------------------------------------------------- /include/Mask.h: -------------------------------------------------------------------------------- 1 | // ----------------------------------------------------------------------------- 2 | // This class is used to whiteout the edges of images for peak detection. 3 | // 4 | // Copyright (c) 2011 Matt Pillar. All rights reserved. 5 | // ----------------------------------------------------------------------------- 6 | 7 | #ifndef API_WHITEOUT_MASK_H 8 | #define API_WHITEOUT_MASK_H 9 | 10 | #include 11 | #include 12 | 13 | namespace cs 14 | { 15 | 16 | // ----------------------------------------------------------------------------- 17 | // class Mask 18 | // ----------------------------------------------------------------------------- 19 | 20 | class Mask 21 | { 22 | private: 23 | // Color components. 24 | uint8_t m_blue; 25 | uint8_t m_green; 26 | uint8_t m_red; 27 | 28 | public: 29 | // Constructor. 30 | Mask(uint8_t blue, uint8_t green, uint8_t red) 31 | { 32 | // White color component is not allowed, because it will confuse the 33 | // whiteout class. 34 | m_blue = blue >= 0x7f ? blue : 0x7f; 35 | m_green = green >= 0x7f ? green : 0x7f; 36 | m_red = red >= 0x7f ? red : 0x7f; 37 | } 38 | 39 | // Accessors 40 | uint8_t GetBlue() const {return m_blue;} 41 | uint8_t GetGreen() const {return m_green;} 42 | uint8_t GetRed() const {return m_red;} 43 | 44 | }; 45 | 46 | } // end namespace cs 47 | 48 | #endif 49 | -------------------------------------------------------------------------------- /include/PeakVector.h: -------------------------------------------------------------------------------- 1 | // ----------------------------------------------------------------------------- 2 | // Copyright (c) 2011-2012 Matt Pillar. All rights reserved. 3 | // ----------------------------------------------------------------------------- 4 | 5 | #ifndef API_PEAKFINDER_PEAKVECTOR_H 6 | #define API_PEAKFINDER_PEAKVECTOR_H 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #include "Rectangle.h" 13 | #include "Space.h" 14 | 15 | namespace cs 16 | { 17 | 18 | // ----------------------------------------------------------------------------- 19 | // class PeakVector 20 | // ----------------------------------------------------------------------------- 21 | 22 | class PeakVector 23 | { 24 | public: 25 | typedef std::vector RectangleVector; 26 | 27 | private: 28 | // The vector of peaks, stored as rectangles. 29 | RectangleVector m_peaks; 30 | 31 | // The space that contains these peaks. 32 | Space m_space; 33 | 34 | public: 35 | PeakVector(Space space) : m_space(space) 36 | { 37 | // no-op 38 | } 39 | 40 | // Mutators 41 | void Push(Rectangle& rect) {m_peaks.push_back(rect);} 42 | void Scale(double factor); 43 | 44 | // Accessors 45 | const RectangleVector& GetVector() const{return m_peaks;} 46 | std::string ToString() const; 47 | unsigned int GetAverageArea() const; 48 | }; 49 | 50 | } // end namespace cs 51 | 52 | #endif 53 | -------------------------------------------------------------------------------- /include/PortMapper.h: -------------------------------------------------------------------------------- 1 | // ----------------------------------------------------------------------------- 2 | // This library converts the given connection and peak vectors to a circuit 3 | // object. 4 | // 5 | // Copyright (c) 2011-2012 Matt Pillar. All rights reserved. 6 | // ----------------------------------------------------------------------------- 7 | 8 | #ifndef API_PORTMAPPER_PORTMAPPER_H 9 | #define API_PORTMAPPER_PORTMAPPER_H 10 | 11 | #include 12 | 13 | #include "ConnFinder.h" 14 | #include "Circuit.h" 15 | 16 | namespace cs 17 | { 18 | 19 | // ----------------------------------------------------------------------------- 20 | // class PortMapper 21 | // ----------------------------------------------------------------------------- 22 | 23 | class PortMapper 24 | { 25 | private: 26 | // The input peaks and connections. 27 | const PeakVector& m_peaks; 28 | const ConnVector& m_conns; 29 | 30 | public: 31 | // Constructor. 32 | PortMapper( 33 | const PeakVector& peaks, 34 | const ConnVector& conns) : 35 | m_peaks(peaks), 36 | m_conns(conns) 37 | { 38 | // no-op 39 | } 40 | 41 | // Convert the given peak vector and connection vector to a circuit object. 42 | // This requires the calculation of the port numbers based on the 3 o'clock 43 | // port numbering convention. 44 | Circuit ConvertToCircuit(); 45 | }; 46 | 47 | } // end namespace cs 48 | 49 | #endif 50 | -------------------------------------------------------------------------------- /include/WhiteoutPerimeter.h: -------------------------------------------------------------------------------- 1 | // ----------------------------------------------------------------------------- 2 | // Copyright (c) 2011-2012 Matt Pillar. All rights reserved. 3 | // ----------------------------------------------------------------------------- 4 | 5 | #ifndef API_WHITEOUT_WHITEOUTPERIMETER_H 6 | #define API_WHITEOUT_WHITEOUTPERIMETER_H 7 | 8 | #include 9 | #include 10 | 11 | #include "Whiteout.h" 12 | 13 | namespace cs 14 | { 15 | 16 | // ----------------------------------------------------------------------------- 17 | // class WhiteoutPerimeter 18 | // ----------------------------------------------------------------------------- 19 | 20 | class WhiteoutPerimeter : public Whiteout 21 | { 22 | protected: 23 | // No additonal check 24 | virtual bool AdditionalCheck(const Point& p) {return false;} 25 | 26 | // Nothing to do when a point is found. 27 | virtual void PointFound(const Point& p) {} 28 | 29 | public: 30 | // Constructor. 31 | WhiteoutPerimeter(IplImage& src) : Whiteout(src) 32 | { 33 | // no-op 34 | } 35 | 36 | // Functions 37 | void DoWhiteoutPerimeter(); // This is viral. 38 | 39 | // Not viral. Will stop at a white cell. 40 | void BlueoutBorder( 41 | Rectangle rect, Point starting, bool clockwise); 42 | void GreenoutBorder( 43 | Rectangle rect, Point starting, bool clockwise); 44 | void RedoutBorder( 45 | Rectangle rect, Point starting, bool clockwise); 46 | }; 47 | 48 | } // end namespace cs 49 | 50 | #endif 51 | -------------------------------------------------------------------------------- /include/SpaceIterator.h: -------------------------------------------------------------------------------- 1 | // ----------------------------------------------------------------------------- 2 | // This particular class is used to iterator over the outside of a rectangle. 3 | // 4 | // Copyright (c) 2011-2012 Matt Pillar. All rights reserved. 5 | // ----------------------------------------------------------------------------- 6 | 7 | #ifndef API_WHITEOUT_SPACEITERATOR_H 8 | #define API_WHITEOUT_SPACEITERATOR_H 9 | 10 | #include 11 | 12 | #include "Space.h" 13 | #include "Point.h" 14 | 15 | namespace cs 16 | { 17 | 18 | // ----------------------------------------------------------------------------- 19 | // class SpaceIterator 20 | // ----------------------------------------------------------------------------- 21 | 22 | class SpaceIterator 23 | { 24 | private: 25 | // The space that we are working with. 26 | Space m_space; 27 | 28 | // Boolean indicating whether we are done or not. 29 | bool m_done; 30 | 31 | // Our current point. 32 | Point m_current; 33 | 34 | // Our starting point. 35 | Point m_starting; 36 | 37 | public: 38 | // Constructor. 39 | SpaceIterator(Space space); 40 | 41 | // Returns true when we are done iterating. 42 | bool Done() const {return m_done;} 43 | 44 | // The opposite of Done(). 45 | bool More() const {return !m_done;} 46 | 47 | // Go to the next point. 48 | void operator++(int); 49 | 50 | // Return the Point we are currently at. 51 | const Point& GetCurrent() const {return m_current;} 52 | }; 53 | 54 | } // end namespace cs 55 | 56 | #endif 57 | -------------------------------------------------------------------------------- /include/Space.h: -------------------------------------------------------------------------------- 1 | // ----------------------------------------------------------------------------- 2 | // This library is used to transform rectangular spaces in one basis to another. 3 | // Similar to an image definition, a space of 2x2 looks like this: 4 | // 5 | // x,x 6 | // x,x 7 | // 8 | // See rectnagle for more discussion on the semantics of spaces and 9 | // rectangles. 10 | // 11 | // Copyright (c) 2011-2012 Matt Pillar. All rights reserved. 12 | // ----------------------------------------------------------------------------- 13 | 14 | #ifndef API_TRANSFORM_SPACE_H 15 | #define API_TRANSFORM_SPACE_H 16 | 17 | #include 18 | #include 19 | 20 | namespace cs 21 | { 22 | 23 | class Transform; 24 | 25 | // ----------------------------------------------------------------------------- 26 | // class Space 27 | // ----------------------------------------------------------------------------- 28 | 29 | class Space 30 | { 31 | private: 32 | unsigned int m_width; 33 | unsigned int m_height; 34 | 35 | public: 36 | Space(unsigned int width, unsigned int height) : 37 | m_width(width), 38 | m_height(height) 39 | { 40 | // no-op 41 | } 42 | 43 | // Accessors 44 | unsigned int GetWidth() const {return m_width;} 45 | unsigned int GetHeight() const {return m_height;} 46 | 47 | std::string ToString() const 48 | { 49 | std::ostringstream os; 50 | os << "{width=" << m_width << ",height=" << m_height << "}"; 51 | return os.str(); 52 | } 53 | }; 54 | 55 | } // end namespace cs 56 | 57 | #endif 58 | -------------------------------------------------------------------------------- /include/PeakFinder.h: -------------------------------------------------------------------------------- 1 | // ----------------------------------------------------------------------------- 2 | // Copyright (c) 2011-2012 Matt Pillar. All rights reserved. 3 | // ----------------------------------------------------------------------------- 4 | 5 | #ifndef API_PEAKFINDER_PEAKFINDER_H 6 | #define API_PEAKFINDER_PEAKFINDER_H 7 | 8 | #include 9 | 10 | #include "WhiteoutPeakFinder.h" 11 | #include "PeakVector.h" 12 | 13 | namespace cs 14 | { 15 | 16 | // ----------------------------------------------------------------------------- 17 | // class PeakFinder 18 | // ----------------------------------------------------------------------------- 19 | 20 | class PeakFinder 21 | { 22 | private: 23 | // The source image. 24 | IplImage& m_src; 25 | 26 | // Our whiteout object. 27 | WhiteoutPeakFinder m_whiteout; 28 | 29 | // The vector of peaks that we will calculate 30 | PeakVector m_vector; 31 | 32 | // Boolean indicating whether or not we have calculated the peaks yet. 33 | bool m_done; 34 | 35 | public: 36 | // Constructor. Input is a 3-channel, small, image containing blobs (peaks) 37 | // that we will find. 38 | // 39 | // If the input does not match this descrption, shit will happen with the 40 | // whiteout object. 41 | PeakFinder(IplImage& src) : 42 | m_src(src), 43 | m_whiteout(src), 44 | m_vector(Space(src.width, src.height)), 45 | m_done(false) 46 | { 47 | // no-op 48 | } 49 | 50 | // Do the work. 51 | PeakVector& FindPeaks(unsigned int scaleFactor); 52 | }; 53 | 54 | } // end namespace cs 55 | 56 | #endif 57 | -------------------------------------------------------------------------------- /include/ConnFinder.h: -------------------------------------------------------------------------------- 1 | // ----------------------------------------------------------------------------- 2 | // Copyright (c) 2011-2012 Matt Pillar. All rights reserved. 3 | // ----------------------------------------------------------------------------- 4 | 5 | #ifndef API_CONNFINDER_CONNFINDER_H 6 | #define API_CONNFINDER_CONNFINDER_H 7 | 8 | #include 9 | 10 | #include "Whiteout.h" 11 | #include "PeakFinder.h" 12 | #include "ConnVector.h" 13 | #include "WhiteoutConnFinder.h" 14 | 15 | namespace cs 16 | { 17 | 18 | // ----------------------------------------------------------------------------- 19 | // class ConnFinder 20 | // ----------------------------------------------------------------------------- 21 | 22 | class ConnFinder 23 | { 24 | private: 25 | // The source image. 26 | IplImage& m_src; 27 | 28 | // Our whiteout object. 29 | WhiteoutConnFinder m_cwhite; 30 | 31 | // Perimeter whiteout object 32 | WhiteoutPerimeter m_pwhite; 33 | 34 | // A reference to the peak vector. 35 | const PeakVector& m_peaks; 36 | 37 | public: 38 | // Constructor. Input is a 3-channel, small, image containing the conn blob 39 | // (ususally the output of filter). 40 | // 41 | // If the input does not match this descrption, shit will happen with the 42 | // whiteout object. 43 | ConnFinder(IplImage& src, const PeakVector& peaks) : 44 | m_src(src), 45 | m_cwhite(src, peaks), 46 | m_pwhite(src), 47 | m_peaks(peaks) 48 | { 49 | // no-op 50 | } 51 | 52 | // Do the work! 53 | const ConnVector& GetConnections(); 54 | }; 55 | 56 | } // end namespace cs 57 | 58 | #endif 59 | -------------------------------------------------------------------------------- /include/Point.h: -------------------------------------------------------------------------------- 1 | // ----------------------------------------------------------------------------- 2 | // This header contains the public prototypes for the whiteout library, 3 | // which is used to whiteout the edges of images for peak detection. 4 | // 5 | // Copyright (c) 2011-2012 Matt Pillar. All rights reserved. 6 | // ----------------------------------------------------------------------------- 7 | 8 | #ifndef API_WHITEOUT_POINT_H 9 | #define API_WHITEOUT_POINT_H 10 | 11 | #include 12 | #include 13 | 14 | namespace cs 15 | { 16 | 17 | // ----------------------------------------------------------------------------- 18 | // class Point 19 | // ----------------------------------------------------------------------------- 20 | 21 | class Point 22 | { 23 | private: 24 | unsigned int m_x; 25 | unsigned int m_y; 26 | 27 | public: 28 | // Constructor. 29 | Point(unsigned int x, unsigned int y) : m_x(x), m_y(y) 30 | { 31 | // no-op 32 | } 33 | 34 | // Accessors 35 | unsigned int GetX() const {return m_x;} 36 | unsigned int GetY() const {return m_y;} 37 | std::string ToString() const; 38 | 39 | // Mutators 40 | void SetX(unsigned int x) {m_x = x;} 41 | void SetY(unsigned int y) {m_y = y;} 42 | 43 | void IncrX() {m_x++;} 44 | void DecrX() {m_x--;} 45 | void IncrY() {m_y++;} 46 | void DecrY() {m_y--;} 47 | 48 | // Statics 49 | static uint8_t* GetDataByPoint(IplImage& img, const Point& p); 50 | 51 | // Operator overloads 52 | bool operator==(const Point& p) const; 53 | bool operator!=(const Point& p) const; 54 | }; 55 | 56 | } // end namespace cs 57 | 58 | #endif 59 | -------------------------------------------------------------------------------- /include/WhiteoutPeakFinder.h: -------------------------------------------------------------------------------- 1 | // ----------------------------------------------------------------------------- 2 | // Copyright (c) 2011-2012 Matt Pillar. All rights reserved. 3 | // ----------------------------------------------------------------------------- 4 | 5 | #ifndef API_WHITEOUT_WHITEOUTPEAKFINDER_H 6 | #define API_WHITEOUT_WHITEOUTPEAKFINDER_H 7 | 8 | #include 9 | #include 10 | 11 | #include "Rectangle.h" 12 | #include "Point.h" 13 | #include "Whiteout.h" 14 | 15 | namespace cs 16 | { 17 | 18 | // ----------------------------------------------------------------------------- 19 | // class WhiteoutPeakFinder 20 | // ----------------------------------------------------------------------------- 21 | 22 | class WhiteoutPeakFinder : public Whiteout 23 | { 24 | private: 25 | // Cache the peak so the callback has it. Store the peak as two points, 26 | // since holding the peak as the meta would be difficult. 27 | Point m_topleft; 28 | Point m_botright; 29 | 30 | protected: 31 | // No additional check. 32 | virtual bool AdditionalCheck(const Point& p) {return false;} 33 | 34 | // Update the peak if applicable. 35 | virtual void PointFound(const Point& p); 36 | 37 | public: 38 | // Constructor. 39 | WhiteoutPeakFinder(IplImage& src) : 40 | Whiteout(src), 41 | m_topleft(0, 0), 42 | m_botright(0, 0) 43 | { 44 | // no-op 45 | } 46 | 47 | // Return the rectangle that holds the peak we just whited out. If the pixel 48 | // was white in the first place, return a zero size rectangle. 49 | Rectangle WhiteoutPeak(const Point& p); 50 | }; 51 | 52 | } // end namespace cs 53 | 54 | #endif 55 | -------------------------------------------------------------------------------- /include/SmartComponentID.h: -------------------------------------------------------------------------------- 1 | // ----------------------------------------------------------------------------- 2 | // The smart ComponentID class is used to map circuit component ID's to semantic 3 | // circuit meanings, i.e. functionality like IsGround() or IsResistor(). 4 | // 5 | // Another spot for this code might've been in the tesseract library, but 6 | // since that library really doesn't care about what type of component it is, 7 | // I put it here. 8 | // 9 | // This implementation file, and the netlist generator implementation file, are 10 | // the only files you need to change if you add a new circuit component to the 11 | // tesseract training library. 12 | // 13 | // Copyright (c) 2011-2012Matt Pillar. All rights reserved. 14 | // ----------------------------------------------------------------------------- 15 | 16 | #ifndef API_CIRCUIT_SMARTCOMPONENTID_H 17 | #define API_CIRCUIT_SMARTCOMPONENTID_H 18 | 19 | #include "ComponentID.h" 20 | #include "Tesseract.h" 21 | 22 | namespace cs 23 | { 24 | 25 | // ----------------------------------------------------------------------------- 26 | // class SmartComponentID 27 | // ----------------------------------------------------------------------------- 28 | 29 | class SmartComponentID : public ComponentID 30 | { 31 | public: 32 | // Intentially do not use an explicit constructor. 33 | // 34 | // If we ever move away from characters, then the parent class will update 35 | // their constructor and we will in turn have to update ours. 36 | SmartComponentID(char id) : ComponentID(id) 37 | { 38 | // no-op 39 | } 40 | 41 | // Operator overloads. 42 | bool operator==(const SmartComponentID& id); 43 | 44 | // Circuit related accessors 45 | bool IsNull() const; 46 | bool IsGround() const; 47 | }; 48 | 49 | } // end namespace cs 50 | 51 | #endif 52 | -------------------------------------------------------------------------------- /src/WhiteoutPerimeter.cpp: -------------------------------------------------------------------------------- 1 | // ----------------------------------------------------------------------------- 2 | // Copyright (c) 2011-2012 Matt Pillar. All rights reserved. 3 | // ----------------------------------------------------------------------------- 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include "WhiteoutPerimeter.h" 10 | 11 | namespace cs 12 | { 13 | 14 | void WhiteoutPerimeter::DoWhiteoutPerimeter() 15 | { 16 | // Recursive clockwise. 17 | DoWhiteout( 18 | Space(m_src.width, m_src.height), 19 | Rectangle(0, 0, m_src.width-1, m_src.height-1), 20 | Point(0, 0), 21 | Mask(0xff, 0xff, 0xff), 22 | true, 23 | true); 24 | } 25 | 26 | void WhiteoutPerimeter::BlueoutBorder(Rectangle rect, 27 | Point starting, bool clockwise) 28 | { 29 | // Not viral. 30 | DoWhiteout( 31 | Space(m_src.width, m_src.height), 32 | rect, 33 | starting, 34 | Mask(0xff, 0x7f, 0x7f), 35 | clockwise, 36 | false); 37 | } 38 | 39 | void WhiteoutPerimeter::GreenoutBorder(Rectangle rect, 40 | Point starting, bool clockwise) 41 | { 42 | // Not viral. 43 | DoWhiteout( 44 | Space(m_src.width, m_src.height), 45 | rect, 46 | starting, 47 | Mask(0x7f, 0xff, 0x7f), 48 | clockwise, 49 | false); 50 | } 51 | 52 | void WhiteoutPerimeter::RedoutBorder(Rectangle rect, 53 | Point starting, bool clockwise) 54 | { 55 | // Not viral. 56 | DoWhiteout( 57 | Space(m_src.width, m_src.height), 58 | rect, 59 | starting, 60 | Mask(0x7f, 0x7f, 0xff), 61 | clockwise, 62 | false); 63 | } 64 | 65 | } // end namespace cs 66 | -------------------------------------------------------------------------------- /include/AlgorithmDebug.h: -------------------------------------------------------------------------------- 1 | // ----------------------------------------------------------------------------- 2 | // Author: Matt Pillar 3 | // 4 | // This header contains the public prototypes for the debug library. This 5 | // library is used to debug the main algorithm library. 6 | // 7 | // Copyright (c) 2011 Matt Pillar. All rights reserved. 8 | // ----------------------------------------------------------------------------- 9 | 10 | #ifndef API_DEBUG_ALGORITHMDEBUG_H 11 | #define API_DEBUG_ALGORITHMDEBUG_H 12 | 13 | #include 14 | #include 15 | 16 | #include "Algorithm.h" 17 | #include "DebugRow.h" 18 | #include "Logger.h" 19 | 20 | namespace cs 21 | { 22 | 23 | // ----------------------------------------------------------------------------- 24 | // class AlgorithmDebug 25 | // ----------------------------------------------------------------------------- 26 | 27 | class AlgorithmDebug : public Algorithm 28 | { 29 | private: 30 | Logger m_logger; 31 | std::vector m_debug; 32 | 33 | protected: 34 | // Defined as debugging functions. 35 | 36 | virtual void CHECKPOINT_STARTING(const Filter& filter); 37 | virtual void CHECKPOINT_NEWTHRESH(unsigned int threshold); 38 | virtual void CHECKPOINT_FOUNDPEAKS(const IplImage& compblob); 39 | virtual void CHECKPOINT_FOUNDCONNS(const IplImage& connblob); 40 | 41 | virtual void CHECKPOINT_TESSERACT(const Rectangle& map); 42 | virtual void CHECKPOINT_CIRCUITS(const ResultCircuits& res); 43 | virtual void CHECKPOINT_ENGLISH(const ResultEnglish& res); 44 | 45 | virtual void CHECKPOINT_HAVECIRCUIT(const Circuit& circuit); 46 | virtual void CHECKPOINT_HAVEGEN(const NetlistGenerator& g); 47 | virtual void CHECKPOINT_HAVECONF(unsigned int conf, unsigned int tconf); 48 | 49 | virtual void CHECKPOINT_DONE(); 50 | 51 | public: 52 | // Constructor. 53 | AlgorithmDebug(IplImage& src) : Algorithm(src) 54 | { 55 | // no-op 56 | } 57 | }; 58 | 59 | } // end namespace cs 60 | 61 | #endif 62 | -------------------------------------------------------------------------------- /include/Connection.h: -------------------------------------------------------------------------------- 1 | // ----------------------------------------------------------------------------- 2 | // This header contains the public prototypes for the connfinder library. 3 | // This class is pretty stupid and is managed by the ConnVector class. It 4 | // really just serves to hold data. 5 | // 6 | // Copyright (c) 2011-2012 Matt Pillar. All rights reserved. 7 | // ----------------------------------------------------------------------------- 8 | 9 | #ifndef API_CONNFINDER_CONNECTION_H 10 | #define API_CONNFINDER_CONNECTION_H 11 | 12 | #include 13 | #include 14 | 15 | #include "Point.h" 16 | #include "PeakVector.h" 17 | 18 | namespace cs 19 | { 20 | 21 | // ----------------------------------------------------------------------------- 22 | // class Connection 23 | // ----------------------------------------------------------------------------- 24 | 25 | class Connection 26 | { 27 | private: 28 | // The index (id) of the first peak and the second peak. Index numbers 29 | // should be managed by the caller. 30 | unsigned int m_peak1; 31 | unsigned int m_peak2; 32 | 33 | // The 2 points that form the connection. 34 | Point m_point1; 35 | Point m_point2; 36 | 37 | public: 38 | // Constructor. 39 | Connection( 40 | unsigned int peak1, unsigned int peak2, 41 | Point point1, Point point2) : 42 | m_peak1(peak1), 43 | m_peak2(peak2), 44 | m_point1(point1), 45 | m_point2(point2) 46 | { 47 | // no-op 48 | } 49 | 50 | // Accessors 51 | unsigned int GetPeak1() const {return m_peak1;} 52 | unsigned int GetPeak2() const {return m_peak2;} 53 | 54 | const Point& GetPoint1() const {return m_point1;} 55 | const Point& GetPoint2() const {return m_point2;} 56 | 57 | unsigned int GetTick1(const PeakVector& lst) const; 58 | unsigned int GetTick2(const PeakVector& lst) const; 59 | 60 | std::string ToString() const; 61 | }; 62 | 63 | } // end namespace cs 64 | 65 | #endif 66 | -------------------------------------------------------------------------------- /include/Whiteout.h: -------------------------------------------------------------------------------- 1 | // ----------------------------------------------------------------------------- 2 | // This header contains the public prototypes for the whiteout library, 3 | // which is used to whiteout the edges of images for peak detection. 4 | // 5 | // Copyright (c) 2011-2012 Matt Pillar. All rights reserved. 6 | // ----------------------------------------------------------------------------- 7 | 8 | #ifndef API_WHITEOUT_WHITEOUT_H 9 | #define API_WHITEOUT_WHITEOUT_H 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | #include "Point.h" 16 | #include "Mask.h" 17 | #include "Transform.h" 18 | 19 | namespace cs 20 | { 21 | 22 | // ----------------------------------------------------------------------------- 23 | // class Whiteout 24 | // ----------------------------------------------------------------------------- 25 | 26 | class Whiteout 27 | { 28 | protected: 29 | // Typedefs 30 | typedef std::list PointVector; 31 | 32 | // The image source. 33 | IplImage& m_src; 34 | 35 | // Return true when the cell is not white. 36 | bool WhiteoutHelper( 37 | bool viral, 38 | Mask& mask, 39 | unsigned int& depth, 40 | PointVector& pv, 41 | Point p); 42 | 43 | // Pure virtual functions. 44 | virtual bool AdditionalCheck(const Point& p) = 0; // True if we should exit. 45 | virtual void PointFound(const Point& p) = 0; 46 | 47 | // Main whiteout function. 48 | void DoWhiteout( 49 | Space space, 50 | Rectangle rect, 51 | Point starting, 52 | Mask mask, 53 | bool clockwise, 54 | bool viral); 55 | 56 | public: 57 | // Constructor. The input image source must be a 3-channel image. This will 58 | // throw if this is not the case. 59 | // 60 | // Normally this image is a small grayscale image that has been converted to 61 | // colour. 62 | Whiteout(IplImage& src); 63 | }; 64 | 65 | } // end namespace cs 66 | 67 | #endif 68 | -------------------------------------------------------------------------------- /src/PeakVector.cpp: -------------------------------------------------------------------------------- 1 | // ----------------------------------------------------------------------------- 2 | // Implementation of the peakfinder library. Used to map rectangular spaces 3 | // in one coordinate basis to another coordinate basis. Useful for manipulating 4 | // multiple instances of the same images that have been resized. 5 | // 6 | // Copyright (c) 2011-2012 Matt Pillar. All rights reserved. 7 | // ----------------------------------------------------------------------------- 8 | 9 | #include 10 | 11 | #include "PeakVector.h" 12 | 13 | namespace cs 14 | { 15 | 16 | std::string PeakVector::ToString() const 17 | { 18 | if (m_peaks.size() == 0) 19 | { 20 | return "{}"; 21 | } 22 | 23 | std::ostringstream os; 24 | 25 | for (RectangleVector::const_iterator it = m_peaks.begin(); 26 | it != m_peaks.end(); it++) 27 | { 28 | os << it->ToString() << ","; 29 | } 30 | 31 | return os.str().substr(0, os.str().size()-1); 32 | } 33 | 34 | void PeakVector::Scale(double factor) 35 | { 36 | for (RectangleVector::iterator it = m_peaks.begin(); 37 | it != m_peaks.end(); it++) 38 | { 39 | it->Scale(factor); 40 | 41 | // Fix the width if it's too big. 42 | if (it->GetX() + it->GetWidth() + 1 >= m_space.GetWidth()) 43 | { 44 | it->SetWidth(m_space.GetWidth() - it->GetX() - 1); 45 | } 46 | 47 | // Fix the height if it's too big. 48 | if (it->GetY() + it->GetHeight() + 1 >= m_space.GetHeight()) 49 | { 50 | it->SetHeight(m_space.GetHeight() - it->GetY() - 1); 51 | } 52 | } 53 | } 54 | 55 | unsigned int PeakVector::GetAverageArea() const 56 | { 57 | if (m_peaks.size() == 0) 58 | { 59 | return 0; 60 | } 61 | 62 | unsigned int sum = 0; 63 | for (RectangleVector::const_iterator it = m_peaks.begin(); 64 | it != m_peaks.end(); it++) 65 | { 66 | sum += it->GetArea(); 67 | } 68 | 69 | return sum/m_peaks.size(); 70 | } 71 | 72 | } // end namespace cs 73 | -------------------------------------------------------------------------------- /src/WhiteoutPeakFinder.cpp: -------------------------------------------------------------------------------- 1 | // ----------------------------------------------------------------------------- 2 | // Implementation of the peakfinder library. This specific implementation 3 | // file could also belong in the whiteout lib, but because it is so 4 | // specific I moved it here. 5 | // 6 | // Copyright (c) 2011-2012 Matt Pillar. All rights reserved. 7 | // ----------------------------------------------------------------------------- 8 | 9 | #include 10 | 11 | #include "WhiteoutPeakFinder.h" 12 | 13 | namespace cs 14 | { 15 | 16 | Rectangle WhiteoutPeakFinder::WhiteoutPeak(const Point& p) 17 | { 18 | // Width and height are invalid index values. 19 | m_topleft = Point(m_src.width, m_src.height); 20 | m_botright = Point(0, 0); 21 | 22 | DoWhiteout( 23 | Space(m_src.width, m_src.height), 24 | Rectangle(p.GetX(), p.GetY(), 0, 0), 25 | p, 26 | Mask(0xff, 0xff, 0xff), 27 | true, 28 | true); 29 | 30 | if (m_botright.GetX() > m_topleft.GetX() && 31 | m_botright.GetY() > m_topleft.GetY()) 32 | { 33 | return Rectangle(m_topleft.GetX(), m_topleft.GetY(), 34 | m_botright.GetX() - m_topleft.GetX() + 1, 35 | m_botright.GetY() - m_topleft.GetY() + 1); 36 | } 37 | 38 | return Rectangle(0, 0, 0, 0); 39 | } 40 | 41 | void WhiteoutPeakFinder::PointFound(const Point& p) 42 | { 43 | if (p.GetX() < m_topleft.GetX()) 44 | { 45 | Point newPoint(p.GetX(), m_topleft.GetY()); 46 | m_topleft = newPoint; 47 | } 48 | 49 | if (p.GetY() < m_topleft.GetY()) 50 | { 51 | Point newPoint(m_topleft.GetX(), p.GetY()); 52 | m_topleft = newPoint; 53 | } 54 | 55 | if (p.GetX() > m_botright.GetX()) 56 | { 57 | Point newPoint(p.GetX(), m_botright.GetY()); 58 | m_botright = newPoint; 59 | } 60 | 61 | if (p.GetY() > m_botright.GetY()) 62 | { 63 | Point newPoint(m_botright.GetX(), p.GetY()); 64 | m_botright = newPoint; 65 | } 66 | } 67 | 68 | } // end namespace cs 69 | -------------------------------------------------------------------------------- /include/Algorithm.h: -------------------------------------------------------------------------------- 1 | // ----------------------------------------------------------------------------- 2 | // Note that I'm really just stubbing out virtual functions here. The only 3 | // reason for doing this over making them non-pure in the base class is this 4 | // has the added advantage of failing to compile if the other derived classes 5 | // do not implement one of the callback functions. 6 | // 7 | // Copyright (c) 2011-2012 Matt Pillar. All rights reserved. 8 | // ----------------------------------------------------------------------------- 9 | 10 | #ifndef API_ALGORITHM_ALGORITHM_H 11 | #define API_ALGORITHM_ALGORITHM_H 12 | 13 | #include 14 | #include "AlgorithmBase.h" 15 | 16 | namespace cs 17 | { 18 | 19 | // ----------------------------------------------------------------------------- 20 | // class Algorithm 21 | // ----------------------------------------------------------------------------- 22 | 23 | class Algorithm : public AlgorithmBase 24 | { 25 | protected: 26 | // ------------------------------------------------------------------------- 27 | // Virtual functions 28 | // ------------------------------------------------------------------------- 29 | 30 | virtual void CHECKPOINT_STARTING(const Filter& filter) {} 31 | virtual void CHECKPOINT_NEWTHRESH(unsigned int threshold) {} 32 | virtual void CHECKPOINT_FOUNDPEAKS(const IplImage& compblob) {} 33 | virtual void CHECKPOINT_FOUNDCONNS(const IplImage& connblob) {} 34 | 35 | virtual void CHECKPOINT_TESSERACT(const Rectangle& map) {} 36 | virtual void CHECKPOINT_CIRCUITS(const ResultCircuits& res) {} 37 | virtual void CHECKPOINT_ENGLISH(const ResultEnglish& res) {} 38 | 39 | virtual void CHECKPOINT_HAVECIRCUIT(const Circuit& circuit) {} 40 | virtual void CHECKPOINT_HAVEGEN(const NetlistGenerator& g) {} 41 | virtual void CHECKPOINT_HAVECONF(unsigned int conf, unsigned int tconf) {} 42 | 43 | virtual void CHECKPOINT_DONE() {} 44 | 45 | public: 46 | // Constructor. 47 | Algorithm(IplImage& src) : AlgorithmBase(src) 48 | { 49 | // no-op 50 | } 51 | }; 52 | 53 | } // end namespace cs 54 | 55 | #endif 56 | -------------------------------------------------------------------------------- /include/OutsideRectangleIterator.h: -------------------------------------------------------------------------------- 1 | // ----------------------------------------------------------------------------- 2 | // This header contains the public prototypes for the whiteout library, 3 | // which is used to whiteout the edges of images for peak detection. 4 | // 5 | // This particular class is used to iterator over the outside of a rectangle. 6 | // 7 | // Copyright (c) 2011-2012 Matt Pillar. All rights reserved. 8 | // ----------------------------------------------------------------------------- 9 | 10 | #ifndef API_WHITEOUT_OUTSIDERECTANGLEITERATOR_H 11 | #define API_WHITEOUT_OUTSIDERECTANGLEITERATOR_H 12 | 13 | #include 14 | 15 | #include "Rectangle.h" 16 | #include "Space.h" 17 | #include "Exception.h" 18 | #include "Point.h" 19 | 20 | namespace cs 21 | { 22 | 23 | // ----------------------------------------------------------------------------- 24 | // class OutsideRectangleIterator 25 | // ----------------------------------------------------------------------------- 26 | 27 | class OutsideRectangleIterator 28 | { 29 | private: 30 | // The space that contains the rectangle. 31 | Space m_space; 32 | 33 | // The rectangle we are going to iterate over. 34 | Rectangle m_rectangle; 35 | 36 | // Boolean indicating whether we are done or not. 37 | bool m_done; 38 | 39 | // Our current point. 40 | Point m_current; 41 | 42 | // Our starting point. 43 | Point m_starting; 44 | 45 | public: 46 | // Constructor. Will throw if the space or rectanle is invalid or if the 47 | // rectangle is not within the space. 48 | OutsideRectangleIterator( 49 | Space space, 50 | Rectangle rectangle, 51 | Point starting); 52 | 53 | // Returns true when we are done iterating the outside of the rectangle and 54 | // false otherwise. 55 | bool Done() const {return m_done;} 56 | 57 | // The opposite of Done(). 58 | bool More() const {return !m_done;} 59 | 60 | // Go to the next point (clockwise). 61 | void operator++(int); 62 | 63 | // Go the the previous point (counter-clockwise). 64 | void operator--(int); 65 | 66 | // Return the Point we are currently at. 67 | const Point& GetCurrent() const {return m_current;} 68 | }; 69 | 70 | } // end namespace cs 71 | 72 | #endif 73 | -------------------------------------------------------------------------------- /include/WhiteoutConnFinder.h: -------------------------------------------------------------------------------- 1 | // ----------------------------------------------------------------------------- 2 | // Copyright (c) 2011-2012 Matt Pillar. All rights reserved. 3 | // ----------------------------------------------------------------------------- 4 | 5 | #ifndef API_CONNFINDER_WHITEOUTCONNFINDER_H 6 | #define API_CONNFINDER_WHITEOUTCONNFINDER_H 7 | 8 | #include 9 | #include 10 | 11 | #include "Rectangle.h" 12 | #include "Point.h" 13 | #include "Whiteout.h" 14 | #include "WhiteoutPerimeter.h" 15 | #include "ConnVector.h" 16 | 17 | namespace cs 18 | { 19 | 20 | // ----------------------------------------------------------------------------- 21 | // class WhiteoutConnFinder 22 | // ----------------------------------------------------------------------------- 23 | 24 | class WhiteoutConnFinder : public Whiteout 25 | { 26 | private: 27 | // connection vector. 28 | ConnVector m_conns; 29 | 30 | // Reference to the peak vector. 31 | const PeakVector& m_peaks; 32 | 33 | // The poitn param to the grayout function. 34 | Point m_graypoint; 35 | 36 | // The index of the rectangle the grayout function is currently working on. 37 | unsigned int m_index; 38 | 39 | // Perimeter whiteout object. 40 | WhiteoutPerimeter m_pwhite; 41 | 42 | protected: 43 | // The addditional check that we're not within the rectangle. 44 | virtual bool AdditionalCheck(const Point& p); 45 | 46 | // Point found: have we found a peak? If yes, add the peak to the peak conn 47 | // matrix, and colour out the border. 48 | virtual void PointFound(const Point& p); 49 | 50 | public: 51 | // Constructor. 52 | WhiteoutConnFinder(IplImage& src, const PeakVector& peaks) : 53 | Whiteout(src), 54 | m_conns(), 55 | m_peaks(peaks), 56 | m_graypoint(0, 0), 57 | m_index(0), 58 | m_pwhite(src) 59 | { 60 | // no-op 61 | } 62 | 63 | // Accessor 64 | const ConnVector& GetConnections() {return m_conns;} 65 | 66 | // Whiteout Derivative. Main function to find connections between two peaks. 67 | // Only mean to be used by the connection finder class. 68 | // 69 | // Return true if a peak was found while searching off this point. 70 | bool GreyoutConnection(const Point& p); 71 | }; 72 | 73 | } // end namespace cs 74 | 75 | #endif 76 | -------------------------------------------------------------------------------- /src/ConnFinder.cpp: -------------------------------------------------------------------------------- 1 | // ----------------------------------------------------------------------------- 2 | // Implementation of the connfinder library. This particular class iterates 3 | // over the set of peaks found by a different module, and looks for connections. 4 | // Metadata is stored in the source image to help us know where we're at. 5 | // 6 | // Copyright (c) 2011-2012 Matt Pillar. All rights reserved. 7 | // ----------------------------------------------------------------------------- 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | #include "Whiteout.h" 14 | #include "ConnVector.h" 15 | #include "ConnFinder.h" 16 | #include "OutsideRectangleIterator.h" 17 | 18 | namespace cs 19 | { 20 | 21 | const ConnVector& ConnFinder::GetConnections() 22 | { 23 | // Start by looping through each peak. 24 | for (PeakVector::RectangleVector::const_iterator pk = 25 | m_peaks.GetVector().begin(); 26 | pk != m_peaks.GetVector().end(); pk++) 27 | { 28 | // Iterate over the outside of the rectangle. Start at the top right 29 | // corner for the 3 o'clock port numbering convention. 30 | for (OutsideRectangleIterator rit( 31 | Space(m_src.width, m_src.height), 32 | (*pk), 33 | Point(pk->GetX()+pk->GetWidth(), pk->GetY())); 34 | rit.More(); rit++) 35 | { 36 | // If it's a dark cell... 37 | uint8_t* data = Point::GetDataByPoint(m_src, rit.GetCurrent()); 38 | if (data[0] < 0x7f || data[1] < 0x7f || data[2] < 0x7f) 39 | { 40 | // Add a green border to this edge (green "from" red "to"). 41 | m_pwhite.GreenoutBorder(*pk, rit.GetCurrent(), true); 42 | data[0] = 0x00; data[1] = 0x00; data[2] = 0x00; 43 | m_pwhite.GreenoutBorder(*pk, rit.GetCurrent(), false); 44 | data[0] = 0x00; data[1] = 0x00; data[2] = 0x00; 45 | 46 | // Find the connections associated with this cell. 47 | m_cwhite.GreyoutConnection(rit.GetCurrent()); 48 | 49 | // Leave it blue, not gray. 50 | data[0] = 0xff; data[1] = 0x7f; data[2] = 0x7f; 51 | } 52 | } 53 | } 54 | 55 | // Return what is currently cached in the cwhite object. 56 | return m_cwhite.GetConnections(); 57 | } 58 | 59 | } // end namespace cs 60 | -------------------------------------------------------------------------------- /include/Filter.h: -------------------------------------------------------------------------------- 1 | // ----------------------------------------------------------------------------- 2 | // This header contains the public prototypes for the filter library. The 3 | // filter library is used to create filters images and connection images of 4 | // input circuit images using the opencv library. 5 | // 6 | // This has previously been done with the image magick library - we are now 7 | // doing it with opencv to experiement with speed. 8 | // 9 | // Copyright (c) 2011-2012 Matt Pillar. All rights reserved. 10 | // ----------------------------------------------------------------------------- 11 | 12 | #ifndef API_FILTER_FILTER_H 13 | #define API_FILTER_FILTER_H 14 | 15 | #include 16 | 17 | namespace cs 18 | { 19 | 20 | // ----------------------------------------------------------------------------- 21 | // class Filter 22 | // ----------------------------------------------------------------------------- 23 | 24 | class Filter 25 | { 26 | private: 27 | // Source image and resized image. 28 | IplImage& m_src; 29 | IplImage* m_sgray; 30 | IplImage* m_connblob; 31 | 32 | // Small temp images (grayscale). 33 | IplImage* m_stmp1; 34 | IplImage* m_stmp2; 35 | 36 | // The max dimension of the resized image. 37 | int m_maxdim; 38 | 39 | // Functions 40 | void Initialize(); 41 | void ConnectionBlobInternal(); 42 | 43 | public: 44 | // Constructor. Ownership of src NOT passed to this object (we are NOT 45 | // responsible for destroying it). 46 | // 47 | // The input IplImage& src must be an RBG image. 48 | Filter(IplImage& src, unsigned int maxdim) : 49 | m_src(src), 50 | m_sgray(0), 51 | m_connblob(0), 52 | m_stmp1(0), 53 | m_stmp2(0), 54 | m_maxdim(maxdim) 55 | { 56 | // Resize so the mutators are ready to do their thing. Also initialize 57 | // m_[s]gray and m_stmpX. After this call, we will be ready to perform 58 | // ConnectionBlob() or ComponentBlob(). 59 | Initialize(); 60 | } 61 | 62 | // Destructor 63 | ~Filter(); 64 | 65 | // Accessors 66 | const IplImage& GetSrc() const {return m_src;} 67 | 68 | // Mutators 69 | const IplImage* SmallGray() {return m_sgray;} 70 | const IplImage* ConnectionBlob() {return m_connblob;} 71 | const IplImage* ComponentBlob(unsigned int threshold); 72 | 73 | // Statics 74 | static bool Invert(IplImage* img); 75 | static IplImage* ResizeGray(IplImage* src, int maxdim); 76 | }; 77 | 78 | } // end namespace cs 79 | 80 | #endif 81 | -------------------------------------------------------------------------------- /include/Rectangle.h: -------------------------------------------------------------------------------- 1 | // ----------------------------------------------------------------------------- 2 | // This header contains the public prototypes for the transform library. 3 | // This library is used to transform rectangular spaces in one basis to another. 4 | // 5 | // Examples of rectangles: 6 | // 7 | // size 0x0: single point at x,y 8 | // size 1x1: x,x 9 | // x,x 10 | // size 2x2: x,x,x 11 | // x,x,x 12 | // x,x,x 13 | // 14 | // Note that the 'width' and the 'height' represent the number of cells we 15 | // "jump" across. That is why the 1x1 cell is actually a space of 2x2. 16 | // 17 | // Copyright (c) 2011-2012 Matt Pillar. All rights reserved. 18 | // ----------------------------------------------------------------------------- 19 | 20 | #ifndef API_TRANSFORM_RECTANGLE_H 21 | #define API_TRANSFORM_RECTANGLE_H 22 | 23 | #include 24 | #include 25 | 26 | namespace cs 27 | { 28 | 29 | // Forward declarations. 30 | class Point; 31 | class Transform; 32 | 33 | // ----------------------------------------------------------------------------- 34 | // class Rectangle 35 | // ----------------------------------------------------------------------------- 36 | 37 | class Rectangle 38 | { 39 | private: 40 | unsigned int m_x; 41 | unsigned int m_y; 42 | unsigned int m_width; 43 | unsigned int m_height; 44 | 45 | public: 46 | Rectangle( 47 | unsigned int x, unsigned int y, 48 | unsigned int width, unsigned int height) : 49 | m_x(x), 50 | m_y(y), 51 | m_width(width), 52 | m_height(height) 53 | { 54 | // no-op 55 | } 56 | 57 | // Accessors. 58 | std::string ToString() const; 59 | 60 | // True when the point is in the rectangle. 61 | bool Contains(const Point& p) const; 62 | 63 | // True when the point is on the border of the rectangle. 64 | bool Borders(const Point& p) const; 65 | 66 | // More accrssors 67 | unsigned int GetX() const {return m_x;} 68 | unsigned int GetY() const {return m_y;} 69 | unsigned int GetWidth() const {return m_width;} 70 | unsigned int GetHeight() const {return m_height;} 71 | unsigned int GetArea() const {return m_width*m_height;} 72 | 73 | bool IsZero() const {return m_width == 0 && m_height == 0;} 74 | unsigned int DistanceTo(const Rectangle& rect) const; 75 | 76 | // Mutators 77 | void Scale(double factor); 78 | 79 | void SetX(unsigned int x) {m_x = x;} 80 | void SetY(unsigned int y) {m_y = y;} 81 | void SetWidth(unsigned int width) {m_width = width;} 82 | void SetHeight(unsigned int height) {m_height = height;} 83 | 84 | void Merge(const Rectangle& rect); 85 | 86 | void IncrX() {m_x++;} 87 | void DecrX() {m_x--;} 88 | void IncrY() {m_y++;} 89 | void DecrY() {m_y--;} 90 | }; 91 | 92 | } // end namespace cs 93 | 94 | #endif 95 | -------------------------------------------------------------------------------- /src/SmartComponentID.cpp: -------------------------------------------------------------------------------- 1 | // ----------------------------------------------------------------------------- 2 | // Implementation of the circuit library. This particular class is used to 3 | // map component ID's to semantic types of circuit components. This class also 4 | // contains some sorting related functionality. 5 | // 6 | // Copyright (c) 2011-2012 Matt Pillar. All rights reserved. 7 | // ----------------------------------------------------------------------------- 8 | 9 | #include 10 | #include 11 | 12 | #include "Circuit.h" 13 | 14 | namespace cs 15 | { 16 | 17 | // Utility for the sorting predicate. New components must be added somewhere to 18 | // this sorting predicate. 19 | // 20 | // When 'sort' is true, the unsigned returned corresponds to the value that 21 | // should be used by the sorting predicate. When false, the unsigned returned 22 | // corresponds to the value that should be used by the operator== overload. 23 | static unsigned int ComponentIDToUnsigned( 24 | bool sort, 25 | const SmartComponentID& comp) 26 | { 27 | // Note the components should be grouped by equality in this switch 28 | // statement. 29 | switch(comp.GetID()) 30 | { 31 | // Active components. 32 | case 'S': return 0; 33 | case 's': return sort ? 1 : 0; 34 | case 'V': return sort ? 2 : 2; 35 | case 'v': return sort ? 3 : 2; 36 | case 'A': return sort ? 4 : 2; 37 | case 'a': return sort ? 5 : 2; 38 | 39 | // Passive components 40 | case 'R': return 100; 41 | case 'r': return sort ? 101 : 100; 42 | 43 | case 'C': return 102; 44 | case 'c': return sort ? 103 : 102; 45 | 46 | case 'L': return 104; 47 | case 'l': return sort ? 105 : 104; 48 | 49 | case 'D': return 106; 50 | case 'd': return sort ? 107 : 106; 51 | case 'E': return sort ? 108 : 106; 52 | case 'e': return sort ? 109 : 106; 53 | 54 | case 'N': return 110; 55 | case 'n': return sort ? 111 : 110; 56 | 57 | case 'P': return 112; 58 | case 'p': return sort ? 113 : 112; 59 | 60 | case 'T': return 114; 61 | case 't': return sort ? 115 : 114; 62 | 63 | case 'Q': return 116; 64 | case 'q': return sort ? 117 : 116; 65 | 66 | // Put unknowns at the bottom of the list. 67 | default: return INT_MAX; 68 | } 69 | } 70 | 71 | bool SmartComponentID::operator==(const SmartComponentID& id) 72 | { 73 | return ComponentIDToUnsigned(false, *this) == 74 | ComponentIDToUnsigned(false, id); 75 | } 76 | 77 | bool ComponentIDSortPredicate( 78 | const CircuitEntity& entity1, 79 | const CircuitEntity& entity2) 80 | { 81 | return ComponentIDToUnsigned(true, entity1.GetComponentID()) < 82 | ComponentIDToUnsigned(true, entity2.GetComponentID()); 83 | } 84 | 85 | bool SmartComponentID::IsGround() const 86 | { 87 | return GetID() == 'G'; 88 | } 89 | 90 | bool SmartComponentID::IsNull() const 91 | { 92 | return GetID() == '\0'; 93 | } 94 | 95 | } 96 | -------------------------------------------------------------------------------- /include/AlgorithmResult.h: -------------------------------------------------------------------------------- 1 | // ----------------------------------------------------------------------------- 2 | // This header contains the public prototypes for the algorithm library. 3 | // The algorithm library brings all the other libraries together; it 4 | // takes an opencv image as an input and the output is a netlist. 5 | // 6 | // Copyright (c) 2011-2012 Matt Pillar. All rights reserved. 7 | // ----------------------------------------------------------------------------- 8 | 9 | #include 10 | 11 | #ifndef API_ALGORITHM_ALGORITHMRESULTH 12 | #define API_ALGORITHM_ALGORITHMRESULTH 13 | 14 | namespace cs 15 | { 16 | 17 | // ----------------------------------------------------------------------------- 18 | // class AlgorithmResult 19 | // ----------------------------------------------------------------------------- 20 | 21 | class AlgorithmBase; 22 | 23 | class __attribute__ ((visibility ("default"))) AlgorithmResult 24 | { 25 | private: 26 | // The resulting netlist. 27 | std::string m_netlist; 28 | 29 | // The warning and error text from the netlist generator. 30 | std::string m_error; 31 | 32 | // The best threshold. 33 | unsigned int m_threshold; 34 | unsigned int m_textThreshold; 35 | 36 | // The best confidence. This value is calculated by averaging the confidence 37 | // values of all the valid components in the circuit. 38 | unsigned int m_confidence; 39 | unsigned int m_textConfidence; 40 | 41 | // The number of not-null connected entities in the circuit object. 42 | unsigned int m_components; 43 | unsigned int m_textComponents; 44 | 45 | public: 46 | // Constructor. 47 | AlgorithmResult() : 48 | m_threshold(0), 49 | m_textThreshold(0), 50 | m_confidence(0), 51 | m_textConfidence(0), 52 | m_components(0), 53 | m_textComponents(0) 54 | { 55 | // no-op 56 | } 57 | 58 | // Return the netlist. If ConvertToNetlist has not been called yet, return 59 | // an empty string. 60 | const std::string& GetNetlist() const {return m_netlist;} 61 | 62 | // Any error or warning messages that result from netlist generation. 63 | const std::string& GetErrorText() const {return m_error;} 64 | 65 | // Return true if there were any warnings generated during netlist 66 | // conversion. 67 | bool HasErrorText() const {return !m_error.empty();} 68 | 69 | // Returns the threshold that we determined was most optimal for this 70 | // particular circuit image. 71 | unsigned int GetBestThreshold() const {return m_threshold;} 72 | unsigned int GetBestTextThreshold() const {return m_textThreshold;} 73 | 74 | // A confidence metric that describes how sure we are that we parsed the 75 | // circuit correctly. 76 | unsigned int GetConfidence() const {return m_confidence;} 77 | unsigned int GetTextConfidence() const {return m_textConfidence;} 78 | 79 | // Get the number of components we found in the resulting circuit. 80 | unsigned int GetNumComponents() const {return m_components;} 81 | unsigned int GetNumTextComponents() const {return m_textComponents;} 82 | 83 | // Friends 84 | friend class AlgorithmBase; 85 | }; 86 | 87 | } // end namespace cs 88 | 89 | #endif 90 | -------------------------------------------------------------------------------- /include/NetlistGenerator.h: -------------------------------------------------------------------------------- 1 | // ----------------------------------------------------------------------------- 2 | // This header contains the public prototypes for the circuit library. 3 | // 4 | // Copyright (c) 2011-2012 Matt Pillar. All rights reserved. 5 | // ----------------------------------------------------------------------------- 6 | 7 | #ifndef API_CIRCUIT_NETLISTGENERATOR_H 8 | #define API_CIRCUIT_NETLISTGENERATOR_H 9 | 10 | #include 11 | #include 12 | 13 | #include "CircuitEntityConnection.h" 14 | #include "CircuitEntity.h" 15 | #include "Circuit.h" 16 | 17 | namespace cs 18 | { 19 | 20 | // ----------------------------------------------------------------------------- 21 | // class NetlistGenerator 22 | // ----------------------------------------------------------------------------- 23 | 24 | class NetlistGenerator 25 | { 26 | private: 27 | // The circuit that we are going to convert. 28 | const Circuit& m_circuit; 29 | 30 | // The modified circuit object, after netlist generation. 31 | Circuit m_modified; 32 | 33 | // Boolean indicating whether or not we have already converted the netlist. 34 | // This is only ever done once per instance. 35 | bool m_done; 36 | 37 | // Strings for the results. 38 | std::ostringstream m_netlist; 39 | std::ostringstream m_error; 40 | 41 | // Utilities for writing netlist output statements. 42 | void WritePortStatement(CircuitEntity& entity, 43 | unsigned int componentNumber, const std::string& netlistIdentifier, 44 | const std::vector& vec); 45 | 46 | void Write2PortStatement(CircuitEntity& entity, 47 | unsigned int componentNumber, std::string netlistIdentifier, 48 | unsigned int port1, unsigned int port2); 49 | 50 | void Write3PortStatement(CircuitEntity& entity, 51 | unsigned int componentNumber, std::string netlistIdentifier, 52 | unsigned int port1, unsigned int port2, unsigned int port3); 53 | 54 | // Called by ConvertToNetlist(). 55 | void ConvertToNetlistInternal(); 56 | 57 | public: 58 | // Constructor. 59 | NetlistGenerator(const Circuit& circuit) : 60 | m_circuit(circuit), 61 | m_done(false) 62 | { 63 | // no-op 64 | } 65 | 66 | // Convert the circuit to a netlist. This will not modify the orignal 67 | // circuit object that was passed in. 68 | void ConvertToNetlist() 69 | { 70 | if (!m_done) 71 | { 72 | ConvertToNetlistInternal(); 73 | m_done = true; 74 | } 75 | } 76 | 77 | // Get the resulting netlist. 78 | std::string GetNetlist() const; 79 | 80 | // Any error or warning messages that result from netlist generation. 81 | std::string GetErrorText() const; 82 | 83 | // Return true if there were any warnings generated during netlist 84 | // conversion. 85 | bool HasErrorText() const {return !m_error.str().empty();} 86 | 87 | // Get the modified netlist that results from circuit generation. 88 | const Circuit& GetModifiedCircuit() const {return m_modified;} 89 | 90 | // Get the default netlist text. 91 | static std::string GetDefaultNetlistText(); 92 | }; 93 | 94 | } // end namespace cs 95 | 96 | #endif 97 | -------------------------------------------------------------------------------- /include/CircuitEntity.h: -------------------------------------------------------------------------------- 1 | // ----------------------------------------------------------------------------- 2 | // This class is also pretty stupid and the node numbers are managed by the 3 | // circuit class. That is why we expose the connection pointers to the caller. 4 | // 5 | // Copyright (c) 2011-2012 Matt Pillar. All rights reserved. 6 | // ----------------------------------------------------------------------------- 7 | 8 | #ifndef API_CIRCUIT_CIRCUITENTITY_H 9 | #define API_CIRCUIT_CIRCUITENTITY_H 10 | 11 | #include 12 | #include 13 | 14 | #include "CircuitEntityConnection.h" 15 | #include "SmartComponentID.h" 16 | #include "Tesseract.h" 17 | 18 | namespace cs 19 | { 20 | 21 | // ----------------------------------------------------------------------------- 22 | // class CircuitEntity 23 | // ----------------------------------------------------------------------------- 24 | 25 | class CircuitEntity 26 | { 27 | public: 28 | // Our vector of connections. 29 | typedef std::vector ConnectionVector; 30 | ConnectionVector m_vector; 31 | 32 | private: 33 | // This entity's component ID. 34 | SmartComponentID m_compid; 35 | 36 | // This entity's value string. 37 | std::string m_value; 38 | 39 | public: 40 | // Constructor. 41 | CircuitEntity(SmartComponentID compid, std::string value) : 42 | m_compid(compid), 43 | m_value(value) 44 | { 45 | // no-op 46 | } 47 | 48 | // Accessors 49 | unsigned int GetNumConnections() const; 50 | unsigned int GetNumSequentialConnections() const; 51 | bool IsConnected() const; 52 | SmartComponentID GetComponentID() const; 53 | const std::string& GetValue() const; 54 | bool HasValue() const; 55 | unsigned int GetNumConnectionsWithNode(unsigned int node) const; 56 | int GetMinNode() const; 57 | 58 | std::string ToString() const; 59 | 60 | // Mutators 61 | void SetComponentID(SmartComponentID compid); 62 | void SetValue(std::string value); 63 | 64 | CircuitEntityConnection RemoveOne(); 65 | void RemoveByNode(unsigned int node); 66 | 67 | // Add the given connection to the connection vector. First checks to see if a connection 68 | // exists on the given port. If it does exist, then that old connection is replaced with this 69 | // one (i.e. the old node number is overwritten). 70 | // 71 | // The caller is responsible for managing the implications of overwriting node numbers. 72 | // 73 | // Returns -1 if there was no connection on the given port. Return the old node number that we 74 | // just wrote over if there was such a connection. 75 | int AddConnection(CircuitEntityConnection conn); 76 | 77 | // If a zero pointer is returned, no such connection entity exists. Do not free the returned 78 | // pointer! It belongs to this class. 79 | CircuitEntityConnection* GetConnectionByPort(unsigned int port); 80 | const CircuitEntityConnection* GetConstConnectionByPort(unsigned int port) const; 81 | 82 | CircuitEntityConnection* GetConnectionByNode(unsigned int node); 83 | const CircuitEntityConnection* GetConstConnectionByNode(unsigned int node) const; 84 | 85 | // Direct access to the vector. 86 | ConnectionVector& GetVector() {return m_vector;} 87 | }; 88 | 89 | } 90 | 91 | #endif 92 | -------------------------------------------------------------------------------- /src/Rectangle.cpp: -------------------------------------------------------------------------------- 1 | // ----------------------------------------------------------------------------- 2 | // Implementation of the transform library. Used to map rectangular spaces 3 | // in one coordinate basis to another coordinate basis. Useful for manipulating 4 | // multiple instances of the same images that have been resized. 5 | // 6 | // Copyright (c) 2011-2012 Matt Pillar. All rights reserved. 7 | // ----------------------------------------------------------------------------- 8 | 9 | #include 10 | 11 | #include "Point.h" 12 | #include "Transform.h" 13 | 14 | namespace cs 15 | { 16 | 17 | bool Rectangle::Contains(const Point& p) const 18 | { 19 | if (p.GetX() > GetX() && 20 | p.GetY() > GetY() && 21 | p.GetX() < GetX() + GetWidth() && 22 | p.GetY() < GetY() + GetHeight()) 23 | { 24 | return true; 25 | } 26 | 27 | return false; 28 | } 29 | 30 | bool Rectangle::Borders(const Point& p) const 31 | { 32 | // It's in the big rectangle and it doesn't contain the point, so it must 33 | // border the point. 34 | if (p.GetX() >= GetX() && 35 | p.GetY() >= GetY() && 36 | p.GetX() <= GetX() + GetWidth() && 37 | p.GetY() <= GetY() + GetHeight() && 38 | !Contains(p)) 39 | { 40 | return true; 41 | } 42 | 43 | return false; 44 | } 45 | 46 | unsigned int Rectangle::DistanceTo(const Rectangle& rect) const 47 | { 48 | double myXMiddle = GetX() + GetWidth()/2.0; 49 | double myYMiddle = GetY() + GetHeight()/2.0; 50 | 51 | double hisXMiddle = rect.GetX() + rect.GetWidth()/2.0; 52 | double hisYMiddle = rect.GetY() + rect.GetHeight()/2.0; 53 | 54 | double xDiff = hisXMiddle - myXMiddle; 55 | double yDiff = hisYMiddle - myYMiddle; 56 | 57 | return (unsigned int) sqrt(xDiff*xDiff + yDiff*yDiff); 58 | } 59 | 60 | std::string Rectangle::ToString() const 61 | { 62 | std::ostringstream os; 63 | 64 | os 65 | << "{x=" << m_x << ",y=" << m_y 66 | << ",width=" << m_width << ",height=" << m_height << "}"; 67 | 68 | return os.str(); 69 | } 70 | 71 | void Rectangle::Scale(double factor) 72 | { 73 | Rectangle rect = *this; 74 | 75 | m_width = (unsigned int) (GetWidth() * factor); 76 | m_height = (unsigned int) (GetHeight() * factor); 77 | 78 | unsigned int subWidth = (GetWidth() - rect.GetWidth()) / 2; 79 | unsigned int subHeight = (GetHeight() - rect.GetHeight()) / 2; 80 | 81 | if (subWidth > rect.m_x) 82 | { 83 | m_x = 0; 84 | } 85 | else 86 | { 87 | m_x = rect.m_x - subWidth; 88 | } 89 | 90 | if (subHeight > rect.m_y) 91 | { 92 | m_y = 0; 93 | } 94 | else 95 | { 96 | m_y = rect.m_y - subHeight; 97 | } 98 | } 99 | 100 | void Rectangle::Merge(const Rectangle& rect) 101 | { 102 | Rectangle newRect(0,0,0,0); 103 | 104 | newRect.m_x = std::min(GetX(), rect.GetX()); 105 | newRect.m_y = std::min(GetY(), rect.GetY()); 106 | 107 | unsigned int rightX, rightY; 108 | 109 | rightX = std::max(GetX()+GetWidth(), rect.GetX()+rect.GetWidth()); 110 | rightY = std::max(GetY()+GetHeight(), rect.GetY()+rect.GetHeight()); 111 | 112 | newRect.m_width = rightX-newRect.GetX(); 113 | newRect.m_height = rightY-newRect.GetY(); 114 | 115 | *this = newRect; 116 | } 117 | 118 | } // end namespace cs 119 | -------------------------------------------------------------------------------- /include/AlgorithmBase.h: -------------------------------------------------------------------------------- 1 | // ----------------------------------------------------------------------------- 2 | // This header contains the public prototypes for the algorithm library. 3 | // The algorithm library brings all the other libraries together; it 4 | // takes an opencv image as an input and the output is a netlist. 5 | // 6 | // Copyright (c) 2011-2012 Matt Pillar. All rights reserved. 7 | // ----------------------------------------------------------------------------- 8 | 9 | #ifndef API_ALGORITHM_ALGORITHMBASE_H 10 | #define API_ALGORITHM_ALGORITHMBASE_H 11 | 12 | #include 13 | 14 | #include "AlgorithmResult.h" 15 | #include "ResultCircuits.h" 16 | #include "ResultEnglish.h" 17 | #include "Rectangle.h" 18 | #include "Filter.h" 19 | #include "Circuit.h" 20 | #include "NetlistGenerator.h" 21 | 22 | namespace cs 23 | { 24 | 25 | // ----------------------------------------------------------------------------- 26 | // class AlgorithmBase 27 | // ----------------------------------------------------------------------------- 28 | 29 | class __attribute__ ((visibility ("default"))) AlgorithmBase 30 | { 31 | protected: 32 | // ------------------------------------------------------------------------- 33 | // Members 34 | // ------------------------------------------------------------------------- 35 | 36 | // The source images. 37 | IplImage& m_src; 38 | 39 | // The algorithm result. 40 | AlgorithmResult m_result; 41 | 42 | // ------------------------------------------------------------------------- 43 | // Virtual functions (checkpoints) 44 | // ------------------------------------------------------------------------- 45 | 46 | virtual void CHECKPOINT_STARTING(const Filter& filter) = 0; 47 | virtual void CHECKPOINT_NEWTHRESH(unsigned int threshold) = 0; 48 | virtual void CHECKPOINT_FOUNDPEAKS(const IplImage& compblob) = 0; 49 | virtual void CHECKPOINT_FOUNDCONNS(const IplImage& connblob) = 0; 50 | 51 | virtual void CHECKPOINT_TESSERACT(const Rectangle& map) = 0; 52 | virtual void CHECKPOINT_CIRCUITS(const ResultCircuits& res) = 0; 53 | virtual void CHECKPOINT_ENGLISH(const ResultEnglish& res) = 0; 54 | 55 | virtual void CHECKPOINT_HAVECIRCUIT(const Circuit& circuit) = 0; 56 | virtual void CHECKPOINT_HAVEGEN(const NetlistGenerator& g) = 0; 57 | virtual void CHECKPOINT_HAVECONF(unsigned int conf, unsigned int tconf) = 0; 58 | 59 | virtual void CHECKPOINT_DONE() = 0; 60 | 61 | // ------------------------------------------------------------------------- 62 | // Defaults 63 | // ------------------------------------------------------------------------- 64 | 65 | public: 66 | // Constructor. 67 | AlgorithmBase(IplImage& src) : 68 | m_src(src) 69 | { 70 | // no-op 71 | } 72 | 73 | // ------------------------------------------------------------------------- 74 | // Accessors 75 | // ------------------------------------------------------------------------- 76 | 77 | const AlgorithmResult& GetResult() const {return m_result;} 78 | 79 | // Constants are here because we want the command line to have access. 80 | static unsigned int ThresholdStart() {return 7;} 81 | static unsigned int ThresholdEnd() {return 18;} 82 | 83 | // ------------------------------------------------------------------------- 84 | // Mutators 85 | // ------------------------------------------------------------------------- 86 | 87 | // Converts the source image to a netlist. May throw a Exception of a 88 | // cv::Exception, so the user is advised to catch. 89 | void ConvertToNetlist( 90 | unsigned int thresholdStart = ThresholdStart(), 91 | unsigned int thresholdEnd = ThresholdEnd()); 92 | }; 93 | 94 | } // end namespace cs 95 | 96 | #endif 97 | -------------------------------------------------------------------------------- /src/WhiteoutConnFinder.cpp: -------------------------------------------------------------------------------- 1 | // ----------------------------------------------------------------------------- 2 | // Implementation of the connfinder library. This specific implementation 3 | // file could also belong in the whiteout lib, but because it is so 4 | // specific I moved it here. 5 | // 6 | // Copyright (c) 2011-2012 Matt Pillar. All rights reserved. 7 | // ----------------------------------------------------------------------------- 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "WhiteoutConnFinder.h" 15 | 16 | namespace cs 17 | { 18 | 19 | bool WhiteoutConnFinder::GreyoutConnection(const Point& p) 20 | { 21 | // Save this for the check at the end of the function, for the return value. 22 | unsigned int size = m_conns.GetVector().size(); 23 | 24 | // Cache the point where the virtual call backs can see it. 25 | m_graypoint = p; 26 | 27 | // Find the index of the peak we're working off of. 28 | unsigned int counter = 0; 29 | m_index = 0; 30 | 31 | for (PeakVector::RectangleVector::const_iterator pk = 32 | m_peaks.GetVector().begin(); 33 | pk != m_peaks.GetVector().end(); pk++, counter++) 34 | { 35 | if (pk->Borders(p)) 36 | { 37 | m_index = counter; 38 | break; 39 | } 40 | } 41 | 42 | // Clockwise (last param) doesn't matter here. 43 | DoWhiteout( 44 | Space(m_src.width, m_src.height), 45 | Rectangle(p.GetX(), p.GetY(), 0, 0), 46 | p, 47 | Mask(0x88, 0x88, 0x88), 48 | true, 49 | true); 50 | 51 | // The size change, therefore a connection was added, therefore return true. 52 | // Could've done this in one line, but this is clearer. 53 | if (size != m_conns.GetVector().size()) 54 | { 55 | return true; 56 | } 57 | 58 | // Otherwise false. 59 | return false; 60 | } 61 | 62 | 63 | // Return true when we should stop searching adjacent cells. 64 | bool WhiteoutConnFinder::AdditionalCheck(const Point& p) 65 | { 66 | // Return true when we are contained within a peak. 67 | for (PeakVector::RectangleVector::const_iterator pk = 68 | m_peaks.GetVector().begin(); 69 | pk != m_peaks.GetVector().end(); pk++) 70 | { 71 | if (pk->Contains(p)) 72 | { 73 | return true; 74 | } 75 | } 76 | 77 | return false; 78 | } 79 | 80 | void WhiteoutConnFinder::PointFound(const Point& p) 81 | { 82 | // Add a connection if we border a peak and it's not the same point we 83 | // started out with. 84 | // 85 | // Also adds a border so we don't count this connection again. 86 | 87 | unsigned int counter = 0; 88 | for (PeakVector::RectangleVector::const_iterator pk = 89 | m_peaks.GetVector().begin(); 90 | pk != m_peaks.GetVector().end(); pk++, counter++) 91 | { 92 | if (pk->Borders(p) && p != m_graypoint) 93 | { 94 | m_conns.Push(Connection(m_index, counter, m_graypoint, p)); 95 | 96 | // Add a red border. 97 | uint8_t* data = Point::GetDataByPoint(m_src, p); 98 | data[0] = 0x00; data[1] = 0x00; data[2] = 0x00; 99 | m_pwhite.RedoutBorder(*pk, p, true); 100 | data[0] = 0x00; data[1] = 0x00; data[2] = 0x00; 101 | m_pwhite.RedoutBorder(*pk, p, false); 102 | 103 | // And leave this particular cell blue-green. 104 | data[0] = 0xff; data[1] = 0xff; data[2] = 0x7f; 105 | 106 | // No need to continue searching. 107 | return; 108 | } 109 | } 110 | } 111 | 112 | } // end namespace cs 113 | -------------------------------------------------------------------------------- /src/Tesseract.cpp: -------------------------------------------------------------------------------- 1 | // ----------------------------------------------------------------------------- 2 | // This file contains the core implementation of the tesseract library. 3 | // This library is intented to replace the old tesseract library that used the 4 | // dirty old c-style api. This library also uses opencv instead of manipulating 5 | // files on disk. 6 | // 7 | // Copyright (c) 2011-2012 Matt Pillar. All rights reserved. 8 | // ----------------------------------------------------------------------------- 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include "Tesseract.h" 17 | #include "ResultEnglish.h" 18 | #include "ResultCircuits.h" 19 | 20 | namespace cs 21 | { 22 | 23 | static int iswhitespace(char c) 24 | { 25 | return (c == ' ' || c == '\r' || c == '\n'); 26 | } 27 | 28 | Tesseract::Tesseract(IplImage& src) : m_src(src) 29 | { 30 | m_gray = cvCreateImage(cvGetSize(&m_src), IPL_DEPTH_8U, 1); 31 | cvCvtColor(&m_src, m_gray, CV_BGR2GRAY); 32 | } 33 | 34 | Tesseract::~Tesseract() 35 | { 36 | cvReleaseImage(&m_gray); 37 | } 38 | 39 | ResultEnglish Tesseract::TesseractEnglish(Rectangle rect) 40 | { 41 | ResultEnglish result; 42 | 43 | // Set the region of interest and copy the image in memory. 44 | cvSetImageROI(m_gray, cvRect(rect.GetX(), rect.GetY(), 45 | rect.GetWidth(),rect.GetHeight())); 46 | 47 | IplImage* roi = cvCreateImage(cvGetSize(m_gray), m_gray->depth, 1); 48 | cvCopy(m_gray, roi); 49 | 50 | // Reset the region of interest. 51 | cvResetImageROI(m_gray); 52 | 53 | // Now, process the copied image, roi. 54 | tesseract::TessBaseAPI api; 55 | api.Init(".", "eng"); 56 | api.SetVariable("save_best_choices", "T"); 57 | api.SetVariable("tessedit_char_whitelist", "pnumkMG0123456789.VHF"); 58 | api.SetPageSegMode(tesseract::PSM_SINGLE_WORD); 59 | 60 | api.SetImage((uint8_t*) roi->imageData, roi->width, roi->height, 1, 61 | roi->widthStep); 62 | 63 | result.m_confidence = api.MeanTextConf(); 64 | 65 | char* utf8 = api.GetUTF8Text(); 66 | result.m_chars = std::string(utf8); 67 | delete[] utf8; 68 | 69 | result.m_chars.erase(std::remove_if(result.m_chars.begin(), 70 | result.m_chars.end(), iswhitespace), result.m_chars.end()); 71 | 72 | // Cleanup and return. 73 | cvReleaseImage(&roi); 74 | return result; 75 | } 76 | 77 | ResultCircuits Tesseract::TesseractCircuits(Rectangle rect) 78 | { 79 | ResultCircuits result; 80 | 81 | // Set the region of interest and copy the image in memory. 82 | cvSetImageROI(m_gray, cvRect(rect.GetX(), rect.GetY(), 83 | rect.GetWidth(),rect.GetHeight())); 84 | 85 | IplImage* roi = cvCreateImage(cvGetSize(m_gray), m_gray->depth, 1); 86 | cvCopy(m_gray, roi); 87 | 88 | // Reset the region of interest. 89 | cvResetImageROI(m_gray); 90 | 91 | // Now, process the copied image, roi. 92 | tesseract::TessBaseAPI api; 93 | api.Init(".", "cir"); 94 | api.SetVariable("save_best_choices", "T"); 95 | api.SetPageSegMode(tesseract::PSM_SINGLE_CHAR); 96 | 97 | api.SetImage((uint8_t*) roi->imageData, roi->width, roi->height, 1, 98 | roi->widthStep); 99 | 100 | result.m_confidence = api.MeanTextConf(); 101 | 102 | char* utf8 = api.GetUTF8Text(); 103 | std::string chars = std::string(utf8); 104 | delete[] utf8; 105 | 106 | chars.erase(std::remove_if(chars.begin(), chars.end(), iswhitespace), 107 | chars.end()); 108 | 109 | for (unsigned int i = 0; i < chars.length(); i++) 110 | { 111 | result.m_vector.push_back(ComponentID(chars[i])); 112 | } 113 | 114 | // Cleanup and return. 115 | cvReleaseImage(&roi); 116 | return result; 117 | } 118 | 119 | } // end namespace cs 120 | -------------------------------------------------------------------------------- /src/Whiteout.cpp: -------------------------------------------------------------------------------- 1 | // ----------------------------------------------------------------------------- 2 | // Author: Matt Pillar 3 | // 4 | // Implementation of the whiteout library. This library is used to whiteout 5 | // the edges of images. 6 | // 7 | // Copyright (c) 2011 Matt Pillar. All rights reserved. 8 | // ----------------------------------------------------------------------------- 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "Whiteout.h" 16 | #include "Exception.h" 17 | #include "OutsideRectangleIterator.h" 18 | 19 | namespace cs 20 | { 21 | 22 | Whiteout::Whiteout(IplImage& src) : m_src(src) 23 | { 24 | if (src.nChannels != 3) 25 | { 26 | throw Exception("Whiteout image must be 3 channel"); 27 | } 28 | } 29 | 30 | bool Whiteout::WhiteoutHelper( 31 | bool viral, 32 | Mask& mask, 33 | unsigned int& depth, 34 | PointVector& pv, 35 | Point p) 36 | { 37 | // Perform the additional check. Note this is a virtual function. 38 | if (AdditionalCheck(p)) 39 | { 40 | // The check says this point is invalid. 41 | return false; 42 | } 43 | 44 | // Get the point. 45 | uint8_t* data = Point::GetDataByPoint(m_src, p); 46 | if (data == 0) 47 | { 48 | // The point is outside the image. 49 | return false; 50 | } 51 | 52 | // This will be on the stack. Bad, but no avoiding this. 53 | Point point(0, 0); 54 | 55 | // If any colour component is less that half intensity, it is considered a 56 | // "dark" cell, and we will "white" it out. 57 | if (data[0] < 0x7f || data[1] < 0x7f || data[2] < 0x7f) 58 | { 59 | // Maximum stack depth. 60 | depth++; 61 | if (depth > 100) 62 | { 63 | pv.push_back(p); 64 | return false; 65 | } 66 | 67 | // Whiteout this cell. 68 | data[0] = mask.GetBlue(); 69 | data[1] = mask.GetGreen(); 70 | data[2] = mask.GetRed(); 71 | 72 | // Point found callback (virtual). 73 | PointFound(p); 74 | 75 | if (viral) 76 | { 77 | // To the right. 78 | point = Point(p.GetX()+1, p.GetY()); 79 | WhiteoutHelper(viral, mask, depth, pv, point); 80 | 81 | // To the bottom. 82 | point = Point(p.GetX(), p.GetY()+1); 83 | WhiteoutHelper(viral, mask, depth, pv, point); 84 | 85 | // To the left. 86 | point = Point(p.GetX()-1, p.GetY()); 87 | WhiteoutHelper(viral, mask, depth, pv, point); 88 | 89 | // To the top. 90 | point = Point(p.GetX(), p.GetY()-1); 91 | WhiteoutHelper(viral, mask, depth, pv, point); 92 | } 93 | 94 | return true; 95 | } 96 | 97 | return false; 98 | } 99 | 100 | void Whiteout::DoWhiteout( 101 | Space space, 102 | Rectangle rect, 103 | Point starting, 104 | Mask mask, 105 | bool clockwise, bool viral) 106 | { 107 | unsigned int depth; 108 | PointVector pv; 109 | 110 | // Loop around the outside of the rectangle. Call whiteout helper at each 111 | // pixel. 112 | for (OutsideRectangleIterator it(space, rect, starting); it.More();) 113 | { 114 | depth = 0; 115 | if (!WhiteoutHelper(viral, mask, depth, pv, it.GetCurrent()) && 116 | !viral) 117 | { 118 | return; 119 | } 120 | 121 | while (!pv.empty()) 122 | { 123 | Point vpoint = pv.front(); 124 | pv.pop_front(); 125 | 126 | depth = 0; 127 | if (!WhiteoutHelper(viral, mask, depth, pv, vpoint) && 128 | !viral) 129 | { 130 | return; 131 | } 132 | } 133 | 134 | // Move either clockwise or counter clockwise. 135 | if (clockwise) 136 | { 137 | it++; 138 | } 139 | else 140 | { 141 | it--; 142 | } 143 | } 144 | } 145 | 146 | } // end namespace cs 147 | -------------------------------------------------------------------------------- /src/AlgorithmDebug.cpp: -------------------------------------------------------------------------------- 1 | // ----------------------------------------------------------------------------- 2 | // Author: Matt Pillar 3 | // 4 | // Implementation of the debug library. This library is used to bring debug 5 | // the main algorithm library. This is the only api library that will 6 | // make opencv highgui calls. It is not linked in the final product. 7 | // 8 | // Copyright (c) 2011 Matt Pillar. All rights reserved. 9 | // ----------------------------------------------------------------------------- 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include "AlgorithmDebug.h" 18 | 19 | namespace cs 20 | { 21 | 22 | void AlgorithmDebug::CHECKPOINT_STARTING(const Filter& filter) 23 | { 24 | m_logger << "Starting 's algorithm..."; 25 | } 26 | 27 | void AlgorithmDebug::CHECKPOINT_NEWTHRESH(unsigned int threshold) 28 | { 29 | std::ostringstream os1; 30 | os1 << "Threshold " << threshold << "..."; 31 | 32 | std::ostringstream os2; 33 | os2 << "Evaluate threshold " << threshold << "..."; 34 | m_logger << os2.str(); 35 | 36 | m_debug.push_back(DebugRow()); 37 | m_debug.back().m_threshold = threshold; 38 | } 39 | 40 | void AlgorithmDebug::CHECKPOINT_FOUNDPEAKS(const IplImage& compblob) 41 | { 42 | std::ostringstream os; 43 | os << m_debug.back().m_threshold << "_compblob.jpg"; 44 | cvSaveImage(os.str().c_str(), &compblob); 45 | } 46 | 47 | static unsigned int g_tesscounter = 0; 48 | void AlgorithmDebug::CHECKPOINT_FOUNDCONNS(const IplImage& connblob) 49 | { 50 | std::ostringstream os; 51 | os << m_debug.back().m_threshold << "_connblob.png"; 52 | cvSaveImage(os.str().c_str(), &connblob); 53 | 54 | g_tesscounter = 0; 55 | } 56 | 57 | void AlgorithmDebug::CHECKPOINT_TESSERACT(const Rectangle& map) 58 | { 59 | std::ostringstream os1; 60 | os1 << "(" << g_tesscounter << ") Applying tesseract on " << map.ToString(); 61 | m_logger << os1.str(); 62 | 63 | cvSetImageROI(&m_src, cvRect(map.GetX(), map.GetY(), map.GetWidth(), 64 | map.GetHeight())); 65 | 66 | std::ostringstream os2; 67 | os2 << m_debug.back().m_threshold << "_debug_" << g_tesscounter << ".jpg"; 68 | cvSaveImage(os2.str().c_str(), &m_src); 69 | cvResetImageROI(&m_src); 70 | 71 | g_tesscounter++; 72 | } 73 | 74 | void AlgorithmDebug::CHECKPOINT_CIRCUITS(const ResultCircuits& res) 75 | { 76 | std::ostringstream os; 77 | os 78 | << "Found circuit component {" << res.ToString() 79 | << "} (confidence=" << res.GetConfidence() << ")"; 80 | m_logger << os.str(); 81 | } 82 | 83 | void AlgorithmDebug::CHECKPOINT_ENGLISH(const ResultEnglish& res) 84 | { 85 | std::ostringstream os; 86 | os 87 | << "Found text {" << res.GetChars() << "}" 88 | << " (confidence=" << res.GetConfidence() << ")"; 89 | m_logger << os.str(); 90 | } 91 | 92 | void AlgorithmDebug::CHECKPOINT_HAVECIRCUIT(const Circuit& circuit) 93 | { 94 | std::cout << circuit.ToString() << std::endl; 95 | 96 | std::ostringstream os; 97 | os 98 | << "We have " << circuit.GetNumConnectedNotNullEntities() 99 | << " circuit components"; 100 | m_logger << os.str(); 101 | 102 | m_debug.back().m_components = 103 | circuit.GetNumConnectedNotNullEntities(); 104 | m_debug.back().m_textComponents = 105 | circuit.GetNumDisconnectedNullEntitiesWithValue(); 106 | } 107 | 108 | void AlgorithmDebug::CHECKPOINT_HAVEGEN(const NetlistGenerator& g) 109 | { 110 | m_logger << "Netlist:"; 111 | std::cout << g.GetNetlist() << std::endl; 112 | 113 | if (g.HasErrorText()) 114 | { 115 | m_logger << "Netlist warnings:"; 116 | std::cout << g.GetErrorText() << std::endl; 117 | m_logger << "Modified circuit:"; 118 | std::cout << g.GetModifiedCircuit().ToString() << std::endl; 119 | } 120 | } 121 | 122 | void AlgorithmDebug::CHECKPOINT_HAVECONF(unsigned int conf, 123 | unsigned int tconf) 124 | { 125 | std::ostringstream os; 126 | os << "conf=" << conf << " tconf=" << tconf; 127 | m_logger << os.str(); 128 | 129 | m_debug.back().m_confidence = conf; 130 | m_debug.back().m_textConfidence = tconf; 131 | } 132 | 133 | void AlgorithmDebug::CHECKPOINT_DONE() 134 | { 135 | // Print the debug table. 136 | m_logger << "Debug table:"; 137 | 138 | std::cout 139 | << "threshold,confidence,components,textConfidence,textComponents" 140 | << std::endl; 141 | 142 | for (std::vector::const_iterator it = m_debug.begin(); 143 | it != m_debug.end(); it++) 144 | { 145 | std::cout << it->ToString() << std::endl; 146 | } 147 | 148 | std::ostringstream os; 149 | os 150 | << "Done. The best circuit and text thresholds are " 151 | << m_result.GetBestThreshold() << " and " 152 | << m_result.GetBestTextThreshold() << " respectively."; 153 | 154 | m_logger << os.str(); 155 | } 156 | 157 | } // end namespace cs 158 | -------------------------------------------------------------------------------- /src/Filter.cpp: -------------------------------------------------------------------------------- 1 | // ----------------------------------------------------------------------------- 2 | // Implementation of the filter library. Contains the opencv equivalent 3 | // of the image magick filters that were originally used for the demo. 4 | // 5 | // Copyright (c) 2011-2012 Matt Pillar. All rights reserved. 6 | // ----------------------------------------------------------------------------- 7 | 8 | #include 9 | 10 | #include "Filter.h" 11 | 12 | namespace cs 13 | { 14 | 15 | // ----------------------------------------------------------------------------- 16 | // Static configuration 17 | // 18 | // Note the component blob smooth parameter is dynamic and specified by the 19 | // user. 20 | // ----------------------------------------------------------------------------- 21 | 22 | // First param (threshold) to canny edge detector in the connection blob 23 | // function. 24 | static unsigned int g_connCannyParam1 = 50; 25 | 26 | // Second param to canny edge detector in the connection blob function. 27 | static unsigned int g_connCannyParam2 = 100; 28 | 29 | // Threshold param to cv threshold the connection blob function. 30 | static unsigned int g_connThreshold = 200; 31 | 32 | // Threshold param to cv threshold in the component blob function. 33 | static unsigned int g_compThreshold = 70; 34 | 35 | // ----------------------------------------------------------------------------- 36 | // filter implementation 37 | // ----------------------------------------------------------------------------- 38 | 39 | void Filter::Initialize() 40 | { 41 | // Resize the image. Store the small gray image in m_sgray. 42 | m_sgray = ResizeGray(&m_src, m_maxdim); 43 | 44 | // Initialize the temps but don't put anything in them yet. 45 | m_connblob = cvCreateImage(cvGetSize(m_sgray), IPL_DEPTH_8U, 1); 46 | m_stmp1 = cvCreateImage(cvGetSize(m_sgray), IPL_DEPTH_8U, 1); 47 | m_stmp2 = cvCreateImage(cvGetSize(m_sgray), IPL_DEPTH_8U, 1); 48 | 49 | // Find the connection blob. 50 | ConnectionBlobInternal(); 51 | } 52 | 53 | Filter::~Filter() 54 | { 55 | // Note: release on a 0 pointer is a no-op. Do NOT destroy src. 56 | cvReleaseImage(&m_sgray); 57 | cvReleaseImage(&m_connblob); 58 | cvReleaseImage(&m_stmp1); 59 | cvReleaseImage(&m_stmp2); 60 | } 61 | 62 | void Filter::ConnectionBlobInternal() 63 | { 64 | // Edge detection and inversion. 65 | cvCanny(m_sgray, m_stmp1, g_connCannyParam1, g_connCannyParam2, 3); 66 | Invert(m_stmp1); 67 | 68 | // Repeating smoothing and thresholding to accent lines. 69 | for (unsigned int i = 0; i < 5; i++) 70 | { 71 | cvSmooth(m_stmp1, m_stmp2); 72 | cvThreshold(m_stmp2, m_stmp1, g_connThreshold, 255, CV_THRESH_BINARY); 73 | } 74 | 75 | cvCopy(m_stmp1, m_connblob); 76 | } 77 | 78 | // 21 is a good param, so 10 is a good threshold. Probably a good idea to vary 79 | // from 0 to 20. 80 | const IplImage* Filter::ComponentBlob(unsigned int threshold) 81 | { 82 | unsigned int param = 2*threshold+1; 83 | 84 | cvSmooth(ConnectionBlob(), m_stmp1, CV_BLUR, param, param); 85 | cvThreshold(m_stmp1, m_stmp2, g_compThreshold, 255, CV_THRESH_BINARY); 86 | 87 | return m_stmp2; 88 | } 89 | 90 | bool Filter::Invert(IplImage* img) 91 | { 92 | uint8_t* data; 93 | int i, j, k; 94 | 95 | if (img == 0) 96 | { 97 | return false; 98 | } 99 | 100 | data = (uint8_t*) img->imageData; 101 | for (i = 0; i < img->height; i++) 102 | { 103 | for (j = 0; j < img->width; j++) 104 | { 105 | for (k = 0; k < img->nChannels; k++) 106 | { 107 | // Could have used point here, but the function 108 | // call would just slow us down. 109 | int index = i*img->widthStep + j*img->nChannels + k; 110 | data[index] = 0xff - data[index]; 111 | } 112 | } 113 | } 114 | 115 | return true; 116 | } 117 | 118 | IplImage* Filter::ResizeGray(IplImage* src, int maxdim) 119 | { 120 | int newHeight = src->height, newWidth = src->width; 121 | bool resizeRequired = false; 122 | 123 | // If we have 2000x1000 (col x row i.e. width x height) then we will end up 124 | // with maxdim x (maxdim/2) after the resize. 125 | // 126 | // Similarly, if we have 1000x2000 we will have (maxdim/2) x maxdim. 127 | if (src->height > src->width && src->height > maxdim) 128 | { 129 | resizeRequired = true; 130 | 131 | newHeight = maxdim; 132 | newWidth = (int) (src->width * ((double) maxdim / src->height)); 133 | } 134 | else if (src->width > src->height && src->width > maxdim) 135 | { 136 | resizeRequired = true; 137 | 138 | newWidth = maxdim; 139 | newHeight = (int) (src->height * ((double) maxdim / src->width)); 140 | } 141 | 142 | // Convert the RGB input image to a grayscale image. 143 | IplImage* gray = cvCreateImage(cvGetSize(src), IPL_DEPTH_8U, 1); 144 | cvCvtColor(src, gray, CV_BGR2GRAY); 145 | 146 | // Perform the resize, if applicable, now that we're finished the math. 147 | CvSize newSize; 148 | newSize.height = newHeight; 149 | newSize.width = newWidth; 150 | 151 | IplImage* sgray; 152 | if (resizeRequired) 153 | { 154 | sgray = cvCreateImage(newSize, IPL_DEPTH_8U, 1); 155 | cvResize(gray, sgray); 156 | } 157 | else 158 | { 159 | sgray = (IplImage*) cvClone(gray); 160 | } 161 | 162 | cvReleaseImage(&gray); 163 | return sgray; 164 | } 165 | 166 | } // end namespace cs 167 | -------------------------------------------------------------------------------- /src/PortMapper.cpp: -------------------------------------------------------------------------------- 1 | // ----------------------------------------------------------------------------- 2 | // Implementation of the portmapper library. This library is used to 3 | // translate a peakvector and a connection vector to a circuit object. The 4 | // circuit object really represents the same information that is contained in 5 | // the two input vectors, just in a more organized manner. 6 | // 7 | // The circuit object also can be created without requiring the two vectors 8 | // using native Circuit mutators, which is convenient for unit testing and 9 | // system testing. 10 | // 11 | // Copyright (c) 2011-2012 Matt Pillar. All rights reserved. 12 | // ----------------------------------------------------------------------------- 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include "PortMapper.h" 21 | #include "OutsideRectangleIterator.h" 22 | 23 | namespace cs 24 | { 25 | 26 | static unsigned int PointPeakToTick( 27 | const Rectangle& peak, 28 | const Point& point) 29 | { 30 | // The number of 'ticks' until we reach the point we are searchng for. Each 31 | // tick is one increment as we iterate the rectangle starting at 3 o'clock. 32 | unsigned int ticks = 0; 33 | 34 | for (OutsideRectangleIterator rit( 35 | Space( 36 | peak.GetX()+peak.GetWidth()+1, 37 | peak.GetY()+peak.GetHeight()+1), 38 | peak, 39 | Point(peak.GetX()+peak.GetWidth(), peak.GetY())); 40 | rit.More(); rit++, ticks++) 41 | { 42 | // Count the ticks until we find the point. 43 | if (rit.GetCurrent() == point) 44 | { 45 | return ticks; 46 | } 47 | } 48 | 49 | // Should never happen unless the index we're passed is screwed up. Again, 50 | // this class is pretty dump and doesn't hold any of this data. 51 | throw Exception("Peak list does not contain the point we want"); 52 | return 0; 53 | } 54 | 55 | // Little structure to hold two integers for tick counting. 56 | struct TwoUnsignedInts 57 | { 58 | unsigned int m_int1; 59 | unsigned int m_int2; 60 | }; 61 | 62 | // Little static function used to loop through the tick vector and the 63 | // connection vector. 64 | // 65 | // It is assumed the connection vector and the tick vector are the same length. 66 | static unsigned int GetPortFromTicks(unsigned int ticks, unsigned int index, 67 | const std::vector& tickvec, 68 | const ConnVector& conns) 69 | { 70 | assert(conns.GetVector().size() == tickvec.size()); 71 | 72 | std::map pointMap; 73 | unsigned int port = 0; 74 | 75 | for (unsigned int i = 0; i < tickvec.size(); i++) 76 | { 77 | if (conns.GetVector()[i].GetPeak1() == index && 78 | tickvec[i].m_int1 < ticks) 79 | { 80 | // Only bump port if we haven't looked at this point already. The 81 | // point ToString() is unique. Better to use a hash lookup here 82 | // with O(ln(n)) than to go through the whole connection vector 83 | // again, which would be O(n). 84 | std::string point = conns.GetVector()[i].GetPoint1().ToString(); 85 | 86 | if (pointMap.find(point) == pointMap.end()) 87 | { 88 | pointMap[point] = 1; 89 | port++; 90 | } 91 | } 92 | 93 | if (conns.GetVector()[i].GetPeak2() == index && 94 | tickvec[i].m_int2 < ticks) 95 | { 96 | std::string point = conns.GetVector()[i].GetPoint1().ToString(); 97 | 98 | if (pointMap.find(point) == pointMap.end()) 99 | { 100 | pointMap[point] = 1; 101 | port++; 102 | } 103 | } 104 | } 105 | 106 | return port; 107 | } 108 | 109 | Circuit PortMapper::ConvertToCircuit() 110 | { 111 | std::vector tickvec; 112 | Circuit circuit; 113 | 114 | // Iterate through each of the connections. 115 | for (ConnVector::ConnectionVector::const_iterator it = 116 | m_conns.GetVector().begin(); 117 | it != m_conns.GetVector().end(); it++) 118 | { 119 | unsigned int tick1 = 120 | PointPeakToTick(m_peaks.GetVector()[it->GetPeak1()], 121 | it->GetPoint1()); 122 | unsigned int tick2 = 123 | PointPeakToTick(m_peaks.GetVector()[it->GetPeak2()], 124 | it->GetPoint2()); 125 | 126 | TwoUnsignedInts ticks = {tick1, tick2}; 127 | tickvec.push_back(ticks); 128 | } 129 | 130 | // Add the appropriate number of entities before we go ahead and start 131 | // adding connections. 132 | for (unsigned int i = 0; i < m_peaks.GetVector().size(); i++) 133 | { 134 | // Add null entities. 135 | circuit.AddEntity(0); 136 | } 137 | 138 | // We have calculated all the ticks and cached them in tickvec. Use the 139 | // GetPortFromTicks() utility above to find the ports and add the 140 | // connections to the circuit object. 141 | for (unsigned int i = 0; i < tickvec.size(); i++) 142 | { 143 | unsigned int index1 = m_conns.GetVector()[i].GetPeak1(); 144 | unsigned int index2 = m_conns.GetVector()[i].GetPeak2(); 145 | 146 | circuit.AddConnection( 147 | index1, 148 | GetPortFromTicks(tickvec[i].m_int1, index1, tickvec, m_conns), 149 | index2, 150 | GetPortFromTicks(tickvec[i].m_int2, index2, tickvec, m_conns)); 151 | } 152 | 153 | return circuit; 154 | } 155 | 156 | } // end namespace cs 157 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "Exception.h" 5 | #include "Algorithm.h" 6 | #include "AlgorithmDebug.h" 7 | 8 | // Program usage. 9 | int usage(char* name, int exitval, std::string message = "") 10 | { 11 | if (!message.empty()) 12 | { 13 | std::cout << std::endl << "Error: " << message << std::endl; 14 | } 15 | 16 | std::cout 17 | << std::endl 18 | << "CircuitSense Algorithm: convert an image of an electrical circuit " 19 | << "to a netlist." 20 | << std::endl << std::endl 21 | << "Usage: " << name << " [--debug|--version|--custom,,] " 22 | << " " 23 | << std::endl << std::endl 24 | << " --debug: Run the algorithm in debug mode. Verbose output will be" 25 | << " enabled and" 26 | << std::endl 27 | << " debug images will be saved in the working directory." 28 | << std::endl 29 | << " --version: Print the current software version." 30 | << std::endl 31 | << " --custom: Run with a custom start and end threshold." 32 | << std::endl << std::endl 33 | << " : The circuit image that we are going to convert." 34 | << std::endl << std::endl; 35 | 36 | exit(exitval); 37 | } 38 | 39 | int main(int argc, char** argv) 40 | { 41 | // Check options. 42 | if (argc == 1 || argc > 3) 43 | { 44 | usage(argv[0], 1); 45 | } 46 | 47 | // Pointers we'll need to clean up. 48 | std::string srcfile; 49 | cs::Algorithm* algorithm; 50 | IplImage* src; 51 | 52 | bool debug = false; 53 | bool custom = false; 54 | 55 | unsigned int tstart = 0, tend = 0; 56 | 57 | if (argc == 2 && 58 | std::string(argv[1]) != "--debug" && 59 | std::string(argv[1]) != "--version" && 60 | std::string(argv[1]).substr(0,9) != "--custom,") 61 | { 62 | // No debug. 63 | srcfile = argv[1]; 64 | src = cvLoadImage(srcfile.c_str()); 65 | 66 | if (src == 0) 67 | { 68 | std::string message = 69 | "Could not load source image '" + srcfile + "'."; 70 | usage(argv[0], 1, message); 71 | } 72 | 73 | algorithm = new cs::Algorithm(*src); 74 | } 75 | else if (std::string(argv[1]) == "--version") 76 | { 77 | std::cout 78 | << std::endl 79 | << "0.2.0" 80 | << std::endl << std::endl; 81 | return 0; 82 | } 83 | else if (std::string(argv[1]) == "--debug") 84 | { 85 | if (argc != 3) 86 | { 87 | usage(argv[0], 1); 88 | } 89 | 90 | // Running with debug. 91 | debug = true; 92 | srcfile = argv[2]; 93 | src = cvLoadImage(srcfile.c_str()); 94 | 95 | if (src == 0) 96 | { 97 | std::string message = 98 | "Could not load debug source image '" + srcfile + "'."; 99 | usage(argv[0], 1, message); 100 | } 101 | 102 | std::cout << std::endl << "RUNNING IN DEBUG MODE" << std::endl; 103 | algorithm = new cs::AlgorithmDebug(*src); 104 | } 105 | else if (std::string(argv[1]).substr(0,9) == "--custom,") 106 | { 107 | if (argc != 3) 108 | { 109 | usage(argv[0], 1); 110 | } 111 | 112 | // Running in custom mode. 113 | custom = true; 114 | srcfile = argv[2]; 115 | src = cvLoadImage(srcfile.c_str()); 116 | 117 | if (src == 0) 118 | { 119 | std::string message = 120 | "Could not load custom source image '" + srcfile + "'."; 121 | usage(argv[0], 1, message); 122 | } 123 | 124 | std::cout 125 | << std::endl << "RUNNING IN CUSTOM MODE" 126 | << std::endl; 127 | 128 | algorithm = new cs::Algorithm(*src); 129 | 130 | // parse the thresholds... 131 | std::string nums = std::string(argv[1]).substr(9, 132 | std::string(argv[1]).size()-9); 133 | size_t found = nums.find_first_of(","); 134 | 135 | if (found == std::string::npos) 136 | { 137 | usage(argv[0], 1); 138 | } 139 | 140 | tstart = atoi(nums.substr(0, found).c_str()); 141 | tend = atoi(nums.substr(found+1, nums.size()-found).c_str()); 142 | } 143 | else 144 | { 145 | usage(argv[0], 1); 146 | } 147 | 148 | std::cout 149 | << std::endl << "Executing cs::'s algorithm on " << srcfile 150 | << "..." << std::endl << std::endl; 151 | 152 | // Execute! 153 | try 154 | { 155 | if (custom) 156 | { 157 | algorithm->ConvertToNetlist(tstart, tend); 158 | } 159 | else 160 | { 161 | algorithm->ConvertToNetlist(); 162 | } 163 | 164 | double elapsed = 0; 165 | if (debug) 166 | { 167 | std::cout << std::endl; 168 | } 169 | 170 | std::cout 171 | << "The algorithm took " << elapsed 172 | << " second(s) to execute." << std::endl 173 | << "The circuit and text confidences are " 174 | << algorithm->GetResult().GetConfidence() << "% and " 175 | << algorithm->GetResult().GetTextConfidence() << "% respectively." 176 | << std::endl 177 | << "There are " << algorithm->GetResult().GetNumComponents() 178 | << " circuit component(s) and " 179 | << algorithm->GetResult().GetNumTextComponents() 180 | << " and text value(s)." 181 | << std::endl << std::endl; 182 | } 183 | catch (cs::Exception& e) 184 | { 185 | std::cout << "Exception: " << e.what() << std::endl << std::endl; 186 | return 1; 187 | } 188 | 189 | // Print the output. 190 | if (algorithm->GetResult().HasErrorText()) 191 | { 192 | std::cout 193 | << "Netlist warnings:" << std::endl 194 | << algorithm->GetResult().GetErrorText() << std::endl << std::endl; 195 | } 196 | 197 | std::cout 198 | << "Final Netlist:" << std::endl 199 | << algorithm->GetResult().GetNetlist() << std::endl << std::endl; 200 | 201 | // Cleanup. 202 | delete algorithm; 203 | cvReleaseImage(&src); 204 | 205 | return 0; 206 | } 207 | -------------------------------------------------------------------------------- /src/OutsideRectangleIterator.cpp: -------------------------------------------------------------------------------- 1 | // ----------------------------------------------------------------------------- 2 | // Implementation of the whiteout library. This library is used to whiteout 3 | // the edges of images. 4 | // 5 | // Copyright (c) 2011-2012 Matt Pillar. All rights reserved. 6 | // ----------------------------------------------------------------------------- 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "OutsideRectangleIterator.h" 15 | #include "Whiteout.h" 16 | 17 | namespace cs 18 | { 19 | 20 | OutsideRectangleIterator::OutsideRectangleIterator( 21 | Space space, 22 | Rectangle rectangle, 23 | Point starting) : 24 | m_space(space), 25 | m_rectangle(rectangle), 26 | m_done(false), 27 | m_current(starting.GetX(), starting.GetY()), 28 | m_starting(starting) 29 | { 30 | // Space cannot be zero. 31 | if (space.GetWidth() == 0 || space.GetHeight() == 0) 32 | { 33 | throw Exception("Space cannot be zero"); 34 | } 35 | 36 | // Is the rectangle within the space? 37 | if (rectangle.GetX() + rectangle.GetWidth() >= space.GetWidth() || 38 | rectangle.GetY() + rectangle.GetHeight() >= space.GetHeight()) 39 | { 40 | std::ostringstream os; 41 | os 42 | << "Rectangle " << rectangle.ToString() 43 | << " is not within space " << space.ToString(); 44 | 45 | throw Exception(os.str()); 46 | } 47 | } 48 | 49 | void OutsideRectangleIterator::operator++(int) 50 | { 51 | // Special handling for the zero case. 52 | if (m_rectangle.IsZero()) 53 | { 54 | m_done = true; 55 | return; 56 | } 57 | 58 | if (Done()) 59 | { 60 | return; 61 | } 62 | 63 | // Top line. 64 | if (m_current.GetY() == m_rectangle.GetY()) 65 | { 66 | if (m_current.GetX() == m_rectangle.GetX() + m_rectangle.GetWidth()) 67 | { 68 | // Top right corner. 69 | m_current.IncrY(); 70 | goto outside_rectangle_iterator_plusplus_done; 71 | } 72 | else 73 | { 74 | // Top line. 75 | m_current.IncrX(); 76 | goto outside_rectangle_iterator_plusplus_done; 77 | } 78 | } 79 | 80 | // Right line. 81 | if (m_current.GetX() == m_rectangle.GetX() + m_rectangle.GetWidth()) 82 | { 83 | if (m_current.GetY() == m_rectangle.GetY() + m_rectangle.GetHeight()) 84 | { 85 | // Bottom right corner. 86 | m_current.DecrX(); 87 | goto outside_rectangle_iterator_plusplus_done; 88 | } 89 | else 90 | { 91 | // Right line. 92 | m_current.IncrY(); 93 | goto outside_rectangle_iterator_plusplus_done; 94 | } 95 | } 96 | 97 | // Bottom line. 98 | if (m_current.GetY() == m_rectangle.GetY() + m_rectangle.GetHeight()) 99 | { 100 | if (m_current.GetX() == m_rectangle.GetX()) 101 | { 102 | // Bottom right corner. 103 | m_current.DecrY(); 104 | goto outside_rectangle_iterator_plusplus_done; 105 | } 106 | else 107 | { 108 | // Bottom line. 109 | m_current.DecrX(); 110 | goto outside_rectangle_iterator_plusplus_done; 111 | } 112 | } 113 | 114 | // Left line. 115 | if (m_current.GetX() == m_rectangle.GetX()) 116 | { 117 | if (m_current.GetY() == m_rectangle.GetY()) 118 | { 119 | // Top left corner. We should never reach here because of the top 120 | // line else. 121 | assert(0); 122 | } 123 | else 124 | { 125 | // Left line. 126 | m_current.DecrY(); 127 | // goto not required. 128 | } 129 | } 130 | 131 | outside_rectangle_iterator_plusplus_done: 132 | 133 | // Process this block before exiting. 134 | if (m_current == m_starting) 135 | { 136 | m_done = true; 137 | } 138 | } 139 | 140 | void OutsideRectangleIterator::operator--(int) 141 | { 142 | // Special handling for the zero case. 143 | if (m_rectangle.IsZero()) 144 | { 145 | m_done = true; 146 | return; 147 | } 148 | 149 | if (Done()) 150 | { 151 | return; 152 | } 153 | 154 | // Left line. 155 | if (m_current.GetX() == m_rectangle.GetX()) 156 | { 157 | if (m_current.GetY() == m_rectangle.GetY() + m_rectangle.GetHeight()) 158 | { 159 | // Bottom left corner. 160 | m_current.IncrX(); 161 | goto outside_rectangle_iterator_minusminus_done; 162 | } 163 | else 164 | { 165 | // Left line. 166 | m_current.IncrY(); 167 | goto outside_rectangle_iterator_minusminus_done; 168 | } 169 | } 170 | 171 | // Bottom line. 172 | if (m_current.GetY() == m_rectangle.GetY() + m_rectangle.GetHeight()) 173 | { 174 | if (m_current.GetX() == m_rectangle.GetX() + m_rectangle.GetWidth()) 175 | { 176 | // Bottom right corner. 177 | m_current.DecrY(); 178 | goto outside_rectangle_iterator_minusminus_done; 179 | } 180 | else 181 | { 182 | // Bottom line. 183 | m_current.IncrX(); 184 | goto outside_rectangle_iterator_minusminus_done; 185 | } 186 | } 187 | 188 | // Right line. 189 | if (m_current.GetX() == m_rectangle.GetX() + m_rectangle.GetWidth()) 190 | { 191 | if (m_current.GetY() == m_rectangle.GetY()) 192 | { 193 | // Top right corner. 194 | m_current.DecrX(); 195 | goto outside_rectangle_iterator_minusminus_done; 196 | } 197 | else 198 | { 199 | // Right line. 200 | m_current.DecrY(); 201 | goto outside_rectangle_iterator_minusminus_done; 202 | } 203 | } 204 | 205 | // Top line. 206 | if (m_current.GetY() == m_rectangle.GetY()) 207 | { 208 | if (m_current.GetX() == m_rectangle.GetX()) 209 | { 210 | // Top left corner. We should never reach here because of the left 211 | // line else. 212 | assert(0); 213 | } 214 | else 215 | { 216 | // Top line. 217 | m_current.DecrX(); 218 | // goto not required. 219 | } 220 | } 221 | 222 | outside_rectangle_iterator_minusminus_done: 223 | 224 | // Process this block before exiting. 225 | if (m_current == m_starting) 226 | { 227 | m_done = true; 228 | } 229 | } 230 | 231 | } // end namespace cs 232 | -------------------------------------------------------------------------------- /include/Circuit.h: -------------------------------------------------------------------------------- 1 | // ----------------------------------------------------------------------------- 2 | // The Circuit class represents a real electrical circuit. Unlike the old 3 | // implementation, however, we distinguish between circuit "entities" and 4 | // circuit "components". 5 | // 6 | // A circuit entity can be either a circuit component, a circuit node (as in a 7 | // wire), or text (as in nothing). These notions are relected in the remove 8 | // operation because when an entity is removed, the connections routed through 9 | // that node are preserved. 10 | // 11 | // Copyright (c) 2011-2012 Matt Pillar. All rights reserved. 12 | // ----------------------------------------------------------------------------- 13 | 14 | #ifndef API_CIRCUIT_CIRCUIT_H 15 | #define API_CIRCUIT_CIRCUIT_H 16 | 17 | #include 18 | #include 19 | 20 | #include "Tesseract.h" 21 | #include "CircuitEntity.h" 22 | 23 | namespace cs 24 | { 25 | 26 | // ----------------------------------------------------------------------------- 27 | // class Circuit 28 | // ----------------------------------------------------------------------------- 29 | 30 | // Please note that this class will throw an exception whenever the user 31 | // provides an invalid index to a public method. 32 | class Circuit 33 | { 34 | private: 35 | // Our vector of circuit entities. 36 | typedef std::vector EntityVector; 37 | EntityVector m_entities; 38 | 39 | // The current (next) maximum node number. Reserve 0 for ground nodes. The 40 | // netlist generator deals with ground node handling. 41 | unsigned int m_maxnode; 42 | 43 | // ------------------------------------------------------------------------- 44 | // Private Mutators 45 | // ------------------------------------------------------------------------- 46 | 47 | // Sorts the list using the sort predicate defined by the component ID 48 | // manager. Not a public function because it will change all the index 49 | // numbers associated with the components that have already been added. 50 | void Sort(); 51 | 52 | // Add m_maxnode to all nodes, then go through the list of entities and do a 53 | // replace node sequentially. The result is a circuit with a sorted, 54 | // predictable node pattern. 55 | // 56 | // Does not touch ground nodes. 57 | void SortNodes(); 58 | 59 | // Remove the entity at the given index. This function is private because it 60 | // will change all the index numbers for the entities currently in our 61 | // entity list. 62 | void RemoveEntity(unsigned int index); 63 | 64 | // Remove all connection references to the given node. Used by the above 65 | // RemoveEntity function. 66 | void RemoveNode(unsigned int node); 67 | 68 | // Shift the node numbers down. Updates the m_maxnode member if appopriate. 69 | // 70 | // Note that this is a very expensive operation and it might make this 71 | // implementation seem silly. This is being done so that it is easy to 72 | // create a netlist for a given circuit. This, after all, is the end game. 73 | void FixNodeNumbers(); 74 | 75 | // Replace obsoleteNode with replaceWith in all of our entities. 76 | void ReplaceNodeNumber(unsigned int obsoleteNode, unsigned int replaceWith); 77 | 78 | public: 79 | // Constructor. Reserve node 0 for grounds. 80 | Circuit() : m_maxnode(1) 81 | { 82 | // no-op 83 | } 84 | 85 | // ------------------------------------------------------------------------- 86 | // Accessors 87 | // ------------------------------------------------------------------------- 88 | 89 | // Get the number of entities currently in this circuit object. 90 | unsigned int GetNumEntities() const; 91 | 92 | // Get the number of entities currently in this circuit object that have one 93 | // or more connections. 94 | unsigned int GetNumConnectedEntities() const; 95 | 96 | // Long name pretty much sums it up. 97 | unsigned int GetNumConnectedNotNullEntities() const; 98 | unsigned int GetNumDisconnectedNullEntities() const; 99 | unsigned int GetNumDisconnectedNullEntitiesWithValue() const; 100 | 101 | // Get the number of connections for the entity at the given index. 102 | unsigned int GetNumConnections(unsigned int index) const; 103 | 104 | // Get the number of sequential connections for the entity at the given 105 | // index. 106 | // 107 | // Define sequential connections with the following example: we have 3 108 | // connections in total on ports 0, 1, and 3. We do not have a connection 109 | // registered on port 2. In this case, we have 3 connections and 2 110 | // sequential connections. 111 | unsigned int GetNumSequentialConnections(unsigned int index) const; 112 | 113 | // Return the component ID for the entity at the given index. 114 | SmartComponentID GetComponentID(unsigned int index) const; 115 | 116 | // Get the value of the entity at index. 117 | const std::string& GetValue(unsigned int index) const; 118 | 119 | // Return true if the entity at index has a value. 120 | bool HasValue(unsigned int index) const; 121 | 122 | // Returns true when the number of connections for the entity at the given 123 | // index is not zero. 124 | bool IsConnected(unsigned int index) const; 125 | 126 | // Returns true if the entities corresponding to the given index values are 127 | // connected at the given ports. 128 | bool IsConnected( 129 | unsigned int index1, unsigned int port1, 130 | unsigned int index2, unsigned int port2) const; 131 | 132 | std::string ToString() const; 133 | 134 | // ------------------------------------------------------------------------- 135 | // Mutators 136 | // ------------------------------------------------------------------------- 137 | 138 | // Add an entity with the given circuit component ID. Return the index of 139 | // the component that we just added. 140 | // 141 | // Uses a default value string. 142 | unsigned int AddEntity(SmartComponentID compid); 143 | unsigned int AddEntity(SmartComponentID compid, std::string value); 144 | 145 | // Set the component ID for the entity at the given index. 146 | void SetComponentID(unsigned int index, SmartComponentID compid); 147 | 148 | // Set the value of the entity at index. 149 | void SetValue(unsigned int index, std::string value); 150 | 151 | // Add a connection between entities with indices index1 and index2 at the 152 | // given ports port1 and port2. 153 | void AddConnection( 154 | unsigned int index1, unsigned int port1, 155 | unsigned int index2, unsigned int port2); 156 | 157 | // ------------------------------------------------------------------------- 158 | // Friends 159 | // ------------------------------------------------------------------------- 160 | 161 | // Friends so the netlist generator has access to the private remove 162 | // functions. 163 | friend class NetlistGenerator; 164 | }; 165 | 166 | } // end namespace cs 167 | 168 | #endif 169 | -------------------------------------------------------------------------------- /src/CircuitEntity.cpp: -------------------------------------------------------------------------------- 1 | // ----------------------------------------------------------------------------- 2 | // Copyright (c) 2011-2012 Matt Pillar. All rights reserved. 3 | // ----------------------------------------------------------------------------- 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include "Circuit.h" 10 | #include "Exception.h" 11 | 12 | namespace cs 13 | { 14 | 15 | unsigned int CircuitEntity::GetNumConnections() const 16 | { 17 | return m_vector.size(); 18 | } 19 | 20 | unsigned int CircuitEntity::GetNumSequentialConnections() const 21 | { 22 | unsigned int counter = 0; 23 | 24 | // We assume we are sorted. The add connection class will always sort us by 25 | // port when a new connection is added, making this assumption valid. 26 | for (ConnectionVector::const_iterator it = m_vector.begin(); 27 | it != m_vector.end(); it++, counter++) 28 | { 29 | if (it->GetPort() != counter) 30 | { 31 | break; 32 | } 33 | } 34 | 35 | return counter; 36 | } 37 | 38 | bool CircuitEntity::IsConnected() const 39 | { 40 | return m_vector.size() > 0; 41 | } 42 | 43 | SmartComponentID CircuitEntity::GetComponentID() const 44 | { 45 | return m_compid; 46 | } 47 | 48 | const std::string& CircuitEntity::GetValue() const 49 | { 50 | return m_value; 51 | } 52 | 53 | bool CircuitEntity::HasValue() const 54 | { 55 | return !m_value.empty(); 56 | } 57 | 58 | unsigned int CircuitEntity::GetNumConnectionsWithNode(unsigned int node) 59 | const 60 | { 61 | unsigned int result = 0; 62 | for (ConnectionVector::const_iterator it = m_vector.begin(); 63 | it != m_vector.end(); it++) 64 | { 65 | if (it->GetNode() == node) 66 | { 67 | result++; 68 | } 69 | } 70 | 71 | return result; 72 | } 73 | 74 | int CircuitEntity::GetMinNode() const 75 | { 76 | int result = -1; 77 | 78 | for (ConnectionVector::const_iterator it = m_vector.begin(); 79 | it != m_vector.end(); it++) 80 | { 81 | if (result == -1 || it->GetNode() < result) 82 | { 83 | result = it->GetNode(); 84 | } 85 | } 86 | 87 | return result; 88 | } 89 | 90 | std::string CircuitEntity::ToString() const 91 | { 92 | std::ostringstream os; 93 | os 94 | << "compid='" << GetComponentID().ToString() << "'" 95 | << " value='" << GetValue() << "'"; 96 | 97 | if (m_vector.empty()) 98 | { 99 | return os.str(); 100 | } 101 | 102 | os << std::endl; 103 | for (ConnectionVector::const_iterator it = m_vector.begin(); 104 | it != m_vector.end(); it++) 105 | { 106 | os << "\t" << it->ToString() << std::endl; 107 | } 108 | 109 | return os.str().substr(0, os.str().size()-1); 110 | } 111 | 112 | void CircuitEntity::SetComponentID(SmartComponentID compid) 113 | { 114 | m_compid = compid; 115 | } 116 | 117 | void CircuitEntity::SetValue(std::string value) 118 | { 119 | m_value = value; 120 | } 121 | 122 | CircuitEntityConnection CircuitEntity::RemoveOne() 123 | { 124 | if (GetNumConnections() == 0) 125 | { 126 | throw Exception("Cannot RemoveOne when the conn vector is empty"); 127 | } 128 | 129 | CircuitEntityConnection result(0,0); 130 | result = m_vector.back(); 131 | m_vector.pop_back(); 132 | 133 | return result; 134 | } 135 | 136 | void CircuitEntity::RemoveByNode(unsigned int node) 137 | { 138 | bool more; 139 | do 140 | { 141 | more = false; 142 | for (ConnectionVector::iterator it = m_vector.begin(); 143 | it != m_vector.end(); it++) 144 | { 145 | if (it->GetNode() == node) 146 | { 147 | // Remove this connection. 148 | m_vector.erase(it); 149 | 150 | // Break out of the loop and start over, because the call to 151 | // erase has invalidated our iterator. 152 | more = true; 153 | break; 154 | } 155 | } 156 | } 157 | while (more); 158 | } 159 | 160 | static bool PortSortPredicate( 161 | const CircuitEntityConnection& c1, 162 | const CircuitEntityConnection& c2) 163 | { 164 | return c1.GetPort() < c2.GetPort(); 165 | } 166 | 167 | int CircuitEntity::AddConnection(CircuitEntityConnection conn) 168 | { 169 | // First, search to see if there exists a connection at the given port. If 170 | // there is, replace that connection node with the new one and return the 171 | // old obsolete node for the caller to handle. 172 | for (ConnectionVector::iterator it = m_vector.begin(); it != m_vector.end(); 173 | it++) 174 | { 175 | if (it->GetPort() == conn.GetPort()) 176 | { 177 | unsigned int node = it->GetNode(); 178 | it->SetNode(conn.GetNode()); 179 | return node; 180 | } 181 | } 182 | 183 | // No connection with the given port was found, so just push this on onto 184 | // the vector. 185 | m_vector.push_back(conn); 186 | 187 | // Since we just added a connection, sort the vector. 188 | std::sort(m_vector.begin(), m_vector.end(), PortSortPredicate); 189 | 190 | return -1; 191 | } 192 | 193 | // Search predicate for port. Use a static global for data access. 194 | static int g_port = 0; 195 | static bool PortFindPredicate(const CircuitEntityConnection& conn) 196 | { 197 | return conn.GetPort() == g_port; 198 | } 199 | 200 | CircuitEntityConnection* CircuitEntity::GetConnectionByPort( 201 | unsigned int port) 202 | { 203 | g_port = port; 204 | ConnectionVector::iterator it = std::find_if(m_vector.begin(), 205 | m_vector.end(), PortFindPredicate); 206 | 207 | return it == m_vector.end() ? 0 : &(*it); 208 | } 209 | 210 | const CircuitEntityConnection* 211 | CircuitEntity::GetConstConnectionByPort( 212 | unsigned int port) const 213 | { 214 | g_port = port; 215 | ConnectionVector::const_iterator it = std::find_if(m_vector.begin(), 216 | m_vector.end(), PortFindPredicate); 217 | 218 | return it == m_vector.end() ? 0 : &(*it); 219 | } 220 | 221 | // Search predicate for node. Use a static global for data access. 222 | static int g_node = 0; 223 | static bool NodeFindPredicate(const CircuitEntityConnection& conn) 224 | { 225 | return conn.GetNode() == g_node; 226 | } 227 | 228 | CircuitEntityConnection* CircuitEntity::GetConnectionByNode( 229 | unsigned int node) 230 | { 231 | g_node = node; 232 | ConnectionVector::iterator it = std::find_if(m_vector.begin(), 233 | m_vector.end(), NodeFindPredicate); 234 | 235 | return it == m_vector.end() ? 0 : &(*it); 236 | } 237 | 238 | const CircuitEntityConnection* 239 | CircuitEntity::GetConstConnectionByNode( 240 | unsigned int node) const 241 | { 242 | g_node = node; 243 | ConnectionVector::const_iterator it = std::find_if(m_vector.begin(), 244 | m_vector.end(), NodeFindPredicate); 245 | 246 | return it == m_vector.end() ? 0 : &(*it); 247 | } 248 | 249 | } // end namespace cs 250 | -------------------------------------------------------------------------------- /src/AlgorithmBase.cpp: -------------------------------------------------------------------------------- 1 | // ----------------------------------------------------------------------------- 2 | // Copyright (c) 2011-2012 Matt Pillar. All rights reserved. 3 | // ----------------------------------------------------------------------------- 4 | 5 | #include 6 | 7 | #include "AlgorithmBase.h" 8 | #include "PeakVector.h" 9 | #include "PeakFinder.h" 10 | #include "WhiteoutPerimeter.h" 11 | #include "ConnFinder.h" 12 | #include "PortMapper.h" 13 | #include "AlgorithmBase.h" 14 | 15 | namespace cs 16 | { 17 | 18 | // ----------------------------------------------------------------------------- 19 | // Static configuration 20 | // ----------------------------------------------------------------------------- 21 | 22 | // The max dimension of the resized blob image that we will apply filters to. 23 | static unsigned int g_smallres = 500; 24 | 25 | // The peak scale value for tesseract (common for circuits and english). 26 | static double g_peakScaleTesseract = 2.0; 27 | 28 | // Additionally scale English values by this much. 29 | static double g_peakScaleEnglish = 2.0; 30 | 31 | // Peak scale value for merging to find peaks. Currently do not merge. 32 | static unsigned int g_peakScaleMerge = 1.0; 33 | 34 | // The cutoff confidence value for circuit components. 35 | static unsigned int g_cutoffCircuits = 75; 36 | 37 | // The cutoff confidence value for text entities. 38 | static unsigned int g_cutoffEnglish = 75; 39 | 40 | // ----------------------------------------------------------------------------- 41 | // The algorithm. 42 | // ----------------------------------------------------------------------------- 43 | 44 | void AlgorithmBase::ConvertToNetlist(unsigned int thresholdStart, 45 | unsigned int thresholdEnd) 46 | { 47 | // 3-channel blob image. 48 | IplImage* blob = 0; 49 | Filter filter(m_src, g_smallres); 50 | 51 | CHECKPOINT_STARTING(filter); 52 | 53 | // Save the best text circuit for later iterations. 54 | Circuit bestTextCircuit; 55 | PeakVector bestPeaks(Space(0,0)); 56 | 57 | // Must run at least once. 58 | if (thresholdEnd <= thresholdStart) 59 | { 60 | thresholdEnd = thresholdStart+1; 61 | } 62 | 63 | // For each threshold to evaluate... 64 | for (unsigned int threshold = thresholdStart; threshold < thresholdEnd; 65 | threshold++) 66 | { 67 | CHECKPOINT_NEWTHRESH(threshold); 68 | 69 | // Find the peaks 70 | const IplImage* compblob = filter.ComponentBlob(threshold); 71 | if (blob == 0) 72 | { 73 | // Only initialize the blob once. 74 | blob = cvCreateImage(cvGetSize(compblob), IPL_DEPTH_8U, 3); 75 | } 76 | 77 | cvCvtColor(compblob, blob, CV_GRAY2BGR); 78 | WhiteoutPerimeter whiteout1(*blob); 79 | whiteout1.DoWhiteoutPerimeter(); 80 | PeakFinder peakfinder(*blob); 81 | 82 | CHECKPOINT_FOUNDPEAKS(*blob); 83 | PeakVector& peaks = peakfinder.FindPeaks(g_peakScaleMerge); 84 | 85 | // Filter the image and get the connection blob 86 | const IplImage* connblob = filter.ConnectionBlob(); 87 | cvCvtColor(connblob, blob, CV_GRAY2BGR); 88 | 89 | // Whiteout the connblob perimeter. 90 | WhiteoutPerimeter whiteout2(*blob); 91 | whiteout2.DoWhiteoutPerimeter(); 92 | 93 | // Scale the peaks 94 | peaks.Scale(g_peakScaleTesseract); 95 | 96 | // Find and print the connections. 97 | ConnFinder connfinder(*blob, peaks); 98 | const ConnVector& conns = connfinder.GetConnections(); 99 | 100 | // Construct the circuit. 101 | PortMapper mapper(peaks, conns); 102 | Circuit circuit = mapper.ConvertToCircuit(); 103 | 104 | // Apply tesseract. 105 | Transform transform( 106 | Space(blob->width, blob->height), 107 | Space(m_src.width, m_src.height)); 108 | Tesseract tess(m_src); 109 | 110 | // The confidence value for this circuit. 111 | unsigned int confidence = 0; 112 | unsigned int textConfidence = 0; 113 | 114 | CHECKPOINT_FOUNDCONNS(*blob); 115 | 116 | unsigned int averageArea = peaks.GetAverageArea(); 117 | for (unsigned int i = 0; i < circuit.GetNumEntities(); i++) 118 | { 119 | Rectangle map = transform.Map(peaks.GetVector()[i]); 120 | CHECKPOINT_TESSERACT(map); 121 | 122 | // Don't apply tesseract for huge components. They are definitely 123 | // wrong. 124 | if (map.GetArea()*2 > m_src.width*m_src.height) 125 | { 126 | continue; 127 | } 128 | 129 | if (circuit.IsConnected(i)) 130 | { 131 | // Ignore the peak if it is too small. Circuits only. 132 | if (peaks.GetVector()[i].GetArea()*2 < averageArea) 133 | { 134 | continue; 135 | } 136 | 137 | // Circuit component. 138 | ResultCircuits cresult = tess.TesseractCircuits(map); 139 | 140 | if (cresult.GetConfidence() > g_cutoffCircuits && 141 | !cresult.GetVector().empty()) 142 | { 143 | // Only take the first one, even if there are multiple 144 | // components. 145 | SmartComponentID id(cresult.GetVector()[0].GetID()); 146 | circuit.SetComponentID(i, id); 147 | 148 | confidence += cresult.GetConfidence(); 149 | } 150 | 151 | CHECKPOINT_CIRCUITS(cresult); 152 | } 153 | else 154 | { 155 | // Scale the map rect some more, for english only. 156 | Rectangle emap = map; 157 | emap.Scale(g_peakScaleEnglish); 158 | 159 | // Text component. 160 | ResultEnglish eresult = tess.TesseractEnglish(emap); 161 | 162 | if (eresult.GetConfidence() > g_cutoffEnglish) 163 | { 164 | // Set this value to be used later. 165 | circuit.SetValue(i, eresult.GetChars()); 166 | textConfidence += eresult.GetConfidence(); 167 | } 168 | 169 | CHECKPOINT_ENGLISH(eresult); 170 | } 171 | } 172 | 173 | // Compute the number of components and the confidence. 174 | unsigned int components = 175 | circuit.GetNumConnectedNotNullEntities(); 176 | unsigned int textComponents = 177 | circuit.GetNumDisconnectedNullEntitiesWithValue(); 178 | 179 | unsigned int averageConfidence = 180 | components != 0 ? confidence/components : 0; 181 | unsigned int averageTextConfidence = 182 | textComponents != 0 ? textConfidence/textComponents : 0; 183 | 184 | // Save the best text circuit for later. 185 | if (textComponents >= m_result.m_textComponents && 186 | averageTextConfidence >= m_result.m_textConfidence) 187 | { 188 | bestTextCircuit = circuit; 189 | bestPeaks = peaks; 190 | 191 | m_result.m_textThreshold = threshold; 192 | m_result.m_textConfidence = averageTextConfidence; 193 | m_result.m_textComponents = textComponents; 194 | } 195 | 196 | // Associate text with a circuit component. Go through the peaks again. 197 | for (unsigned int i = 0; i < bestTextCircuit.GetNumEntities(); i++) 198 | { 199 | if (!bestTextCircuit.IsConnected(i) && bestTextCircuit.HasValue(i)) 200 | { 201 | // Find a component to associate with. 202 | int best = -1; 203 | int bestDistance = 0; 204 | Rectangle rect = bestPeaks.GetVector()[i]; 205 | 206 | for (unsigned int j = 0; j < circuit.GetNumEntities(); j++) 207 | { 208 | // Just connect it to the closest valid connected component. 209 | // This may connect it to the wrong component in the case 210 | // that the correct one has a missing connection. It's not 211 | // that bad a failure condition though, because there will 212 | // be a corresponding netlist warning. 213 | // 214 | // I'm doing it's better than adding an extra loop and 215 | // finding the "min distance over the min distances" and 216 | // adding that one first. 217 | if (circuit.IsConnected(j) && 218 | !circuit.HasValue(j) && 219 | !circuit.GetComponentID(j).IsNull()) 220 | { 221 | unsigned int distance = 222 | rect.DistanceTo(peaks.GetVector()[j]); 223 | 224 | if (best == -1 || distance < bestDistance) 225 | { 226 | best = j; 227 | bestDistance = distance; 228 | } 229 | } 230 | } 231 | 232 | // Otherwise, just discard the text. 233 | if (best != -1) 234 | { 235 | circuit.SetValue(best, bestTextCircuit.GetValue(i)); 236 | } 237 | } 238 | } 239 | 240 | // Find the netlist. 241 | NetlistGenerator generator(circuit); 242 | generator.ConvertToNetlist(); 243 | 244 | CHECKPOINT_HAVECONF(averageConfidence, averageTextConfidence); 245 | 246 | // Update the members if this is the best circuit object so far. 247 | if (components >= m_result.m_components && 248 | averageConfidence >= m_result.m_confidence) 249 | { 250 | m_result.m_netlist = generator.GetNetlist(); 251 | m_result.m_error = generator.GetErrorText(); 252 | 253 | m_result.m_threshold = threshold; 254 | m_result.m_confidence = averageConfidence; 255 | m_result.m_components = components; 256 | } 257 | 258 | CHECKPOINT_HAVECIRCUIT(circuit); 259 | CHECKPOINT_HAVEGEN(generator); 260 | } 261 | 262 | CHECKPOINT_DONE(); 263 | 264 | // Release working blob image. 265 | cvReleaseImage(&blob); 266 | } 267 | 268 | } 269 | -------------------------------------------------------------------------------- /src/Circuit.cpp: -------------------------------------------------------------------------------- 1 | // ----------------------------------------------------------------------------- 2 | // Implementation of the circuit library. This particular class holds 3 | // information related to a circuit object and converts it to a netlist. 4 | // 5 | // Copyright (c) 2011-2012 Matt Pillar. All rights reserved. 6 | // ----------------------------------------------------------------------------- 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #include "Circuit.h" 13 | 14 | namespace cs 15 | { 16 | 17 | // ----------------------------------------------------------------------------- 18 | // Accessors 19 | // ----------------------------------------------------------------------------- 20 | 21 | unsigned int Circuit::GetNumEntities() const 22 | { 23 | return m_entities.size(); 24 | } 25 | 26 | unsigned int Circuit::GetNumConnectedEntities() const 27 | { 28 | unsigned int connectedEntities = 0; 29 | for (EntityVector::const_iterator it = m_entities.begin(); 30 | it != m_entities.end(); it++) 31 | { 32 | if (it->IsConnected()) 33 | { 34 | connectedEntities++; 35 | } 36 | } 37 | 38 | return connectedEntities; 39 | } 40 | 41 | unsigned int Circuit::GetNumConnectedNotNullEntities() const 42 | { 43 | unsigned int entities = 0; 44 | for (EntityVector::const_iterator it = m_entities.begin(); 45 | it != m_entities.end(); it++) 46 | { 47 | if (it->IsConnected() && !it->GetComponentID().IsNull()) 48 | { 49 | entities++; 50 | } 51 | } 52 | 53 | return entities; 54 | } 55 | 56 | unsigned int Circuit::GetNumDisconnectedNullEntities() const 57 | { 58 | unsigned int entities = 0; 59 | for (EntityVector::const_iterator it = m_entities.begin(); 60 | it != m_entities.end(); it++) 61 | { 62 | if (!it->IsConnected() && 63 | it->GetComponentID().IsNull()) 64 | { 65 | entities++; 66 | } 67 | } 68 | 69 | return entities; 70 | } 71 | 72 | unsigned int Circuit::GetNumDisconnectedNullEntitiesWithValue() const 73 | { 74 | unsigned int entities = 0; 75 | for (EntityVector::const_iterator it = m_entities.begin(); 76 | it != m_entities.end(); it++) 77 | { 78 | if (!it->IsConnected() && 79 | it->GetComponentID().IsNull() && 80 | it->HasValue()) 81 | { 82 | entities++; 83 | } 84 | } 85 | 86 | return entities; 87 | } 88 | 89 | unsigned int Circuit::GetNumConnections(unsigned int index) const 90 | { 91 | return m_entities[index].GetNumConnections(); 92 | } 93 | 94 | unsigned int Circuit::GetNumSequentialConnections(unsigned int index) const 95 | { 96 | return m_entities[index].GetNumSequentialConnections(); 97 | } 98 | 99 | SmartComponentID Circuit::GetComponentID(unsigned int index) const 100 | { 101 | return m_entities[index].GetComponentID(); 102 | } 103 | 104 | const std::string& Circuit::GetValue(unsigned int index) const 105 | { 106 | return m_entities[index].GetValue(); 107 | } 108 | 109 | bool Circuit::HasValue(unsigned int index) const 110 | { 111 | return m_entities[index].HasValue(); 112 | } 113 | 114 | bool Circuit::IsConnected(unsigned int index) const 115 | { 116 | return m_entities[index].IsConnected(); 117 | } 118 | 119 | bool Circuit::IsConnected( 120 | unsigned int index1, unsigned int port1, 121 | unsigned int index2, unsigned int port2) const 122 | { 123 | const CircuitEntityConnection* entity1 = 124 | m_entities[index1].GetConstConnectionByPort(port1); 125 | const CircuitEntityConnection* entity2 = 126 | m_entities[index2].GetConstConnectionByPort(port2); 127 | 128 | if (entity1 == 0 || entity2 == 0) 129 | { 130 | return false; 131 | } 132 | 133 | return entity1->GetNode() == entity2->GetNode(); 134 | } 135 | 136 | std::string Circuit::ToString() const 137 | { 138 | if (GetNumEntities() == 0) 139 | { 140 | return std::string(); 141 | } 142 | 143 | std::ostringstream os; 144 | unsigned int index = 0; 145 | 146 | for (EntityVector::const_iterator it = m_entities.begin(); 147 | it != m_entities.end(); it++, index++) 148 | { 149 | os << "index=" << index << " " << it->ToString() << std::endl; 150 | } 151 | 152 | return os.str().substr(0, os.str().size()-1); 153 | } 154 | 155 | // ----------------------------------------------------------------------------- 156 | // Mutators 157 | // ----------------------------------------------------------------------------- 158 | 159 | unsigned int Circuit::AddEntity(SmartComponentID compid) 160 | { 161 | return AddEntity(compid, std::string()); 162 | } 163 | 164 | unsigned int Circuit::AddEntity(SmartComponentID compid, 165 | std::string value) 166 | { 167 | m_entities.push_back(CircuitEntity(compid, value)); 168 | return m_entities.size()-1; 169 | } 170 | 171 | void Circuit::SetComponentID(unsigned int index, 172 | SmartComponentID compid) 173 | { 174 | m_entities[index].SetComponentID(compid); 175 | } 176 | 177 | void Circuit::SetValue(unsigned int index, std::string value) 178 | { 179 | m_entities[index].SetValue(value); 180 | } 181 | 182 | void Circuit::AddConnection( 183 | unsigned int index1, unsigned int port1, 184 | unsigned int index2, unsigned int port2) 185 | { 186 | // Note there is no special handling for ground because we might change the 187 | // component ID anyway. The ground handling is done in the netlist 188 | // generator. 189 | 190 | // Add the connections and use the return value to see if we need to fix up 191 | // our node numbers. 192 | int result1 = m_entities[index1].AddConnection( 193 | CircuitEntityConnection(port1, m_maxnode)); 194 | int result2 = m_entities[index2].AddConnection( 195 | CircuitEntityConnection(port2, m_maxnode)); 196 | 197 | bool shift = false; 198 | if (result1 >= 0) 199 | { 200 | ReplaceNodeNumber(result1, m_maxnode); 201 | shift = true; 202 | } 203 | if (result2 >= 0) 204 | { 205 | ReplaceNodeNumber(result2, m_maxnode); 206 | shift = true; 207 | } 208 | 209 | // Increment the next max node. 210 | m_maxnode++; 211 | 212 | if (shift) 213 | { 214 | // Shift the node numbers down. 215 | FixNodeNumbers(); 216 | } 217 | } 218 | 219 | // ----------------------------------------------------------------------------- 220 | // Private Mutators 221 | // ----------------------------------------------------------------------------- 222 | 223 | // This is actually defined in SmartComponentID. This was done to minimize 224 | // the number of places code needs to be changed when a new component is added. 225 | bool ComponentIDSortPredicate( 226 | const CircuitEntity& entity1, 227 | const CircuitEntity& entity2); 228 | 229 | void Circuit::Sort() 230 | { 231 | std::sort(m_entities.begin(), m_entities.end(), 232 | ComponentIDSortPredicate); 233 | } 234 | 235 | void Circuit::SortNodes() 236 | { 237 | // Add m_maxnode to every node in every entity. 238 | for (EntityVector::iterator it = m_entities.begin(); 239 | it != m_entities.end(); it++) 240 | { 241 | for (CircuitEntity::ConnectionVector::iterator cit = 242 | it->GetVector().begin(); 243 | cit != it->GetVector().end(); cit++) 244 | { 245 | if (cit->GetNode() != 0) 246 | { 247 | cit->SetNode(cit->GetNode() + m_maxnode); 248 | } 249 | } 250 | } 251 | 252 | // Now go through and replace nodes sequentially. Assuming the entities are 253 | // sorted, the result is a nice predictable netlist. 254 | unsigned int counter = 1; 255 | for (EntityVector::iterator it = m_entities.begin(); 256 | it != m_entities.end(); it++) 257 | { 258 | for (CircuitEntity::ConnectionVector::iterator cit = 259 | it->GetVector().begin(); 260 | cit != it->GetVector().end(); cit++) 261 | { 262 | if (cit->GetNode() > m_maxnode) 263 | { 264 | ReplaceNodeNumber(cit->GetNode(), counter); 265 | counter++; 266 | } 267 | } 268 | } 269 | } 270 | 271 | void Circuit::RemoveEntity(unsigned int index) 272 | { 273 | if (m_entities[index].GetNumConnections() > 0) 274 | { 275 | // The node that we will change the others to. This must be the lowest 276 | // node attached to this entity. 277 | unsigned int changeNode = m_entities[index].GetMinNode(); 278 | 279 | // While this node still has some connections... 280 | while (m_entities[index].GetNumConnections() > 0) 281 | { 282 | // Replace the node with the change node. 283 | CircuitEntityConnection conn = m_entities[index].RemoveOne(); 284 | ReplaceNodeNumber(conn.GetNode(), changeNode); 285 | 286 | } 287 | 288 | // Check if we need to remove the node corresponding to this connection 289 | // (the change node). We'll have to seach for it within all entities. 290 | unsigned int connections = 0; 291 | 292 | for (EntityVector::const_iterator it = m_entities.begin(); 293 | it != m_entities.end(); it++) 294 | { 295 | connections += it->GetNumConnectionsWithNode(changeNode); 296 | 297 | if (connections > 1) 298 | { 299 | // No need to search anymore. 300 | break; 301 | } 302 | } 303 | 304 | if (connections < 2) 305 | { 306 | // Two cases: 307 | // 1. Only one port connected to this node (we have severed the 308 | // connection). 309 | // 2. There is nothing connected to this node anymore. 310 | // 311 | // In either case, we need to remove references to this node. 312 | RemoveNode(changeNode); 313 | } 314 | } 315 | 316 | // The node's connections have been deleted and so have the references to 317 | // stray nodes. 318 | // 319 | // Now, delete the entity at index and fix the node numbers, in case we 320 | // broken them. 321 | 322 | m_entities.erase(m_entities.begin()+index); 323 | FixNodeNumbers(); 324 | } 325 | 326 | void Circuit::RemoveNode(unsigned int node) 327 | { 328 | for (EntityVector::iterator it = m_entities.begin(); 329 | it != m_entities.end(); it++) 330 | { 331 | it->RemoveByNode(node); 332 | } 333 | 334 | // Fix the node numbers because we have deleted a node. It might be the case 335 | // that there actually was no node deleted, but attempt to fix anyway. 336 | // 337 | // Since this function is only called internall, we assume there was a 338 | // reason for wanting to delete the node. 339 | FixNodeNumbers(); 340 | } 341 | 342 | void Circuit::FixNodeNumbers() 343 | { 344 | // Loop through all the node numbers that we currently think we have. Do not 345 | // start at the zero node, those are reserved for grounds. 346 | for (unsigned int node = 1; node < m_maxnode; node++) 347 | { 348 | bool found = false; 349 | 350 | // Now loop through all the entities and see if we can find this node 351 | // number. 352 | for (EntityVector::const_iterator it = m_entities.begin(); 353 | it != m_entities.end(); it++) 354 | { 355 | const CircuitEntityConnection* connection = 356 | it->GetConstConnectionByNode(node); 357 | 358 | if (connection != 0) 359 | { 360 | found = true; 361 | break; 362 | } 363 | } 364 | 365 | if (!found) 366 | { 367 | // There are no references to this node. Replaces all instances of 368 | // m_maxnode-1 with this node, and decrement the max node. 369 | ReplaceNodeNumber(m_maxnode-1, node); 370 | m_maxnode--; 371 | } 372 | } 373 | } 374 | 375 | void Circuit::ReplaceNodeNumber(unsigned int obsoleteNode, 376 | unsigned int replaceWith) 377 | { 378 | // First, iterator through our entitiies. 379 | for (EntityVector::iterator it = m_entities.begin(); it != m_entities.end(); 380 | it++) 381 | { 382 | // See if this entity has a connection on the obsolete node. 383 | CircuitEntityConnection* entity = 384 | it->GetConnectionByNode(obsoleteNode); 385 | 386 | if (entity != 0) 387 | { 388 | // They have a connection with this node. Update it to use the new 389 | // node. 390 | entity->SetNode(replaceWith); 391 | } 392 | } 393 | } 394 | 395 | } // end namespace cs 396 | -------------------------------------------------------------------------------- /src/NetlistGenerator.cpp: -------------------------------------------------------------------------------- 1 | // ----------------------------------------------------------------------------- 2 | // Implementation of the circuit library. This particular class is used to 3 | // convert a Circuit object to a netlist. 4 | // 5 | // This file and the component aggregate implementation file are the only files 6 | // you need to change when you add a circuit component to the tesseract training 7 | // library (when you are using the new infrastructure algorithm, i.e. the 8 | // infrastructure). 9 | // 10 | // Copyright (c) 2011-2012 Matt Pillar. All rights reserved. 11 | // ----------------------------------------------------------------------------- 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include "NetlistGenerator.h" 19 | #include "Circuit.h" 20 | 21 | namespace cs 22 | { 23 | 24 | static void SetUnknownValueIfEmpty(CircuitEntity& entity) 25 | { 26 | if (entity.GetValue().empty()) 27 | { 28 | entity.SetValue("???"); 29 | } 30 | } 31 | 32 | // Utility to help write port statements and log errors. 33 | void NetlistGenerator::WritePortStatement( 34 | CircuitEntity& entity, 35 | unsigned int componentNumber, 36 | const std::string& netlistIdentifier, 37 | const std::vector& vec) 38 | { 39 | unsigned int sequential = entity.GetNumSequentialConnections(); 40 | if (sequential != vec.size()) 41 | { 42 | m_error 43 | << "Component with ID '" << entity.GetComponentID().GetID() 44 | << "' has " << sequential << " sequential connection(s), expected " 45 | << vec.size() << "." << std::endl; 46 | } 47 | 48 | m_netlist << netlistIdentifier << componentNumber; 49 | 50 | for (std::vector::const_iterator it = vec.begin(); 51 | it != vec.end(); it++) 52 | { 53 | CircuitEntityConnection* conn = entity.GetConnectionByPort(*it); 54 | if (conn == 0) 55 | { 56 | m_netlist << " ???"; 57 | } 58 | else 59 | { 60 | m_netlist << " " << conn->GetNode(); 61 | } 62 | } 63 | 64 | if (entity.HasValue()) 65 | { 66 | m_netlist << " " << entity.GetValue(); 67 | } 68 | 69 | m_netlist << std::endl; 70 | } 71 | 72 | void NetlistGenerator::Write2PortStatement( 73 | CircuitEntity& entity, 74 | unsigned int componentNumber, 75 | std::string netlistIdentifier, 76 | unsigned int port1, 77 | unsigned int port2) 78 | { 79 | std::vector vec; 80 | 81 | vec.push_back(port1); 82 | vec.push_back(port2); 83 | 84 | WritePortStatement(entity, componentNumber, netlistIdentifier, vec); 85 | } 86 | 87 | void NetlistGenerator::Write3PortStatement( 88 | CircuitEntity& entity, 89 | unsigned int componentNumber, 90 | std::string netlistIdentifier, 91 | unsigned int port1, 92 | unsigned int port2, 93 | unsigned int port3) 94 | { 95 | std::vector vec; 96 | 97 | vec.push_back(port1); 98 | vec.push_back(port2); 99 | vec.push_back(port3); 100 | 101 | WritePortStatement(entity, componentNumber, netlistIdentifier, vec); 102 | } 103 | 104 | void NetlistGenerator::ConvertToNetlistInternal() 105 | { 106 | // Make a copy of the circuit object for use in this function. We're going 107 | // to perform removes and sorts and we don't want to muck with the caller's 108 | // object. 109 | m_modified = m_circuit; 110 | 111 | // Find the grounds and set any nodes that touch ground to be the special 112 | // case zero node. Delete the ground once the switch has been made. 113 | bool more; 114 | do 115 | { 116 | more = false; 117 | for (Circuit::EntityVector::iterator it = 118 | m_modified.m_entities.begin(); 119 | it != m_modified.m_entities.end(); it++) 120 | { 121 | if (it->GetComponentID().IsGround()) 122 | { 123 | // This is similar to the remove function, except that instead 124 | // of deleting stray references we just set them to zero. 125 | while (it->GetNumConnections() > 0) 126 | { 127 | CircuitEntityConnection conn = it->RemoveOne(); 128 | m_modified.ReplaceNodeNumber(conn.GetNode(), 0); 129 | } 130 | 131 | // Now we can delete the entity. Break out and start over 132 | // because this iterator is not invalid. 133 | m_modified.m_entities.erase(it); 134 | more = true; 135 | break; 136 | } 137 | } 138 | } 139 | while (more); 140 | 141 | // Delete the null entities (these have been converted to text or are 142 | // actually nodes). 143 | for (unsigned int i = 0; i < m_modified.GetNumEntities(); i++) 144 | { 145 | if (m_modified.m_entities[i].GetComponentID().IsNull()) 146 | { 147 | m_modified.RemoveEntity(i); 148 | i--; 149 | } 150 | } 151 | 152 | // Ground nodes and null entries have now been removed. Sort the entities. 153 | m_modified.Sort(); 154 | m_modified.FixNodeNumbers(); 155 | 156 | // Sort the node numbers. Should not need to fix node numbers afterward. 157 | m_modified.SortNodes(); 158 | 159 | // Used to count consecutive component types for number the netlist. 160 | unsigned int componentNumber = 0; 161 | SmartComponentID prevID(0); 162 | 163 | // Go though each entity (component) and write the output statements. 164 | for (Circuit::EntityVector::iterator it = 165 | m_modified.m_entities.begin(); 166 | it != m_modified.m_entities.end(); it++) 167 | { 168 | char id = it->GetComponentID().GetID(); 169 | 170 | // Update the previous ID and the counter. 171 | if (prevID == it->GetComponentID()) 172 | { 173 | componentNumber++; 174 | } 175 | else 176 | { 177 | componentNumber = 0; 178 | } 179 | prevID = it->GetComponentID(); 180 | 181 | switch (id) 182 | { 183 | // ----------------------------------------------------------------- 184 | // Start component specific handling. New components must be added 185 | // here. 186 | // ----------------------------------------------------------------- 187 | 188 | // Resistors 189 | case 'R': 190 | case 'r': 191 | { 192 | SetUnknownValueIfEmpty(*it); 193 | Write2PortStatement(*it, componentNumber, "R", 1, 0); 194 | break; 195 | } 196 | 197 | // Inductors 198 | case 'L': 199 | case 'l': 200 | { 201 | SetUnknownValueIfEmpty(*it); 202 | Write2PortStatement(*it, componentNumber, "L", 1, 0); 203 | break; 204 | } 205 | 206 | // Capacitors 207 | case 'C': 208 | case 'c': 209 | { 210 | SetUnknownValueIfEmpty(*it); 211 | Write2PortStatement(*it, componentNumber, "C", 1, 0); 212 | break; 213 | } 214 | 215 | // Signal Generators 216 | case 'S': 217 | case 's': 218 | { 219 | SetUnknownValueIfEmpty(*it); 220 | std::ostringstream os; 221 | os << "AC SIN(0 " << it->GetValue() << " 60 0 0)"; 222 | it->SetValue(os.str()); 223 | Write2PortStatement(*it, componentNumber, "V", 224 | id == 'S' ? 0 : 1, 225 | id == 'S' ? 1 : 0); 226 | break; 227 | } 228 | 229 | // DC voltage sources 230 | case 'V': 231 | case 'a': 232 | case 'v': 233 | case 'A': 234 | { 235 | SetUnknownValueIfEmpty(*it); 236 | std::ostringstream os; 237 | os << "DC " << it->GetValue(); 238 | it->SetValue(os.str()); 239 | Write2PortStatement(*it, componentNumber, "V", 240 | id == 'V' || id == 'a' ? 1 : 0, 241 | id == 'V' || id == 'a' ? 0 : 1); 242 | break; 243 | } 244 | 245 | // Diodes 246 | case 'D': 247 | case 'e': 248 | case 'd': 249 | case 'E': 250 | { 251 | std::ostringstream os1, os2; 252 | os1 << ".MODEL DIODE" << componentNumber << " D" << std::endl; 253 | os1 << "D"; 254 | os2 << "DIODE" << componentNumber << " " << it->GetValue(); 255 | it->SetValue(os2.str()); 256 | Write2PortStatement(*it, componentNumber, os1.str(), 257 | id == 'D' || id == 'e' ? 1 : 0, 258 | id == 'D' || id == 'e' ? 0 : 1); 259 | break; 260 | } 261 | 262 | // NPN BJT's 263 | case 'N': 264 | case 'n': 265 | { 266 | std::ostringstream os1, os2; 267 | os1 << ".MODEL BJT" << componentNumber << " NPN" << std::endl; 268 | os1 << "Q"; 269 | os2 << "BJT" << componentNumber << " " << it->GetValue(); 270 | it->SetValue(os2.str()); 271 | Write3PortStatement(*it, componentNumber, os1.str(), 272 | id == 'N' ? 0 : 2, 273 | id == 'N' ? 2 : 0, 274 | 1); 275 | break; 276 | } 277 | 278 | // PNP BJT's 279 | case 'P': 280 | case 'p': 281 | { 282 | std::ostringstream os1, os2; 283 | os1 << ".MODEL BJT" << componentNumber << " PNP" << std::endl; 284 | os1 << "Q"; 285 | os2 << "BJT" << componentNumber << " " << it->GetValue(); 286 | it->SetValue(os2.str()); 287 | Write3PortStatement(*it, componentNumber, os1.str(), 288 | 1, 289 | id == 'P' ? 2 : 0, 290 | id == 'p' ? 0 : 2); 291 | break; 292 | } 293 | 294 | // NMOS Transistors 295 | case 'T': 296 | case 't': 297 | { 298 | std::ostringstream os1, os2; 299 | os1 << ".MODEL MOS" << componentNumber << " NMOS" << std::endl; 300 | os1 << "M"; 301 | os2 << "MOS" << componentNumber << " " << it->GetValue(); 302 | it->SetValue(os2.str()); 303 | Write3PortStatement(*it, componentNumber, os1.str(), 304 | id == 'T' ? 0 : 2, 305 | id == 'T' ? 2 : 0, 306 | 1); 307 | break; 308 | } 309 | 310 | // PMOS Transistors 311 | case 'Q': 312 | case 'q': 313 | { 314 | std::ostringstream os1, os2; 315 | os1 << ".MODEL MOS" << componentNumber << " PMOS" << std::endl; 316 | os1 << "M"; 317 | os2 << "MOS" << componentNumber << " " << it->GetValue(); 318 | it->SetValue(os2.str()); 319 | Write3PortStatement(*it, componentNumber, os1.str(), 320 | 1, 321 | id == 'P' ? 2 : 0, 322 | id == 'p' ? 0 : 2); 323 | break; 324 | } 325 | 326 | // ----------------------------------------------------------------- 327 | // End component specific handling. 328 | // ----------------------------------------------------------------- 329 | 330 | default: 331 | { 332 | m_error 333 | << "Unknown component ID '" 334 | << it->GetComponentID().GetID() << "'" << std::endl; 335 | break; 336 | } 337 | } 338 | } 339 | 340 | // Default footer. 341 | m_netlist << std::endl; 342 | m_netlist << ".CONTROL" << std::endl; 343 | m_netlist << "ac dec 10 60 600k" << std::endl; 344 | m_netlist << "op" << std::endl; 345 | m_netlist << "tran 1us 10us 0" << std::endl; 346 | m_netlist << "print v(1)" << std::endl; 347 | m_netlist << ".ENDC" << std::endl; 348 | m_netlist << ".END" << std::endl; 349 | } 350 | 351 | std::string NetlistGenerator::GetNetlist() const 352 | { 353 | if (!m_netlist.str().empty()) 354 | { 355 | return m_netlist.str().substr(0, m_netlist.str().size()-1); 356 | } 357 | 358 | return std::string(); 359 | } 360 | 361 | std::string NetlistGenerator::GetErrorText() const 362 | { 363 | if (!m_error.str().empty()) 364 | { 365 | return m_error.str().substr(0, m_error.str().size()-1); 366 | } 367 | 368 | return std::string(); 369 | } 370 | 371 | std::string NetlistGenerator::GetDefaultNetlistText() 372 | { 373 | Circuit circuit; 374 | 375 | NetlistGenerator generator(circuit); 376 | generator.ConvertToNetlist(); 377 | 378 | std::ostringstream os; 379 | os << std::endl << generator.GetNetlist(); 380 | 381 | return os.str(); 382 | } 383 | 384 | } // end namespace cs 385 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | {one line to give the program's name and a brief idea of what it does.} 635 | Copyright (C) {year} {name of author} 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | {project} Copyright (C) {year} {fullname} 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | --------------------------------------------------------------------------------