opencv python中的图像解析(连接组件分析)

yb3bgrhw  于 2022-11-24  发布在  Python
关注(0)|答案(1)|浏览(140)

我有一个数学公式的图像,我需要解析它的符号,但也保存他们的位置(每个符号的中心)。例如,像这样的图像需要转换成15个不同的图像75x75,每个符号1。
我试过的是:
1.先转换为灰度,然后转换为二值:接近白色(〉250)的像素变为255,而其它像素变为0
1.使用BNF查找所有组件,然后将其转换为图像(通过重新缩放和其他操作)
但我敢肯定这不是最好的办法,也许有标准的方法来解决这个问题存在呢?
下面是我的代码:

class Parser:

    def init(self, targetSizes=(75, 75), binaryThreshold=cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU,
                 scaleFully=False, scaleFullyRate=0.9, whiteThreshold=249, blackThreshold=0,
                 rescalingInterpolation=cv2.INTER_AREA, pixelsInImageThreshold=20,
                 rescaleOriginalImage=True, rescaleToAtLeast=200, rescaleToAtMaximum=1000):
        self.targetWidth = targetSizes[0]
        self.targetHeight = targetSizes[1]

        self.binaryThreshold = binaryThreshold

        self.scaleFully = scaleFully
        self.scaleFullyRate = scaleFullyRate

        self.whiteThreshold = whiteThreshold
        self.blackThreshold = blackThreshold

        self.rescalingInterpolation = rescalingInterpolation
        self.pixelsInIMageThreshold = pixelsInImageThreshold

        self.rescaleOriginalImage = rescaleOriginalImage
        self.rescaleOriginalMin = rescaleToAtLeast
        self.rescaleOriginalMax = rescaleToAtMaximum

        self.parseMode = 1

    def _imageToBinary(self, image, zeroValueTrash=0, oneValueTrash=253):
        grayImage = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

        ret, binary = cv2.threshold(grayImage, self.blackThreshold, self.whiteThreshold, self.binaryThreshold)
        # cv2.imwrite("Test.png", binary)
        return binary

    def _BNF(self, binaryImage):

        Q = MyQueue()
        whitePixels = []
        gg = 0
        for i in range(len(binaryImage)):
            for j in range(len(binaryImage[i])):
                if binaryImage[i][j] > self.whiteThreshold-1:
                    Q.put((i, j))
                    binaryImage[i][j] = 0
                    obj = []
                    gg += 1
                    while not Q.empty():
                        i, j = Q.pop()

                        obj.append((i, j))

                        if i + 1 < len(binaryImage) and binaryImage[i + 1][j] != 0:
                            Q.put((i + 1, j))
                            binaryImage[i + 1][j] = 0

                        if j - 1 > 0 and binaryImage[i][j - 1] != 0:
                            Q.put((i, j - 1))
                            binaryImage[i][j - 1] = 0

                        if i - 1 > 0 and binaryImage[i - 1][j] != 0:
                            Q.put((i - 1, j))
                            binaryImage[i - 1][j] = 0

                        if j + 1 < len(binaryImage[i]) and binaryImage[i][j + 1] != 0:
                            Q.put((i, j + 1))
                            binaryImage[i][j + 1] = 0

                        if self.parseMode == 1:
                            if i + 1 < len(binaryImage) and j+1 < len(binaryImage[i+1]) and binaryImage[i + 1][j+1] != 0:
                                Q.put((i + 1, j+1))
                                binaryImage[i + 1][j+1] = 0
                            if i + 1 < len(binaryImage) and j - 1 > 0 and binaryImage[i + 1][j-1] != 0:
                                Q.put((i + 1, j-1))
                                binaryImage[i + 1][j-1] = 0
                            if i - 1 > 0 and j - 1 > 0 and binaryImage[i - 1][j - 1] != 0:
                                Q.put((i - 1, j - 1))
                                binaryImage[i - 1][j - 1] = 0
                            if i - 1 > 0 and j + 1 < len(binaryImage[i-1]) and binaryImage[i - 1][j + 1] != 0:
                                Q.put((i - 1, j + 1))
                                binaryImage[i - 1][j + 1] = 0

                    cv2.imwrite("tmp/{}.png".format(gg), binaryImage)
                    whitePixels.append(obj)
        return whitePixels

    def parseImage(self, image_path: str) -> list:

        image = cv2.imread(image_path)
        if self.rescaleOriginalImage:
            image = self.scaleOriginal(image)

        binary = self._imageToBinary(image)

        whitePixels = self._BNF(binary)

        return whitePixels

    def isScaleable(self, imageShape):
        return True

    def scaleOriginal(self, image: np.ndarray):
        # To be created
        return image

@staticmethod
    def _getImageAndCenterFromDotes(Dotes, originalImage=None):
        i_mx, j_mx = -1, -1
        i_mn, j_mn = 100500, 100500  # just big numbers

        # finding upper right and lower left corner of image
        for el in Dotes:
            i, j = el

            if i_mx < i:
                i_mx = i
            if j_mx < j:
                j_mx = j
            if j_mn > j:
                j_mn = j
            if i_mn > i:
                i_mn = i

        # updating image center
        imageCenter = Point((i_mx + i_mn) // 2, (j_mx + j_mn) // 2)

        # finding out size of image
        width, height = i_mx - i_mn + 1, j_mx - j_mn + 1
        image = np.zeros((width, height)) if originalImage is None else np.zeros((width, height, 3))

        # recreating image from dotes
        if originalImage is not None:
            for el in Dotes:
                i, j = el
                image[i - i_mn][j - j_mn] = originalImage[i][j]
        else:
            for el in Dotes:
                i, j = el
                image[i - i_mn][j - j_mn] = 255

        return image, imageCenter

    def scaleParsedImage(self, image: np.ndarray):
        """
        :param image: np.ndarray
        :return: scaledImage np.ndarray
        """
        width, height = image.shape if len(image.shape) == 2 else image.shape[0], image.shape[1]

        newWidth = self.targetWidth if width > self.targetHeight else width
        newHeight = self.targetHeight if height > self.targetHeight else height
        if self.scaleFully and newHeight < self.targetHeight * self.scaleFullyRate and newWidth * self.scaleFullyRate:
            scaleRate = min((self.targetWidth * self.scaleFullyRate / newWidth), (
                    self.targetHeight * self.scaleFullyRate / newHeight))

            newWidth = math.ceil(newWidth * scaleRate)
            newHeight = math.ceil(newHeight * scaleRate)


        scaled = cv2.resize(image, (newHeight, newWidth), interpolation=self.rescalingInterpolation)

        # pasting our scaled image in the middle
        x_add, y_add = (self.targetWidth - newWidth) // 2, (self.targetHeight - newHeight) // 2
        resized = np.zeros((self.targetWidth, self.targetHeight)) if len(image.shape) == 2 else np.zeros((self.targetWidth, self.targetHeight, 3))
        for x in range(newWidth):
            for y in range(newHeight):
                resized[x + x_add][y + y_add] = scaled[x][y]

        return resized

    def parseAndConvert(self, image_name: str) -> list:
        imagesInDotes = self.parseImage(image_name)
        original = 255 - cv2.imread(image_name)

        images = []

        for dotes in imagesInDotes:
            image = self._getImageAndCenterFromDotes(dotes, original)
            images.append([self.scaleParsedImage(image[0]), image[1]])
        rawImages = []
        for image, center in images:
            rawImages.append(RawImage(image, center))

        return rawImages
t0ybt7op

t0ybt7op1#

看一下函数cv.findContours()(恕我直言,命名并不太直观):https://docs.opencv.org/3.4/d4/d73/tutorial_py_contours_begin.html
它应该可以完成你现在手动完成的大多数事情,即提取和测量二进制对象。
如果您遇到单个符号由多个对象组成的问题(如i%"),请查看形态学操作,将它们合并为单个符号:erode()dilate(),或通过morphologyEx() * 打开 * 和 * 关闭 *(此处为教程:https://docs.opencv.org/4.x/d9/d61/tutorial_py_morphological_ops.html)的数据。

相关问题