Beispiel #1
0
 internal ExpandedPair(DataCharacter leftChar,
              DataCharacter rightChar,
              FinderPattern finderPattern,
              bool mayBeLast)
 {
    LeftChar = leftChar;
    RightChar = rightChar;
    FinderPattern = finderPattern;
    MayBeLast = mayBeLast;
 }
 public FinderPatternInfo(FinderPattern[] patternCenters)
 {
     this.bottomLeft = patternCenters[0];
     this.topLeft = patternCenters[1];
     this.topRight = patternCenters[2];
 }
        /// <returns> the 3 best {@link FinderPattern}s from our list of candidates. The "best" are
        /// those that have been detected at least {@link #CENTER_QUORUM} times, and whose module
        /// size differs from the average among those patterns the least
        /// </returns>
        /// <throws>  ReaderException if 3 such finder patterns do not exist </throws>
        private FinderPattern[][] selectBestPatterns()
        {
            var possibleCenters = PossibleCenters;
            int size = possibleCenters.Count;

            if (size < 3)
            {
                // Couldn't find enough finder patterns
                throw ReaderException.Instance;
            }

            /*
            * Begin HE modifications to safely detect multiple codes of equal size
            */
            if (size == 3)
            {
                return new FinderPattern[][]{new FinderPattern[]{(FinderPattern) possibleCenters[0], (FinderPattern) possibleCenters[1], (FinderPattern) possibleCenters[2]}};
            }

            // Sort by estimated module size to speed up the upcoming checks
            Collections.insertionSort(possibleCenters, new ModuleSizeComparator());

            /*
            * Now lets start: build a list of tuples of three finder locations that
            *  - feature similar module sizes
            *  - are placed in a distance so the estimated module count is within the QR specification
            *  - have similar distance between upper left/right and left top/bottom finder patterns
            *  - form a triangle with 90° angle (checked by comparing top right/bottom left distance
            *    with pythagoras)
            *
            * Note: we allow each point to be used for more than one code region: this might seem
            * counterintuitive at first, but the performance penalty is not that big. At this point,
            * we cannot make a good quality decision whether the three finders actually represent
            * a QR code, or are just by chance layouted so it looks like there might be a QR code there.
            * So, if the layout seems right, lets have the decoder try to decode.
            */

            List<FinderPattern[]> results = new List<FinderPattern[]>(10); // holder for the results

            for (int i1 = 0; i1 < (size - 2); i1++)
            {
                FinderPattern p1 = (FinderPattern) possibleCenters[i1];
                if (p1 == null)
                {
                    continue;
                }

                for (int i2 = i1 + 1; i2 < (size - 1); i2++)
                {
                    FinderPattern p2 = (FinderPattern) possibleCenters[i2];
                    if (p2 == null)
                    {
                        continue;
                    }

                    // Compare the expected module sizes; if they are really off, skip
                    float vModSize12 = (p1.EstimatedModuleSize - p2.EstimatedModuleSize) / (System.Math.Min(p1.EstimatedModuleSize, p2.EstimatedModuleSize));
                    float vModSize12A = System.Math.Abs(p1.EstimatedModuleSize - p2.EstimatedModuleSize);
                    if (vModSize12A > DIFF_MODSIZE_CUTOFF && vModSize12 >= DIFF_MODSIZE_CUTOFF_PERCENT)
                    {
                        // break, since elements are ordered by the module size deviation there cannot be
                        // any more interesting elements for the given p1.
                        break;
                    }

                    for (int i3 = i2 + 1; i3 < size; i3++)
                    {
                        FinderPattern p3 = (FinderPattern) possibleCenters[i3];
                        if (p3 == null)
                        {
                            continue;
                        }

                        // Compare the expected module sizes; if they are really off, skip
                        float vModSize23 = (p2.EstimatedModuleSize - p3.EstimatedModuleSize) / (System.Math.Min(p2.EstimatedModuleSize, p3.EstimatedModuleSize));
                        float vModSize23A = System.Math.Abs(p2.EstimatedModuleSize - p3.EstimatedModuleSize);
                        if (vModSize23A > DIFF_MODSIZE_CUTOFF && vModSize23 >= DIFF_MODSIZE_CUTOFF_PERCENT)
                        {
                            // break, since elements are ordered by the module size deviation there cannot be
                            // any more interesting elements for the given p1.
                            break;
                        }

                        FinderPattern[] test = new FinderPattern[]{p1, p2, p3};
                        ResultPoint.orderBestPatterns(test);

                        // Calculate the distances: a = topleft-bottomleft, b=topleft-topright, c = diagonal
                        FinderPatternInfo info = new FinderPatternInfo(test);
                        float dA = ResultPoint.distance(info.TopLeft, info.BottomLeft);
                        float dC = ResultPoint.distance(info.TopRight, info.BottomLeft);
                        float dB = ResultPoint.distance(info.TopLeft, info.TopRight);

                        // Check the sizes
                        float estimatedModuleCount = ((dA + dB) / p1.EstimatedModuleSize) / 2;
                        if (estimatedModuleCount > MAX_MODULE_COUNT_PER_EDGE || estimatedModuleCount < MIN_MODULE_COUNT_PER_EDGE)
                        {
                            continue;
                        }

                        // Calculate the difference of the edge lengths in percent
                        float vABBC = System.Math.Abs(((dA - dB) / System.Math.Min(dA, dB)));
                        if (vABBC >= 0.1f)
                        {
                            continue;
                        }

                        // Calculate the diagonal length by assuming a 90° angle at topleft
                        //UPGRADE_WARNING: Data types in Visual C# might be different.  Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
                        float dCpy = (float) System.Math.Sqrt(dA * dA + dB * dB);
                        // Compare to the real distance in %
                        float vPyC = System.Math.Abs(((dC - dCpy) / System.Math.Min(dC, dCpy)));

                        if (vPyC >= 0.1f)
                        {
                            continue;
                        }

                        // All tests passed!
                        results.Add(test);
                    } // end iterate p3
                } // end iterate p2
            } // end iterate p1

            if (!(results.Count == 0))
            {
                FinderPattern[][] resultArray = new FinderPattern[results.Count][];
                for (int i = 0; i < results.Count; i++)
                {
                    resultArray[i] = (FinderPattern[]) results[i];
                }
                return resultArray;
            }

            // Nothing found!
            throw ReaderException.Instance;
        }
Beispiel #4
0
 private static bool isNotA1left(FinderPattern pattern, bool isOddPattern, bool leftChar)
 {
    // A1: pattern.getValue is 0 (A), and it's an oddPattern, and it is a left char
    return !(pattern.Value == 0 && isOddPattern && leftChar);
 }
Beispiel #5
0
      internal DataCharacter decodeDataCharacter(BitArray row,
                                        FinderPattern pattern,
                                        bool isOddPattern,
                                        bool leftChar)
      {
         int[] counters = getDataCharacterCounters();
         counters[0] = 0;
         counters[1] = 0;
         counters[2] = 0;
         counters[3] = 0;
         counters[4] = 0;
         counters[5] = 0;
         counters[6] = 0;
         counters[7] = 0;

         if (leftChar)
         {
            if (!recordPatternInReverse(row, pattern.StartEnd[0], counters))
               return null;
         }
         else
         {
            if (!recordPattern(row, pattern.StartEnd[1] + 1, counters))
               return null;
            // reverse it
            for (int i = 0, j = counters.Length - 1; i < j; i++, j--)
            {
               int temp = counters[i];
               counters[i] = counters[j];
               counters[j] = temp;
            }
         }//counters[] has the pixels of the module

         int numModules = 17; //left and right data characters have all the same length
         float elementWidth = (float)count(counters) / (float)numModules;

         int[] oddCounts = getOddCounts();
         int[] evenCounts = getEvenCounts();
         float[] oddRoundingErrors = getOddRoundingErrors();
         float[] evenRoundingErrors = getEvenRoundingErrors();

         for (int i = 0; i < counters.Length; i++)
         {
            float divided = 1.0f * counters[i] / elementWidth;
            int rounded = (int)(divided + 0.5f); // Round
            if (rounded < 1)
            {
               rounded = 1;
            }
            else if (rounded > 8)
            {
               rounded = 8;
            }
            int offset = i >> 1;
            if ((i & 0x01) == 0)
            {
               oddCounts[offset] = rounded;
               oddRoundingErrors[offset] = divided - rounded;
            }
            else
            {
               evenCounts[offset] = rounded;
               evenRoundingErrors[offset] = divided - rounded;
            }
         }

         if (!adjustOddEvenCounts(numModules))
            return null;

         int weightRowNumber = 4 * pattern.Value + (isOddPattern ? 0 : 2) + (leftChar ? 0 : 1) - 1;

         int oddSum = 0;
         int oddChecksumPortion = 0;
         for (int i = oddCounts.Length - 1; i >= 0; i--)
         {
            if (isNotA1left(pattern, isOddPattern, leftChar))
            {
               int weight = WEIGHTS[weightRowNumber][2 * i];
               oddChecksumPortion += oddCounts[i] * weight;
            }
            oddSum += oddCounts[i];
         }
         int evenChecksumPortion = 0;
         int evenSum = 0;
         for (int i = evenCounts.Length - 1; i >= 0; i--)
         {
            if (isNotA1left(pattern, isOddPattern, leftChar))
            {
               int weight = WEIGHTS[weightRowNumber][2 * i + 1];
               evenChecksumPortion += evenCounts[i] * weight;
            }
            evenSum += evenCounts[i];
         }
         int checksumPortion = oddChecksumPortion + evenChecksumPortion;

         if ((oddSum & 0x01) != 0 || oddSum > 13 || oddSum < 4)
         {
            return null;
         }

         int group = (13 - oddSum) / 2;
         int oddWidest = SYMBOL_WIDEST[group];
         int evenWidest = 9 - oddWidest;
         int vOdd = RSSUtils.getRSSvalue(oddCounts, oddWidest, true);
         int vEven = RSSUtils.getRSSvalue(evenCounts, evenWidest, false);
         int tEven = EVEN_TOTAL_SUBSET[group];
         int gSum = GSUM[group];
         int value = vOdd * tEven + vEven + gSum;

         return new DataCharacter(value, checksumPortion);
      }
Beispiel #6
0
      private bool checkPairSequence(List<ExpandedPair> previousPairs, FinderPattern pattern, out bool mayBeLast)
      {
         mayBeLast = false;
         int currentSequenceLength = previousPairs.Count + 1;
         if (currentSequenceLength > currentSequence.Length)
         {
            return false;
         }

         for (int pos = 0; pos < previousPairs.Count; ++pos)
         {
            currentSequence[pos] = previousPairs[pos].FinderPattern.Value;
         }

         currentSequence[currentSequenceLength - 1] = pattern.Value;

         foreach (int[] validSequence in FINDER_PATTERN_SEQUENCES)
         {
            if (validSequence.Length >= currentSequenceLength)
            {
               bool valid = true;
               for (int pos = 0; pos < currentSequenceLength; ++pos)
               {
                  if (currentSequence[pos] != validSequence[pos])
                  {
                     valid = false;
                     break;
                  }
               }

               if (valid)
               {
                  mayBeLast = currentSequenceLength == validSequence.Length;
                  return true;
               }
            }
         }

         return false;
      }
 /// <summary> <p>This is called when a horizontal scan finds a possible alignment pattern. It will
 /// cross check with a vertical scan, and if successful, will, ah, cross-cross-check
 /// with another horizontal scan. This is needed primarily to locate the real horizontal
 /// center of the pattern in cases of extreme skew.</p>
 /// 
 /// <p>If that succeeds the finder pattern location is added to a list that tracks
 /// the number of times each location has been nearly-matched as a finder pattern.
 /// Each additional find is more evidence that the location is in fact a finder
 /// pattern center
 /// 
 /// </summary>
 /// <param name="stateCount">reading state module counts from horizontal scan
 /// </param>
 /// <param name="i">row where finder pattern may be found
 /// </param>
 /// <param name="j">end of possible finder pattern in row
 /// </param>
 /// <returns> true if a finder pattern candidate was found this time
 /// </returns>
 protected internal virtual bool handlePossibleCenter(int[] stateCount, int i, int j)
 {
     int stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2] + stateCount[3] + stateCount[4];
     float centerJ = centerFromEnd(stateCount, j);
     //UPGRADE_WARNING: Data types in Visual C# might be different.  Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
     float centerI = crossCheckVertical(i, (int)centerJ, stateCount[2], stateCountTotal);
     if (!System.Single.IsNaN(centerI))
     {
         // Re-cross check
         //UPGRADE_WARNING: Data types in Visual C# might be different.  Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
         centerJ = crossCheckHorizontal((int)centerJ, (int)centerI, stateCount[2], stateCountTotal);
         if (!System.Single.IsNaN(centerJ))
         {
             //UPGRADE_WARNING: Data types in Visual C# might be different.  Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
             float estimatedModuleSize = (float)stateCountTotal / 7.0f;
             bool found = false;
             int max = possibleCenters.Count;
             for (int index = 0; index < max; index++)
             {
                 FinderPattern center = (FinderPattern)possibleCenters[index];
                 // Look for about the same center and module size:
                 if (center.aboutEquals(estimatedModuleSize, centerI, centerJ))
                 {
                     center.incrementCount();
                     found = true;
                     break;
                 }
             }
             if (!found)
             {
                 ResultPoint point = new FinderPattern(centerJ, centerI, estimatedModuleSize);
                 possibleCenters.Add(point);
                 if (resultPointCallback != null)
                 {
                     resultPointCallback.foundPossibleResultPoint(point);
                 }
             }
             return true;
         }
     }
     return false;
 }
Beispiel #8
0
 internal Pair(int value, int checksumPortion, FinderPattern finderPattern)
    : base(value, checksumPortion)
 {
    FinderPattern = finderPattern;
 }
      public void testDecodeDataCharacter()
      {
         RSSExpandedReader rssExpandedReader = new RSSExpandedReader();

         String path = "test/data/blackbox/rssexpanded-1/3.png";
         if (!File.Exists(path))
         {
            // Support running from project root too
            path = Path.Combine("..\\..\\..\\Source", path);
         }

#if !SILVERLIGHT
         var image = new Bitmap(Image.FromFile(path));
#else
         var image = new WriteableBitmap(0, 0);
         image.SetSource(File.OpenRead(path));
#endif
         BinaryBitmap binaryMap = new BinaryBitmap(new GlobalHistogramBinarizer(new BitmapLuminanceSource(image)));
         BitArray row = binaryMap.getBlackRow(binaryMap.Height / 2, null);

         int[] startEnd = { 145, 243 };//image pixels where the A1 pattern starts (at 124) and ends (at 214)
         int value = 0; // A
#if !SILVERLIGHT
         FinderPattern finderPatternA1 = new FinderPattern(value, startEnd, startEnd[0], startEnd[1], image.Height / 2);
#else
         FinderPattern finderPatternA1 = new FinderPattern(value, startEnd, startEnd[0], startEnd[1], image.PixelHeight / 2);
#endif
         //{1, 8, 4, 1, 1};
         DataCharacter dataCharacter = rssExpandedReader.decodeDataCharacter(row, finderPatternA1, true, false);

         Assert.AreEqual(19, dataCharacter.Value);
         Assert.AreEqual(1007, dataCharacter.ChecksumPortion);
      }
Beispiel #10
0
        /// <summary>
        /// Refines the width of a finder pattern by following a line between two finder patterns in one direction and then the other.
        /// </summary>
        /// <param name="startFinderPattern">The finder pattern whose width will be refined.</param>
        /// <param name="endFinderPattern">The second finder pattern which determines the direction in which the width of the first pattern will be measured.</param>
        /// <returns></returns>
        private static int[] finderPatternWidth(FinderPattern startFinderPattern, FinderPattern endFinderPattern)
        {
            int startX = startFinderPattern.X;
            int startY = startFinderPattern.Y;
            int endX = endFinderPattern.X;
            int endY = endFinderPattern.Y;

            // Bresenham's line algorithm implementation, see http://en.wikipedia.org/wiki/Bresenham's_line_algorithm
            bool steep = Math.Abs(endY - startY) > Math.Abs(endX - startX);
            if (steep)
            {
                int temp = startX;
                startX = startY;
                startY = temp;
                temp = endX;
                endX = endY;
                endY = temp;
            }

            int dx = Math.Abs(endX - startX);
            int dy = Math.Abs(endY - startY);
            int error = -dx >> 1;
            int ystep = startY < endY ? 1 : -1;
            int xstep = startX < endX ? 1 : -1;
            int currentState = 0;
            int x1 = 0;
            int x2 = 0;
            int y1 = 0;
            int y2 = 0;
            int[] stateCount = new int[5];

            for (int x = startX, y = startY; x != endX; x += xstep)
            {
                int realX = steep ? y : x;
                int realY = steep ? x : y;
                if (currentState == 0)
                {
                    // Current pixel is black, looking for white
                    if (_imageArray[realY * _width + realX] == 0) currentState++;
                    else stateCount[2]++;
                }

                if (currentState == 1)
                {
                    if (_imageArray[realY * _width + realX] == 1) currentState++;
                    else stateCount[1]++;
                }

                if (currentState == 2)
                {
                    if (_imageArray[realY * _width + realX] == 0) currentState++;
                    else stateCount[0]++;
                }

                if (currentState == 3)
                {
                    // Got black/white/black and white again, we're done
                    x1 = x;
                    y1 = y;
                    ystep = (ystep == 1) ? -1 : 1;
                    xstep = (xstep == 1) ? -1 : 1;
                    x = endX - xstep;
                    continue;
                }
                error += dy;
                if (error > 0)
                {
                    if (y == endY) break;
                    y += ystep;
                    error -= dx;
                }
            }

            currentState = 0;
            x2 = 0;
            y2 = 0;

            for (int x = startX, y = startY; x != endX; x += xstep)
            {
                int realX = steep ? y : x;
                int realY = steep ? x : y;

                if (currentState == 0)
                {
                    // Current pixel is black, looking for white
                    if (_imageArray[realY * _width + realX] == 0) currentState++;
                    else stateCount[2]++;
                }

                if (currentState == 1)
                {
                    if (_imageArray[realY * _width + realX] == 1) currentState++;
                    else stateCount[3]++;
                }

                if (currentState == 2)
                {
                    if (_imageArray[realY * _width + realX] == 0) currentState++;
                    else stateCount[4]++;
                }

                if (currentState == 3)
                {
                    // Got black and white again, we're done
                    x2 = x;
                    y2 = y;
                    ystep = (ystep == 1) ? -1 : 1;
                    xstep = (xstep == 1) ? -1 : 1;
                    x = endX - xstep;
                    continue;
                }
                error += dy;
                if (error > 0)
                {
                    if (y == endY) break;
                    y += ystep;
                    error -= dx;
                }
            }

            int xDifference = Math.Abs(x2 - x1);
            int yDifference = Math.Abs(y2 - y1);

            float distance = (float)Math.Sqrt(xDifference * xDifference + yDifference * yDifference);

            stateCount[0] = stateCount[1] = stateCount[3] = stateCount[4] = (int)Math.Round(distance / 7.0f);
            stateCount[2] = (int)Math.Round(distance / 7.0f * 3.0f);

            //stateCount[2]--; // Decrement the state count of the middle black pattern by 1 (we counted one pixel twice)
            return stateCount;
        }