Алгоритм позволяет разделить пиксели двух классов («полезные» и «фоновые»), рассчитывая такой порог, чтобы внутриклассовая дисперсия была минимальной[1]. Метод Оцу также имеет улучшенную версию для поддержки нескольких уровней изображения[2], который получил название мульти-Оцу метод.
В различных русскоязычных источниках можно встретить различные способы написания фамилии автора, например, можно встретить Метод Отса или Метод Отсу.
Метод Оцу ищет порог, уменьшающий дисперсию внутри класса, которая определяется как взвешенная сумма дисперсий двух классов:
,
где — число, указывающее на класс пикселя, веса — это вероятности двух классов, разделённых порогом , — дисперсия этих классов.
Оцу показал, что минимизация дисперсии внутри класса равносильна максимизации дисперсии между классами:[1]
которая выражается в терминах вероятности и среднего арифметического класса , которое, в свою очередь, может обновляться итеративно. Эта идея привела к эффективному алгоритму.
Алгоритм
Пусть дано полутоновое изображение Счётчик повторений
Вычислить гистограмму изображения и частоту для каждого уровня интенсивности изображения .
Вычислить начальные значения для и
Для каждого значения — полутона — горизонтальная ось гистограммы:
Обновляем и ;
Вычисляем минимизацию дисперсии внутри класса: ;
Если больше, чем имеющееся, то запоминаем и значение порога , ведь искомый порог соответствует максимуму , иначе повторяем действие 3.
Искомый порог соответствует максимуму
Вычисляем итеративно обновляемое среднее арифметическое класса :
,
,
Исходное изображение
Бинаризация с порогом по Оцу
Программная реализация
JavaScript
В данной функции аргумент pixelsNumber является общим числом пикселей изображения, а аргумент histogram — гистограмма 8-битового полутонового изображения, представленная в виде одномерного массива, в котором номер элемента кодирует номер полутона, а значение поля — количество пикселей с этим полутоном.
functionotsu(histogram,pixelsNumber){varsum=0,sumB=0,wB=0,wF=0,mB,mF,max=0,between,threshold=0;for(vari=0;i<256;++i){wB+=histogram[i];if(wB==0)continue;wF=pixelsNumber-wB;if(wF==0)break;sumB+=i*histogram[i];mB=sumB/wB;mF=(sum-sumB)/wF;between=wB*wF*Math.pow(mB-mF,2);if(between>max){max=between;threshold=i;}}returnthreshold;}// To test: open any image in browser and run code in consolevarim=document.getElementsByTagName('img')[0],cnv=document.createElement('canvas'),ctx=cnv.getContext('2d');cnv.width=im.width;cnv.height=im.height;ctx.drawImage(im,0,0);varimData=ctx.getImageData(0,0,cnv.width,cnv.height),histogram=Array(256),i,red,green,blue,gray;for(i=0;i<256;++i)histogram[i]=0;for(i=0;i<imData.data.length;i+=4){red=imData.data[i];blue=imData.data[i+1];green=imData.data[i+2];// alpha = imData.data[i + 3];// https://en.wikipedia.org/wiki/Grayscalegray=red*.2126+green*.7152+blue*.0722;histogram[Math.round(gray)]+=1;}varthreshold=otsu(histogram,imData.data.length/4);console.log("threshold =%s",threshold);for(i=0;i<imData.data.length;i+=4){imData.data[i]=imData.data[i+1]=imData.data[i+2]=imData.data[i]>=threshold?255:0;// opacity 255 = 100%imData.data[i+3]=255;}ctx.putImageData(imData,0,0);document.body.appendChild(cnv);console.log("finished");
При выполнении этого кода в браузере рядом с оригиналом появится чёрно-белое изображение, которое было получено при следовании этого алгоритма. Примерный результат можно найти здесь.
Реализация на языке C
// Количество уровней интенсивности у изображения.// Стандартно для серого изображения -- это 256. От 0 до 255.constintINTENSITY_LAYER_NUMBER=256;// Возвращает гистограмму по интенсивности изображения от 0 до 255 включительноvoidcalculateHist(constIMAGE*image,constintsize,int*hist){// Иницилизируем гистограмму нулямиmemset(hist,0,INTENSITY_LAYER_NUMBER*sizeof(*hist));// Вычисляем гистограммуfor(inti=0;i<size;++i){++hist[image[i]];}}// Посчитать сумму всех интенсивностейintcalculateIntensitySum(constIMAGE*image,constintsize){intsum=0;for(inti=0;i<size;++i){sum+=image[i];}returnsum;}// Функция возвращает порог бинаризации для полутонового изображения image с общим числом пикселей size.// const IMAGE *image содержит интенсивность изображения от 0 до 255 включительно.// size -- количество пикселей в image.intotsuThreshold(constIMAGE*image,constintsize){inthist[INTENSITY_LAYER_NUMBER];calculateHist(image,size,hist);// Необходимы для быстрого пересчёта разности дисперсий между классамиintall_pixel_count=size;intall_intensity_sum=calculateIntensitySum(image,size);intbest_thresh=0;doublebest_sigma=0.0;intfirst_class_pixel_count=0;intfirst_class_intensity_sum=0;// Перебираем границу между классами// thresh < INTENSITY_LAYER_NUMBER - 1, т.к. при 255 в ноль уходит знаменатель внутри forfor(intthresh=0;thresh<INTENSITY_LAYER_NUMBER-1;++thresh){first_class_pixel_count+=hist[thresh];first_class_intensity_sum+=thresh*hist[thresh];doublefirst_class_prob=first_class_pixel_count/(double)all_pixel_count;doublesecond_class_prob=1.0-first_class_prob;doublefirst_class_mean=first_class_intensity_sum/(double)first_class_pixel_count;doublesecond_class_mean=(all_intensity_sum-first_class_intensity_sum)/(double)(all_pixel_count-first_class_pixel_count);doublemean_delta=first_class_mean-second_class_mean;doublesigma=first_class_prob*second_class_prob*mean_delta*mean_delta;if(sigma>best_sigma){best_sigma=sigma;best_thresh=thresh;}}returnbest_thresh;}
Примечания
12N. Otsu. A threshold selection method from gray-level histograms (англ.) // IEEE Trans. Sys., Man., Cyber. : journal. — 1979. — Vol. 9. — P. 62—66.
Ping-Sung Liao and Tse-Sheng Chen and Pau-Choo Chung. A Fast Algorithm for Multilevel Thresholding (неопр.) // J. Inf. Sci. Eng.. — 2001. — Т. 17. — С. 713—727.