def _optimise_barcodes(
        self, data, min_bar_height=20, min_bar_count=100, max_gap_size=30,
        min_percent_white=0.2, max_percent_white=0.8, **kwargs
    ):
        """
        min_bar_height    = Minimum height of black bars in px. Set this too
                            low and it might pick up text and data matrices,
                            too high and it might pick up borders, tables, etc.
        min_bar_count     = Minimum number of parallel black bars before a
                            pattern is considered a potential barcode.
        max_gap_size      = Biggest white gap in px allowed between black bars.
                            This is only important if you have multiple
                            barcodes next to each other.
        min_percent_white = Minimum percentage of white bars between black
                            bars. This helps to ignore solid rectangles.
        max_percent_white = Maximum percentage of white bars between black
                            bars. This helps to ignore solid rectangles.
        """

        re_bars = re.compile(r'1{%s,}' % min_bar_height)

        bars = {}
        for i, line in enumerate(data):
            for match in re_bars.finditer(line):
                try:
                    bars[match.span()].append(i)
                except KeyError:
                    bars[match.span()] = [i]

        grouped_bars = []
        for span, seen_at in bars.items():
            group = []
            for coords in seen_at:
                if group and coords - group[-1] > max_gap_size:
                    grouped_bars.append((span, group))
                    group = []
                group.append(coords)
            grouped_bars.append((span, group))

        suspected_barcodes = []
        for span, seen_at in grouped_bars:
            if len(seen_at) < min_bar_count:
                continue
            pc_white = len(seen_at) / float(seen_at[-1] - seen_at[0])
            if pc_white >= min_percent_white and pc_white <= max_percent_white:
                suspected_barcodes.append((span, seen_at))

        for span, seen_at in suspected_barcodes:
            barcode = []
            for line in data[seen_at[0]:seen_at[-1]+1]:
                barcode.append(line[span[0]])
            barcode = ''.join(barcode)

            # Do the actual optimisation
            barcode = self._optimise_barcode(barcode)

            barcode = list(barcode)
            barcode.reverse()
            width = span[1] - span[0]
            for i in range(seen_at[0], seen_at[-1]+1):
                line = data[i]
                line = (
                    line[:span[0]] + (barcode.pop() * width) + line[span[1]:]
                )
                data[i] = line

        return data
min_bar_height    = Minimum height of black bars in px. Set this too
                            low and it might pick up text and data matrices,
                            too high and it might pick up borders, tables, etc.
        min_bar_count     = Minimum number of parallel black bars before a
                            pattern is considered a potential barcode.
        max_gap_size      = Biggest white gap in px allowed between black bars.
                            This is only important if you have multiple
                            barcodes next to each other.
        min_percent_white = Minimum percentage of white bars between black
                            bars. This helps to ignore solid rectangles.
        max_percent_white = Maximum percentage of white bars between black
                            bars. This helps to ignore solid rectangles.