# 文件与Bitmap间的方法
1. 从文件载入Bitmap
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
/**
* @brief 从文件载入Bitmap
* @param path 图片路径
* @param opts 选项
* @return Bitmap
*/
public
Bitmap loadFromFile(String path, Options opts) {
try
{
File f =
new
File(path);
if
(!f.exists() || f.isDirectory()) {
return
null
;
}
Bitmap bm = BitmapFactory.decodeFile(path, opts);
return
bm;
}
catch
(Exception e) {
return
null
;
}
}
/**
* @brief 从文件载入Bitmap
* @param path 图片路径
* @return Bitmap
*/
public
Bitmap loadFromFile(String path) {
return
loadFromFile(path,
null
);
}
|
2. 载入取样的Bitmap
原宽度和高度的各1/sampleSize大小。
显示图片文件时一般都是取样图,否则很容易outofmemory。
1
2
3
4
5
6
7
8
9
|
/**
* @brief 从文件载入采样后的Bitmap
* @see android.graphics.BitmapFactory.Options#inSampleSize
*/
public
Bitmap loadSampleSize(String path,
int
sampleSize) {
Options opts =
new
Options();
opts.inSampleSize = sampleSize;
return
loadFromFile(path, opts);
}
|
3. 载入Bitmap边框
其返回Bitmap为null,但Options.outxxx会被填充值。包括outHeight, outWidth, outMimeType。
只读取其高宽信息的话,就不需要读取全部Bitmap了。可结合上个方法,获取倍数缩小的样图。
1
2
3
4
5
6
7
8
9
10
|
/**
* @brief 从文件载入只获边框的Bitmap
* @see android.graphics.BitmapFactory.Options#inJustDecodeBounds
*/
public
Options loadJustDecodeBounds(String path) {
Options opts =
new
Options();
opts.inJustDecodeBounds =
true
;
loadFromFile(path, opts);
return
opts;
}
|
4. 保存Bitmap至文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
/**
* @brief 保存Bitmap至文件
* @param bm Bitmap
* @param path 图片路径
* @return 成功与否
*/
public
boolean
compressBitmap(Bitmap bm, String path) {
FileOutputStream out =
null
;
try
{
out =
new
FileOutputStream(path);
bm.compress(Bitmap.CompressFormat.JPEG,
100
, out);
out.flush();
}
catch
(Exception e) {
return
false
;
}
finally
{
try
{
if
(out !=
null
) {
out.close();
}
}
catch
(IOException e) {
}
}
return
true
;
}
|
5. 读取图片方向信息
Bitmap图片的方法==!!!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
/**
* @brief 读取图片方向信息
* @param path 图片路径
* @return 角度
*/
public
int
readPhotoDegree(String path) {
int
degree =
0
;
try
{
ExifInterface exifInterface =
new
ExifInterface(path);
int
orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION,
ExifInterface.ORIENTATION_NORMAL);
switch
(orientation) {
case
ExifInterface.ORIENTATION_ROTATE_90:
degree =
90
;
break
;
case
ExifInterface.ORIENTATION_ROTATE_180:
degree =
180
;
break
;
case
ExifInterface.ORIENTATION_ROTATE_270:
degree =
270
;
break
;
default
:
degree =
0
;
}
}
catch
(IOException e) {
e.printStackTrace();
}
return
degree;
}
|
# 处理Bitmap的方法
1. 生成缩略图
1
2
3
4
|
public
Bitmap extractThumbnail(Bitmap src,
int
width,
int
height) {
return
ThumbnailUtils.extractThumbnail(src, width, height,
ThumbnailUtils.OPTIONS_RECYCLE_INPUT);
}
|
2. 缩放
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
/**
* @brief 缩放Bitmap,自动回收原Bitmap
* @see ImageUtil#scaleBitmap(Bitmap, int, int, boolean)
*/
public
Bitmap scaleBitmap(Bitmap src,
int
dstWidth,
int
dstHeight) {
return
scaleBitmap(src, dstWidth, dstHeight,
true
);
}
/**
* @brief 缩放Bitmap
* @param src 源Bitmap
* @param dstWidth 目标宽度
* @param dstHeight 目标高度
* @param isRecycle 是否回收原图像
* @return Bitmap
*/
public
Bitmap scaleBitmap(Bitmap src,
int
dstWidth,
int
dstHeight,
boolean
isRecycle) {
if
(src.getWidth() == dstWidth && src.getHeight() == dstHeight) {
return
src;
}
Bitmap dst = Bitmap.createScaledBitmap(src, dstWidth, dstHeight,
false
);
if
(isRecycle && dst != src) {
src.recycle();
}
return
dst;
}
|
3. 裁剪
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
/**
* @brief 裁剪Bitmap,自动回收原Bitmap
* @see ImageUtil#cropBitmap(Bitmap, int, int, int, int, boolean)
*/
public
Bitmap cropBitmap(Bitmap src,
int
x,
int
y,
int
width,
int
height) {
return
cropBitmap(src, x, y, width, height,
true
);
}
/**
* @brief 裁剪Bitmap
* @param src 源Bitmap
* @param x 开始x坐标
* @param y 开始y坐标
* @param width 截取宽度
* @param height 截取高度
* @param isRecycle 是否回收原图像
* @return Bitmap
*/
public
Bitmap cropBitmap(Bitmap src,
int
x,
int
y,
int
width,
int
height,
boolean
isRecycle) {
if
(x ==
0
&& y ==
0
&& width == src.getWidth() && height == src.getHeight()) {
return
src;
}
Bitmap dst = Bitmap.createBitmap(src, x, y, width, height);
if
(isRecycle && dst != src) {
src.recycle();
}
return
dst;
}
|
4. 旋转
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
/**
* @brief 旋转Bitmap,自动回收原Bitmap
* @see ImageUtil#rotateBitmap(Bitmap, int, boolean)
*/
public
Bitmap rotateBitmap(Bitmap src,
int
degree) {
return
rotateBitmap(src, degree,
true
);
}
/**
* @brief 旋转Bitmap,顺时针
* @param src 源Bitmap
* @param degree 旋转角度
* @param isRecycle 是否回收原图像
* @return Bitmap
*/
public
Bitmap rotateBitmap(Bitmap src,
int
degree,
boolean
isRecycle) {
if
(degree %
360
==
0
) {
return
src;
}
int
w = src.getWidth();
int
h = src.getHeight();
Matrix matrix =
new
Matrix();
matrix.postRotate(degree);
Bitmap dst = Bitmap.createBitmap(src,
0
,
0
, w, h, matrix,
true
);
if
(isRecycle && dst != src) {
src.recycle();
}
return
dst;
}
|
# OpenCV处理Bitmap的方法
除了导入OpenCV的jar包,我们只需要libopencv_java.so,就可以下做进行操作了。
1. 常规性的init
1
2
3
4
5
|
static
{
if
(!OpenCVLoader.initDebug()) {
Log.e(TAG,
"OpenCVLoader initDebug failed."
);
}
}
|
2. 常规性处理流程
bmp -> mat -> 接口处理 -> mat_new -> bmp_new。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
private
interface
IHandler {
Mat proc(Mat mat_bmp);
}
private
Bitmap handle(Bitmap src, IHandler handler) {
Mat mat_bmp =
new
Mat(src.getHeight(), src.getWidth(), CvType.CV_8UC4);
Utils.bitmapToMat(src, mat_bmp,
false
);
// Bitmap->Mat
Mat mat_new = handler.proc(mat_bmp);
// handle mat
Bitmap bmp_new = Bitmap.createBitmap(mat_new.cols(), mat_new.rows(),
Bitmap.Config.ARGB_8888);
Utils.matToBitmap(mat_new, bmp_new,
false
);
// Mat->Bitmap
src.recycle();
return
bmp_new;
}
|
3. 缩放
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
/**
* @brief 缩放Bitmap
* @param src 源Bitmap
* @param dstWidth 目标宽度
* @param dstHeight 目标高度
* @return Bitmap
*/
public
Bitmap scaleBitmap2(Bitmap src,
final
int
dstWidth,
final
int
dstHeight) {
if
(src.getWidth() == dstWidth && src.getHeight() == dstHeight) {
return
src;
}
return
handle(src,
new
IHandler() {
@Override
public
Mat proc(Mat mat_bmp) {
Mat mat_new =
new
Mat();
Imgproc.resize(mat_bmp, mat_new,
new
Size(dstWidth, dstHeight));
return
mat_new;
}
});
}
|
4. 裁剪
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
/**
* @brief 裁剪Bitmap
* @param src 源Bitmap
* @param x 开始x坐标
* @param y 开始y坐标
* @param width 截取宽度
* @param height 截取高度
* @return Bitmap
*/
public
Bitmap cropBitmap2(Bitmap src,
final
int
x,
final
int
y,
final
int
width,
final
int
height) {
if
(x ==
0
&& y ==
0
&& width == src.getWidth() && height == src.getHeight()) {
return
src;
}
if
(x + width > src.getWidth()) {
throw
new
IllegalArgumentException(
"x + width must be <= bitmap.width()"
);
}
if
(y + height > src.getHeight()) {
throw
new
IllegalArgumentException(
"y + height must be <= bitmap.height()"
);
}
return
handle(src,
new
IHandler() {
@Override
public
Mat proc(Mat mat_bmp) {
Rect roi =
new
Rect(x, y, width, height);
Mat mat_new =
new
Mat(mat_bmp, roi);
return
mat_new;
}
});
}
|
5. 旋转
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
|
/**
* @brief 旋转Bitmap,逆时针
* @param src 源Bitmap
* @param degree 旋转角度
* @return Bitmap
* @see <a href="http://stackoverflow.com/questions/12852578/image-rotation-with-opencv-in-android-cuts-off-the-edges-of-an-image">More</a>
*/
public
Bitmap rotateBitmap2(Bitmap src,
final
int
degree) {
if
(degree %
360
==
0
) {
return
src;
}
return
handle(src,
new
IHandler() {
@Override
public
Mat proc(Mat mat_bmp) {
// 计算旋转后图像的宽高
double
radians = Math.toRadians(degree);
double
sin = Math.abs(Math.sin(radians));
double
cos = Math.abs(Math.cos(radians));
int
width = mat_bmp.width();
int
height = mat_bmp.height();
int
newWidth = (
int
) (width * cos + height * sin);
int
newHeight = (
int
) (width * sin + height * cos);
// 能把原图像和旋转后图像同时放入的外框
int
frameWidth = Math.max(width, newWidth);
int
frameHeight = Math.max(height, newHeight);
Size frameSize =
new
Size(frameWidth, frameHeight);
Mat mat_frame =
new
Mat(frameSize, mat_bmp.type());
// 将原图像copy进外框
int
offsetX = (frameWidth - width) /
2
;
int
offsetY = (frameHeight - height) /
2
;
Mat mat_frame_submat = mat_frame.submat(offsetY, offsetY + height, offsetX, offsetX
+ width);
mat_bmp.copyTo(mat_frame_submat);
// 旋转外框
Point center =
new
Point(frameWidth /
2
, frameHeight /
2
);
Mat mat_rot = Imgproc.getRotationMatrix2D(center, degree,
1.0
);
Mat mat_res =
new
Mat();
// result
Imgproc.warpAffine(mat_frame, mat_res, mat_rot, frameSize, Imgproc.INTER_LINEAR,
Imgproc.BORDER_CONSTANT, Scalar.all(
0
));
// 从旋转后的外框获取新图像
offsetX = (frameWidth - newWidth) /
2
;
offsetY = (frameHeight - newHeight) /
2
;
Mat mat_res_submat = mat_res.submat(offsetY, offsetY + newHeight, offsetX, offsetX
+ newWidth);
return
mat_res_submat;
}
});
}
|
6. Bitmap效果器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
|
/**
* @brief Bitmap效果器
* @author join
*/
public
static
class
Effector {
private
Config config;
private
Mat mat;
private
boolean
isGray;
private
Mat mSepiaKernel;
/**
* @brief 构造函数
* @param bmp 源Bitmap
* @param config 'ARGB_8888' or 'RGB_565'
*/
public
Effector(Bitmap bmp, Config config) {
Mat mat_bmp =
new
Mat(bmp.getHeight(), bmp.getWidth(), CvType.CV_8UC4);
Utils.bitmapToMat(bmp, mat_bmp,
false
);
// Bitmap->Mat
this
.mat = mat_bmp;
this
.config = config;
this
.isGray =
false
;
}
/**
* @brief 构造函数,config默认为RGB_565
* @see #BitmapUtil(Bitmap, Config)
*/
public
Effector(Bitmap bmp) {
this
(bmp, Bitmap.Config.RGB_565);
}
/**
* @brief 创建Bitmap
* @return Bitmap
*/
public
Bitmap create() {
Mat mat_new =
this
.mat;
if
(isGray) {
Mat mat_gray =
new
Mat(mat_new.rows(), mat_new.cols(), CvType.CV_8UC4);
Imgproc.cvtColor(mat_new, mat_gray, Imgproc.COLOR_GRAY2BGRA,
4
);
// 转为灰度4通道Mat
mat_new = mat_gray;
}
Bitmap bmp_new = Bitmap.createBitmap(mat_new.cols(), mat_new.rows(),
this
.config);
Utils.matToBitmap(mat_new, bmp_new,
false
);
// Mat->Bitmap
return
bmp_new;
}
/**
* @brief 灰度化Bitmap
*/
public
Effector gray() {
Mat mat_bmp =
this
.mat;
Mat mat_gray =
new
Mat();
Imgproc.cvtColor(mat_bmp, mat_gray, Imgproc.COLOR_BGRA2GRAY,
1
);
// 转为灰度单通道Mat
this
.mat = mat_gray;
this
.isGray =
true
;
return
this
;
}
/**
* @brief Bitmap二值化
* @pre 需先灰度化{@link #gray()}
* @param thresh 阈值。type为THRESH_OTSU时无用,其自适应区域阈值。
* @param maxval 最大值。type为THRESH_BINARY或THRESH_BINARY_INV时才使用。
* @param type 运算类型
* @see Imgproc#threshold(Mat, Mat, double, double, int)
* @see THRESH_OTSU: {@link Imgproc#adaptiveThreshold(Mat, Mat, double, int, int, int, double)}
*/
public
Effector threshold(
double
thresh,
double
maxval,
int
type) {
if
(!isGray) {
// throw new IllegalArgumentException("must call gray() before this.");
gray();
}
Mat mat_gray =
this
.mat;
Imgproc.threshold(mat_gray, mat_gray, thresh, maxval, type);
return
this
;
}
/**
* @brief Bitmap二值化
* @details thresh: 127; maxval: 255; type: THRESH_OTSU;
* @see #threshold(double, double, int)
*/
public
Effector threshold() {
return
threshold(
127
,
255
, Imgproc.THRESH_OTSU);
}
/**
* @brief Canny算子边缘检测
* @param threshold1 控制边缘连接的下限阈值
* @param threshold2 控制强边缘的初始分割的上阈限值
* 如果一个像素的梯度大于上阈限值,则被认为是边缘像素,如果小于下限阈值,则被抛弃。
* 如果该点的梯度在两者之间则当这个点与高于上阈限值的像素点连接时我们才保留,否则抛弃。
*/
public
Effector canny(
final
double
threshold1,
final
double
threshold2) {
Mat mat =
this
.mat;
Imgproc.Canny(mat, mat, threshold1, threshold2,
3
,
false
);
// Canny边缘检测
return
this
;
}
/**
* @brief Canny边缘检测,返回为RGB_565
* @details threshold1: 80; threshold2: 90;
* @see #canny(Bitmap, Config)
*/
public
Effector canny() {
return
canny(
80
,
90
);
}
/**
* @brief Sobel处理
*/
public
Effector sobel() {
Mat mat =
this
.mat;
Imgproc.Sobel(mat, mat, CvType.CV_8U,
1
,
1
);
// 一阶差分
Core.convertScaleAbs(mat, mat,
10
,
0
);
// 线性变换
return
this
;
}
/**
* @brief 棕褐色
*/
public
Effector sepia() {
Mat mat =
this
.mat;
Core.transform(mat, mat, getSepiaKernel());
return
this
;
}
private
Mat getSepiaKernel() {
if
(mSepiaKernel !=
null
) {
return
mSepiaKernel;
}
mSepiaKernel =
new
Mat(
4
,
4
, CvType.CV_32F);
mSepiaKernel.put(
0
,
0
,
/* R */0.189f, 0.769f, 0.393f, 0f);
mSepiaKernel.put(1, 0, /* G */0.168f, 0.686f, 0.349f, 0f);
mSepiaKernel.put(2, 0, /* B */0.131f, 0.534f, 0.272f, 0f);
mSepiaKernel.put(3, 0, /* A */
0
.000f,
0
.000f,
0
.000f, 1f);
return
mSepiaKernel;
}
}
|
# Over
常用的一些方法就这样了^^。
本文转自winorlose2000 51CTO博客,原文链接:http://blog.51cto.com/vaero/1202128,如需转载请自行联系原作者