Правильный метод и пакет Python, который может найти ширину объекта изображения - PullRequest
3 голосов
/ 19 марта 2012

spectrum

Вход представляет собой спектр с красочными (извините) вертикальными линиями на черном фоне. Учитывая приблизительную координату x этой полосы (как отмечено X), я хочу найти ширину этой полосы.

Я не знаком с обработкой изображений. Пожалуйста, направьте меня к правильному методу обработки изображений и Python * для обработки изображений package , который может сделать то же самое.


Я думаю, что PIL, OpenCV дал мне впечатление, что я переборщил с этим конкретным приложением.

Что, если я хочу сделать эту экспертную систему, которая может классифицировать их в будущем?

Ответы [ 3 ]

3 голосов
/ 19 марта 2012

Я приведу полный минимальный рабочий пример (как предложено sega_sai).У меня нет доступа к вашему исходному изображению, но вы увидите, что это не имеет значения!Распределения пиков, найденные с помощью кода ниже:

Средние значения при: 26.2840960523 80.8255092125

import Image
from scipy import *
from scipy.optimize import leastsq

# Load the picture with PIL, process if needed
pic         = asarray(Image.open("band2.png"))

# Average the pixel values along vertical axis
pic_avg     = pic.mean(axis=2)
projection  = pic_avg.sum(axis=0)

# Set the min value to zero for a nice fit
projection /= projection.mean()
projection -= projection.min()

# Fit function, two gaussians, adjust as needed
def fitfunc(p,x):
    return p[0]*exp(-(x-p[1])**2/(2.0*p[2]**2)) + \
        p[3]*exp(-(x-p[4])**2/(2.0*p[5]**2))
errfunc = lambda p, x, y: fitfunc(p,x)-y

# Use scipy to fit, p0 is inital guess
p0 = array([0,20,1,0,75,10])
X  = xrange(len(projection))
p1, success = leastsq(errfunc, p0, args=(X,projection))
Y = fitfunc(p1,X)

# Output the result
print "Mean values at: ", p1[1], p1[4]

# Plot the result
from pylab import *
subplot(211)
imshow(pic)
subplot(223)
plot(projection)
subplot(224)
plot(X,Y,'r',lw=5)
show()

enter image description here

2 голосов
/ 19 марта 2012

Ниже приведен простой метод определения порогов для нахождения линий и их ширины, он должен работать достаточно надежно для любого количества строк. Приведенное ниже желто-черное изображение было обработано с использованием этого сценария, а черно-красное изображение иллюстрирует найденные линии с использованием параметров threshold = 0.3, min_line_width = 5)

enter image description here

Сценарий усредняет строки изображения, а затем определяет базовые начальные и конечные позиции каждой строки на основе порога (который можно установить в диапазоне от 0 до 1) и минимальной ширины линии (в пикселях). Используя пороговое значение и минимальную ширину линии, вы можете легко фильтровать входные изображения, чтобы получить линии из них. Первая функция find_lines возвращает все строки в изображении в виде списка кортежей, содержащих начало, конец, центр и ширину каждой строки. Вторая функция find_closest_band_width вызывается с указанным x_position и возвращает ширину ближайшей линии к этой позиции (при условии, что вы хотите расстояние до центра для каждой линии). Поскольку линии насыщены (255 отсечений на канал), их поперечные сечения не далеки от равномерного распределения, поэтому я не верю, что попытка подобрать какой-либо тип распределения действительно слишком сильно поможет, просто излишне усложнит .

import Image, ImageStat

def find_lines(image_file, threshold, min_line_width):
    im = Image.open(image_file)
    width, height = im.size
    hist = []
    lines = []
    start = end = 0
    for x in xrange(width):
        column = im.crop((x, 0, x + 1, height))
        stat = ImageStat.Stat(column)
        ## normalises by 2 * 255 as in your example the colour is yellow
        ## if your images start using white lines change this to 3 * 255
        hist.append(sum(stat.sum) / (height * 2 * 255)) 

    for index, value in enumerate(hist):
        if value > threshold and end >= start:
            start = index
        if value < threshold and end < start:
            if index - start < min_line_width:
                start = 0
            else:
                end = index
                center = start + (end - start) / 2.0
                width = end - start
                lines.append((start, end, center, width))
    return lines

def find_closest_band_width(x_position, lines):
    distances = [((value[2] - x_position) ** 2) for value in lines]
    index = distances.index(min(distances))
    return lines[index][3]

## set your threshold, and min_line_width for finding lines
lines = find_lines("8IxWA_sample.png", 0.7, 4)
## sets x_position to 59th pixel
print 'width of nearest line:', find_closest_band_width(59, lines)
1 голос
/ 19 марта 2012

Я не думаю, что вам нужно что-то необычное для вашей конкретной задачи.

Я бы просто использовал PIL + scipy.Этого должно быть достаточно.

Поскольку вам, по сути, нужно сделать снимок, сделать его 1D-проекцию, а затем подогнать к нему гауссов или что-то подобное.Информация о приблизительном местоположении группы должна использоваться в качестве первого предположения для установщика.

...