http://www.it165.net/pro/html/201305/5795.html
# 文件与Bitmap间的方法
1. 从文件载入Bitmap
01.
/**
02.
* @brief 从文件载入Bitmap
03.
* @param path 图片路径
04.
* @param opts 选项
05.
* @return Bitmap
06.
*/
07.
public
Bitmap loadFromFile(String path, Options opts) {
08.
try
{
09.
File f =
new
File(path);
10.
if
(!f.exists() || f.isDirectory()) {
11.
return
null;
12.
}
13.
Bitmap bm = BitmapFactory.decodeFile(path, opts);
14.
return
bm;
15.
}
catch
(Exception e) {
16.
return
null;
17.
}
18.
}
19.
/**
20.
* @brief 从文件载入Bitmap
21.
* @param path 图片路径
22.
* @return Bitmap
23.
*/
24.
public
Bitmap loadFromFile(String path) {
25.
return
loadFromFile(path, null);
26.
}
2. 载入取样的Bitmap
原宽度和高度的各1/sampleSize大小。
显示图片文件时一般都是取样图,否则很容易outofmemory。
01.
/**
02.
* @brief 从文件载入采样后的Bitmap
03.
* @see android.graphics.BitmapFactory.Options#inSampleSize
04.
*/
05.
public
Bitmap loadSampleSize(String path,
int
sampleSize) {
06.
Options opts =
new
Options();
07.
opts.inSampleSize = sampleSize;
08.
return
loadFromFile(path, opts);
09.
}
3. 载入Bitmap边框
其返回Bitmap为null,但Options.outxxx会被填充值。包括outHeight, outWidth, outMimeType。
只读取其高宽信息的话,就不需要读取全部Bitmap了。可结合上个方法,获取倍数缩小的样图。
01.
/**
02.
* @brief 从文件载入只获边框的Bitmap www.it165.net
03.
* @see android.graphics.BitmapFactory.Options#inJustDecodeBounds
04.
*/
05.
public
Options loadJustDecodeBounds(String path) {
06.
Options opts =
new
Options();
07.
opts.inJustDecodeBounds =
true
;
08.
loadFromFile(path, opts);
09.
return
opts;
10.
}
4. 保存Bitmap至文件
01.
/**
02.
* @brief 保存Bitmap至文件
03.
* @param bm Bitmap
04.
* @param path 图片路径
05.
* @return 成功与否
06.
*/
07.
public
boolean compressBitmap(Bitmap bm, String path) {
08.
FileOutputStream out = null;
09.
try
{
10.
out =
new
FileOutputStream(path);
11.
bm.compress(Bitmap.CompressFormat.JPEG, 100, out);
12.
out.flush();
13.
}
catch
(Exception e) {
14.
return
false
;
15.
} finally {
16.
try
{
17.
if
(out != null) {
18.
out.close();
19.
}
20.
}
catch
(IOException e) {
21.
}
22.
}
23.
return
true
;
24.
}
5. 读取图片方向信息
Bitmap图片的方法==!!!
01.
/**
02.
* @brief 读取图片方向信息
03.
* @param path 图片路径
04.
* @return 角度
05.
*/
06.
public
int
readPhotoDegree(String path) {
07.
int
degree = 0;
08.
try
{
09.
ExifInterface exifInterface =
new
ExifInterface(path);
10.
int
orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION,
11.
ExifInterface.ORIENTATION_NORMAL);
12.
switch
(orientation) {
13.
case
ExifInterface.ORIENTATION_ROTATE_90:
14.
degree = 90;
15.
break
;
16.
case
ExifInterface.ORIENTATION_ROTATE_180:
17.
degree = 180;
18.
break
;
19.
case
ExifInterface.ORIENTATION_ROTATE_270:
20.
degree = 270;
21.
break
;
22.
default
:
23.
degree = 0;
24.
}
25.
}
catch
(IOException e) {
26.
e.printStackTrace();
27.
}
28.
return
degree;
29.
}
# 处理Bitmap的方法
1. 生成缩略图
1.
public
Bitmap extractThumbnail(Bitmap src,
int
width,
int
height) {
2.
return
ThumbnailUtils.extractThumbnail(src, width, height,
3.
ThumbnailUtils.OPTIONS_RECYCLE_INPUT);
4.
}
2. 缩放
01.
/**
02.
* @brief 缩放Bitmap,自动回收原Bitmap
03.
* @see ImageUtil#scaleBitmap(Bitmap, int, int, boolean)
04.
*/
05.
public
Bitmap scaleBitmap(Bitmap src,
int
dstWidth,
int
dstHeight) {
06.
return
scaleBitmap(src, dstWidth, dstHeight,
true
);
07.
}
08.
/**
09.
* @brief 缩放Bitmap
10.
* @param src 源Bitmap
11.
* @param dstWidth 目标宽度
12.
* @param dstHeight 目标高度
13.
* @param isRecycle 是否回收原图像
14.
* @return Bitmap
15.
*/
16.
public
Bitmap scaleBitmap(Bitmap src,
int
dstWidth,
int
dstHeight, boolean isRecycle) {
17.
if
(src.getWidth() == dstWidth && src.getHeight() == dstHeight) {
18.
return
src;
19.
}
20.
Bitmap dst = Bitmap.createScaledBitmap(src, dstWidth, dstHeight,
false
);
21.
if
(isRecycle && dst != src) {
22.
src.recycle();
23.
}
24.
return
dst;
25.
}
3. 裁剪
01.
/**
02.
* @brief 裁剪Bitmap,自动回收原Bitmap
03.
* @see ImageUtil#cropBitmap(Bitmap, int, int, int, int, boolean)
04.
*/
05.
public
Bitmap cropBitmap(Bitmap src,
int
x,
int
y,
int
width,
int
height) {
06.
return
cropBitmap(src, x, y, width, height,
true
);
07.
}
08.
/**
09.
* @brief 裁剪Bitmap
10.
* @param src 源Bitmap
11.
* @param x 开始x坐标
12.
* @param y 开始y坐标
13.
* @param width 截取宽度
14.
* @param height 截取高度
15.
* @param isRecycle 是否回收原图像
16.
* @return Bitmap
17.
*/
18.
public
Bitmap cropBitmap(Bitmap src,
int
x,
int
y,
int
width,
int
height, boolean isRecycle) {
19.
if
(x == 0 && y == 0 && width == src.getWidth() && height == src.getHeight()) {
20.
return
src;
21.
}
22.
Bitmap dst = Bitmap.createBitmap(src, x, y, width, height);
23.
if
(isRecycle && dst != src) {
24.
src.recycle();
25.
}
26.
return
dst;
27.
}
4. 旋转
01.
/**
02.
* @brief 旋转Bitmap,自动回收原Bitmap
03.
* @see ImageUtil#rotateBitmap(Bitmap, int, boolean)
04.
*/
05.
public
Bitmap rotateBitmap(Bitmap src,
int
degree) {
06.
return
rotateBitmap(src, degree,
true
);
07.
}
08.
/**
09.
* @brief 旋转Bitmap,顺时针
10.
* @param src 源Bitmap
11.
* @param degree 旋转角度
12.
* @param isRecycle 是否回收原图像
13.
* @return Bitmap
14.
*/
15.
public
Bitmap rotateBitmap(Bitmap src,
int
degree, boolean isRecycle) {
16.
if
(degree % 360 == 0) {
17.
return
src;
18.
}
19.
int
w = src.getWidth();
20.
int
h = src.getHeight();
21.
Matrix matrix =
new
Matrix();
22.
matrix.postRotate(degree);
23.
Bitmap dst = Bitmap.createBitmap(src, 0, 0, w, h, matrix,
true
);
24.
if
(isRecycle && dst != src) {
25.
src.recycle();
26.
}
27.
return
dst;
28.
}
# OpenCV处理Bitmap的方法
除了导入OpenCV的jar包,我们只需要libopencv_java.so,就可以下做进行操作了。
1. 常规性的init
1.
static
{
2.
if
(!OpenCVLoader.initDebug()) {
3.
Log.e(TAG,
"OpenCVLoader initDebug failed."
);
4.
}
5.
}
2. 常规性处理流程
bmp -> mat -> 接口处理 -> mat_new -> bmp_new。
01.
private
interface IHandler {
02.
Mat proc(Mat mat_bmp);
03.
}
04.
private
Bitmap handle(Bitmap src, IHandler handler) {
05.
Mat mat_bmp =
new
Mat(src.getHeight(), src.getWidth(), CvType.CV_8UC4);
06.
Utils.bitmapToMat(src, mat_bmp,
false
);
// Bitmap->Mat
07.
Mat mat_new = handler.proc(mat_bmp);
// handle mat
08.
Bitmap bmp_new = Bitmap.createBitmap(mat_new.cols(), mat_new.rows(),
09.
Bitmap.Config.ARGB_8888);
10.
Utils.matToBitmap(mat_new, bmp_new,
false
);
// Mat->Bitmap
11.
src.recycle();
12.
return
bmp_new;
13.
}
3. 缩放
01.
/**
02.
* @brief 缩放Bitmap
03.
* @param src 源Bitmap
04.
* @param dstWidth 目标宽度
05.
* @param dstHeight 目标高度
06.
* @return Bitmap
07.
*/
08.
public
Bitmap scaleBitmap2(Bitmap src, final
int
dstWidth, final
int
dstHeight) {
09.
if
(src.getWidth() == dstWidth && src.getHeight() == dstHeight) {
10.
return
src;
11.
}
12.
return
handle(src,
new
IHandler() {
13.
@Override
14.
public
Mat proc(Mat mat_bmp) {
15.
Mat mat_new =
new
Mat();
16.
Imgproc.resize(mat_bmp, mat_new,
new
Size(dstWidth, dstHeight));
17.
return
mat_new;
18.
}
19.
});
20.
}
4. 裁剪
01.
/**
02.
* @brief 裁剪Bitmap
03.
* @param src 源Bitmap
04.
* @param x 开始x坐标
05.
* @param y 开始y坐标
06.
* @param width 截取宽度
07.
* @param height 截取高度
08.
* @return Bitmap
09.
*/
10.
public
Bitmap cropBitmap2(Bitmap src, final
int
x, final
int
y, final
int
width,
11.
final
int
height) {
12.
if
(x == 0 && y == 0 && width == src.getWidth() && height == src.getHeight()) {
13.
return
src;
14.
}
15.
if
(x + width > src.getWidth()) {
16.
throw
new
IllegalArgumentException(
"x + width must be <= bitmap.width()"
);
17.
}
18.
if
(y + height > src.getHeight()) {
19.
throw
new
IllegalArgumentException(
"y + height must be <= bitmap.height()"
);
20.
}
21.
return
handle(src,
new
IHandler() {
22.
@Override
23.
public
Mat proc(Mat mat_bmp) {
24.
Rect roi =
new
Rect(x, y, width, height);
25.
Mat mat_new =
new
Mat(mat_bmp, roi);
26.
return
mat_new;
27.
}
28.
});
29.
}
5. 旋转
01.
/**
02.
* @brief 旋转Bitmap,逆时针
03.
* @param src 源Bitmap
04.
* @param degree 旋转角度
05.
* @return Bitmap
06.
* @see <a href="http://stackoverflow.com/questions/12852578/image-rotation-with-opencv-in-android-cuts-off-the-edges-of-an-image">More</a>
07.
*/
08.
public
Bitmap rotateBitmap2(Bitmap src, final
int
degree) {
09.
if
(degree % 360 == 0) {
10.
return
src;
11.
}
12.
return
handle(src,
new
IHandler() {
13.
@Override
14.
public
Mat proc(Mat mat_bmp) {
15.
// 计算旋转后图像的宽高
16.
double
radians = Math.toRadians(degree);
17.
double
sin
= Math.
abs
(Math.
sin
(radians));
18.
double
cos
= Math.
abs
(Math.
cos
(radians));
19.
int
width = mat_bmp.width();
20.
int
height = mat_bmp.height();
21.
int
newWidth = (
int
) (width *
cos
+ height *
sin
);
22.
int
newHeight = (
int
) (width *
sin
+ height *
cos
);
23.
// 能把原图像和旋转后图像同时放入的外框
24.
int
frameWidth = Math.max(width, newWidth);
25.
int
frameHeight = Math.max(height, newHeight);
26.
Size frameSize =
new
Size(frameWidth, frameHeight);
27.
Mat mat_frame =
new
Mat(frameSize, mat_bmp.type());
28.
// 将原图像copy进外框
29.
int
offsetX = (frameWidth - width) / 2;
30.
int
offsetY = (frameHeight - height) / 2;
31.
Mat mat_frame_submat = mat_frame.submat(offsetY, offsetY + height, offsetX, offsetX
32.
+ width);
33.
mat_bmp.copyTo(mat_frame_submat);
34.
// 旋转外框
35.
Point center =
new
Point(frameWidth / 2, frameHeight / 2);
36.
Mat mat_rot = Imgproc.getRotationMatrix2D(center, degree, 1.0);
37.
Mat mat_res =
new
Mat();
// result
38.
Imgproc.warpAffine(mat_frame, mat_res, mat_rot, frameSize, Imgproc.INTER_LINEAR,
39.
Imgproc.BORDER_CONSTANT, Scalar.all(0));
40.
// 从旋转后的外框获取新图像
41.
offsetX = (frameWidth - newWidth) / 2;
42.
offsetY = (frameHeight - newHeight) / 2;
43.
Mat mat_res_submat = mat_res.submat(offsetY, offsetY + newHeight, offsetX, offsetX
44.
+ newWidth);
45.
return
mat_res_submat;
46.
}
47.
});
48.
}
6. Bitmap效果器
001.
/**
002.
* @brief Bitmap效果器
003.
* @author join
004.
*/
005.
public
static
class
Effector {
006.
private
Config config;
007.
private
Mat mat;
008.
private
boolean isGray;
009.
private
Mat mSepiaKernel;
010.
/**
011.
* @brief 构造函数
012.
* @param bmp 源Bitmap
013.
* @param config 'ARGB_8888' or 'RGB_565'
014.
*/
015.
public
Effector(Bitmap bmp, Config config) {
016.
Mat mat_bmp =
new
Mat(bmp.getHeight(), bmp.getWidth(), CvType.CV_8UC4);
017.
Utils.bitmapToMat(bmp, mat_bmp,
false
);
// Bitmap->Mat
018.
this
.mat = mat_bmp;
019.
this
.config = config;
020.
this
.isGray =
false
;
021.
}
022.
/**
023.
* @brief 构造函数,config默认为RGB_565
024.
* @see #BitmapUtil(Bitmap, Config)
025.
*/
026.
public
Effector(Bitmap bmp) {
027.
this
(bmp, Bitmap.Config.RGB_565);
028.
}
029.
/**
030.
* @brief 创建Bitmap
031.
* @return Bitmap
032.
*/
033.
public
Bitmap create() {
034.
Mat mat_new =
this
.mat;
035.
if
(isGray) {
036.
Mat mat_gray =
new
Mat(mat_new.rows(), mat_new.cols(), CvType.CV_8UC4);
037.
Imgproc.cvtColor(mat_new, mat_gray, Imgproc.COLOR_GRAY2BGRA, 4);
// 转为灰度4通道Mat
038.
mat_new = mat_gray;
039.
}
040.
Bitmap bmp_new = Bitmap.createBitmap(mat_new.cols(), mat_new.rows(),
this
.config);
041.
Utils.matToBitmap(mat_new, bmp_new,
false
);
// Mat->Bitmap
042.
return
bmp_new;
043.
}
044.
/**
045.
* @brief 灰度化Bitmap
046.
*/
047.
public
Effector gray() {
048.
Mat mat_bmp =
this
.mat;
049.
Mat mat_gray =
new
Mat();
050.
Imgproc.cvtColor(mat_bmp, mat_gray, Imgproc.COLOR_BGRA2GRAY, 1);
// 转为灰度单通道Mat
051.
this
.mat = mat_gray;
052.
this
.isGray =
true
;
053.
return
this
;
054.
}
055.
/**
056.
* @brief Bitmap二值化
057.
* @pre 需先灰度化{@link #gray()}
058.
* @param thresh 阈值。type为THRESH_OTSU时无用,其自适应区域阈值。
059.
* @param maxval 最大值。type为THRESH_BINARY或THRESH_BINARY_INV时才使用。
060.
* @param type 运算类型
061.
* @see Imgproc#threshold(Mat, Mat, double, double, int)
062.
* @see THRESH_OTSU: {@link Imgproc#adaptiveThreshold(Mat, Mat, double, int, int, int, double)}
063.
*/
064.
public
Effector threshold(
double
thresh,
double
maxval,
int
type) {
065.
if
(!isGray) {
066.
// throw new IllegalArgumentException("must call gray() before this.");
067.
gray();
068.
}
069.
Mat mat_gray =
this
.mat;
070.
Imgproc.threshold(mat_gray, mat_gray, thresh, maxval, type);
071.
return
this
;
072.
}
073.
/**
074.
* @brief Bitmap二值化
075.
* @details thresh: 127; maxval: 255; type: THRESH_OTSU;
076.
* @see #threshold(double, double, int)
077.
*/
078.
public
Effector threshold() {
079.
return
threshold(127, 255, Imgproc.THRESH_OTSU);
080.
}
081.
/**
082.
* @brief Canny算子边缘检测
083.
* @param threshold1 控制边缘连接的下限阈值
084.
* @param threshold2 控制强边缘的初始分割的上阈限值
085.
* 如果一个像素的梯度大于上阈限值,则被认为是边缘像素,如果小于下限阈值,则被抛弃。
086.
* 如果该点的梯度在两者之间则当这个点与高于上阈限值的像素点连接时我们才保留,否则抛弃。
087.
*/
088.
public
Effector canny(final
double
threshold1, final
double
threshold2) {
089.
Mat mat =
this
.mat;
090.
Imgproc.Canny(mat, mat, threshold1, threshold2, 3,
false
);
// Canny边缘检测
091.
return
this
;
092.
}
093.
/**
094.
* @brief Canny边缘检测,返回为RGB_565
095.
* @details threshold1: 80; threshold2: 90;
096.
* @see #canny(Bitmap, Config)
097.
*/
098.
public
Effector canny() {
099.
return
canny(80, 90);
100.
}
101.
/**
102.
* @brief Sobel处理
103.
*/
104.
public
Effector sobel() {
105.
Mat mat =
this
.mat;
106.
Imgproc.Sobel(mat, mat, CvType.CV_8U, 1, 1);
// 一阶差分
107.
Core.convertScaleAbs(mat, mat, 10, 0);
// 线性变换
108.
return
this
;
109.
}
110.
/**
111.
* @brief 棕褐色
112.
*/
113.
public
Effector sepia() {
114.
Mat mat =
this
.mat;
115.
Core.transform(mat, mat, getSepiaKernel());
116.
return
this
;
117.
}
118.
private
Mat getSepiaKernel() {
119.
if
(mSepiaKernel != null) {
120.
return
mSepiaKernel;
121.
}
122.
mSepiaKernel =
new
Mat(4, 4, CvType.CV_32F);
123.
mSepiaKernel.put(0, 0,
/* R */
0.189f, 0.769f, 0.393f, 0f);
124.
mSepiaKernel.put(1, 0,
/* G */
0.168f, 0.686f, 0.349f, 0f);
125.
mSepiaKernel.put(2, 0,
/* B */
0.131f, 0.534f, 0.272f, 0f);
126.
mSepiaKernel.put(3, 0,
/* A */
0.000f, 0.000f, 0.000f, 1f);
127.
return
mSepiaKernel;
128.
}
129.
}
# Over
常用的一些方法就这样了^^。