I need to convert image to a hexadecimal value. When I try to get a color code of specific pixel on uiimage I got the wrong value. In Java I'm using bitmap.getpixel() method to reach pixel color and I'm getting correct value which is 255 but on swift same image pixel returning 0. Do you have any comment on this? or Is there a convenient way to convert image to a hexadecimal?
Java Code Reference
private func createPrintableObject (img:UIImage) -> String{
let sb = StringBuilder()
let height:Int = Int(img.size.height)
let width:Int = Int(img.size.width)
var index = 0
var rgb:Int,red:Int,green:Int,blue:Int
var rgbColor:UIColor
var auxBinaryChar: [Character] = ["0","0","0","0","0","0","0","0"]
widthBytes = width / 8
if(width % 8 > 0){
widthBytes = ((Int)(width/8))+1
}else{
widthBytes = width / 8
}
self.total = widthBytes * height
let pixelData = img.cgImage!.dataProvider!.data
let data: UnsafePointer<UInt8> = CFDataGetBytePtr(pixelData)
for h in 0...height-1 {
for w in 0...width-1 {
let value = Int(data[((width * w) + h) * 4])
//value returns in Java "-1" but In Swift returning "0"
// JAVA -> rgb = bitmapImage.getPixel(w, h); returning "-1"
red = (value >> 16) & 0x000000FF
green = (value >> 8) & 0x000000FF
blue = (value) & 0x000000FF
let totalColor = red + green + blue
var auxChar = "1"
if(totalColor > self.blackLimit){
auxChar = "0"
}
auxBinaryChar[index] = auxChar[0]
index+=1
if(index == 8 || w == (width-1)){
sb.append(self.fourByteBinary(binaryStr: String(auxBinaryChar)))
auxBinaryChar = ["0","0","0","0","0","0","0","0"]
index = 0
}
}
sb.append("\n")
}
return sb.toString()
}
private func fourByteBinary(binaryStr:String) -> String{
var decimal:Int = Int(binaryStr,radix: 2) ?? 0
if(decimal > 15){
return String(decimal, radix: 16, uppercase: true)
}else{
decimal = Int(String(decimal),radix:16) ?? 0
return "0" + String(decimal, radix: 16, uppercase: true)
}
}
Related
I'm writing a game of checkers with QML, and I have this component which encompasses the player piece and it's mouse area.
When the mouse is pressed, and the user moves the mouse, the cell underneath is highlighted yellow. This and snapping works on Ubuntu, but when I test it on Android, only dragging pieces seems to work.
This is it working on Ubuntu
Here's the code for the player piece:
Component {
id: playerPiece
Image {
id: image
property int cx: 0
property int cy: 0
x: board.x + (cx * window.cellSize)
y: board.y + (cy * window.cellSize)
property int pieceIndex: -1
property int pieceType: 0
MouseArea {
id: ma
anchors.fill: parent
enabled: GameState.playerTurnId == 1 && (pieceType & 1) == 1
drag.target: parent
drag.axis: Drag.XAxis | Drag.YAxis
property int targetCellX: -1
property int targetCellY: -1
onReleased: {
// console.log("released")
if (GameState.lastHighlightedIndex != -1
&& GameState.lastHighlightedY != -1) {
board.children[GameState.lastHighlightedY].children[GameState.lastHighlightedX].children[0].visible = false
if (targetCellX != -1 && targetCellY != -1) {
console.log(targetCellX)
var piece = GameState.playerPieceQMLItems[image.pieceIndex]
console.log(`cx ${targetCellX} cy ${targetCellY}`)
piece.cx = targetCellX
piece.cy = targetCellY
//hack to recalculate positions
board.x++
board.x--
board.y++
board.y--
targetCellX = -1
targetCellY = -1
}
}
}
onPositionChanged: {
if (drag.active) {
// console.log("dragging")
var mousePos = NativeFunctions.globalMousePos()
targetCellX = Math.floor(
(mousePos.x - window.x - board.x) / window.cellSize)
targetCellY = Math.floor(
(mousePos.y - window.y - board.y) / window.cellSize)
if ((targetCellX > -1 && targetCellX < 8) && (targetCellY > -1
&& targetCellY < 8)) {
GameState.tileState[targetCellX + (targetCellY * 8)] = 3
//update the board row column child item
var item = board.children[targetCellY].children[targetCellX]
if (item instanceof Rectangle) {
//remove the highlight from the last highlighted cell
if (GameState.lastHighlightedIndex != -1
&& GameState.lastHighlightedY != -1) {
board.children[GameState.lastHighlightedY].children[GameState.lastHighlightedX].children[0].visible = false
}
item.children[0].visible = true
//console.log(`selected ${GameState.playerPieceQMLItems[image.pieceIndex]}, pieceIndex ${image.pieceIndex}`)
//console.log(`${item.children[1].id}`)
GameState.lastHighlightedX = targetCellX
GameState.lastHighlightedY = targetCellY
}
}
}
}
}
sourceClipRect: {
if (GameState.cf(pieceType, GameState.TS_P1)) {
//king flag set
if (GameState.cf(pieceType, GameState.TS_PK)) {
return Qt.rect(0, 326, 338, 338)
} else {
return Qt.rect(0, 0, 338, 338)
}
} else if (GameState.cf(pieceType, GameState.TS_P2)) {
//king flag set
if (GameState.cf(pieceType, GameState.TS_PK)) {
return Qt.rect(0, 326, 338, 338)
} else {
return Qt.rect(534, 0, 338, 338)
}
}
}
source: "qrc:/pieces.png"
}
}
Which is a part of https://github.com/ben-cottrell-nz/checkers/blob/master/main.qml.
Why is the highlighting and snapping functionality not working when I test my application on Android?
I found out that my method exposed from C++, NativeMethods::globalMousePos was the issue: on Android it was returning -2147483648 for x and y, this was being returned from QCursor::pos.
I changed mousePos in my QML code to use the builtin function mapToGlobal:
var mousePos = mapToGlobal(mouse.x, mouse.y)
Highlighting and snapping to Rectangles works on Android now.
how can i make user to choose(by clicking on image) between 2 images n times using for loop?
below, i have created an array of 4 drawable images and set it to rank2.
on each loop, user will choose one between 2 images, then selected image is saved in rank1 array.
because there are only 4 drawables right now, it will only loop 2 times.
val imageView1 = findViewById<ImageView>(R.id.imageView1);
val imageView2 = findViewById<ImageView>(R.id.imageView2);
val textView = findViewById<TextView>(R.id.textView);
var rank2 = mutableListOf(R.drawable.img1,R.drawable.img2,R.drawable.img3,R.drawable.img4)
val rank1: MutableList<Int> = ArrayList()
var x = 0;
while(x<2) {//run it 2 times
var temp = arrayOf(rank2[x*2], rank2[(x*2)+1]);
imageView1.setImageResource(temp[0]);
imageView2.setImageResource(temp[1]);
imageView1.setOnClickListener{
rank1.add(temp[0]);
x++;
}
imageView1.setOnClickListener{
rank1.add(temp[1]);
x++;
}
}
but, this one falls into infinite loop.
how can i fix this?
No need to use while loop. A counting variable is all you need.
var rank2 = mutableListOf(R.drawable.img1,R.drawable.img2,R.drawable.img3,R.drawable.img4)
val rank1: MutableList<Int> = ArrayList()
imageView1.setImageResource(rank2[0]);
imageView2.setImageResource(rank2[1]);
var x = 0
imageView1.setOnClickListener{
if (x < 2) {
rank1.add(rank2[2 * x])
imageView1.setImageResource(rank2[2 * (x + 1)])
imageView2.setImageResource(rank2[2 * (x + 1) + 1])
x++
}
}
imageView2.setOnClickListener{
if (x < 2) {
rank1.add(rank2[2 * x + 1])
imageView1.setImageResource(rank2[2 * (x + 1)])
imageView2.setImageResource(rank2[2 * (x + 1) + 1])
x++
}
}
Try to create a recursive method instead of a cycle that calls itself nLoops times. Try something like this:
fun c(b: Int, nLoops: Int) {
if (b!=nLoops) {
var x = b;
var temp = arrayOf(rank2[x * 2], rank2[(x * 2) + 1]);
imageView1.setImageResource(temp[0]);
imageView2.setImageResource(temp[1]);
imageView1.setOnClickListener
{
rank1.add(temp[0]);
x++;
c(x, nLoops);
}
imageView1.setOnClickListener
{
rank1.add(temp[1]);
x++;
c(x, nLoops);
}
}
}
When you run somethink like this with b=0 and nLoops = 2 it will execute your code only ehwn you press the image for 2 times.
I have been studying the RingDroid source, trying to figure out how to draw waveforms on Android device. However, I got stuck in the section about reading the WAV file at CheapWAV.java.
public void ReadFile(File inputFile)
throws java.io.FileNotFoundException,
java.io.IOException {
super.ReadFile(inputFile);
mFileSize = (int)mInputFile.length();
if (mFileSize < 128) {
throw new java.io.IOException("File too small to parse");
}
FileInputStream stream = new FileInputStream(mInputFile);
byte[] header = new byte[12];
stream.read(header, 0, 12);
mOffset += 12;
if (header[0] != 'R' ||
header[1] != 'I' ||
header[2] != 'F' ||
header[3] != 'F' ||
header[8] != 'W' ||
header[9] != 'A' ||
header[10] != 'V' ||
header[11] != 'E') {
throw new java.io.IOException("Not a WAV file");
}
mChannels = 0;
mSampleRate = 0;
while (mOffset + 8 <= mFileSize) {
byte[] chunkHeader = new byte[8];
stream.read(chunkHeader, 0, 8);
mOffset += 8;
int chunkLen =
((0xff & chunkHeader[7]) << 24) |
((0xff & chunkHeader[6]) << 16) |
((0xff & chunkHeader[5]) << 8) |
((0xff & chunkHeader[4]));
if (chunkHeader[0] == 'f' &&
chunkHeader[1] == 'm' &&
chunkHeader[2] == 't' &&
chunkHeader[3] == ' ') {
if (chunkLen < 16 || chunkLen > 1024) {
throw new java.io.IOException(
"WAV file has bad fmt chunk");
}
byte[] fmt = new byte[chunkLen];
stream.read(fmt, 0, chunkLen);
mOffset += chunkLen;
int format =
((0xff & fmt[1]) << 8) |
((0xff & fmt[0]));
mChannels =
((0xff & fmt[3]) << 8) |
((0xff & fmt[2]));
mSampleRate =
((0xff & fmt[7]) << 24) |
((0xff & fmt[6]) << 16) |
((0xff & fmt[5]) << 8) |
((0xff & fmt[4]));
if (format != 1) {
throw new java.io.IOException(
"Unsupported WAV file encoding");
}
} else if (chunkHeader[0] == 'd' &&
chunkHeader[1] == 'a' &&
chunkHeader[2] == 't' &&
chunkHeader[3] == 'a') {
if (mChannels == 0 || mSampleRate == 0) {
throw new java.io.IOException(
"Bad WAV file: data chunk before fmt chunk");
}
int frameSamples = (mSampleRate * mChannels) / 50;
mFrameBytes = frameSamples * 2;
mNumFrames = (chunkLen + (mFrameBytes - 1)) / mFrameBytes;
mFrameOffsets = new int[mNumFrames];
mFrameLens = new int[mNumFrames];
mFrameGains = new int[mNumFrames];
byte[] oneFrame = new byte[mFrameBytes];
int i = 0;
int frameIndex = 0;
while (i < chunkLen) {
int oneFrameBytes = mFrameBytes;
if (i + oneFrameBytes > chunkLen) {
i = chunkLen - oneFrameBytes;
}
stream.read(oneFrame, 0, oneFrameBytes);
int maxGain = 0;
for (int j = 1; j < oneFrameBytes; j += 4 * mChannels) {
int val = java.lang.Math.abs(oneFrame[j]);
if (val > maxGain) {
maxGain = val;
}
}
mFrameOffsets[frameIndex] = mOffset;
mFrameLens[frameIndex] = oneFrameBytes;
mFrameGains[frameIndex] = maxGain;
frameIndex++;
mOffset += oneFrameBytes;
i += oneFrameBytes;
if (mProgressListener != null) {
boolean keepGoing = mProgressListener.reportProgress(
i * 1.0 / chunkLen);
if (!keepGoing) {
break;
}
}
}
} else {
stream.skip(chunkLen);
mOffset += chunkLen;
}
}
}
Everything seems straight forward until I reach
int frameSamples = (mSampleRate * mChannels) / 50;
mFrameBytes = frameSamples * 2;
mNumFrames = (chunkLen + (mFrameBytes - 1)) / mFrameBytes;
Q1. Where did the 50 magic number came from? Is it just assuming the frame duration is 50?
Q2. Why is mFrameBytes = frameSample * 2? Is it assuming each sample is 2 byte? But why?
for (int j = 1; j < oneFrameBytes; j += 4 * mChannels) {
int val = java.lang.Math.abs(oneFrame[j]);
if (val > maxGain) {
maxGain = val;
}
}
Q3. Why is j incrementing by 4 * mChannels? How was 4 justified?
Q4. What does frameGains mean actually? I've went through although articles/blogs such as
https://ccrma.stanford.edu/courses/422/projects/WaveFormat/2
http://blogs.msdn.com/b/dawate/archive/2009/06/23/intro-to-audio-programming-part-2-demystifying-the-wav-format.aspx
http://www.speakingcode.com/2011/12/31/primer-on-digital-audio-and-pulse-code-modulation-pcm/
But I don't see such term mentioned any where.
Hope someone can shed some light on this. Thank you.
Q1. Where did the 50 magic number came from? Is it just assuming the frame duration is 50?
A1. That calculates 1/50th of a second as a frame. So the app would have to process 50 frame buffers if audio data per second.
Q2. Why is mFrameBytes = frameSample * 2? Is it assuming each sample is 2 byte? But why?
A2. I'm guessing this because he is assuming 16bit samples.
Q3. Why is j incrementing by 4 * mChannels? How was 4 justified?
A3. I think the key here is to note it starts from offset 1. Which means he is only sampling the high order byte for the sample. The 4 is probably just an optimisation so he's only processing a half the buffer (remember he's assuming 2 bytes per sample)
Q4. What does frameGains mean actually?
Well it's exactly what it says. It's the gain of that frame (1/50th of a second) See http://en.m.wikipedia.org/wiki/Gain or Google for: Audio Gain.
This should also help: https://ccrma.stanford.edu/courses/422/projects/WaveFormat/
I want to get the dominant color in an Android CvCameraViewFrame object. I use the following OpenCV Android code to do that. This code is converted from OpenCV c++ code to OpenCV Android code. In the following code I loop through all the pixels in my camera frame and find the color of each pixel and store them in a HashMap to find the dominant color at the end of the loop. To loop through each pixel it takes about 30 seconds. This is unacceptable for me. Could somebody please review this code and point me how can I find the dominant color in a camera frame.
private String[] colors = {"cBLACK", "cWHITE", "cGREY", "cRED", "cORANGE", "cYELLOW", "cGREEN", "cAQUA", "cBLUE", "cPURPLE", "cPINK", "cRED"};
public Mat onCameraFrame(CvCameraViewFrame inputFrame) {
mRgba = inputFrame.rgba();
if (mIsColorSelected) {
Imgproc.cvtColor(mRgba, mRgba, Imgproc.COLOR_BGR2HSV);
int h = mRgba.height(); // Pixel height
int w = mRgba.width(); // Pixel width
int rowSize = (int)mRgba.step1(); // Size of row in bytes, including extra padding
float initialConfidence = 1.0f;
Map<String, Integer> tallyColors = new HashMap<String, Integer>();
byte[] pixelsTotal = new byte[h*rowSize];
mRgba.get(0,0,pixelsTotal);
//This for loop takes about 30 seconds to process for my camera frame
for (int y=0; y<h; y++) {
for (int x=0; x<w; x++) {
// Get the HSV pixel components
int hVal = (int)pixelsTotal[(y*rowSize) + x + 0]; // Hue
int sVal = (int)pixelsTotal[(y*rowSize) + x + 1]; // Saturation
int vVal = (int)pixelsTotal[(y*rowSize) + x + 2]; // Value (Brightness)
// Determine what type of color the HSV pixel is.
String ctype = getPixelColorType(hVal, sVal, vVal);
// Keep count of these colors.
int totalNum = 0;
try{
totalNum = tallyColors.get(ctype);
} catch(Exception ex){
totalNum = 0;
}
totalNum++;
tallyColors.put(ctype, totalNum);
}
}
int tallyMaxIndex = 0;
int tallyMaxCount = -1;
int pixels = w * h;
for (int i=0; i<colors.length; i++) {
String v = colors[i];
int pixCount;
try{
pixCount = tallyColors.get(v);
} catch(Exception e){
pixCount = 0;
}
Log.i(TAG, v + " - " + (pixCount*100/pixels) + "%, ");
if (pixCount > tallyMaxCount) {
tallyMaxCount = pixCount;
tallyMaxIndex = i;
}
}
float percentage = initialConfidence * (tallyMaxCount * 100 / pixels);
Log.i(TAG, "Color of currency note: " + colors[tallyMaxIndex] + " (" + percentage + "% confidence).");
}
return mRgba;
}
private String getPixelColorType(int H, int S, int V)
{
String color;
if (V < 75)
color = "cBLACK";
else if (V > 190 && S < 27)
color = "cWHITE";
else if (S < 53 && V < 185)
color = "cGREY";
else { // Is a color
if (H < 14)
color = "cRED";
else if (H < 25)
color = "cORANGE";
else if (H < 34)
color = "cYELLOW";
else if (H < 73)
color = "cGREEN";
else if (H < 102)
color = "cAQUA";
else if (H < 127)
color = "cBLUE";
else if (H < 149)
color = "cPURPLE";
else if (H < 175)
color = "cPINK";
else // full circle
color = "cRED"; // back to Red
}
return color;
}
Thank you very much.
OpenCV has an Histogram method which counts all image colors. After the histogram is calculated all you would have to do is to chose the one with the biggest count...
Check here for a tutorial (C++): Histogram Calculation.
You might also the this stackoverflow answer which shows an example on how to use Android's histogram function Imgproc.calcHist().
Think about to resize your images, then you may multiply the results by the same scale:
resize( larg_image, smallerImage , interpolation=cv.CV_INTER_CUBIC );
Or,
you may check these solutions:
You could find dominant color using k-mean clustering method.
this link will be useful.
https://www.youtube.com/watch?v=f54-x3PckH8
in Android I would like to draw a PieChart with a dynamically number of pies. Each pie should have a different color from a gradient.
For example I would like to have a gradient from light brown to dark brown. If I need to draw five pies, I need five volors from the start to the end of this gradient.
How can I do that in Java with the Android framework?
I found out that I can create a LinearGradient for a line, i.e.:
LinearGradient lg = new LinearGradient(1, 1, 5, 5, toRGB("lightbrown"), toRGB("darkbrown"), TileMode.REPEAT);
But I did not found any function to get a color from this line, i.e.:
// for the five needed RGB colors from the gradient line
lg.getRGBColor(1, 1);
lg.getRGBColor(2, 2);
lg.getRGBColor(3, 3);
lg.getRGBColor(4, 4);
lg.getRGBColor(5, 5);
Do you have any ideas how I can get this?
Thanks!
You cannot get these values directly from the LinearGradient. The gradient doesn't contain the actual drawing. To get these values, you can paint them to a canvas and pull the colors out of the canvas, or what I'd suggest would be to calculate the values yourself.
It's a repeating linear gradient in five steps and you have the RGB values for the first and last color. The rest is just math. Here's the pseudo code:
int r1 = startColor.red;
int g1 = startColor.green;
int b1 = startColor.blue;
int r2 = endColor.red;
int g2 = endColor.green;
int b2 = endColor.blue;
int redStep = r2 - r1 / 4;
int greenStep = g2 - g1 / 4;
int blueStep = b2 - b1 / 4;
firstColor = new Color(r1, g1, b1);
secondColor = new Color(r1 + redStep, g1 + greenStep, b1 + blueStep);
thirdColor = new Color(r1 + redStep * 2, g1 + greenStep * 2, b1 + blueStep * 2);
fourthColor = new Color(r1 + redStep * 3, g1 + greenStep * 3, b1 + blueStep * 3);
fifthColor = new Color(r1 + redStep * 4, g1 + greenStep * 4, b1 + blueStep * 4);
Another approach that is a little more reusable (I seem to bump into this problem all the time). It's a bit more code. Here is the usage:
int[] colors = {toRGB("lightbrown"), toRGB("darkbrown")};//assuming toRGB : String -> Int
float[] positions = {1, 5};
getColorFromGradient( colors, positions, 1 )
//...
getColorFromGradient( colors, positions, 5 )
Supporting functions
public static int getColorFromGradient(int[] colors, float[] positions, float v ){
if( colors.length == 0 || colors.length != positions.length ){
throw new IllegalArgumentException();
}
if( colors.length == 1 ){
return colors[0];
}
if( v <= positions[0]) {
return colors[0];
}
if( v >= positions[positions.length-1]) {
return colors[positions.length-1];
}
for( int i = 1; i < positions.length; ++i ){
if( v <= positions[i] ){
float t = (v - positions[i-1]) / (positions[i] - positions[i-1]);
return lerpColor(colors[i-1], colors[i], t);
}
}
//should never make it here
throw new RuntimeException();
}
public static int lerpColor( int colorA, int colorB, float t){
int alpha = (int)Math.floor(Color.alpha(colorA) * ( 1 - t ) + Color.alpha(colorB) * t);
int red = (int)Math.floor(Color.red(colorA) * ( 1 - t ) + Color.red(colorB) * t);
int green = (int)Math.floor(Color.green(colorA) * ( 1 - t ) + Color.green(colorB) * t);
int blue = (int)Math.floor(Color.blue(colorA) * ( 1 - t ) + Color.blue(colorB) * t);
return Color.argb(alpha, red, green, blue);
}
I have wrote the util class for calculate colors gradient.
via very, very simple Kotlin code:
val pink = Colar(245, 9, 253)
val lime = Colar(0, 253, 32)
lp_1.colors = (pink toColor lime).run {
gradient { 0 upTo 3 }
}
lp_2.colors = (pink toColor lime).run {
gradient { 0 upTo 9 }
}
lp_3.colors = (pink toColor lime).run {
gradient { 3 upTo 9}
}
Supporting util class
class StepGradientUtil(private var colar1: Colar?, private var colar2: Colar?) {
private var mSteps: Int = 0
infix fun StepGradientUtil.gradient(f: () -> IntRange): IntArray {
val result = f.invoke().map {
it.colorStep()
}.toIntArray()
recycler()
return result
}
infix fun Int.upTo(steps: Int): IntRange {
mSteps = steps
return (this until steps)
}
private fun recycler() {
mSteps = 0
colar1 = null
colar2 = null
}
private fun Int.colorStep() = Color.rgb(
(colar1!!.r * (mSteps - this) + colar2!!.r * this) / mSteps,
(colar1!!.g * (mSteps - this) + colar2!!.g * this) / mSteps,
(colar1!!.b * (mSteps - this) + colar2!!.b * this) / mSteps
)
}
data class Colar(
val r: Int,
val g: Int,
val b: Int
)
infix fun Colar.toColor(colar: Colar) = StepGradientUtil(colar1 = this, colar2 = colar)
See full source code sample on repo