threshold algorithm: The simplest image segmentation method.

All thresholding algorithms take a source image (src) and a threshold value (thresh) as input and produce an output image (dst) by comparing the pixel value at source pixel( x , y ) to the threshold. If src ( x , y ) > thresh , then dst ( x , y ) is assigned a some value. Otherwise dst ( x , y ) is assigned some other value.

Otsu binarization: in simple words, it automatically calculates a threshold value from image histogram for a bimodal image. (For images which are not bimodal,binarization won’t be accurate.). working with bimodal images, Otsu’s algorithmtries to find a threshold value (t) which minimizes the weighted within-class variance. It actually finds a value of t which lies in between two peaks such that variances to both classes are minimum.

Otsu's thresholding method involves iterating through all the possible threshold values and calculating a measure of spread for the pixel levels each side of the threshold, i.e. the pixels that either fall in foreground or background.The aim is to find the threshold value where the sum of foreground and background spreads is at its minimum.

Triangle algorithm: A line is constructed between the maximum of the histogram at brightness bmax and the lowest value bmin in the image. The distance d between the line and the histogram h[b] is computed for all values of b from b = bmin to b = bmax. The brightness value bo where the distance between h[bo] and the line is maximal is the threshold value, that is, threshold = bo. This technique is particularly effective when the object pixels produce a weak peak in the histogram.

图像二值化就是将图像上的像素点的灰度值设置为两个值,一般为0,255或者指定的某个值。

Otsu:

目前fbc_cv库中支持uchar和float两种数据类型,经测试,与OpenCV3.1结果完全一致。

实现代码threshold.hpp:

// fbc_cv is free software and uses the same licence as OpenCV
// Email: fengbingchun@163.com

#ifndef FBC_CV_THRESHOLD_HPP_
#define FBC_CV_THRESHOLD_HPP_

/* reference: include/opencv2/imgproc.hpp
              modules/imgproc/src/thresh.cpp
*/

#include <typeinfo>
#include "core/mat.hpp"
#include "imgproc.hpp"

namespace fbc {

template<typename _Tp, int chs> static double getThreshVal_Otsu_8u(const Mat_<_Tp, chs>& src);
template<typename _Tp, int chs> static double getThreshVal_Triangle_8u(const Mat_<_Tp, chs>& src);
template<typename _Tp, int chs> static void thresh_8u(const Mat_<_Tp, chs>& _src, Mat_<_Tp, chs>& _dst, uchar thresh, uchar maxval, int type);
template<typename _Tp, int chs> static void thresh_32f(const Mat_<_Tp, chs>& _src, Mat_<_Tp, chs>& _dst, float thresh, float maxval, int type);

// applies fixed-level thresholding to a single-channel array
// the Otsu's and Triangle methods are implemented only for 8-bit images
// support type: uchar/float, single-channel
template<typename _Tp, int chs>
double threshold(const Mat_<_Tp, chs>& src, Mat_<_Tp, chs>& dst, double thresh, double maxval, int type)
{
	FBC_Assert(typeid(uchar).name() == typeid(_Tp).name() || typeid(float).name() == typeid(_Tp).name()); // uchar || float
	if (dst.empty()) {
		dst = Mat_<_Tp, chs>(src.rows, src.cols);
	} else {
		FBC_Assert(src.rows == dst.rows && src.cols == dst.cols);
	}

	int automatic_thresh = (type & ~THRESH_MASK);
	type &= THRESH_MASK;

	FBC_Assert(automatic_thresh != (THRESH_OTSU | THRESH_TRIANGLE));
	if (automatic_thresh == THRESH_OTSU) {
		FBC_Assert(sizeof(_Tp) == 1);
		thresh = getThreshVal_Otsu_8u(src);
	} else if (automatic_thresh == THRESH_TRIANGLE) {
		FBC_Assert(sizeof(_Tp) == 1);
		thresh = getThreshVal_Triangle_8u(src);
	}

	if (sizeof(_Tp) == 1) {
		int ithresh = fbcFloor(thresh);
		thresh = ithresh;
		int imaxval = fbcRound(maxval);
		if (type == THRESH_TRUNC)
			imaxval = ithresh;
		imaxval = saturate_cast<uchar>(imaxval);

		if (ithresh < 0 || ithresh >= 255) {
			if (type == THRESH_BINARY || type == THRESH_BINARY_INV ||
				((type == THRESH_TRUNC || type == THRESH_TOZERO_INV) && ithresh < 0) ||
				(type == THRESH_TOZERO && ithresh >= 255)) {
				int v = type == THRESH_BINARY ? (ithresh >= 255 ? 0 : imaxval) :
					type == THRESH_BINARY_INV ? (ithresh >= 255 ? imaxval : 0) :
					/*type == THRESH_TRUNC ? imaxval :*/ 0;
				dst.setTo(v);
			}
			else
				src.copyTo(dst);
			return thresh;
		}
		thresh = ithresh;
		maxval = imaxval;
	} else if (sizeof(_Tp) == 4) {
	} else {
		FBC_Error("UnsupportedFormat");
	}

	if (sizeof(_Tp) == 1) {
		thresh_8u(src, dst, (uchar)thresh, (uchar)maxval, type);
	} else {
		thresh_32f(src, dst, (float)thresh, (float)maxval, type);
	}

	return 0;
}

template<typename _Tp, int chs>
static double getThreshVal_Otsu_8u(const Mat_<_Tp, chs>& _src)
{
	Size size = _src.size();
	const int N = 256;
	int i, j, h[N] = { 0 };

	for (i = 0; i < size.height; i++) {
		const uchar* src = _src.ptr(i);
		j = 0;
		for (; j <= size.width - 4; j += 4) {
			int v0 = src[j], v1 = src[j + 1];
			h[v0]++; h[v1]++;
			v0 = src[j + 2]; v1 = src[j + 3];
			h[v0]++; h[v1]++;
		}
		for (; j < size.width; j++)
			h[src[j]]++;
	}

	double mu = 0, scale = 1. / (size.width*size.height);
	for (i = 0; i < N; i++)
		mu += i*(double)h[i];

	mu *= scale;
	double mu1 = 0, q1 = 0;
	double max_sigma = 0, max_val = 0;

	for (i = 0; i < N; i++) {
		double p_i, q2, mu2, sigma;

		p_i = h[i] * scale;
		mu1 *= q1;
		q1 += p_i;
		q2 = 1. - q1;

		if (std::min(q1, q2) < FLT_EPSILON || std::max(q1, q2) > 1. - FLT_EPSILON)
			continue;

		mu1 = (mu1 + i*p_i) / q1;
		mu2 = (mu - q1*mu1) / q2;
		sigma = q1*q2*(mu1 - mu2)*(mu1 - mu2);
		if (sigma > max_sigma) {
			max_sigma = sigma;
			max_val = i;
		}
	}

	return max_val;
}

template<typename _Tp, int chs>
static double getThreshVal_Triangle_8u(const Mat_<_Tp, chs>& _src)
{
	Size size = _src.size();
	const int N = 256;
	int i, j, h[N] = { 0 };

	for (i = 0; i < size.height; i++) {
		const uchar* src = _src.ptr(i);
		j = 0;
		for (; j <= size.width - 4; j += 4) {
			int v0 = src[j], v1 = src[j + 1];
			h[v0]++; h[v1]++;
			v0 = src[j + 2]; v1 = src[j + 3];
			h[v0]++; h[v1]++;
		}

		for (; j < size.width; j++)
			h[src[j]]++;
	}

	int left_bound = 0, right_bound = 0, max_ind = 0, max = 0;
	int temp;
	bool isflipped = false;

	for (i = 0; i < N; i++) {
		if (h[i] > 0) {
			left_bound = i;
			break;
		}
	}
	if (left_bound > 0)
		left_bound--;

	for (i = N - 1; i > 0; i--) {
		if (h[i] > 0) {
			right_bound = i;
			break;
		}
	}
	if (right_bound < N - 1)
		right_bound++;

	for (i = 0; i < N; i++) {
		if (h[i] > max) {
			max = h[i];
			max_ind = i;
		}
	}

	if (max_ind - left_bound < right_bound - max_ind) {
		isflipped = true;
		i = 0, j = N - 1;
		while (i < j) {
			temp = h[i]; h[i] = h[j]; h[j] = temp;
			i++; j--;
		}
		left_bound = N - 1 - right_bound;
		max_ind = N - 1 - max_ind;
	}

	double thresh = left_bound;
	double a, b, dist = 0, tempdist;

	// We do not need to compute precise distance here. Distance is maximized, so some constants can
	// be omitted. This speeds up a computation a bit.
	a = max; b = left_bound - max_ind;
	for (i = left_bound + 1; i <= max_ind; i++) {
		tempdist = a*i + b*h[i];
		if (tempdist > dist) {
			dist = tempdist;
			thresh = i;
		}
	}
	thresh--;

	if (isflipped)
		thresh = N - 1 - thresh;

	return thresh;
}

template<typename _Tp, int chs>
static void thresh_8u(const Mat_<_Tp, chs>& _src, Mat_<_Tp, chs>& _dst, uchar thresh, uchar maxval, int type)
{
	int i, j, j_scalar = 0;
	uchar tab[256];
	Size roi = _src.size();
	roi.width *= _src.channels;

	switch (type) {
	case THRESH_BINARY:
		for (i = 0; i <= thresh; i++)
			tab[i] = 0;
		for (; i < 256; i++)
			tab[i] = maxval;
		break;
	case THRESH_BINARY_INV:
		for (i = 0; i <= thresh; i++)
			tab[i] = maxval;
		for (; i < 256; i++)
			tab[i] = 0;
		break;
	case THRESH_TRUNC:
		for (i = 0; i <= thresh; i++)
			tab[i] = (uchar)i;
		for (; i < 256; i++)
			tab[i] = thresh;
		break;
	case THRESH_TOZERO:
		for (i = 0; i <= thresh; i++)
			tab[i] = 0;
		for (; i < 256; i++)
			tab[i] = (uchar)i;
		break;
	case THRESH_TOZERO_INV:
		for (i = 0; i <= thresh; i++)
			tab[i] = (uchar)i;
		for (; i < 256; i++)
			tab[i] = 0;
		break;
	default:
		FBC_Error("Unknown threshold type");
	}

	if (j_scalar < roi.width) {
		for (i = 0; i < roi.height; i++) {
			const uchar* src = _src.ptr(i);
			uchar* dst = _dst.ptr(i);
			j = j_scalar;

			for (; j <= roi.width - 4; j += 4) {
				uchar t0 = tab[src[j]];
				uchar t1 = tab[src[j + 1]];

				dst[j] = t0;
				dst[j + 1] = t1;

				t0 = tab[src[j + 2]];
				t1 = tab[src[j + 3]];

				dst[j + 2] = t0;
				dst[j + 3] = t1;
			}

			for (; j < roi.width; j++)
				dst[j] = tab[src[j]];
		}
	}
}

template<typename _Tp, int chs>
static void thresh_32f(const Mat_<_Tp, chs>& _src, Mat_<_Tp, chs>& _dst, float thresh, float maxval, int type)
{
	int i, j;
	Size roi = _src.size();
	roi.width *= _src.channels;
	const float* src = (const float*)_src.ptr();
	float* dst = (float*)_dst.ptr();
	size_t src_step = _src.step / sizeof(src[0]);
	size_t dst_step = _dst.step / sizeof(dst[0]);

	switch (type) {
	case THRESH_BINARY:
		for (i = 0; i < roi.height; i++, src += src_step, dst += dst_step) {
			for (j = 0; j < roi.width; j++)
				dst[j] = src[j] > thresh ? maxval : 0;
		}
		break;

	case THRESH_BINARY_INV:
		for (i = 0; i < roi.height; i++, src += src_step, dst += dst_step) {
			for (j = 0; j < roi.width; j++)
				dst[j] = src[j] <= thresh ? maxval : 0;
		}
		break;

	case THRESH_TRUNC:
		for (i = 0; i < roi.height; i++, src += src_step, dst += dst_step) {
			for (j = 0; j < roi.width; j++)
				dst[j] = std::min(src[j], thresh);
		}
		break;

	case THRESH_TOZERO:
		for (i = 0; i < roi.height; i++, src += src_step, dst += dst_step) {
			for (j = 0; j < roi.width; j++) {
				float v = src[j];
				dst[j] = v > thresh ? v : 0;
			}
		}
		break;

	case THRESH_TOZERO_INV:
		for (i = 0; i < roi.height; i++, src += src_step, dst += dst_step) {
			for (j = 0; j < roi.width; j++) {
				float v = src[j];
				dst[j] = v <= thresh ? v : 0;
			}
		}
		break;
	default:
		FBC_Error("BadArg");
	}
}

} // namespace fbc

#endif // FBC_CV_THRESHOLD_HPP_

测试代码test_threshold.cpp:

#include "test_threshold.hpp"
#include <assert.h>

#include <threshold.hpp>
#include <opencv2/opencv.hpp>

int test_threshold_uchar()
{
	cv::Mat matSrc = cv::imread("E:/GitCode/OpenCV_Test/test_images/lena.png", 1);
	if (!matSrc.data) {
		std::cout << "read image fail" << std::endl;
		return -1;
	}
	cv::cvtColor(matSrc, matSrc, CV_BGR2GRAY);

	int width = matSrc.cols;
	int height = matSrc.rows;
	int types[8] = {0, 1, 2, 3, 4, 7, 8, 16};

	for (int i = 0; i < 8; i++) {
		if (types[i] == 7) continue;
		double thresh = 135.0;
		double maxval = 255.0;

		fbc::Mat_<uchar, 1> mat1(height, width, matSrc.data);
		fbc::Mat_<uchar, 1> mat2(height, width);
		fbc::threshold(mat1, mat2, thresh, maxval, types[i]);

		cv::Mat mat1_(height, width, CV_8UC1, matSrc.data);
		cv::Mat mat2_;
		cv::threshold(mat1_, mat2_, thresh, maxval, types[i]);

		assert(mat2.rows == mat2_.rows && mat2.cols == mat2_.cols && mat2.step == mat2_.step);
		for (int y = 0; y < mat2.rows; y++) {
			const fbc::uchar* p1 = mat2.ptr(y);
			const uchar* p2 = mat2_.ptr(y);

			for (int x = 0; x < mat2.step; x++) {
				assert(p1[x] == p2[x]);
			}
		}
	}

	return 0;
}

int test_threshold_float()
{
	cv::Mat matSrc = cv::imread("E:/GitCode/OpenCV_Test/test_images/lena.png", 1);
	if (!matSrc.data) {
		std::cout << "read image fail" << std::endl;
		return -1;
	}
	cv::cvtColor(matSrc, matSrc, CV_BGR2GRAY);
	matSrc.convertTo(matSrc, CV_32FC1);

	int width = matSrc.cols;
	int height = matSrc.rows;
	int types[6] = { 0, 1, 2, 3, 4, 7 };

	for (int i = 0; i < 6; i++) {
		if (types[i] == 7) continue;
		double thresh = 135.0;
		double maxval = 255.0;

		fbc::Mat_<float, 1> mat1(height, width, matSrc.data);
		fbc::Mat_<float, 1> mat2(height, width);
		fbc::threshold(mat1, mat2, thresh, maxval, types[i]);

		cv::Mat mat1_(height, width, CV_32FC1, matSrc.data);
		cv::Mat mat2_;
		cv::threshold(mat1_, mat2_, thresh, maxval, types[i]);

		assert(mat2.rows == mat2_.rows && mat2.cols == mat2_.cols && mat2.step == mat2_.step);
		for (int y = 0; y < mat2.rows; y++) {
			const fbc::uchar* p1 = mat2.ptr(y);
			const uchar* p2 = mat2_.ptr(y);

			for (int x = 0; x < mat2.step; x++) {
				assert(p1[x] == p2[x]);
			}
		}
	}

	return 0;
}

GitHubhttps://github.com/fengbingchun/OpenCV_Test

OpenCV代码提取: threshold函数的实现的更多相关文章

  1. OpenCV代码提取:transpose函数的实现

    OpenCV中的transpose函数实现图像转置,公式为: 目前fbc_cv库中也实现了transpose函数,支持多通道,uchar和float两种数据类型,经测试,与OpenCV3.1结果完全一 ...

  2. OpenCV代码提取:flip函数的实现

    OpenCV中实现图像翻转的函数flip,公式为: 目前fbc_cv库中也实现了flip函数,支持多通道,uchar和float两种数据类型,经测试,与OpenCV3.1结果完全一致. 实现代码fli ...

  3. OpenCV代码提取:dft函数的实现

    The Fourier Transform will decompose an image into its sinus and cosines components. In other words, ...

  4. OpenCV代码提取:遍历指定目录下指定文件的实现

    前言 OpenCV 3.1之前的版本,在contrib目录下有提供遍历文件的函数,用起来比较方便.但是在最新的OpenCV 3.1版本给去除掉了.为了以后使用方便,这里将OpenCV 2.4.9中相关 ...

  5. OpenCV中threshold函数的使用

    转自:https://blog.csdn.net/u012566751/article/details/77046445 一篇很好的介绍threshold文章: 图像的二值化就是将图像上的像素点的灰度 ...

  6. OpenCV 学习笔记03 threshold函数

    opencv-python   4.0.1 简介:该函数是对数组中的每一个元素(each array element)应用固定级别阈值(Applies a fixed-level threshold) ...

  7. opencv二值化的cv2.threshold函数

    (一)简单阈值 简单阈值当然是最简单,选取一个全局阈值,然后就把整幅图像分成了非黑即白的二值图像了.函数为cv2.threshold() 这个函数有四个参数,第一个原图像,第二个进行分类的阈值,第三个 ...

  8. OpenCV中的绘图函数-OpenCV步步精深

    OpenCV 中的绘图函数 画线 首先要为画的线创造出环境,就要生成一个空的黑底图像 img=np.zeros((512,512,3), np.uint8) 这是黑色的底,我们的画布,我把窗口名叫做i ...

  9. 基础学习笔记之opencv(24):imwrite函数的使用

    http://www.cnblogs.com/tornadomeet/archive/2012/12/26/2834336.html 前言 OpenCV中保存图片的函数在c++版本中变成了imwrit ...

随机推荐

  1. 关于bootstrap-table服务端分页问题

    昨天项目中涉及到了前端表格分页问题.数据一共有1万多条,所以选择了后端分页. 之前用的都是前端分页,第一次使用后端分页.网上也找到了一些例子,最后做出来了. 这里用的是bootstrap-table插 ...

  2. html版本

    1.html4/4.01 (SGML) 非常通用的语言,少写闭合,大小写混合了,浏览器都会去容错,就是html怎么写都不会导致浏览器挂掉,大家都觉得这种方式是不科学的 2.XHTML(XML) 基于x ...

  3. JZ2440学习笔记之通过J-Link单步裸机程序(Keil+J-Link)

    我们还是使用JZ2440学习笔记之第一个裸机程序(Keil-MDK)里面的程序,但是把延时拿掉,要不然单步的时候一直在delay里面: int main(void) { // Set GPF4/5/6 ...

  4. 一组div跟随鼠标移动,反应鼠标轨迹

    <!DOCTYPE html> <html> <head> <title>div随鼠标移动</title> <style type=& ...

  5. JavaScript:改变 HTML 样式

    <!DOCTYPE html><html><head><meta http-equiv="Content-Type" content=&q ...

  6. 前端DOM知识点

    DOM即文档对象模型(Document Object Model,DOM)是一种用于HTML和XML文档的编程接口.它给文档提供了一种结构化的表示方法,可以改变文档的内容和呈现方式.DOM把网页和脚本 ...

  7. git使用过程的问题与解决办法

    一.什么是Git Git是目前世界上最先进的分布式版本控制系统.工作原理 / 流程: Workspace:工作区Index / Stage:暂存区Repository:仓库区(或本地仓库)Remote ...

  8. aes 加密,解密(2)

    JavaScript加密,解密 1,此为AES加密后,转换为16进制编码 var encodePwd = function (data,key){ var keyHex = CryptoJS.enc. ...

  9. 【2018 CCPC网络赛 1004】Find Integer(勾股数+费马大定理)

    Problem Description people in USSS love math very much, and there is a famous math problem . give yo ...

  10. 替换html里面的\r\n及解决记事本中的每个段落只有一行的情形

    1. 在用python爬取小说的时候, 发现在内容里每次换行都有\r\n(即回车, 换行)出现. 此时可以采用  s.replace('\\r\\n','') , 其中s为字符串类型. 2. 在爬取完 ...