Beispiel #1
0
        /// <summary>
        /// Tries to decode a QR Code in a given bitmap.
        /// </summary>
        /// <param name="bm">The bitmap to be parsed.</param>
        /// <returns>A string with the contents of the barcode if successful, null otherwise</returns>
        public static unsafe string TryDecode(Bitmap bm, bool inverted)
        {
            _width = bm.Width;
            _height = bm.Height;

            BitmapData bmData = bm.LockBits(new Rectangle(0, 0, _width, _height), ImageLockMode.ReadOnly, bm.PixelFormat);

            int imageBpp = 0;

            switch (bm.PixelFormat)
            {
                case PixelFormat.Format32bppArgb: imageBpp = 4; break;
                case PixelFormat.Format24bppRgb: imageBpp = 3; break;
                case PixelFormat.Format8bppIndexed: imageBpp = 1; break;
                default: break;
            }

            byte* ptr = (byte*)bmData.Scan0.ToPointer();
            int padding = bmData.Stride - _width * imageBpp;

            int[] imgArray = new int[_width * _height];

            for (int y = 0; y < _height; y++)
            {
                for (int x = 0; x < (_width * imageBpp); x += imageBpp)
                {
                    if (!inverted)
                    {
                        if (ptr[0] < 127)
                        {
                            imgArray[y * _width + x / imageBpp] = 1;
                        }
                        else if (ptr[0] > 127) imgArray[y * _width + x / imageBpp] = 0;
                    }
                    else
                    {
                        if (ptr[0] < 127) imgArray[y * _width + x / imageBpp] = 0;
                        else if (ptr[0] > 127) imgArray[y * _width + x / imageBpp] = 1;
                    }
                    ptr += imageBpp;
                }
                ptr += padding;
            }

            bm.UnlockBits(bmData);
            _imageArray = imgArray;

            FixedSizeQueue<int> stateCount = new FixedSizeQueue<int>(5);
            FixedSizeQueue<int> pixelColor = new FixedSizeQueue<int>(5);
            int[] correctFinderPatternArray = new int[5] { 1, 0, 1, 0, 1 };
            List<FinderPattern> finderPatterns = new List<FinderPattern>();
            int currentColorCount = 0;
            bool correctPixelColor = true;
            bool foundStartPixelColor = false;
            bool doneSearching = false;

            int currentState = 0;

            for (int y = 0; y < _height; y++)
            {
                for (int x = 0; x < _width; x++)
                {
                    int currentPixel = imgArray[y * _width + x];
                    if (currentPixel == 1) // Current pixel is black
                    {
                        currentColorCount++;
                        if (x == _width - 1 && y == _height - 1)
                        {
                            doneSearching = true;
                        }
                        if (!doneSearching && imgArray[y * _width + x + 1] == 0)
                        {
                            // Next pixel in line is white
                            stateCount.Enqueue(currentColorCount);
                            if (!foundStartPixelColor)
                            {
                                pixelColor.Enqueue(1);
                                int[] array = pixelColor.GetArray();
                                if (correctFinderPatternArray.SequenceEqual(array))
                                    foundStartPixelColor = true;
                            }
                            else correctPixelColor = !correctPixelColor;
                            currentColorCount = 0;
                            currentState++;
                            if (currentState >= 5)
                            {
                                int[] stateCountArray = stateCount.GetArray();
                                // We found a black/white/black/white/black pattern, now check its ratios
                                //if (x > 2009 && y > 880) System.Diagnostics.Debugger.Break();
                                if (validFinderPattern(stateCountArray) && correctPixelColor)
                                {
                                    float patternMiddle = verticalFinderPatternCheck(stateCountArray, x, y);
                                    if (!float.IsNaN(patternMiddle))
                                    {
                                        finderPatterns.Add(new FinderPattern(stateCountArray, (int)Math.Round(stateCountCenter(stateCountArray, x)), (int)Math.Round(patternMiddle)));
                                    }
                                }
                            }
                        }
                    }
                    else if (currentPixel == 0) // Current pixel is white
                    {
                        currentColorCount++;
                        if (x == _width - 1 && y == _height - 1)
                        {
                            doneSearching = true;
                        }
                        if (!doneSearching && imgArray[y * _width + x + 1] == 1)
                        {
                            stateCount.Enqueue(currentColorCount);
                            if (!foundStartPixelColor)
                            {
                                pixelColor.Enqueue(0);
                                int[] array = pixelColor.GetArray();
                                if (correctFinderPatternArray.SequenceEqual(array))
                                    foundStartPixelColor = true;
                            }
                            else correctPixelColor = !correctPixelColor;
                            currentColorCount = 0;
                            currentState++; // Next pixel in line is black
                        }
                    }
                }
            }

            if (doneSearching)
            {
                List<FinderPattern> uniqueFinderPatterns = new List<FinderPattern>();
                foreach (FinderPattern pattern in finderPatterns)
                {
                    int currentX = pattern.X;
                    int currentY = pattern.Y;
                    bool listContainsFinderPattern = false;
                    foreach (FinderPattern uniquePattern in uniqueFinderPatterns)
                    {
                        if (Math.Abs(currentX - uniquePattern.X) < (0.05f * currentX) && Math.Abs(currentY - uniquePattern.Y) < (0.05f * currentY)) listContainsFinderPattern = true;
                    }
                    if (!listContainsFinderPattern) uniqueFinderPatterns.Add(pattern);
                }

                if (uniqueFinderPatterns.Count > 3)
                {
                    TryDecode(ImageProcessing.RotateImageByAngle(bm, 45.0f), false);
                }

                if (uniqueFinderPatterns.Count < 3 && !inverted) // We do not have three finder patterns, repeat the process with an inverted image, as per the spec
                {
                    TryDecode(bm, true);
                }
                else // We have the necessary number of finder patterns, proceed with decoding
                {
                    float codeAngle;

                    for (int i = 0; i < uniqueFinderPatterns.Count; i++)
                    {

                    }

                    //uniqueFinderPatterns[2] = uniqueFinderPatterns[3];

                    bool result = sortUniqueFinderPatterns(ref uniqueFinderPatterns, out codeAngle);
                    if (result)
                    {
                        middleOfFinderPatterns(ref uniqueFinderPatterns);
                        updateStateCounts(ref uniqueFinderPatterns);

                        float dx = (float)Math.Cos(codeAngle);
                        float dy = (float)Math.Sin(codeAngle);

                        float d = (float)Math.Sqrt(Math.Pow(uniqueFinderPatterns[1].X - uniqueFinderPatterns[0].X, 2) + Math.Pow(uniqueFinderPatterns[1].Y - uniqueFinderPatterns[0].Y, 2));
                        int[] upperLeftStateCount = uniqueFinderPatterns[0].StateCount;
                        int[] upperRightStateCount = uniqueFinderPatterns[1].StateCount;
                        int widthUpperLeft = upperLeftStateCount[0] + upperLeftStateCount[1] + upperLeftStateCount[2] + upperLeftStateCount[3] + upperLeftStateCount[4];
                        int widthUpperRight = upperRightStateCount[0] + upperRightStateCount[1] + upperRightStateCount[2] + upperRightStateCount[3] + upperRightStateCount[4];

                        float nominalXDimension = (widthUpperLeft + widthUpperRight) / 14.0f;
                        int symbolVersion = (int)Math.Round(((d / nominalXDimension) - 10) / 4.0f);
                        float alpha = (codeAngle / 180.0f) * (float)Math.PI;

                        if (symbolVersion >= 7)
                        {
                            float upperRightModuleSize = widthUpperRight / 7.0f;

                            string versionInformation1 = "";
                            float versionInformation1X = uniqueFinderPatterns[1].X - (7.0f * nominalXDimension);
                            float versionInformation1Y = uniqueFinderPatterns[1].Y - (3.0f * nominalXDimension);

                            for (int versionY = -3; versionY < 3; versionY++)
                            {
                                for (int versionX = -7; versionX < -4; versionX++)
                                {
                                    float beta = (float)Math.Atan2(versionY, versionX);
                                    float distance = versionX * upperRightModuleSize / (float)Math.Cos(beta);

                                    int xC = (int)Math.Round(distance * Math.Cos(alpha + beta) + uniqueFinderPatterns[1].X);
                                    int yC = (int)Math.Round(distance * Math.Sin(alpha + beta) + uniqueFinderPatterns[1].Y);
                                    if (_imageArray[yC * _width + xC] == 1) versionInformation1 += "1";
                                    else versionInformation1 += "0";
                                }
                            }

                            int? readVersion = qrCodeVersion(versionInformation1);

                            if (readVersion != null) symbolVersion = (int)readVersion;
                            else return "";
                        }

                        int numberOfModules = 17 + symbolVersion * 4;
                        int xDifference = Math.Abs(uniqueFinderPatterns[1].X - uniqueFinderPatterns[0].X);
                        int yDifference = Math.Abs(uniqueFinderPatterns[1].Y - uniqueFinderPatterns[0].Y);
                        float finderPatternDistance = (float)Math.Sqrt(xDifference * xDifference + yDifference * yDifference);
                        float moduleSize = finderPatternDistance / (numberOfModules - 7);
                        bool[,] qrCode = new bool[numberOfModules,numberOfModules];

                        for (int symbolY = -3; symbolY < numberOfModules - 3; symbolY++)
                        {
                            string str = "";
                            for (int symbolX = -3; symbolX < numberOfModules - 3; symbolX++)
                            {
                                float distance;
                                float beta = (float)Math.Atan2(symbolY, symbolX);
                                if (beta < 0) beta += 2.0f * (float)Math.PI;
                                if ((beta > 0.25f * Math.PI && beta < 0.75f * Math.PI) || (beta > 1.25f * Math.PI && beta < 1.75f * Math.PI))
                                {
                                    distance = symbolY * moduleSize / (float)Math.Sin(beta);
                                }
                                else
                                {
                                    distance = symbolX * moduleSize / (float)Math.Cos(beta);
                                }

                                int xC = (int)Math.Round(distance * Math.Cos(alpha + beta) + uniqueFinderPatterns[0].X);
                                int yC = (int)Math.Round(distance * Math.Sin(alpha + beta) + uniqueFinderPatterns[0].Y);
                                if (xC < 0) xC = 0;
                                if (yC < 0) yC = 0;
                                if (_imageArray[yC * _width + xC] == 1)
                                {
                                    qrCode[symbolX + 3, symbolY + 3] = true;
                                    str += "1";
                                }
                                else
                                {
                                    qrCode[symbolX + 3, symbolY + 3] = false;
                                    str += "0";
                                }
                            }
                        }
                        System.Diagnostics.Debug.WriteLine(qrCode[4, 4]);
                        int ecLevel; // M=0; L=1; H=2; Q=3
                        int dataMaskingPattern;
                        formatInformation(qrCode, numberOfModules, out ecLevel, out dataMaskingPattern);
                        releaseDataMasking(ref qrCode, numberOfModules, dataMaskingPattern);
                        byte[] rawData = performDecode(qrCode, numberOfModules);
                        int ordinalECLevel = getOrdinalECLevel(ecLevel);

                        QRCodeVersion currentVersion = versions[arrayVersionNumber(numberOfModules)];
                        ECBlocks currentECBlocks = currentVersion.ECBlock(ordinalECLevel);
                        ECB[] ecBlockArray = currentECBlocks.getECBlocks();

                        int blockNumber = currentECBlocks.NumBlocks;

                        int[] dataCodewordsPerBlock = new int[blockNumber];

                        for (int i = 0; i < ecBlockArray[0].Count; i++)
                        {
                            dataCodewordsPerBlock[i] = ecBlockArray[0].DataCodewords;
                        }

                        if (ecBlockArray.Length == 2)
                        {
                            for (int i = 0; i < ecBlockArray[1].Count; i++)
                            {
                                dataCodewordsPerBlock[i] = ecBlockArray[1].DataCodewords;
                            }
                        }

                        int ecCodewordsPerBlock = currentECBlocks.ECCodewordsPerBlock;
                        int errorCorrectionBoundary = 0;

                        byte[][] orderedDataCodewords = new byte[blockNumber][];
                        byte[][] orderedECCodewords = new byte[blockNumber][];
                        int runCounter = 0;

                        for (int i = 0; i < ecBlockArray.Length; i++)
                        {
                            ECB currentECBlock = ecBlockArray[i];
                            int count = currentECBlock.Count;
                            int numberOfDataCodewords = currentECBlock.DataCodewords;
                            for (int j = 0; j < count; j++)
                            {
                                byte[] currentBlockDataCodewords = new byte[numberOfDataCodewords];
                                for (int k = 0; k < numberOfDataCodewords; k++)
                                {
                                    int rawDataBytePosition = j + k * count;
                                    currentBlockDataCodewords[k] = rawData[rawDataBytePosition];
                                    errorCorrectionBoundary++;
                                }
                                orderedDataCodewords[runCounter++] = currentBlockDataCodewords;
                            }
                        }

                        for (int i = 0; i < blockNumber; i++)
                        {
                            byte[] currentBlockECCodewords = new byte[ecCodewordsPerBlock];
                            for (int j = 0; j < ecCodewordsPerBlock; j++)
                            {
                                int rawDataBytePosition = errorCorrectionBoundary + i + j * blockNumber;
                                currentBlockECCodewords[j] = rawData[rawDataBytePosition];
                            }
                            orderedECCodewords[i] = currentBlockECCodewords;
                        }

                        byte[][] orderedCodewords = new byte[blockNumber][];

                        for (int i = 0; i < orderedDataCodewords.GetLength(0); i++)
                        {
                            byte[] temp = new byte[orderedDataCodewords[i].Length + orderedECCodewords[i].Length];
                            orderedDataCodewords[i].CopyTo(temp, 0);
                            orderedECCodewords[i].CopyTo(temp, orderedDataCodewords[i].Length);
                            orderedCodewords[i] = temp;
                        }

                        int p = currentVersion.getNumberOfMisdecodeProtectionCodewords(ordinalECLevel);
                        int numberOfSyndromes = ecCodewordsPerBlock - p;
                        int currentBlock = 0;
                        List<int> finalDataCodewords = new List<int>();

                        foreach (byte[] block in orderedCodewords)
                        {
                            int[] intBlock = byteArrayToIntArray(block);
                            int[] corrected = ReedSolomon.CorrectMessage(intBlock, numberOfSyndromes);
                            if (!corrected.SequenceEqual<int>(new int[] { -1 }))
                            {
                                List<int> list = corrected.ToList<int>();
                                int dataCodewords = dataCodewordsPerBlock[currentBlock];
                                finalDataCodewords = finalDataCodewords.Concat(list.GetRange(0, dataCodewords)).ToList();
                            }
                            else return null;
                            currentBlock++;
                        }

                        System.Diagnostics.Debug.WriteLine(finalDataCodewords);
                        int fLength = finalDataCodewords.Count;
                        int[] shiftedFinalDataCodewords = new int[fLength - 2];
                        int messageLength = ((finalDataCodewords[0] & 0xf) << 4) ^ ((finalDataCodewords[1] & 0xf0) >> 4);
                        for (int i = 1; i < fLength - 1; i++)
                        {
                            int currentCodeword = finalDataCodewords[i];
                            int nextCodeword = finalDataCodewords[i + 1];
                            int temp = (int)((currentCodeword & 0xf) << 4);
                            int temp2 = (int)((nextCodeword & 0xf0) >> 4);
                            int final = temp ^ temp2;
                            shiftedFinalDataCodewords[i - 1] = final;
                        }

                        return System.Text.Encoding.GetEncoding("iso-8859-1").GetString(intArrayToByteArray(shiftedFinalDataCodewords)).Substring(0, messageLength);

                    }
                }
            }

            return "";
        }