Last post demonstrate HSV in graphical presentation. By changing of HSV, we can adjust hue, saturation, and brightness of the image. See the video to know the effect on the image.
To adjust the HSV, refer to the method updateHSV(Bitmap src) in the code.
package com.example.androidcolor;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import android.os.AsyncTask;
import android.os.Bundle;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.TextView;
public class MainActivity extends Activity {
ImageView imgSource, imgTarget;
ImageView imgHue, imgSat, imgVal;
TextView textHue, textSat, textVal;
SeekBar barHue, barSat, barVal;
Button buttonProcess, buttonReset;
Bitmap bitmapSource = null;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imgSource = (ImageView)findViewById(R.id.imgsource);
imgTarget = (ImageView)findViewById(R.id.imgtarget);
imgHue = (ImageView)findViewById(R.id.imghue);
imgSat = (ImageView)findViewById(R.id.imgsat);
imgVal = (ImageView)findViewById(R.id.imgval);
textHue = (TextView)findViewById(R.id.huetext);
textSat = (TextView)findViewById(R.id.sattext);
textVal = (TextView)findViewById(R.id.valtext);
barHue = (SeekBar)findViewById(R.id.huebar);
barSat = (SeekBar)findViewById(R.id.satbar);
barVal = (SeekBar)findViewById(R.id.valbar);
buttonProcess = (Button)findViewById(R.id.process);
buttonReset = (Button)findViewById(R.id.reset);
barHue.setOnSeekBarChangeListener(seekBarChangeListener);
barSat.setOnSeekBarChangeListener(seekBarChangeListener);
barVal.setOnSeekBarChangeListener(seekBarChangeListener);
buttonProcess.setOnClickListener(buttonProcessOnClickListener);
buttonReset.setOnClickListener(buttonResetOnClickListener);
//Load bitmap from internet
String onLineImgSource = "http://goo.gl/yxNeG";
URL urlImgSource;
try {
urlImgSource = new URL(onLineImgSource);
new MyNetworkTask(imgSource, imgTarget, imgHue, imgSat, imgVal)
.execute(urlImgSource);
} catch (MalformedURLException e) {
e.printStackTrace();
}
}
OnClickListener buttonProcessOnClickListener
= new OnClickListener(){
@Override
public void onClick(View arg0) {
if(bitmapSource != null){
buttonProcess.setEnabled(false);
GroupBitmap groupBMResult = updateHSV(bitmapSource);
imgTarget.setImageBitmap(groupBMResult.bitmapDest);
imgHue.setImageBitmap(groupBMResult.bitmapHue);
imgSat.setImageBitmap(groupBMResult.bitmapSat);
imgVal.setImageBitmap(groupBMResult.bitmapVal);
buttonProcess.setEnabled(true);
}
}};
OnClickListener buttonResetOnClickListener
= new OnClickListener(){
@Override
public void onClick(View v) {
barHue.setProgress(256);
barSat.setProgress(256);
barVal.setProgress(256);
}
};
OnSeekBarChangeListener seekBarChangeListener
= new OnSeekBarChangeListener(){
@Override
public void onProgressChanged(SeekBar arg0, int arg1, boolean arg2) {
int progressHue = barHue.getProgress() - 256;
int progressSat = barSat.getProgress() - 256;
int progressVal = barVal.getProgress() - 256;
/*
* Hue (0 .. 360)
* Saturation (0...1)
* Value (0...1)
*/
float settingHue = (float)progressHue * 360 / 256;
float settingSat = (float)progressSat / 256;
float settingVal = (float)progressVal / 256;
textHue.setText("Hue: " + String.format("%.02f", settingHue));
textSat.setText("Sat: " + String.format("%.02f", settingSat));
textVal.setText("Val: " + String.format("%.02f", settingVal));
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
// TODO Auto-generated method stub
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
// TODO Auto-generated method stub
}};
private class MyNetworkTask extends AsyncTask<URL, Void, Bitmap>{
ImageView ivSource, ivTarget;
ImageView ivHue, ivSat, ivVal;
public MyNetworkTask(ImageView iSource, ImageView iTarget,
ImageView iHue, ImageView iSat, ImageView iVal){
ivSource = iSource;
ivTarget = iTarget;
ivHue = iHue;
ivSat = iSat;
ivVal = iVal;
}
@Override
protected Bitmap doInBackground(URL... urls) {
Bitmap networkBitmap = null;
URL networkUrl = urls[0]; //Load the first element
try {
networkBitmap = BitmapFactory.decodeStream(
networkUrl.openConnection().getInputStream());
} catch (IOException e) {
e.printStackTrace();
}
//To save space, scale bitmap by 1/2
//return networkBitmap;
Bitmap shrinkedBitmap = Bitmap.createScaledBitmap(
networkBitmap,
networkBitmap.getWidth()/2,
networkBitmap.getHeight()/2,
false);
return shrinkedBitmap;
}
@Override
protected void onPostExecute(Bitmap result) {
bitmapSource = result;
ivSource.setImageBitmap(result);
GroupBitmap groupBMResult = convertColorHSVColor(result);
ivTarget.setImageBitmap(groupBMResult.bitmapDest);
ivHue.setImageBitmap(groupBMResult.bitmapHue);
ivSat.setImageBitmap(groupBMResult.bitmapSat);
ivVal.setImageBitmap(groupBMResult.bitmapVal);
}
}
class GroupBitmap {
Bitmap bitmapHue;
Bitmap bitmapSat;
Bitmap bitmapVal;
Bitmap bitmapDest;
};
//Convert Bitmap from Color to HSV, then HSV to Color
private GroupBitmap convertColorHSVColor(Bitmap src){
GroupBitmap convertedGroupBitmap = new GroupBitmap();
int w = src.getWidth();
int h = src.getHeight();
int[] mapSrcColor = new int[w * h];
int[] mapDestColor= new int[w * h];
int[] mapHue = new int[w * h];
int[] mapSat = new int[w * h];
int[] mapVal = new int[w * h];
float[] pixelHSV = new float[3];
/*
* pixelHSV[0] : Hue (0 .. 360)
* pixelHSV[1] : Saturation (0...1)
* pixelHSV[2] : Value (0...1)
*/
src.getPixels(mapSrcColor, 0, w, 0, 0, w, h);
/*
* getPixels (int[] pixels, int offset, int stride, int x, int y, int width, int height)
* - Returns in pixels[] a copy of the data in the bitmap. Each value is a packed int representing a Color.
*
* pixels: The array to receive the bitmap's colors
* offset: The first index to write into pixels[]
* stride: The number of entries in pixels[] to skip between rows (must be >= bitmap's width). Can be negative.
* x: The x coordinate of the first pixel to read from the bitmap
* y: The y coordinate of the first pixel to read from the bitmap
* width: The number of pixels to read from each row
* height: The number of rows to read
*
*/
int index = 0;
for(int y = 0; y < h; ++y) {
for(int x = 0; x < w; ++x) {
//Convert from Color to HSV
Color.colorToHSV(mapSrcColor[index], pixelHSV);
/*
* Represent Hue, Saturation and Value in separated color
* of R, G, B.
*/
mapHue[index] = Color.rgb((int)(pixelHSV[0] * 255/360), 0, 0);
mapSat[index] = Color.rgb(0, (int)(pixelHSV[1] * 255), 0);
mapVal[index] = Color.rgb(0, 0, (int)(pixelHSV[2] * 255));
//Convert back from HSV to Color
mapDestColor[index] = Color.HSVToColor(pixelHSV);
index++;
}
}
Config destConfig = src.getConfig();
/*
* If the bitmap's internal config is in one of the public formats, return that config,
* otherwise return null.
*/
if (destConfig == null){
destConfig = Config.RGB_565;
}
convertedGroupBitmap.bitmapHue = Bitmap.createBitmap(mapHue, w, h, Config.RGB_565);
convertedGroupBitmap.bitmapSat = Bitmap.createBitmap(mapSat, w, h, Config.RGB_565);
convertedGroupBitmap.bitmapVal = Bitmap.createBitmap(mapVal, w, h, Config.RGB_565);
convertedGroupBitmap.bitmapDest = Bitmap.createBitmap(mapDestColor, w, h, destConfig);
return convertedGroupBitmap;
}
//Update HSV according to SeekBar setting
private GroupBitmap updateHSV(Bitmap src){
int progressHue = barHue.getProgress() - 256;
int progressSat = barSat.getProgress() - 256;
int progressVal = barVal.getProgress() - 256;
float settingHue = (float)progressHue * 360 / 256;
float settingSat = (float)progressSat / 256;
float settingVal = (float)progressVal / 256;
GroupBitmap convertedGroupBitmap = new GroupBitmap();
int w = src.getWidth();
int h = src.getHeight();
int[] mapSrcColor = new int[w * h];
int[] mapDestColor= new int[w * h];
int[] mapHue = new int[w * h];
int[] mapSat = new int[w * h];
int[] mapVal = new int[w * h];
float[] pixelHSV = new float[3];
src.getPixels(mapSrcColor, 0, w, 0, 0, w, h);
int index = 0;
for(int y = 0; y < h; ++y) {
for(int x = 0; x < w; ++x) {
//Convert from Color to HSV
Color.colorToHSV(mapSrcColor[index], pixelHSV);
//Adjust HSV
pixelHSV[0] = pixelHSV[0] + settingHue;
if(pixelHSV[0] < 0){
pixelHSV[0] = 0;
}else if (pixelHSV[0] > 360){
pixelHSV[0] = 360;
}
pixelHSV[1] = pixelHSV[1] + settingSat;
if(pixelHSV[1] < 0){
pixelHSV[1] = 0;
}else if (pixelHSV[1] > 1){
pixelHSV[1] = 1;
}
pixelHSV[2] = pixelHSV[2] + settingVal;
if(pixelHSV[2] < 0){
pixelHSV[2] = 0;
}else if (pixelHSV[2] > 1){
pixelHSV[2] = 1;
}
/*
* Represent Hue, Saturation and Value in separated color
* of R, G, B.
*/
mapHue[index] = Color.rgb((int)(pixelHSV[0] * 255/360), 0, 0);
mapSat[index] = Color.rgb(0, (int)(pixelHSV[1] * 255), 0);
mapVal[index] = Color.rgb(0, 0, (int)(pixelHSV[2] * 255));
//Convert back from HSV to Color
mapDestColor[index] = Color.HSVToColor(pixelHSV);
index++;
}
}
Config destConfig = src.getConfig();
/*
* If the bitmap's internal config is in one of the public formats, return that config,
* otherwise return null.
*/
if (destConfig == null){
destConfig = Config.RGB_565;
}
convertedGroupBitmap.bitmapHue = Bitmap.createBitmap(mapHue, w, h, Config.RGB_565);
convertedGroupBitmap.bitmapSat = Bitmap.createBitmap(mapSat, w, h, Config.RGB_565);
convertedGroupBitmap.bitmapVal = Bitmap.createBitmap(mapVal, w, h, Config.RGB_565);
convertedGroupBitmap.bitmapDest = Bitmap.createBitmap(mapDestColor, w, h, destConfig);
return convertedGroupBitmap;
}
}
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hello_world"
tools:context=".MainActivity" />
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<HorizontalScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Source"/>
<ImageView
android:id="@+id/imgsource"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Result"/>
<ImageView
android:id="@+id/imgtarget"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
</LinearLayout>
</HorizontalScrollView>
<HorizontalScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/huetext"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hue"/>
<SeekBar
android:id="@+id/huebar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:max="511"
android:progress="256"/>
<ImageView
android:id="@+id/imghue"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/sattext"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Saturation"/>
<SeekBar
android:id="@+id/satbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:max="511"
android:progress="256"/>
<ImageView
android:id="@+id/imgsat"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/valtext"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Value"/>
<SeekBar
android:id="@+id/valbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:max="511"
android:progress="256"/>
<ImageView
android:id="@+id/imgval"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
</LinearLayout>
</HorizontalScrollView>
<Button
android:id="@+id/process"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Process"/>
<Button
android:id="@+id/reset"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Reset"/>
</LinearLayout>
</ScrollView>
</LinearLayout>
Remark: Permission of "android.permission.INTERNET" is needed in this exercise to load Bitmap from internet.
Download the files.
hi,
ReplyDeleteThis is a nice blog.
Please help me for creating a capture wheel like this....
http://cdn.androidpolice.com/wp-content/uploads/2012/09/nexusae0_ScreenShot20120906at12.41.29.png
it is a main feature of https://play.google.com/store/apps/details?id=com.threebanana.notes
please help me
Thank you! Works perfectly! Wish I could just find a way to make it faster
ReplyDelete