Push Notifications for Cordova using Firebase

Push Notifications for Cordova using Firebase

אני מניח שלא צריך להציג את Cordova (לשעבר PhoneGap. פלטפורמה ליצירת אפליקציות היברידיות – תוכן web-י מותאם למערכות הפעלה שונות, כמו אנדרואיד ו- iOS. זהו). וגם לא את Firebase (שירות של גוגל ליצירת backend עבור אפליקציות. זהו).

אבל איך משלבים ביניהם?

יצירת והגדרת פרוייקט

(אם עדיין לא מותקן אצלכם cordova – אז npm install -g cordova יעשה את העבודה).

ניצור פרוייקט-

cordova create fireBaseApp com.firebase.app FireBaseApp

נעבור לתיקייה של הפרוייקט (cd fireBaseApp) ונוסיף את אנדרואיד-

cordova platform add android

כדי להשתמש ב- Firebase נתקין את cordova-plugin-firebase

cordova plugin add cordova-plugin-firebase@0.1.21 --save

יצירת פרוייקט Firebase

נכנס לקונסולת הניהול, ונבחר Add Project. נכניס את פרטי הפרוייקט:

נלחץ על "Add Firebase to your Android app". נמלא את פרטי האפליקציה:

בשלב הבא נוריד את הקובץ שנוצר, google-service.json ונצרף אותו לתיקיה הראשית של הפרוייקט שלנו.

שליחת הודעה

אפשר עכשיו להריץ את האפליקציה:

cordova run android

ננסה לשלוח הודעה. בקונסולה נלחץ על "Notifications" ואז "Send your first message".

נמלא את פרטי ההודעה ונשלח:

בשלב זה, ההודעה אמורה להופיע.

אם אתם משתמשים באנדרואיד 6 ומעלה, כנראה תקבלו הודעת שגיאה. הסיבה לכך, שהתוסף (בגירסתו הנוכחית, לפחות) מצפה שניצור קובץ xml עם הצבעים בהם תופיע ההודעה. מכיוון שלצורך הפרוייקט הזה לא מעניינים אותנו צבעים, פשוט נעביר להערה את הקוד המדובר.

בתיקיית הפרוייקט, בתוך platforms/android/src/org/apache/corodva/firebase, ישנו קובץ FirebasePluginMessagingService.java. נבצע בו את השינוי הבא:

if(android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M)
            {
		//		int accentID = getResources().getIdentifier("accent", "color", getPackageName());
                // notificationBuilder.setColor(getResources().getColor(accentID, null));
            }

זהו, עכשיו זה יעבוד.

 

Share on FacebookShare on Google+Tweet about this on TwitterShare on LinkedIn
Look and Say

Look and Say

למתעניינים ב- Python, או סתם לאוהבי חידות, ישנו אתר שנקרא "Python Challenge". בכל שלב צריך לפתור חידה בעזרת python (או למעשה כל שפת תכנות אחרת)  והפתרון מוביל לכתובת אינטרנט של החידה הבאה.

באחת החידות מופיעה הסדרה הבאה:

a = [1, 11, 21, 1211, 111221

והחידה היא:

len(a[30]) = ?

המטרה היא כמובן למצוא את החוקיות של הסדרה, ואז למצוא את אורך האיבר ה- 30.

פתרון

הסדרה נקראת "Look and Say". בכל איבר בסדרה (מלבד הראשון) מקריאים (ורושמים) את מספר הספרות שמתארות את האיבר הקודם. כלומר, אם התחלנו ב- 1, אז האיבר שני יתאר פעם אחת 1 – 11. האיבר השלישי יתאר פעמיים 1 – 21. האיבר הרביעי יתאר פעם אחת 2 ופעם אחת 1 – 1211, וכן הלאה.

כל מה שנשאר זה למצוא את האיבר ה- 30. אפשר למצוא אותו ידנית, אבל זה ייקח הרבה זמן – סדרת "הבט ואמור" (או "סדרת קונווי" אם אתם מתעקשים) גדלה במהירות. חוץ מזה, בשביל זה יש python.

קוד

נתחיל מפתרון נאיבי:

first = '1'
second = ''
for i in range(0, 30):
    j = 0
    k = 0
    while j < len(first):
        while k < len(first) and first[k] == first[j]:
            k += 1
        second += str(k-j) + first[j]
        j = k
    print second
    first = second
    second = ''
print "Final answer:", len(first)

ההתייחסות לכל איבר בסדרה היא כ- string ולא כמספר. בכל איטרציה רצים על התוים משמאל לימין וסופרים את מספר הפעמים בהם תו מסויים מופיע ברצף.

ב- Python יש כלי שיכול לפשט משמעותית את הקוד הנ"ל. נשתמש בספריית re (המשמשת ל- regular expressions) כדי למצוא אורך של תו בכל הופעה שלו. לדוגמא:

re.findall("(\\d)(\\1*)", "111221")

נקבל כתשובה:

[('1', '11'), ('2', '2'), ('1', ")]

קיבלנו מערך של צמדים (tuples) – באיבר הראשון יש את התו של הרצף הנוכחי, ואחריו את שארית הרצף. אם נעבור על המערך ומכל צמד ניקח את אורך האיבר שני (פלוס 1) ונצמיד אותו לאיבר הראשון (שהוא התו הנוכחי של הרצף) נקבל את התוצאה הרצויה – האיבר הבא בסדרה. לדוגמא במערך הנ"ל: האורך של '11' + 1, עם התו '1' => 31. האורך של '2' + 1, עם התו '2' => 22. האורך של " + 1, עם התו '1' => 11. ולסיכום: 312211.

ובקוד:

"".join([str(len(i+j))+i for i,j in re.findall(r"(\d)(\1*)", 111221)])

זה נותן לנו את האפשרות להזין איבר ולקבל את האיבר הבא. עכשיו נעשה את זה עבור האיבר ה- 30:

import re
 
current = "1" #start from '1'
for n in range(30):
    current = "".join([str(len(j) + 1)+i for i, j in re.findall(r"(\d)(\1*)", current)])
 
print "Final answer:", len(current)

התשובה, אגב, היא 5808.

Share on FacebookShare on Google+Tweet about this on TwitterShare on LinkedIn
בקטנה: StringBuilder

בקטנה: StringBuilder

במהלך עבודה עם Java ניתקל מדי פעם בצורך לשרשר Strings. לדוגמא, לצרף לכתובת url פרמטרים שונים, ועוד מקרים נוספים.

זו לא בעיה, נכון? שפת Java (כמו שפות מודרניות רבות) תומכת באופרטור '+' כדי לשרשר Strings. כלומר, אפשר פשוט לכתוב כך:

String url = baseUrl + "?q=hello&amp;p=win";

אבל מה עם מדובר בעבודה קצת יותר רצינית מאשר לחבר שני סטרינגים קצרים? לדוגמא, פונקציה שמשרשרת מערך של סטרינגים (באורך לא ידוע) ל- String אחד ארוך.

אפשר לכתוב את זה ככה:

String joinWords(String[] words) {
  String result = "";
  for (String w : words) {
    result = result + w;
  }
  return result;
}

אבל יש כאן בעיה: זה מאד לא יעיל. String, הוא אובייקט immutable, כלומר, לא ניתן לבצע עליו שינויים. בכל מניפולציה שעושים על String – נוצר אחד חדש. String מכיל בתוכו מערך של תווים, כאשר משרשרים שני סטרינגים אחד אחרי השני, אנחנו בעצם יוצרים מחדש שני מערכי תווים אחד אחרי השני אל תוך מערך אחד גדול.

במקום זה, אפשר להשתמש ב- StringBuilder:

String joinWords(String[] words) {
  StringBuilder result = new StringBuilder();
  for (String w : words) {
    result.append(w);
  }
  return result.toString();
}

StringBuilder עושה את השינויים במקום, עם התווים שכבר קיימים, מה שנותן זמן ריצה אופטימלי.

Share on FacebookShare on Google+Tweet about this on TwitterShare on LinkedIn
ASCII Control Characters

ASCII Control Characters

בהמשך לפוסט על קידוד תווים מצאתי את הדף הזה, עם הסבר על תופעה מעניינת בתווי ASCII.

כזכור, 32 התווים הראשונים בטבלת ASCII הם Control Characters – תווים שאין להם ייצוג ״מודפס״ אלא רק משמעות עבור המחשב -הם מתפקדים כסיגנלים. ניתן להפעיל את התווים הללו בטרמינל באמצעות CTRL (כמובן, תווי Control) ומקש נוסף. לדוגמא – כדי להפעיל את ESC, ניתן ללחוץ על CTRL + ]. כדי להפסיק פעולה (ETX) – לחיצה על CTRL + C.

אבל… למה? מה מיוחד דווקא בצירופי המקשים האלו?

התשובה מתקבלת כאשר ניקח את טבלת ASCII, ונסדר אותה בארבעה טורים.

00 01 10 11
NUL Spc @ ` 00000
SOH ! A a 00001
STX " B b 00010
ETX # C c 00011
EOT $ D d 00100
ENQ % E e 00101
ACK & F f 00110
BEL ' G g 00111
BS ( H h 01000
TAB ) I i 01001
LF * J j 01010
VT + K k 01011
FF , L l 01100
CR M m 01101
SO . N n 01110
SI / O o 01111
DLE 0 P p 10000
DC1 1 Q q 10001
DC2 2 R r 10010
DC3 3 S s 10011
DC4 4 T t 10100
NAK 5 U u 10101
SYN 6 V v 10110
ETB 7 W w 10111
CAN 8 X x 11000
EM 9 Y y 11001
SUB : Z z 11010
ESC ; [ { 11011
FS < \ | 11100
GS = ] } 11101
RS > ^ ~ 11110
US ? _ DEL 11111

בכל טור 32 תווים. הטור השמאלי הוא של תווי הבקרה.

כזכור, אורכו של תו ASCII הוא 7 ביטים, מ- 0000000 עד 1111111. בכל שורה בטבלה – חמשת הביטים הימניים יהיו זהים ושני הביטים השמאליים ישתנו בהתאם לעמודה: בעמודה הראשונה הם יהיו 00, בשניה 01, וכן הלאה.

נסתכל על השורה של תו הבקרה ETX: ערכו הבינארי הוא 0000011. באותה שורה, הערך של התו C הוא 1100011.

כאשר לוחצים על CTRL ואז על מקש נוסף – אנחנו מאפסים את שני הביטים השמאליים של אותו מקש! כלומר- 1100011 הופך ל- 0000011, וכתוצאה מכך מופעל תו הבקרה ETX (הוראה של End of Text).

Share on FacebookShare on Google+Tweet about this on TwitterShare on LinkedIn
libGDX Scene2d tutorial – Part 2 – Splash Screen

libGDX Scene2d tutorial – Part 2 – Splash Screen

(חלק 1 נמצא כאן)

עברנו את השלב הראשון – טענו את קבצי התמונות/קול לזיכרון. לפני שאנחנו עוברים למשחק עצמו, אנחנו רוצים להציג את הלוגו של חברת המשחקים שלנו.

משהו כזה:

Splash Screen

כדי לעשות זאת, נשתמש (לראשונה) בספריית Scene2d.

Read More Read More

Share on FacebookShare on Google+Tweet about this on TwitterShare on LinkedIn
libGDX Scene2d tutorial – Part 1 – Loading Screen

libGDX Scene2d tutorial – Part 1 – Loading Screen

בעבר כתבתי על איך ליצור פרוייקט libGDX, ואיך לכתוב משחק פשוט. היום נתחיל בסדרת מדריכים לכתיבת משחק ב- libGDX עם שימוש ב- Scene2d – ספרייה שמקלה על ניהול ה- UI.

התחלה

ניצור פרוייקט חדש. אין צורך ב- Box2d או ספריות נוספות, scene2d מגיעה באופן מובנה בתוך libGDX.

המשחק שלנו יכלול 5 מסכים:

  • Loading – מסך טעינה, בו נטען לזיכרון את כל ה- assets הדרושים, כמו תמונות, קבצי קול וכו׳.
  • Splash – מסך ספלאש, בו נציג את הלוגו שלנו.
  • Menu – מסך תפריט ראשי, בו נציג את ממשק ניהול המשחק (כפתור להתחלת משחק, שיתוף וכו׳).
  • Play – מסך משחק, העיקר.
  • Game Over – מסך סיום משחק בו נציג את התוצאה.


Read More Read More

Share on FacebookShare on Google+Tweet about this on TwitterShare on LinkedIn
Konami Code

Konami Code

"קוד קונמי" (Konami Code) הוא ככל הנראה ה"צ'יט" (cheat) המפורסם ביותר בעולם משחקי המחשב.

מדובר על צירוף המקשים: למעלה-למעלה-למטה-למטה-שמאל-ימין-שמאל-ימין-האות B-האות A.

כלומר (משמאל לימין):

BA

קוד קונאמי הומצא עבור משחקי ניטנדו, אלו הכפתורים שיש ב- controller. אבל גם כיום הקוד עדיין שימושי.

אתרים רבים משתמשים ברצף הפעולות של קוד קונאמי כ- Easter Egg – הפעלה שלו תגרום למשהו לא צפוי באתר (נסו את Buzzfeed או את Digg. ויש דוגמאות נוספות)

קוד

זה הכל טוב ויפה, אבל איך עושים את זה?

אז ככה.

const pressed = [];
const secretCode = 'ArrowUpArrowUpArrowDownArrowDownArrowLeftArrowRightArrowLeftArrowRightBA';
const KonamiCodeLength = 10;

ניצור מערך בו נשמור את עשרת המקשים האחרונים שהוקשו, ובכל פעם נבדוק האם הרצף נכון:

window.addEventListener('keyup', function(e) {
  console.log(e.key);
  pressed.push(e.key);
  pressed.splice(-KonamiCodeLength - 1, pressed.length - KonamiCodeLength);
  if (pressed.join('').includes(secretCode)) {
    console.log('Hooray');
    cornify_add();
  }
});

כאשר הרצף הוא אכן ״קוד קונאמי״, נפעיל את ה- Easter Egg. למשל להוסיף חדי קרן לעמוד. בשביל זה צריך גם את הסקריפט:

<script type="text/javascript" src="http://www.cornify.com/js/cornify.js"></script>

עד כאן הכל פשוט, זה סקריפט שמופיע באינטרנט בהמון וריאציות. אבל… אני רוצה להוסיף גם תמיכה במובייל. אני אוותר על המקשים B ו- A – נזהה רצף של 8 swipes בכיוונים שונים.

השיטה תהיה זהה לזו של זיהוי רצף מקשים, רק צריך להוסיף דרך לזהות כל פעם swipe. נאזין ל- touchstart ול- touchend: בתחילת התנועה נשמור את הקואורדינטות, ובסיום נראה איפה הקוארדינטות ביחס להתחלה. בצורה כזו נדע באיזה כיוון מדובר.

const gestures = [];
const gesturesCode = "uuddlrlr";
var lastX, lastY;
window.addEventListener('touchstart', function(e) {
  lastX = e.pageX;lastY = e.pageY;
}, false);
window.addEventListener('touchend', function(e) {
  if (e.pageY - 100 &gt; lastY) {
    console.log("down");
    gestures.push("d");
  }
  if (e.pageY + 100 &lt; lastY) { console.log("up"); gestures.push("u"); } if (e.pageX - 100 &gt; lastX) {
    console.log("right");
    gestures.push("r");
  }
  if (e.pageX + 100 &lt; lastX) {
    console.log("left");
    gestures.push("l");
  }
  gestures.splice(-gesturesCode.length - 1, gestures.length - gesturesCode.length);
  console.log(gestures);
  if (gestures.join('').includes(gesturesCode)) {
    console.log('Hooray');
    cornify_add();
  }
}, false);

 

Share on FacebookShare on Google+Tweet about this on TwitterShare on LinkedIn
Locked Out

Locked Out

אז נכון, לאחרונה תוכנה זה לא הצד החזק של Apple. אבל עדיין לא ציפיתי שזה יקרה:

אני משתמש ב- macOS Sierra. אחרי restart זה מה שקיבלתי – מסך login בלי שם המשתמש שלי. למעשה בלי אף שם משתמש – כשניסיתי להכניס שם וסיסמא, זה לא עבד.

זה כנראה אחד הדברים הכי מלחיצים שיכולים לקרות למתכנת: להינעל מחוץ למחשב, בלי גישה למידע שלך.

כדי להתמודד עם מצבים מהסוג הזה, קיימים מספר כלים (בדומה ל- Safe Mode של Windows), שמאפשרים לעשות boot לא מהדיסק הרגיל, וכך לתקן את הבעיה ״מבחוץ״.

הפעלתי את המחשב ב- ״Recovery Mode״ (לחיצה על CMD+R מיד אחרי אתחול). מתקבלות מספר אפשרויות:

  • שחזור לגיבוי האחרון שעשיתי – לא תודה, זה כרוך במחיקת כל המידע שנוסף מאז.
  • בדיקה ותיקון הדיסק קשיח – בדקתי, הכל בסדר טוב תודה.
  • התקנה מחדש של מערכת ההפעלה – זה נראה מבטיח, ניסיתי, ואחרי התקנה מחדש… קיבלתי חזרה בדיוק את אותו המסך.

מלבד האפשרויות הנ״ל, אפשר גם לפתוח טרמינל. הרצתי ממנו את הפקודה resetpassword, שאמורה להציג את כל ה- accounts הקיימים במחשב ואפשרות לאפס אותם – אבל לא נמצאו כל accounts. חשוד מאד. משהו קרה ל- account ממנו אני עובד (ככל הנראה בעקבות התקנה לא מוצלחת של תוכנה כלשהי).

מה עושים?

קיים קובץ בשם AppleSetupDone. שמחזיק את המידע על חשבונות admin שבמחשב. אם הקובץ לא קיים, כשמערכת ההפעלה עולה, ייטען התפריט של יצירת חשבון חדש – מה שבעצם יפתור את הבעיה. בקיצור כל מה שצריך זה למחוק את הקובץ הזה.

נריץ מהטרמינל שתי פקודות:

mount -uw

נעשה "mount" לדיסק קשיח, כדי שנוכל לעשות בו שינויים.

rm /var/db/.AppleSetupDone

נמחק את הקובץ. עכשיו אחרי restart נוכל ליצור מחדש את החשבון והכל בא על מקומו בשלום.

Share on FacebookShare on Google+Tweet about this on TwitterShare on LinkedIn
Character Encoding

Character Encoding

מכירים את זה ש-?????? ואז äîùáø?

בטח שמכירים. כשמנסים לפתוח תוכן שמקודד בצורה מסויימת לפי קידוד שונה, יתקבל משהו בסגנון. בעבר זה היה קורה מדי פעם בגלישה לאתרים בעברית (כיום כמעט ולא). היום ניתקל בזה בעיקר כאשר ננסה לפתוח מסמכים שנכתבו בסביבת Windows במערכות מבוססות Unix, או כאשר נעביר לנגן mp3 קבצים עם עברית (לדוגמא).

אני אנסה בפוסט הקרוב להסביר מה זה קידוד, מהם סוגי הקידוד הנפוצים, ואיך אפשר להתמודד עם בעיות של קידוד לא תואם.

מה זה קידוד

קידוד זו היכולת לבטא תווים באמצעי מוסכם אחר. מורס, או ברייל, הם סוגים של קידוד – כלומר, הוסכם שהרצף נקודה-קו משמעותו התו 'A'. אין משהו מיוחד בצורה של נקודה וקו שמרמזים על A, זה פשוט עניין של הסכמה. תרגום של שפה לקידוד מעניק לנו את האפשרות להשתמש בה בכלים שונים. לדוגמא, באמצעות מורס אפשר להעביר מידע למרחקים, באמצעות ברייל ניתן להעביר מידע ע"י מישוש.

המחשב מתרגם כל תוכן למספרים. כדי שנוכל להציג תווים על המסך, צריך "להסביר" למחשב איזה מספר מייצג כל תו.

ASCII

עד תחילת שנות השישים לא היה אף סטנדרט אחיד לקידוד תוים. עד כדי כך, שלעיתים על אותו מחשב רצו מספר תוכנות שכל אחת התייחסה לתווים בצורה שונה – התוכנות לא יכלו לתקשר ביניהן. בשנת 1963 הוסכם על קידוד אחיד: ASCII, שזה: American Standard Code for Information Interchange.

קידוד ASCII מכיל 128 תווים (27). הנה חלק מהטבלה:

  • (במקומות #0-31 – תווי בקרה שכבר לא בשימוש).
  • #32 – תו ריק (רווח).
  • #48-57 – ספרות.
  • #65-90 – אותיות גדולות (A, B, C..).
  • #97-122 – אותיות קטנות (a, b, c..).

החסרון של ASCII שהוא… אמריקאי. הוא עונה על הצרכים של דוברי אנגלית, אבל הוא מוגבל רק ל- 128 תווים – אין לו מקום לתווים בעברית, סינית או פולינזית. בזמן שהוא יצא הוא עשה מהפכה בעולם המחשוב, ולראשונה היתה שפה אחידה שתוכנות יכלו ״לדבר״ ביניהן, אבל היום זה לא ממש מספיק.

ISO-8859

הפיתרון לבעיות של ה- ASCII, היה להוסיף ביט אחד.

[הסבר קצר: המחשב מתרגם כל מספר לבינארי – בסיס 2. כל ספרה נוספת בה משתמשים מצויינת בביט. 8 ביטים = בייט.

הערכים של ASCII הם בין 0 ל- 127, שזה בבינארית 1111111. אם נוסיף ביט, כלומר נשתמש בבייט שלם – נוכל להכפיל את טווח הערכים. 11111111 יהיה הערך המקסימלי – 255.]

קיבלנו 128 ״מקומות פנויים״ לתווים נוספים. יש תקן עבור כל שפה שיש בה תווים מיוחדים, והם מאוכלסים ב- 128 מקומות האלו. כאשר המחשב מקבל את רצף הבייטים אותו הוא הולך לפענח לצורך הצגת תגים על המסך, הוא צריך לדעת את שם התקן כך שיוכל להשתמש בטבלה של השפה המתאימה.

מי שאחראי על התקינה הוא ISO, שזה International Organization for Standardization. זהו גוף שיוצר תקנים עבור כל דבר – ממידות נעליים, רמזורים ועד לכוסות לטעימת יין. התקן לקידוד לשפות שונות נקרא ISO-8859 באופן כללי, עם מספר נוסף שמציין את השפה הספציפית. ISO-8859-8 הוא התקן לקידוד התווים בעברית.

מיקרוסופט אימצו את התקן, ויצרו קידוד מקביל עבור כל שפה. לדוגמא הקידוד לעברית נקרא Windows-1255.

החסרונות של ISO-8859-x:

  • לא ניתן להשתמש באותו מסמך ביותר מקידוד אחד. כל מסמך יתורגם בצורה אחידה לפי קידוד ספציפי. ניתן לכתוב באנגלית + שפה נוספת וזהו.
  • התוכנה צריכה לדעת מראש במה הטקסט מקודד – האם מדובר בלטינית (ISO-8859-1), או ביוונית (ISO-8859-7). ניתן לבצע זיהוי אוטומטי, שמנסה לפענח חלק מהתווים לפי קידוד אחד ובודק אם זה יוצר רצף הגיוני, אבל זה לא תמיד עובד.

תווי כל העולם התאחדו

בגלל הבעיות הנ״ל, בסוף שנות ה- 80 הגיעו למסקנה שחייבים קידוד אחד עבור כל התווים, וכך נולד ה- Unicode.

ה- 256 תווים ראשונים זהים לקידוד ISO-8859-1, מה שאומר 128 תווי ASCII, ו- 128 תווי לטינית.

אחריהם יש… הכל. unicode מכיל כל תו קיים: עברית, יוונית או הירוגליפים מצריים – טבלה ענקית בה ממופים כל התווים. את הטבלה הזו מחלקים למישורים – planes, כל מישור מכיל 65536 תווים (216). במישור הראשון יש את התווים הנפוצים ביותר (הוא נקרא BMP – Basic Multilingual Plane) ואחריו 16 מישורים נוספים. בחשבון פשוט, קידוד unicode יכול להכיל 1,114,112 תווים (17 כפול 216).

אבל רק להמציא את הטבלה זה לא מספיק. המחשב בסופו של דבר מקבל רצף של מספרים והוא צריך לדעת איך לחלק אותם – כמה מספרים (או בייטים) יוצרים תו אחד. הגישה הנאיבית תהיה לבדוק כמה בייטים צריך כדי לייצג את המספר הגדול ביותר, וזה יהיה מספר הבייטים עבור כל תו. הבעיה היא שזה בזבוז – אם נסתכל על התווים הראשונים – שהם הכי שימושיים – מספיק בייט אחד לייצג אותם. אם נשתמש בגישה כזו נפח המידע שנצטרך יהיה פי 4.

UTF-8 בא לפתור את הבעיה הזו, Unicode Transformation Format – זהו קידוד מבוסס Unicode. הוא בנוי כך שלפי מספר ביטים בתחילת כל תו ניתן לדעת כמה בייטים לקרוא עבורו. לדוגמא לתווי ה- ASCII שנמצאים בהתחלה (128 הראשונים) – והם גם השימושיים ביותר – נוכל להשתמש בבייט אחד, בצורה הזו: 0xxxxxxx. ככל שמתקדמים בטבלה נצטרך לכל תו יותר בייטים. כאן יש הסבר על איך זה בנוי.

כיום UTF-8 פופולארי ברוב אתרי האינטרנט, ובמערכות הפעלה מבוססות Unix. הוא מאפשר קידוד אחד לכל התווים, בצורה יעילה, והוא מאפשר תאימות אחורה – כל עוד המסמך מכיל רק תווי ASCII הם ייראו אותו דבר.

קוד

כדי לפתור את בעיית פתיחת מסמכים שנשמרו ב- Windows בסביבת Unix – נשתמש בקוד הבא (python):

#!/usr/bin/python
 
import codecs, sys
 
if len(sys.argv) &gt; 1:
    path = sys.argv[1]
else:
    print "arg not found. Run encode.py "
    sys.exit()
# open with Windows format
sourceFile = codecs.open(path, "r", "iso-8859-8")
# save decoded content
contents = sourceFile.read()
# close file so we can re-open it for writing
sourceFile.close()
# open with utf-8 format
targetFile = codecs.open(path, "w", "utf-8")
targetFile.write(contents)
targetFile.close()

זו המקבילה בקוד לפתרון של פתח-בפנקס-רשימות-ושמור-בפורמט-אחר. נפתח את הקובץ כשאנחנו מתייחסים אליו כפורמט iso-8859. נשמור את התוכן, ואז נפתח אותו מחדש בפורמט utf-8 ונכתוב לתוכו את התוכן.

בנוסף, ספריית python שימושית: Chardet. כשמה, היא מזהה את הקידוד בו נכתב הקובץ. בקטע קוד הנ״ל אני מניח שהקידוד של הקובץ הוא ISO-8859-8 (כי זה ידוע לי), אבל אפשר להשתמש ב- Chardet כדי לזהות אוטומטית את הקידוד, וכך הקוד יעבוד גם על עוד קידודים (=שפות).

סיכום

ישנם 3 קידודים בהם ייתכן וניתקל: ASCII, ISO-8859-x, UTF-8. ההבדלים ביניהם יצוצו רק כאשר נשתמש בתווים שהם לא אנגלית/מספרים.

נראה שבעתיד כולם יעברו להשתמש ב- Unicode. גם אם UTF-8 לא נתמך רשמית ב- Windows ברמת מערכת ההפעלה, לא מעט תוכנות כבר משתמשות בו כברירת מחדל.

עד אז, äîùáø.

Share on FacebookShare on Google+Tweet about this on TwitterShare on LinkedIn
Get Alive

Get Alive

lenna

זו Lenna. כל מאמר שעוסק ב- image proccessing משתמש בתמונה הזו להדגמה (למה? תקראו בלינק).

אני רוצה להראות עליה איך יוצרים מתמונה רגילה, animated gif:

fadeinlenna

תאוריה

בתור התחלה, צריך ליצור תמונת בסיס ממנה ניצור את הפריימים שיהיו ב- gif הסופי:

  • נמיר את התמונה המקורית ל- grayscale (שחור-לבן).
  • נפעיל על התמונה פילטר של ״זיהוי קצוות״ (edge detection). ישנם מספר פילטרים שעושים את זה, כמו sobel או canny. הרעיון הוא, שהם מדגישים את השינויים הבולטים בין הפיקסלים בתמונה. בצורה כזו נקבל מעין "sketch" של התמונה.
  • התוצר של edge detection יהיה לבן על רקע שחור, לכן נעשה inversion (היפוך צבעים) – ונקבל את תמונת הבסיס.

תכלס

בשביל המימוש נשתמש בספריית Pillow, שהיא fork מתוך PIL הותיקה – ספריית python לעיבוד תמונה.

נפתח טרמינל python, ונתחיל עם הפקודות הבאות:

>>> from PIL import Image
>>> im = Image.open("Lenna.png")

נשנה את התמונה ל- grayscale, כלומר שרק חלק ה- Luminance יישאר:

>>> im = im.convert("L")
>>> im.show()

וזו התוצאה:

screen-shot-2016-11-30-at-15-03-24

נפעיל את ה- edge detection המובנה של PIL:

>>> from PIL import ImageFilter
>>> im = im.filter(ImageFilter.FIND_EDGES)
>>> im.show()

ונקבל:

screen-shot-2016-11-30-at-15-06-36

מה שנשאר זה רק לעשות invert, כלומר להפוך את הצבעים:

>>> from PIL import ImageOps
>>> im = ImageOps.invert(im)
>>> im.show()

זו התמונה הסופית איתה נעבוד:

screen-shot-2016-11-30-at-15-07-24

סקריפט

נרכז את כל הקוד הנ״ל לסקריפט הזה:

import sys, os, shutil
import numpy as np
from PIL import Image, ImageFilter, ImageOps
 
if len(sys.argv) > 1:
    imageFileName = sys.argv[1]
else:
    print "arg not found."
    sys.exit()
# load image
original = Image.open(imageFileName)
# convert to grayscale -> find edges -> invert
edges = ImageOps.invert(original.convert("L").filter(ImageFilter.FIND_EDGES))

התמונה שיש לנו היא שחור לבן, כלומר ערכים בין 0 ל- 255. נכתוב פונקציה שמקבלת ערך סף (threshold) ויוצרת תמונה חדשה שבה כל מה שמעל ה- threshold יהיה צבע רקע. ככל שערך הסף נמוך יותר, נראה פחות מהציור ויותר מהרקע.

def changeColors(image, bgColor, threshold):
    # Because we want to support in colorful gifs - to allow rgb bgColor -
    # we covert the image back to rgb
    image = image.convert("RGB")
    # We use numpy to work with the image data array directly
    data = np.array(image)
    # Get the three channels of the image to compare to
    red, green, blue = data[:,:,0], data[:,:,1], data[:,:,2]
    # Create mask to check if all channels (that probably all the same)
    # are bigger than threshold
    bgMask = (red > threshold) & (green > threshold) & (blue > threshold)
    # Replace all pixels that fit for the criteria with the bg color
    data[:,:,:3][bgMask] = bgColor
    # create back image from data array
    return Image.fromarray(data)

הרעיון הוא ליצור סט של פריימים בכל פעם עם threshold יותר גבוה, כך שבכל פריים נראה יותר מהתמונה. נשמור את כל הפריימים בתיקיה זמנית, ואחרי שניצור את ה- gif נמחק אותה.

folderName = "gifTemp"
filePath = "{0}/{1}.png"
for threshold in range(0, 255, 25):
    changeColors(image.copy(), bgColor, threshold).save(filePath.format(folderName, index))
    index += 1

אחרי שהרצנו את הסקריפט יש לנו תיקיה עם פריימים (בפורמט png). עכשיו צריך ליצור מהם gif.

בעיקרון, יש דרך ליצור מתוך python קבצי animated gif. לדוגמא images2gif. אבל בכל האפשרויות שניסיתי, לא הייתי מרוצה – מבחינת איכות ונפח הקובץ של התוצאה.

לכן בחרתי להשתמש בכלים חיצוניים ffmpeg ו- gifsicle. הם open source, וקיימים בדר״כ בסביבות פיתוח (לפחות ffmpeg).

נוסיף לסקריפט שלנו שתי פקודות:

os.system("ffmpeg -i " + folderName + "/%01d.png gifResults/fadeIn.gif")

הפקודה הזו מריצה את ffmpeg שלוקח את כל קבצי png שקיימים בתיקייה ומתחילים במספר – לפי הסדר – ויוצר מהם gif בשם fadeIn.

עכשיו הבעיה היא התזמון. אנחנו רוצים שכל הפריימים יהיו אותו זמן, חוץ מהאחרון – שם אנחנו רוצים להתעכב קצת, שנוכל לשים לב לתמונה הסופית. בשביל זה נריץ את gifsicle:

os.system("gifsicle -b gifResults/fadeIn.gif -d10 '#0--2' -d75 '#-1'")

פריים "-1" – כלומר הפריים האחרון מהסוף יהיה 0.75 שנייה (שנייה שלמה זה 100), וכל שאר הפריימים יהיו עשירית שנייה.

אפשר למצוא את הסקריפט המלא כאן.

 

Share on FacebookShare on Google+Tweet about this on TwitterShare on LinkedIn