VOTING POWER78.88%
DOWNVOTE POWER100.00%
RESOURCE CREDITS100.00%
REPUTATION PROGRESS0.00%
Net Worth
1,689.755USD
HIVE
214.681HIVE
HBD
0.000HBD
Effective Power
6,196.024HP
├── Own HP
4,009.707HP
├── Incoming DelegationsDeleg
+2,529.778HP
└── Outgoing DelegationsDeleg
-343.461HP
Detailed Balance
| HIVE | ||
| balance | 214.681HIVE | HIVE |
| market_balance | 0.000HIVE | HIVE |
| savings_balance | 0.000HIVE | HIVE |
| reward_hive_balance | 0.000HIVE | HIVE |
| HIVE POWER | ||
| Own HP | 4,009.707HP | HP |
| Delegated Out | 343.461HP | HP |
| Delegation In | 2,529.778HP | HP |
| Effective Power | 6,196.024HP | HP |
| Reward HP (pending) | 0.080HP | HP |
| HBD | ||
| hbd_balance | 0.000HBD | HBD |
| hbd_conversions | 0.000HBD | HBD |
| hbd_market_balance | 0.000HBD | HBD |
| savings_hbd_balance | 0.000HBD | HBD |
| reward_hbd_balance | 0.000HBD | HBD |
{
"balance": "214.681 HIVE",
"savings_balance": "0.000 HIVE",
"reward_hive_balance": "0.000 HIVE",
"vesting_shares": "6509603.023860 VESTS",
"delegated_vesting_shares": "557595.784818 VESTS",
"received_vesting_shares": "4106995.174504 VESTS",
"hbd_balance": "0.000 HBD",
"savings_hbd_balance": "0.000 HBD",
"reward_hbd_balance": "0.000 HBD"
}Account Info
| name | scipio |
| id | 422033 |
| rank | 0 |
| reputation | 0 |
| created | 2017-10-24T09:41:36 |
| recovery_account | blocktrades |
| proxy | None |
| invited_by | null |
| post_count | 2,611 |
| comment_count | 0 |
| lifetime_vote_count | 0 |
| witnesses_voted_for | 21 |
| last_post | 2026-06-06T07:05:06 |
| last_root_post | 2026-06-06T07:05:06 |
| last_vote_time | 2026-06-06T08:21:24 |
| proxied_vsf_votes | 0, 0, 0, 0 |
| can_vote | 1 |
| voting_power | 7,584 |
| delayed_votes | None |
| governance_vote_expiration_ts | 2027-04-22T00:58:12 |
| balance | 214.681 HIVE |
| savings_balance | 0.000 HIVE |
| hbd_balance | 0.000 HBD |
| savings_hbd_balance | 0.000 HBD |
| vesting_shares | 6509603.023860 VESTS |
| delegated_vesting_shares | 557595.784818 VESTS |
| received_vesting_shares | 4106995.174504 VESTS |
| reward_vesting_balance | 129.922049 VESTS |
| vesting_balance | 0.000 HIVE |
| vesting_withdraw_rate | 0.000000 VESTS |
| next_vesting_withdrawal | 1969-12-31T23:59:59 |
| withdrawn | 0 |
| to_withdraw | 0 |
| withdraw_routes | 0 |
| savings_withdraw_requests | 0 |
| last_account_recovery | 1970-01-01T00:00:00 |
| reset_account | null |
| last_owner_update | 1970-01-01T00:00:00 |
| last_account_update | 2019-06-15T07:21:54 |
| mined | No |
| hbd_seconds | 0 |
| hbd_last_interest_payment | 2020-05-12T20:42:45 |
| savings_hbd_last_interest_payment | 1970-01-01T00:00:00 |
{
"active": {
"account_auths": [],
"key_auths": [
[
"STM8RLCJUVQyKPiGzkZQpXLmoy2w5bMTv8shWEAfBPPaP4FpUX7To",
1
]
],
"weight_threshold": 1
},
"balance": "214.681 HIVE",
"can_vote": true,
"comment_count": 0,
"created": "2017-10-24T09:41:36",
"curation_rewards": 623302,
"delayed_votes": [],
"delegated_vesting_shares": "557595.784818 VESTS",
"downvote_manabar": {
"current_mana": 2514750603386,
"last_update_time": 1780734084
},
"governance_vote_expiration_ts": "2027-04-22T00:58:12",
"guest_bloggers": [],
"hbd_balance": "0.000 HBD",
"hbd_last_interest_payment": "2020-05-12T20:42:45",
"hbd_seconds": "0",
"hbd_seconds_last_update": "2020-05-12T20:42:45",
"id": 422033,
"json_metadata": "{\"profile\":{\"name\":\"scipio\",\"about\":\"Does it matter who's right, or who's left?\",\"cover_image\":\"https://static.pexels.com/photos/290386/pexels-photo-290386.jpeg\",\"profile_image\":\"https://cdn.steemitimages.com/DQmSHCeBbjgGEsnigAh9qJTdcFuKvm8vaxRYACdkvPi8WBg/scipio.jpg\"}}",
"last_account_recovery": "1970-01-01T00:00:00",
"last_account_update": "2019-06-15T07:21:54",
"last_owner_update": "1970-01-01T00:00:00",
"last_post": "2026-06-06T07:05:06",
"last_root_post": "2026-06-06T07:05:06",
"last_vote_time": "2026-06-06T08:21:24",
"lifetime_vote_count": 0,
"market_history": [],
"memo_key": "STM8ZMNP7R48LekiaQu9Kz4UNVYcnVwFsY1fRhgY7MGDx2hxxukvS",
"mined": false,
"name": "scipio",
"next_vesting_withdrawal": "1969-12-31T23:59:59",
"open_recurrent_transfers": 0,
"other_history": [],
"owner": {
"account_auths": [],
"key_auths": [
[
"STM54sX7GE6LRFVn9b3rRyZkkgrAJeXAHsaupuxTCfrjqAuHgf6Ej",
1
]
],
"weight_threshold": 1
},
"pending_claimed_accounts": 0,
"pending_transfers": 0,
"post_bandwidth": 0,
"post_count": 2611,
"post_history": [],
"post_voting_power": "10059002.413546 VESTS",
"posting": {
"account_auths": [
[
"utopian.app",
1
],
[
"utopianpay",
1
]
],
"key_auths": [
[
"STM8eHBTT5K4piEfvYAk99g3CxxuAGFQdNqA98BPtRp1SucFxgBqD",
1
]
],
"weight_threshold": 1
},
"posting_json_metadata": "{\"profile\":{\"name\":\"scipio\",\"about\":\"Does it matter who's right, or who's left?\",\"cover_image\":\"https://static.pexels.com/photos/290386/pexels-photo-290386.jpeg\",\"profile_image\":\"https://cdn.steemitimages.com/DQmSHCeBbjgGEsnigAh9qJTdcFuKvm8vaxRYACdkvPi8WBg/scipio.jpg\"}}",
"posting_rewards": 4889451,
"previous_owner_update": "1970-01-01T00:00:00",
"proxied_vsf_votes": [
0,
0,
0,
0
],
"proxy": "",
"received_vesting_shares": "4106995.174504 VESTS",
"recovery_account": "blocktrades",
"reputation": 0,
"reset_account": "null",
"reward_hbd_balance": "0.000 HBD",
"reward_hive_balance": "0.000 HIVE",
"reward_vesting_balance": "129.922049 VESTS",
"reward_vesting_hive": "0.080 HIVE",
"savings_balance": "0.000 HIVE",
"savings_hbd_balance": "0.000 HBD",
"savings_hbd_last_interest_payment": "1970-01-01T00:00:00",
"savings_hbd_seconds": "0",
"savings_hbd_seconds_last_update": "1970-01-01T00:00:00",
"savings_withdraw_requests": 0,
"tags_usage": [],
"to_withdraw": 0,
"transfer_history": [],
"vesting_balance": "0.000 HIVE",
"vesting_shares": "6509603.023860 VESTS",
"vesting_withdraw_rate": "0.000000 VESTS",
"vote_history": [],
"voting_manabar": {
"current_mana": 7629133205792,
"last_update_time": 1780734084
},
"voting_power": 7584,
"withdraw_routes": 0,
"withdrawn": 0,
"witness_votes": [
"arcange",
"ausbitbank",
"blocktrades",
"blue-witness",
"dalz",
"deathwing",
"drakos",
"good-karma",
"gtg",
"guiltyparties",
"mahdiyari",
"neoxian",
"nuthman",
"pharesim",
"rishi556",
"roelandp",
"someguy123",
"steempeak",
"stoodkev",
"threespeak",
"ura-soul"
],
"witnesses_voted_for": 21,
"rank": 0
}Withdraw Routes
| Incoming | Outgoing |
|---|---|
Empty | Empty |
{
"incoming": [],
"outgoing": []
}From Date
To Date
gamersclassifiedeffective vote applied for @scipio / learn-ethical-hacking-48-insider-threats-when-the-call-is-coming-from-inside-the-house2026/06/06 11:43:15
gamersclassifiedeffective vote applied for @scipio / learn-ethical-hacking-48-insider-threats-when-the-call-is-coming-from-inside-the-house
2026/06/06 11:43:15
| author | scipio |
| pending payout | 1.867 HBD |
| permlink | learn-ethical-hacking-48-insider-threats-when-the-call-is-coming-from-inside-the-house |
| rshares | 4562672246 |
| total vote weight | 24559119576897 |
| voter | gamersclassified |
| weight | 4562672246 |
| Transaction Info | Block #107034274/Trx 362be342f3073c4ccd1993cb121bacba05cf3ef5 |
View Raw JSON Data
{
"block": 107034274,
"op": [
"effective_comment_vote",
{
"author": "scipio",
"pending_payout": "1.867 HBD",
"permlink": "learn-ethical-hacking-48-insider-threats-when-the-call-is-coming-from-inside-the-house",
"rshares": 4562672246,
"total_vote_weight": 24559119576897,
"voter": "gamersclassified",
"weight": 4562672246
}
],
"op_in_trx": 1,
"timestamp": "2026-06-06T11:43:15",
"trx_id": "362be342f3073c4ccd1993cb121bacba05cf3ef5",
"trx_in_block": 12,
"virtual_op": true
}2026/06/06 11:43:15
2026/06/06 11:43:15
| author | scipio |
| permlink | learn-ethical-hacking-48-insider-threats-when-the-call-is-coming-from-inside-the-house |
| voter | gamersclassified |
| weight | 210 (2.10%) |
| Transaction Info | Block #107034274/Trx 362be342f3073c4ccd1993cb121bacba05cf3ef5 |
View Raw JSON Data
{
"block": 107034274,
"op": [
"vote",
{
"author": "scipio",
"permlink": "learn-ethical-hacking-48-insider-threats-when-the-call-is-coming-from-inside-the-house",
"voter": "gamersclassified",
"weight": 210
}
],
"op_in_trx": 0,
"timestamp": "2026-06-06T11:43:15",
"trx_id": "362be342f3073c4ccd1993cb121bacba05cf3ef5",
"trx_in_block": 12,
"virtual_op": false
}scipioreceived 0.006 HP curation reward for @steevc / 18712824092-31188191312026/06/06 11:35:57
scipioreceived 0.006 HP curation reward for @steevc / 18712824092-3118819131
2026/06/06 11:35:57
| author | steevc |
| curator | scipio |
| payout must be claimed | true |
| permlink | 18712824092-3118819131 |
| reward | 9.744111 VESTS |
| Transaction Info | Block #107034129/Virtual Operation 4294967295:115 |
View Raw JSON Data
{
"block": 107034129,
"op": [
"curation_reward",
{
"author": "steevc",
"curator": "scipio",
"payout_must_be_claimed": true,
"permlink": "18712824092-3118819131",
"reward": "9.744111 VESTS"
}
],
"op_in_trx": 115,
"timestamp": "2026-06-06T11:35:57",
"trx_id": "0000000000000000000000000000000000000000",
"trx_in_block": 4294967295,
"virtual_op": true
}scipioreceived 0.013 HP curation reward for @papilloncharity / crow-guards-on-duty2026/06/06 11:16:48
scipioreceived 0.013 HP curation reward for @papilloncharity / crow-guards-on-duty
2026/06/06 11:16:48
| author | papilloncharity |
| curator | scipio |
| payout must be claimed | true |
| permlink | crow-guards-on-duty |
| reward | 21.112264 VESTS |
| Transaction Info | Block #107033747/Virtual Operation 4294967295:115 |
View Raw JSON Data
{
"block": 107033747,
"op": [
"curation_reward",
{
"author": "papilloncharity",
"curator": "scipio",
"payout_must_be_claimed": true,
"permlink": "crow-guards-on-duty",
"reward": "21.112264 VESTS"
}
],
"op_in_trx": 115,
"timestamp": "2026-06-06T11:16:48",
"trx_id": "0000000000000000000000000000000000000000",
"trx_in_block": 4294967295,
"virtual_op": true
}maarnioeffective vote applied for @scipio / learn-ai-series-89-medical-and-scientific-imaging2026/06/06 11:07:03
maarnioeffective vote applied for @scipio / learn-ai-series-89-medical-and-scientific-imaging
2026/06/06 11:07:03
| author | scipio |
| pending payout | 1.386 HBD |
| permlink | learn-ai-series-89-medical-and-scientific-imaging |
| rshares | 457662559098 |
| total vote weight | 18256169497750 |
| voter | maarnio |
| weight | 457662559098 |
| Transaction Info | Block #107033552/Trx 02d2a1799687dfa91679e6bf38f9bbca72eaca32 |
View Raw JSON Data
{
"block": 107033552,
"op": [
"effective_comment_vote",
{
"author": "scipio",
"pending_payout": "1.386 HBD",
"permlink": "learn-ai-series-89-medical-and-scientific-imaging",
"rshares": 457662559098,
"total_vote_weight": 18256169497750,
"voter": "maarnio",
"weight": 457662559098
}
],
"op_in_trx": 1,
"timestamp": "2026-06-06T11:07:03",
"trx_id": "02d2a1799687dfa91679e6bf38f9bbca72eaca32",
"trx_in_block": 1,
"virtual_op": true
}maarnioupvoted (100.00%) @scipio / learn-ai-series-89-medical-and-scientific-imaging2026/06/06 11:07:03
maarnioupvoted (100.00%) @scipio / learn-ai-series-89-medical-and-scientific-imaging
2026/06/06 11:07:03
| author | scipio |
| permlink | learn-ai-series-89-medical-and-scientific-imaging |
| voter | maarnio |
| weight | 10000 (100.00%) |
| Transaction Info | Block #107033552/Trx 02d2a1799687dfa91679e6bf38f9bbca72eaca32 |
View Raw JSON Data
{
"block": 107033552,
"op": [
"vote",
{
"author": "scipio",
"permlink": "learn-ai-series-89-medical-and-scientific-imaging",
"voter": "maarnio",
"weight": 10000
}
],
"op_in_trx": 0,
"timestamp": "2026-06-06T11:07:03",
"trx_id": "02d2a1799687dfa91679e6bf38f9bbca72eaca32",
"trx_in_block": 1,
"virtual_op": false
}scipioreceived 0.008 HP curation reward for @maxwellmarcusart / drawing-a-portrait-21092026/06/06 10:47:18
scipioreceived 0.008 HP curation reward for @maxwellmarcusart / drawing-a-portrait-2109
2026/06/06 10:47:18
| author | maxwellmarcusart |
| curator | scipio |
| payout must be claimed | true |
| permlink | drawing-a-portrait-2109 |
| reward | 12.992183 VESTS |
| Transaction Info | Block #107033158/Virtual Operation 4294967295:71 |
View Raw JSON Data
{
"block": 107033158,
"op": [
"curation_reward",
{
"author": "maxwellmarcusart",
"curator": "scipio",
"payout_must_be_claimed": true,
"permlink": "drawing-a-portrait-2109",
"reward": "12.992183 VESTS"
}
],
"op_in_trx": 71,
"timestamp": "2026-06-06T10:47:18",
"trx_id": "0000000000000000000000000000000000000000",
"trx_in_block": 4294967295,
"virtual_op": true
}scipioreceived 0.012 HP curation reward for @indiaunited / indiaunited-new-contest-and-last-ae0ff15b5f76c2026/06/06 10:47:09
scipioreceived 0.012 HP curation reward for @indiaunited / indiaunited-new-contest-and-last-ae0ff15b5f76c
2026/06/06 10:47:09
| author | indiaunited |
| curator | scipio |
| payout must be claimed | true |
| permlink | indiaunited-new-contest-and-last-ae0ff15b5f76c |
| reward | 19.488274 VESTS |
| Transaction Info | Block #107033155/Virtual Operation 4294967295:49 |
View Raw JSON Data
{
"block": 107033155,
"op": [
"curation_reward",
{
"author": "indiaunited",
"curator": "scipio",
"payout_must_be_claimed": true,
"permlink": "indiaunited-new-contest-and-last-ae0ff15b5f76c",
"reward": "19.488274 VESTS"
}
],
"op_in_trx": 49,
"timestamp": "2026-06-06T10:47:09",
"trx_id": "0000000000000000000000000000000000000000",
"trx_in_block": 4294967295,
"virtual_op": true
}scipioreceived 0.005 HP curation reward for @michupa / earthquake-fly-away2026/06/06 10:46:24
scipioreceived 0.005 HP curation reward for @michupa / earthquake-fly-away
2026/06/06 10:46:24
| author | michupa |
| curator | scipio |
| payout must be claimed | true |
| permlink | earthquake-fly-away |
| reward | 8.120114 VESTS |
| Transaction Info | Block #107033140/Virtual Operation 4294967295:198 |
View Raw JSON Data
{
"block": 107033140,
"op": [
"curation_reward",
{
"author": "michupa",
"curator": "scipio",
"payout_must_be_claimed": true,
"permlink": "earthquake-fly-away",
"reward": "8.120114 VESTS"
}
],
"op_in_trx": 198,
"timestamp": "2026-06-06T10:46:24",
"trx_id": "0000000000000000000000000000000000000000",
"trx_in_block": 4294967295,
"virtual_op": true
}scipioreceived 0.007 HP curation reward for @forykw / powerup-june-2026-9-years-on-hive2026/06/06 10:28:42
scipioreceived 0.007 HP curation reward for @forykw / powerup-june-2026-9-years-on-hive
2026/06/06 10:28:42
| author | forykw |
| curator | scipio |
| payout must be claimed | true |
| permlink | powerup-june-2026-9-years-on-hive |
| reward | 11.368171 VESTS |
| Transaction Info | Block #107032787/Virtual Operation 4294967295:456 |
View Raw JSON Data
{
"block": 107032787,
"op": [
"curation_reward",
{
"author": "forykw",
"curator": "scipio",
"payout_must_be_claimed": true,
"permlink": "powerup-june-2026-9-years-on-hive",
"reward": "11.368171 VESTS"
}
],
"op_in_trx": 456,
"timestamp": "2026-06-06T10:28:42",
"trx_id": "0000000000000000000000000000000000000000",
"trx_in_block": 4294967295,
"virtual_op": true
}scipioreceived 0.004 HP curation reward for @riverflows / winter-walk-urqhuarts-to-sunnymead2026/06/06 10:16:36
scipioreceived 0.004 HP curation reward for @riverflows / winter-walk-urqhuarts-to-sunnymead
2026/06/06 10:16:36
| author | riverflows |
| curator | scipio |
| payout must be claimed | true |
| permlink | winter-walk-urqhuarts-to-sunnymead |
| reward | 6.496102 VESTS |
| Transaction Info | Block #107032546/Virtual Operation 4294967295:204 |
View Raw JSON Data
{
"block": 107032546,
"op": [
"curation_reward",
{
"author": "riverflows",
"curator": "scipio",
"payout_must_be_claimed": true,
"permlink": "winter-walk-urqhuarts-to-sunnymead",
"reward": "6.496102 VESTS"
}
],
"op_in_trx": 204,
"timestamp": "2026-06-06T10:16:36",
"trx_id": "0000000000000000000000000000000000000000",
"trx_in_block": 4294967295,
"virtual_op": true
}scipioreceived 0.006 HP curation reward for @grindle / art-for-the-gamer-gameplay-playing-for-impact-moma-vilnius2026/06/06 09:25:33
scipioreceived 0.006 HP curation reward for @grindle / art-for-the-gamer-gameplay-playing-for-impact-moma-vilnius
2026/06/06 09:25:33
| author | grindle |
| curator | scipio |
| payout must be claimed | true |
| permlink | art-for-the-gamer-gameplay-playing-for-impact-moma-vilnius |
| reward | 9.744179 VESTS |
| Transaction Info | Block #107031526/Virtual Operation 4294967295:229 |
View Raw JSON Data
{
"block": 107031526,
"op": [
"curation_reward",
{
"author": "grindle",
"curator": "scipio",
"payout_must_be_claimed": true,
"permlink": "art-for-the-gamer-gameplay-playing-for-impact-moma-vilnius",
"reward": "9.744179 VESTS"
}
],
"op_in_trx": 229,
"timestamp": "2026-06-06T09:25:33",
"trx_id": "0000000000000000000000000000000000000000",
"trx_in_block": 4294967295,
"virtual_op": true
}vegoutt-traveleffective vote applied for @scipio / learn-ai-series-89-medical-and-scientific-imaging2026/06/06 09:15:27
vegoutt-traveleffective vote applied for @scipio / learn-ai-series-89-medical-and-scientific-imaging
2026/06/06 09:15:27
| author | scipio |
| pending payout | 1.345 HBD |
| permlink | learn-ai-series-89-medical-and-scientific-imaging |
| rshares | 57752650434 |
| total vote weight | 17798506938652 |
| voter | vegoutt-travel |
| weight | 57752650434 |
| Transaction Info | Block #107031324/Trx 239443a0b1ba2e3b01c5baf3a5c434315920a343 |
View Raw JSON Data
{
"block": 107031324,
"op": [
"effective_comment_vote",
{
"author": "scipio",
"pending_payout": "1.345 HBD",
"permlink": "learn-ai-series-89-medical-and-scientific-imaging",
"rshares": 57752650434,
"total_vote_weight": 17798506938652,
"voter": "vegoutt-travel",
"weight": 57752650434
}
],
"op_in_trx": 1,
"timestamp": "2026-06-06T09:15:27",
"trx_id": "239443a0b1ba2e3b01c5baf3a5c434315920a343",
"trx_in_block": 3,
"virtual_op": true
}vegoutt-travelupvoted (40.00%) @scipio / learn-ai-series-89-medical-and-scientific-imaging2026/06/06 09:15:27
vegoutt-travelupvoted (40.00%) @scipio / learn-ai-series-89-medical-and-scientific-imaging
2026/06/06 09:15:27
| author | scipio |
| permlink | learn-ai-series-89-medical-and-scientific-imaging |
| voter | vegoutt-travel |
| weight | 4000 (40.00%) |
| Transaction Info | Block #107031324/Trx 239443a0b1ba2e3b01c5baf3a5c434315920a343 |
View Raw JSON Data
{
"block": 107031324,
"op": [
"vote",
{
"author": "scipio",
"permlink": "learn-ai-series-89-medical-and-scientific-imaging",
"voter": "vegoutt-travel",
"weight": 4000
}
],
"op_in_trx": 0,
"timestamp": "2026-06-06T09:15:27",
"trx_id": "239443a0b1ba2e3b01c5baf3a5c434315920a343",
"trx_in_block": 3,
"virtual_op": false
}captainhiveeffective vote applied for @scipio / learn-ai-series-89-medical-and-scientific-imaging2026/06/06 09:15:27
captainhiveeffective vote applied for @scipio / learn-ai-series-89-medical-and-scientific-imaging
2026/06/06 09:15:27
| author | scipio |
| pending payout | 1.340 HBD |
| permlink | learn-ai-series-89-medical-and-scientific-imaging |
| rshares | 1283080327354 |
| total vote weight | 17740754288218 |
| voter | captainhive |
| weight | 1283080327354 |
| Transaction Info | Block #107031324/Trx 8f80a2d4ff5d768fa6fd635fc905b6b84ff1ec04 |
View Raw JSON Data
{
"block": 107031324,
"op": [
"effective_comment_vote",
{
"author": "scipio",
"pending_payout": "1.340 HBD",
"permlink": "learn-ai-series-89-medical-and-scientific-imaging",
"rshares": 1283080327354,
"total_vote_weight": 17740754288218,
"voter": "captainhive",
"weight": 1283080327354
}
],
"op_in_trx": 1,
"timestamp": "2026-06-06T09:15:27",
"trx_id": "8f80a2d4ff5d768fa6fd635fc905b6b84ff1ec04",
"trx_in_block": 2,
"virtual_op": true
}captainhiveupvoted (40.00%) @scipio / learn-ai-series-89-medical-and-scientific-imaging2026/06/06 09:15:27
captainhiveupvoted (40.00%) @scipio / learn-ai-series-89-medical-and-scientific-imaging
2026/06/06 09:15:27
| author | scipio |
| permlink | learn-ai-series-89-medical-and-scientific-imaging |
| voter | captainhive |
| weight | 4000 (40.00%) |
| Transaction Info | Block #107031324/Trx 8f80a2d4ff5d768fa6fd635fc905b6b84ff1ec04 |
View Raw JSON Data
{
"block": 107031324,
"op": [
"vote",
{
"author": "scipio",
"permlink": "learn-ai-series-89-medical-and-scientific-imaging",
"voter": "captainhive",
"weight": 4000
}
],
"op_in_trx": 0,
"timestamp": "2026-06-06T09:15:27",
"trx_id": "8f80a2d4ff5d768fa6fd635fc905b6b84ff1ec04",
"trx_in_block": 2,
"virtual_op": false
}spectrumeconseffective vote applied for @scipio / learn-ai-series-89-medical-and-scientific-imaging2026/06/06 09:14:54
spectrumeconseffective vote applied for @scipio / learn-ai-series-89-medical-and-scientific-imaging
2026/06/06 09:14:54
| author | scipio |
| pending payout | 1.243 HBD |
| permlink | learn-ai-series-89-medical-and-scientific-imaging |
| rshares | 3499611450480 |
| total vote weight | 16457673960864 |
| voter | spectrumecons |
| weight | 3499611450480 |
| Transaction Info | Block #107031313/Trx 1b52815f4e61a23be05f3a1c2e075f8b98b98a34 |
View Raw JSON Data
{
"block": 107031313,
"op": [
"effective_comment_vote",
{
"author": "scipio",
"pending_payout": "1.243 HBD",
"permlink": "learn-ai-series-89-medical-and-scientific-imaging",
"rshares": 3499611450480,
"total_vote_weight": 16457673960864,
"voter": "spectrumecons",
"weight": 3499611450480
}
],
"op_in_trx": 1,
"timestamp": "2026-06-06T09:14:54",
"trx_id": "1b52815f4e61a23be05f3a1c2e075f8b98b98a34",
"trx_in_block": 5,
"virtual_op": true
}spectrumeconsupvoted (40.00%) @scipio / learn-ai-series-89-medical-and-scientific-imaging2026/06/06 09:14:54
spectrumeconsupvoted (40.00%) @scipio / learn-ai-series-89-medical-and-scientific-imaging
2026/06/06 09:14:54
| author | scipio |
| permlink | learn-ai-series-89-medical-and-scientific-imaging |
| voter | spectrumecons |
| weight | 4000 (40.00%) |
| Transaction Info | Block #107031313/Trx 1b52815f4e61a23be05f3a1c2e075f8b98b98a34 |
View Raw JSON Data
{
"block": 107031313,
"op": [
"vote",
{
"author": "scipio",
"permlink": "learn-ai-series-89-medical-and-scientific-imaging",
"voter": "spectrumecons",
"weight": 4000
}
],
"op_in_trx": 0,
"timestamp": "2026-06-06T09:14:54",
"trx_id": "1b52815f4e61a23be05f3a1c2e075f8b98b98a34",
"trx_in_block": 5,
"virtual_op": false
}2026/06/06 09:05:06
2026/06/06 09:05:06
| author | oflyhigh |
| curator | scipio |
| payout must be claimed | true |
| permlink | 4x6d6a-o |
| reward | 11.368222 VESTS |
| Transaction Info | Block #107031117/Virtual Operation 4294967295:143 |
View Raw JSON Data
{
"block": 107031117,
"op": [
"curation_reward",
{
"author": "oflyhigh",
"curator": "scipio",
"payout_must_be_claimed": true,
"permlink": "4x6d6a-o",
"reward": "11.368222 VESTS"
}
],
"op_in_trx": 143,
"timestamp": "2026-06-06T09:05:06",
"trx_id": "0000000000000000000000000000000000000000",
"trx_in_block": 4294967295,
"virtual_op": true
}opticuseffective vote applied for @scipio / learn-ethical-hacking-48-insider-threats-when-the-call-is-coming-from-inside-the-house2026/06/06 08:23:21
opticuseffective vote applied for @scipio / learn-ethical-hacking-48-insider-threats-when-the-call-is-coming-from-inside-the-house
2026/06/06 08:23:21
| author | scipio |
| pending payout | 1.850 HBD |
| permlink | learn-ethical-hacking-48-insider-threats-when-the-call-is-coming-from-inside-the-house |
| rshares | 14708332734 |
| total vote weight | 24554556904651 |
| voter | opticus |
| weight | 14708332734 |
| Transaction Info | Block #107030285/Trx 0d179b7f4072058695285263e278affedce456e7 |
View Raw JSON Data
{
"block": 107030285,
"op": [
"effective_comment_vote",
{
"author": "scipio",
"pending_payout": "1.850 HBD",
"permlink": "learn-ethical-hacking-48-insider-threats-when-the-call-is-coming-from-inside-the-house",
"rshares": 14708332734,
"total_vote_weight": 24554556904651,
"voter": "opticus",
"weight": 14708332734
}
],
"op_in_trx": 1,
"timestamp": "2026-06-06T08:23:21",
"trx_id": "0d179b7f4072058695285263e278affedce456e7",
"trx_in_block": 7,
"virtual_op": true
}2026/06/06 08:23:21
2026/06/06 08:23:21
| author | scipio |
| permlink | learn-ethical-hacking-48-insider-threats-when-the-call-is-coming-from-inside-the-house |
| voter | opticus |
| weight | 420 (4.20%) |
| Transaction Info | Block #107030285/Trx 0d179b7f4072058695285263e278affedce456e7 |
View Raw JSON Data
{
"block": 107030285,
"op": [
"vote",
{
"author": "scipio",
"permlink": "learn-ethical-hacking-48-insider-threats-when-the-call-is-coming-from-inside-the-house",
"voter": "opticus",
"weight": 420
}
],
"op_in_trx": 0,
"timestamp": "2026-06-06T08:23:21",
"trx_id": "0d179b7f4072058695285263e278affedce456e7",
"trx_in_block": 7,
"virtual_op": false
}scipioeffective vote applied for @tarazkp / barriers-against-puusti2026/06/06 08:21:27
scipioeffective vote applied for @tarazkp / barriers-against-puusti
2026/06/06 08:21:27
| author | tarazkp |
| pending payout | 1.051 HBD |
| permlink | barriers-against-puusti |
| rshares | 201130048271 |
| total vote weight | 13955287494153 |
| voter | scipio |
| weight | 201130048271 |
| Transaction Info | Block #107030248/Trx 78c9700491394b05089d9065eff2d498d064ef3f |
View Raw JSON Data
{
"block": 107030248,
"op": [
"effective_comment_vote",
{
"author": "tarazkp",
"pending_payout": "1.051 HBD",
"permlink": "barriers-against-puusti",
"rshares": 201130048271,
"total_vote_weight": 13955287494153,
"voter": "scipio",
"weight": 201130048271
}
],
"op_in_trx": 1,
"timestamp": "2026-06-06T08:21:27",
"trx_id": "78c9700491394b05089d9065eff2d498d064ef3f",
"trx_in_block": 0,
"virtual_op": true
}scipioupvoted (100.00%) @tarazkp / barriers-against-puusti2026/06/06 08:21:27
scipioupvoted (100.00%) @tarazkp / barriers-against-puusti
2026/06/06 08:21:27
| author | tarazkp |
| permlink | barriers-against-puusti |
| voter | scipio |
| weight | 10000 (100.00%) |
| Transaction Info | Block #107030248/Trx 78c9700491394b05089d9065eff2d498d064ef3f |
View Raw JSON Data
{
"block": 107030248,
"op": [
"vote",
{
"author": "tarazkp",
"permlink": "barriers-against-puusti",
"voter": "scipio",
"weight": 10000
}
],
"op_in_trx": 0,
"timestamp": "2026-06-06T08:21:27",
"trx_id": "78c9700491394b05089d9065eff2d498d064ef3f",
"trx_in_block": 0,
"virtual_op": false
}scipioreceived 0.012 HP curation reward for @stresskiller / how-i-spended-my-tax2026/06/06 08:18:12
scipioreceived 0.012 HP curation reward for @stresskiller / how-i-spended-my-tax
2026/06/06 08:18:12
| author | stresskiller |
| curator | scipio |
| payout must be claimed | true |
| permlink | how-i-spended-my-tax |
| reward | 19.488429 VESTS |
| Transaction Info | Block #107030183/Virtual Operation 4294967295:103 |
View Raw JSON Data
{
"block": 107030183,
"op": [
"curation_reward",
{
"author": "stresskiller",
"curator": "scipio",
"payout_must_be_claimed": true,
"permlink": "how-i-spended-my-tax",
"reward": "19.488429 VESTS"
}
],
"op_in_trx": 103,
"timestamp": "2026-06-06T08:18:12",
"trx_id": "0000000000000000000000000000000000000000",
"trx_in_block": 4294967295,
"virtual_op": true
}valerianiseffective vote applied for @scipio / learn-ethical-hacking-48-insider-threats-when-the-call-is-coming-from-inside-the-house2026/06/06 08:03:21
valerianiseffective vote applied for @scipio / learn-ethical-hacking-48-insider-threats-when-the-call-is-coming-from-inside-the-house
2026/06/06 08:03:21
| author | scipio |
| pending payout | 1.846 HBD |
| permlink | learn-ethical-hacking-48-insider-threats-when-the-call-is-coming-from-inside-the-house |
| rshares | 477600532 |
| total vote weight | 24539848571917 |
| voter | valerianis |
| weight | 477600532 |
| Transaction Info | Block #107029887/Trx c5ddbec6257fb3caae3c7f4fa30c9a8efb2cd18c |
View Raw JSON Data
{
"block": 107029887,
"op": [
"effective_comment_vote",
{
"author": "scipio",
"pending_payout": "1.846 HBD",
"permlink": "learn-ethical-hacking-48-insider-threats-when-the-call-is-coming-from-inside-the-house",
"rshares": 477600532,
"total_vote_weight": 24539848571917,
"voter": "valerianis",
"weight": 477600532
}
],
"op_in_trx": 1,
"timestamp": "2026-06-06T08:03:21",
"trx_id": "c5ddbec6257fb3caae3c7f4fa30c9a8efb2cd18c",
"trx_in_block": 3,
"virtual_op": true
}2026/06/06 08:03:21
2026/06/06 08:03:21
| author | scipio |
| permlink | learn-ethical-hacking-48-insider-threats-when-the-call-is-coming-from-inside-the-house |
| voter | valerianis |
| weight | 126 (1.26%) |
| Transaction Info | Block #107029887/Trx c5ddbec6257fb3caae3c7f4fa30c9a8efb2cd18c |
View Raw JSON Data
{
"block": 107029887,
"op": [
"vote",
{
"author": "scipio",
"permlink": "learn-ethical-hacking-48-insider-threats-when-the-call-is-coming-from-inside-the-house",
"voter": "valerianis",
"weight": 126
}
],
"op_in_trx": 0,
"timestamp": "2026-06-06T08:03:21",
"trx_id": "c5ddbec6257fb3caae3c7f4fa30c9a8efb2cd18c",
"trx_in_block": 3,
"virtual_op": false
}retrodroideffective vote applied for @scipio / learn-ai-series-89-medical-and-scientific-imaging2026/06/06 07:37:33
retrodroideffective vote applied for @scipio / learn-ai-series-89-medical-and-scientific-imaging
2026/06/06 07:37:33
| author | scipio |
| pending payout | 0.973 HBD |
| permlink | learn-ai-series-89-medical-and-scientific-imaging |
| rshares | 7562166456 |
| total vote weight | 12958062510384 |
| voter | retrodroid |
| weight | 7562166456 |
| Transaction Info | Block #107029373/Trx a2fc1aa9c9ab216a6da10e407cb4b6da3ecedf74 |
View Raw JSON Data
{
"block": 107029373,
"op": [
"effective_comment_vote",
{
"author": "scipio",
"pending_payout": "0.973 HBD",
"permlink": "learn-ai-series-89-medical-and-scientific-imaging",
"rshares": 7562166456,
"total_vote_weight": 12958062510384,
"voter": "retrodroid",
"weight": 7562166456
}
],
"op_in_trx": 1,
"timestamp": "2026-06-06T07:37:33",
"trx_id": "a2fc1aa9c9ab216a6da10e407cb4b6da3ecedf74",
"trx_in_block": 6,
"virtual_op": true
}retrodroidupvoted (11.00%) @scipio / learn-ai-series-89-medical-and-scientific-imaging2026/06/06 07:37:33
retrodroidupvoted (11.00%) @scipio / learn-ai-series-89-medical-and-scientific-imaging
2026/06/06 07:37:33
| author | scipio |
| permlink | learn-ai-series-89-medical-and-scientific-imaging |
| voter | retrodroid |
| weight | 1100 (11.00%) |
| Transaction Info | Block #107029373/Trx a2fc1aa9c9ab216a6da10e407cb4b6da3ecedf74 |
View Raw JSON Data
{
"block": 107029373,
"op": [
"vote",
{
"author": "scipio",
"permlink": "learn-ai-series-89-medical-and-scientific-imaging",
"voter": "retrodroid",
"weight": 1100
}
],
"op_in_trx": 0,
"timestamp": "2026-06-06T07:37:33",
"trx_id": "a2fc1aa9c9ab216a6da10e407cb4b6da3ecedf74",
"trx_in_block": 6,
"virtual_op": false
}darth-crypticeffective vote applied for @scipio / learn-ai-series-89-medical-and-scientific-imaging2026/06/06 07:37:33
darth-crypticeffective vote applied for @scipio / learn-ai-series-89-medical-and-scientific-imaging
2026/06/06 07:37:33
| author | scipio |
| pending payout | 0.972 HBD |
| permlink | learn-ai-series-89-medical-and-scientific-imaging |
| rshares | 17314614294 |
| total vote weight | 12950500343928 |
| voter | darth-cryptic |
| weight | 17314614294 |
| Transaction Info | Block #107029373/Trx 6a72b841ef82cbe975e12a51e86d3dbd85e50cfa |
View Raw JSON Data
{
"block": 107029373,
"op": [
"effective_comment_vote",
{
"author": "scipio",
"pending_payout": "0.972 HBD",
"permlink": "learn-ai-series-89-medical-and-scientific-imaging",
"rshares": 17314614294,
"total_vote_weight": 12950500343928,
"voter": "darth-cryptic",
"weight": 17314614294
}
],
"op_in_trx": 1,
"timestamp": "2026-06-06T07:37:33",
"trx_id": "6a72b841ef82cbe975e12a51e86d3dbd85e50cfa",
"trx_in_block": 2,
"virtual_op": true
}darth-crypticupvoted (11.00%) @scipio / learn-ai-series-89-medical-and-scientific-imaging2026/06/06 07:37:33
darth-crypticupvoted (11.00%) @scipio / learn-ai-series-89-medical-and-scientific-imaging
2026/06/06 07:37:33
| author | scipio |
| permlink | learn-ai-series-89-medical-and-scientific-imaging |
| voter | darth-cryptic |
| weight | 1100 (11.00%) |
| Transaction Info | Block #107029373/Trx 6a72b841ef82cbe975e12a51e86d3dbd85e50cfa |
View Raw JSON Data
{
"block": 107029373,
"op": [
"vote",
{
"author": "scipio",
"permlink": "learn-ai-series-89-medical-and-scientific-imaging",
"voter": "darth-cryptic",
"weight": 1100
}
],
"op_in_trx": 0,
"timestamp": "2026-06-06T07:37:33",
"trx_id": "6a72b841ef82cbe975e12a51e86d3dbd85e50cfa",
"trx_in_block": 2,
"virtual_op": false
}grider123effective vote applied for @scipio / learn-ai-series-89-medical-and-scientific-imaging2026/06/06 07:37:33
grider123effective vote applied for @scipio / learn-ai-series-89-medical-and-scientific-imaging
2026/06/06 07:37:33
| author | scipio |
| pending payout | 0.971 HBD |
| permlink | learn-ai-series-89-medical-and-scientific-imaging |
| rshares | 6863139872 |
| total vote weight | 12933185729634 |
| voter | grider123 |
| weight | 6863139872 |
| Transaction Info | Block #107029373/Trx 1e4ddb10e40a83f5a0b328d449a690e538f0508d |
View Raw JSON Data
{
"block": 107029373,
"op": [
"effective_comment_vote",
{
"author": "scipio",
"pending_payout": "0.971 HBD",
"permlink": "learn-ai-series-89-medical-and-scientific-imaging",
"rshares": 6863139872,
"total_vote_weight": 12933185729634,
"voter": "grider123",
"weight": 6863139872
}
],
"op_in_trx": 1,
"timestamp": "2026-06-06T07:37:33",
"trx_id": "1e4ddb10e40a83f5a0b328d449a690e538f0508d",
"trx_in_block": 0,
"virtual_op": true
}grider123upvoted (11.00%) @scipio / learn-ai-series-89-medical-and-scientific-imaging2026/06/06 07:37:33
grider123upvoted (11.00%) @scipio / learn-ai-series-89-medical-and-scientific-imaging
2026/06/06 07:37:33
| author | scipio |
| permlink | learn-ai-series-89-medical-and-scientific-imaging |
| voter | grider123 |
| weight | 1100 (11.00%) |
| Transaction Info | Block #107029373/Trx 1e4ddb10e40a83f5a0b328d449a690e538f0508d |
View Raw JSON Data
{
"block": 107029373,
"op": [
"vote",
{
"author": "scipio",
"permlink": "learn-ai-series-89-medical-and-scientific-imaging",
"voter": "grider123",
"weight": 1100
}
],
"op_in_trx": 0,
"timestamp": "2026-06-06T07:37:33",
"trx_id": "1e4ddb10e40a83f5a0b328d449a690e538f0508d",
"trx_in_block": 0,
"virtual_op": false
}darth-azraeleffective vote applied for @scipio / learn-ai-series-89-medical-and-scientific-imaging2026/06/06 07:36:57
darth-azraeleffective vote applied for @scipio / learn-ai-series-89-medical-and-scientific-imaging
2026/06/06 07:36:57
| author | scipio |
| pending payout | 0.970 HBD |
| permlink | learn-ai-series-89-medical-and-scientific-imaging |
| rshares | 85179127707 |
| total vote weight | 12926322589762 |
| voter | darth-azrael |
| weight | 85179127707 |
| Transaction Info | Block #107029361/Trx 975d47e092dbef70999f6ff1924dffc1a1f88aa8 |
View Raw JSON Data
{
"block": 107029361,
"op": [
"effective_comment_vote",
{
"author": "scipio",
"pending_payout": "0.970 HBD",
"permlink": "learn-ai-series-89-medical-and-scientific-imaging",
"rshares": 85179127707,
"total_vote_weight": 12926322589762,
"voter": "darth-azrael",
"weight": 85179127707
}
],
"op_in_trx": 1,
"timestamp": "2026-06-06T07:36:57",
"trx_id": "975d47e092dbef70999f6ff1924dffc1a1f88aa8",
"trx_in_block": 1,
"virtual_op": true
}darth-azraelupvoted (11.00%) @scipio / learn-ai-series-89-medical-and-scientific-imaging2026/06/06 07:36:57
darth-azraelupvoted (11.00%) @scipio / learn-ai-series-89-medical-and-scientific-imaging
2026/06/06 07:36:57
| author | scipio |
| permlink | learn-ai-series-89-medical-and-scientific-imaging |
| voter | darth-azrael |
| weight | 1100 (11.00%) |
| Transaction Info | Block #107029361/Trx 975d47e092dbef70999f6ff1924dffc1a1f88aa8 |
View Raw JSON Data
{
"block": 107029361,
"op": [
"vote",
{
"author": "scipio",
"permlink": "learn-ai-series-89-medical-and-scientific-imaging",
"voter": "darth-azrael",
"weight": 1100
}
],
"op_in_trx": 0,
"timestamp": "2026-06-06T07:36:57",
"trx_id": "975d47e092dbef70999f6ff1924dffc1a1f88aa8",
"trx_in_block": 1,
"virtual_op": false
}jeronimorubioeffective vote applied for @scipio / learn-ai-series-89-medical-and-scientific-imaging2026/06/06 07:20:51
jeronimorubioeffective vote applied for @scipio / learn-ai-series-89-medical-and-scientific-imaging
2026/06/06 07:20:51
| author | scipio |
| pending payout | 0.963 HBD |
| permlink | learn-ai-series-89-medical-and-scientific-imaging |
| rshares | 11162786581 |
| total vote weight | 12841143462055 |
| voter | jeronimorubio |
| weight | 11162786581 |
| Transaction Info | Block #107029040/Trx 3f16cf087c7e7b4b8e0fdd53d8ad39745f75a10c |
View Raw JSON Data
{
"block": 107029040,
"op": [
"effective_comment_vote",
{
"author": "scipio",
"pending_payout": "0.963 HBD",
"permlink": "learn-ai-series-89-medical-and-scientific-imaging",
"rshares": 11162786581,
"total_vote_weight": 12841143462055,
"voter": "jeronimorubio",
"weight": 11162786581
}
],
"op_in_trx": 1,
"timestamp": "2026-06-06T07:20:51",
"trx_id": "3f16cf087c7e7b4b8e0fdd53d8ad39745f75a10c",
"trx_in_block": 8,
"virtual_op": true
}jeronimorubioupvoted (100.00%) @scipio / learn-ai-series-89-medical-and-scientific-imaging2026/06/06 07:20:51
jeronimorubioupvoted (100.00%) @scipio / learn-ai-series-89-medical-and-scientific-imaging
2026/06/06 07:20:51
| author | scipio |
| permlink | learn-ai-series-89-medical-and-scientific-imaging |
| voter | jeronimorubio |
| weight | 10000 (100.00%) |
| Transaction Info | Block #107029040/Trx 3f16cf087c7e7b4b8e0fdd53d8ad39745f75a10c |
View Raw JSON Data
{
"block": 107029040,
"op": [
"vote",
{
"author": "scipio",
"permlink": "learn-ai-series-89-medical-and-scientific-imaging",
"voter": "jeronimorubio",
"weight": 10000
}
],
"op_in_trx": 0,
"timestamp": "2026-06-06T07:20:51",
"trx_id": "3f16cf087c7e7b4b8e0fdd53d8ad39745f75a10c",
"trx_in_block": 8,
"virtual_op": false
}scipioclaimed reward balance: 5.070 HIVE, 5.472 HP2026/06/06 07:12:24
scipioclaimed reward balance: 5.070 HIVE, 5.472 HP
2026/06/06 07:12:24
| account | scipio |
| reward hbd | 0.000 HBD |
| reward hive | 5.070 HIVE |
| reward vests | 8883.533179 VESTS |
| Transaction Info | Block #107028871/Trx 8f879266ecc097496541e0ab5c367b5d8c8f9ddb |
View Raw JSON Data
{
"block": 107028871,
"op": [
"claim_reward_balance",
{
"account": "scipio",
"reward_hbd": "0.000 HBD",
"reward_hive": "5.070 HIVE",
"reward_vests": "8883.533179 VESTS"
}
],
"op_in_trx": 0,
"timestamp": "2026-06-06T07:12:24",
"trx_id": "8f879266ecc097496541e0ab5c367b5d8c8f9ddb",
"trx_in_block": 0,
"virtual_op": false
}scipioreceived 0.007 HP curation reward for @carminasalazarte / exposicion-pictorica-expresiones-de-miguel-pedriquez-en-puerto-ordaz-conociendo-al-maestroengesp2026/06/06 07:11:33
scipioreceived 0.007 HP curation reward for @carminasalazarte / exposicion-pictorica-expresiones-de-miguel-pedriquez-en-puerto-ordaz-conociendo-al-maestroengesp
2026/06/06 07:11:33
| author | carminasalazarte |
| curator | scipio |
| payout must be claimed | true |
| permlink | exposicion-pictorica-expresiones-de-miguel-pedriquez-en-puerto-ordaz-conociendo-al-maestroengesp |
| reward | 11.368291 VESTS |
| Transaction Info | Block #107028854/Virtual Operation 4294967295:101 |
View Raw JSON Data
{
"block": 107028854,
"op": [
"curation_reward",
{
"author": "carminasalazarte",
"curator": "scipio",
"payout_must_be_claimed": true,
"permlink": "exposicion-pictorica-expresiones-de-miguel-pedriquez-en-puerto-ordaz-conociendo-al-maestroengesp",
"reward": "11.368291 VESTS"
}
],
"op_in_trx": 101,
"timestamp": "2026-06-06T07:11:33",
"trx_id": "0000000000000000000000000000000000000000",
"trx_in_block": 4294967295,
"virtual_op": true
}hive-117638effective vote applied for @scipio / learn-ai-series-89-medical-and-scientific-imaging2026/06/06 07:11:15
hive-117638effective vote applied for @scipio / learn-ai-series-89-medical-and-scientific-imaging
2026/06/06 07:11:15
| author | scipio |
| pending payout | 0.962 HBD |
| permlink | learn-ai-series-89-medical-and-scientific-imaging |
| rshares | 2754500389 |
| total vote weight | 12829980675474 |
| voter | hive-117638 |
| weight | 2754500389 |
| Transaction Info | Block #107028848/Trx 583c1ca4466508b3227930736d199acbc32e95f9 |
View Raw JSON Data
{
"block": 107028848,
"op": [
"effective_comment_vote",
{
"author": "scipio",
"pending_payout": "0.962 HBD",
"permlink": "learn-ai-series-89-medical-and-scientific-imaging",
"rshares": 2754500389,
"total_vote_weight": 12829980675474,
"voter": "hive-117638",
"weight": 2754500389
}
],
"op_in_trx": 1,
"timestamp": "2026-06-06T07:11:15",
"trx_id": "583c1ca4466508b3227930736d199acbc32e95f9",
"trx_in_block": 12,
"virtual_op": true
}hive-117638upvoted (50.00%) @scipio / learn-ai-series-89-medical-and-scientific-imaging2026/06/06 07:11:15
hive-117638upvoted (50.00%) @scipio / learn-ai-series-89-medical-and-scientific-imaging
2026/06/06 07:11:15
| author | scipio |
| permlink | learn-ai-series-89-medical-and-scientific-imaging |
| voter | hive-117638 |
| weight | 5000 (50.00%) |
| Transaction Info | Block #107028848/Trx 583c1ca4466508b3227930736d199acbc32e95f9 |
View Raw JSON Data
{
"block": 107028848,
"op": [
"vote",
{
"author": "scipio",
"permlink": "learn-ai-series-89-medical-and-scientific-imaging",
"voter": "hive-117638",
"weight": 5000
}
],
"op_in_trx": 0,
"timestamp": "2026-06-06T07:11:15",
"trx_id": "583c1ca4466508b3227930736d199acbc32e95f9",
"trx_in_block": 12,
"virtual_op": false
}commentatorseffective vote applied for @scipio / learn-ai-series-89-medical-and-scientific-imaging2026/06/06 07:10:51
commentatorseffective vote applied for @scipio / learn-ai-series-89-medical-and-scientific-imaging
2026/06/06 07:10:51
| author | scipio |
| pending payout | 0.961 HBD |
| permlink | learn-ai-series-89-medical-and-scientific-imaging |
| rshares | 1674622319 |
| total vote weight | 12827226175085 |
| voter | commentators |
| weight | 1674622319 |
| Transaction Info | Block #107028840/Trx 4de6d04f55a268045a9c0d2eefc2962e7ec3eee9 |
View Raw JSON Data
{
"block": 107028840,
"op": [
"effective_comment_vote",
{
"author": "scipio",
"pending_payout": "0.961 HBD",
"permlink": "learn-ai-series-89-medical-and-scientific-imaging",
"rshares": 1674622319,
"total_vote_weight": 12827226175085,
"voter": "commentators",
"weight": 1674622319
}
],
"op_in_trx": 1,
"timestamp": "2026-06-06T07:10:51",
"trx_id": "4de6d04f55a268045a9c0d2eefc2962e7ec3eee9",
"trx_in_block": 5,
"virtual_op": true
}commentatorsupvoted (5.00%) @scipio / learn-ai-series-89-medical-and-scientific-imaging2026/06/06 07:10:51
commentatorsupvoted (5.00%) @scipio / learn-ai-series-89-medical-and-scientific-imaging
2026/06/06 07:10:51
| author | scipio |
| permlink | learn-ai-series-89-medical-and-scientific-imaging |
| voter | commentators |
| weight | 500 (5.00%) |
| Transaction Info | Block #107028840/Trx 4de6d04f55a268045a9c0d2eefc2962e7ec3eee9 |
View Raw JSON Data
{
"block": 107028840,
"op": [
"vote",
{
"author": "scipio",
"permlink": "learn-ai-series-89-medical-and-scientific-imaging",
"voter": "commentators",
"weight": 500
}
],
"op_in_trx": 0,
"timestamp": "2026-06-06T07:10:51",
"trx_id": "4de6d04f55a268045a9c0d2eefc2962e7ec3eee9",
"trx_in_block": 5,
"virtual_op": false
}nuthmaneffective vote applied for @scipio / learn-ai-series-89-medical-and-scientific-imaging2026/06/06 07:10:42
nuthmaneffective vote applied for @scipio / learn-ai-series-89-medical-and-scientific-imaging
2026/06/06 07:10:42
| author | scipio |
| pending payout | 0.961 HBD |
| permlink | learn-ai-series-89-medical-and-scientific-imaging |
| rshares | 4993395831814 |
| total vote weight | 12825551552766 |
| voter | nuthman |
| weight | 4993395831814 |
| Transaction Info | Block #107028837/Trx dbd4dca1d35abb8344113c70f062dccc9e41c155 |
View Raw JSON Data
{
"block": 107028837,
"op": [
"effective_comment_vote",
{
"author": "scipio",
"pending_payout": "0.961 HBD",
"permlink": "learn-ai-series-89-medical-and-scientific-imaging",
"rshares": 4993395831814,
"total_vote_weight": 12825551552766,
"voter": "nuthman",
"weight": 4993395831814
}
],
"op_in_trx": 1,
"timestamp": "2026-06-06T07:10:42",
"trx_id": "dbd4dca1d35abb8344113c70f062dccc9e41c155",
"trx_in_block": 2,
"virtual_op": true
}nuthmanupvoted (100.00%) @scipio / learn-ai-series-89-medical-and-scientific-imaging2026/06/06 07:10:42
nuthmanupvoted (100.00%) @scipio / learn-ai-series-89-medical-and-scientific-imaging
2026/06/06 07:10:42
| author | scipio |
| permlink | learn-ai-series-89-medical-and-scientific-imaging |
| voter | nuthman |
| weight | 10000 (100.00%) |
| Transaction Info | Block #107028837/Trx dbd4dca1d35abb8344113c70f062dccc9e41c155 |
View Raw JSON Data
{
"block": 107028837,
"op": [
"vote",
{
"author": "scipio",
"permlink": "learn-ai-series-89-medical-and-scientific-imaging",
"voter": "nuthman",
"weight": 10000
}
],
"op_in_trx": 0,
"timestamp": "2026-06-06T07:10:42",
"trx_id": "dbd4dca1d35abb8344113c70f062dccc9e41c155",
"trx_in_block": 2,
"virtual_op": false
}hive-103505effective vote applied for @scipio / learn-ai-series-89-medical-and-scientific-imaging2026/06/06 07:10:27
hive-103505effective vote applied for @scipio / learn-ai-series-89-medical-and-scientific-imaging
2026/06/06 07:10:27
| author | scipio |
| pending payout | 0.587 HBD |
| permlink | learn-ai-series-89-medical-and-scientific-imaging |
| rshares | 4222182602 |
| total vote weight | 7832155720952 |
| voter | hive-103505 |
| weight | 4222182602 |
| Transaction Info | Block #107028832/Trx 8bb6d8ad18471a31567b6d341c44cf0c26946f98 |
View Raw JSON Data
{
"block": 107028832,
"op": [
"effective_comment_vote",
{
"author": "scipio",
"pending_payout": "0.587 HBD",
"permlink": "learn-ai-series-89-medical-and-scientific-imaging",
"rshares": 4222182602,
"total_vote_weight": 7832155720952,
"voter": "hive-103505",
"weight": 4222182602
}
],
"op_in_trx": 3,
"timestamp": "2026-06-06T07:10:27",
"trx_id": "8bb6d8ad18471a31567b6d341c44cf0c26946f98",
"trx_in_block": 0,
"virtual_op": true
}hive-103505upvoted (5.00%) @scipio / learn-ai-series-89-medical-and-scientific-imaging2026/06/06 07:10:27
hive-103505upvoted (5.00%) @scipio / learn-ai-series-89-medical-and-scientific-imaging
2026/06/06 07:10:27
| author | scipio |
| permlink | learn-ai-series-89-medical-and-scientific-imaging |
| voter | hive-103505 |
| weight | 500 (5.00%) |
| Transaction Info | Block #107028832/Trx 8bb6d8ad18471a31567b6d341c44cf0c26946f98 |
View Raw JSON Data
{
"block": 107028832,
"op": [
"vote",
{
"author": "scipio",
"permlink": "learn-ai-series-89-medical-and-scientific-imaging",
"voter": "hive-103505",
"weight": 500
}
],
"op_in_trx": 2,
"timestamp": "2026-06-06T07:10:27",
"trx_id": "8bb6d8ad18471a31567b6d341c44cf0c26946f98",
"trx_in_block": 0,
"virtual_op": false
}psychophiloeffective vote applied for @scipio / learn-ai-series-89-medical-and-scientific-imaging2026/06/06 07:10:27
psychophiloeffective vote applied for @scipio / learn-ai-series-89-medical-and-scientific-imaging
2026/06/06 07:10:27
| author | scipio |
| pending payout | 0.587 HBD |
| permlink | learn-ai-series-89-medical-and-scientific-imaging |
| rshares | 31890756 |
| total vote weight | 7827933538350 |
| voter | psychophilo |
| weight | 31890756 |
| Transaction Info | Block #107028832/Trx 8bb6d8ad18471a31567b6d341c44cf0c26946f98 |
View Raw JSON Data
{
"block": 107028832,
"op": [
"effective_comment_vote",
{
"author": "scipio",
"pending_payout": "0.587 HBD",
"permlink": "learn-ai-series-89-medical-and-scientific-imaging",
"rshares": 31890756,
"total_vote_weight": 7827933538350,
"voter": "psychophilo",
"weight": 31890756
}
],
"op_in_trx": 1,
"timestamp": "2026-06-06T07:10:27",
"trx_id": "8bb6d8ad18471a31567b6d341c44cf0c26946f98",
"trx_in_block": 0,
"virtual_op": true
}psychophiloupvoted (5.00%) @scipio / learn-ai-series-89-medical-and-scientific-imaging2026/06/06 07:10:27
psychophiloupvoted (5.00%) @scipio / learn-ai-series-89-medical-and-scientific-imaging
2026/06/06 07:10:27
| author | scipio |
| permlink | learn-ai-series-89-medical-and-scientific-imaging |
| voter | psychophilo |
| weight | 500 (5.00%) |
| Transaction Info | Block #107028832/Trx 8bb6d8ad18471a31567b6d341c44cf0c26946f98 |
View Raw JSON Data
{
"block": 107028832,
"op": [
"vote",
{
"author": "scipio",
"permlink": "learn-ai-series-89-medical-and-scientific-imaging",
"voter": "psychophilo",
"weight": 500
}
],
"op_in_trx": 0,
"timestamp": "2026-06-06T07:10:27",
"trx_id": "8bb6d8ad18471a31567b6d341c44cf0c26946f98",
"trx_in_block": 0,
"virtual_op": false
}we-are-aieffective vote applied for @scipio / learn-ai-series-89-medical-and-scientific-imaging2026/06/06 07:10:18
we-are-aieffective vote applied for @scipio / learn-ai-series-89-medical-and-scientific-imaging
2026/06/06 07:10:18
| author | scipio |
| pending payout | 0.587 HBD |
| permlink | learn-ai-series-89-medical-and-scientific-imaging |
| rshares | 32628042712 |
| total vote weight | 7827901647594 |
| voter | we-are-ai |
| weight | 32628042712 |
| Transaction Info | Block #107028829/Trx 55e0e401d967ec1d63bafeee7ca620c5a4443a94 |
View Raw JSON Data
{
"block": 107028829,
"op": [
"effective_comment_vote",
{
"author": "scipio",
"pending_payout": "0.587 HBD",
"permlink": "learn-ai-series-89-medical-and-scientific-imaging",
"rshares": 32628042712,
"total_vote_weight": 7827901647594,
"voter": "we-are-ai",
"weight": 32628042712
}
],
"op_in_trx": 1,
"timestamp": "2026-06-06T07:10:18",
"trx_id": "55e0e401d967ec1d63bafeee7ca620c5a4443a94",
"trx_in_block": 6,
"virtual_op": true
}we-are-aiupvoted (5.00%) @scipio / learn-ai-series-89-medical-and-scientific-imaging2026/06/06 07:10:18
we-are-aiupvoted (5.00%) @scipio / learn-ai-series-89-medical-and-scientific-imaging
2026/06/06 07:10:18
| author | scipio |
| permlink | learn-ai-series-89-medical-and-scientific-imaging |
| voter | we-are-ai |
| weight | 500 (5.00%) |
| Transaction Info | Block #107028829/Trx 55e0e401d967ec1d63bafeee7ca620c5a4443a94 |
View Raw JSON Data
{
"block": 107028829,
"op": [
"vote",
{
"author": "scipio",
"permlink": "learn-ai-series-89-medical-and-scientific-imaging",
"voter": "we-are-ai",
"weight": 500
}
],
"op_in_trx": 0,
"timestamp": "2026-06-06T07:10:18",
"trx_id": "55e0e401d967ec1d63bafeee7ca620c5a4443a94",
"trx_in_block": 6,
"virtual_op": false
}alexis555effective vote applied for @scipio / learn-ai-series-89-medical-and-scientific-imaging2026/06/06 07:09:48
alexis555effective vote applied for @scipio / learn-ai-series-89-medical-and-scientific-imaging
2026/06/06 07:09:48
| author | scipio |
| pending payout | 0.584 HBD |
| permlink | learn-ai-series-89-medical-and-scientific-imaging |
| rshares | 6288924139541 |
| total vote weight | 7795273604882 |
| voter | alexis555 |
| weight | 6288924139541 |
| Transaction Info | Block #107028819/Trx 62f1029f49b092b6f7d45eb29a26726a5d6d357a |
View Raw JSON Data
{
"block": 107028819,
"op": [
"effective_comment_vote",
{
"author": "scipio",
"pending_payout": "0.584 HBD",
"permlink": "learn-ai-series-89-medical-and-scientific-imaging",
"rshares": 6288924139541,
"total_vote_weight": 7795273604882,
"voter": "alexis555",
"weight": 6288924139541
}
],
"op_in_trx": 1,
"timestamp": "2026-06-06T07:09:48",
"trx_id": "62f1029f49b092b6f7d45eb29a26726a5d6d357a",
"trx_in_block": 8,
"virtual_op": true
}alexis555upvoted (41.00%) @scipio / learn-ai-series-89-medical-and-scientific-imaging2026/06/06 07:09:48
alexis555upvoted (41.00%) @scipio / learn-ai-series-89-medical-and-scientific-imaging
2026/06/06 07:09:48
| author | scipio |
| permlink | learn-ai-series-89-medical-and-scientific-imaging |
| voter | alexis555 |
| weight | 4100 (41.00%) |
| Transaction Info | Block #107028819/Trx 62f1029f49b092b6f7d45eb29a26726a5d6d357a |
View Raw JSON Data
{
"block": 107028819,
"op": [
"vote",
{
"author": "scipio",
"permlink": "learn-ai-series-89-medical-and-scientific-imaging",
"voter": "alexis555",
"weight": 4100
}
],
"op_in_trx": 0,
"timestamp": "2026-06-06T07:09:48",
"trx_id": "62f1029f49b092b6f7d45eb29a26726a5d6d357a",
"trx_in_block": 8,
"virtual_op": false
}newsrxeffective vote applied for @scipio / learn-ai-series-89-medical-and-scientific-imaging2026/06/06 07:05:51
newsrxeffective vote applied for @scipio / learn-ai-series-89-medical-and-scientific-imaging
2026/06/06 07:05:51
| author | scipio |
| pending payout | 0.112 HBD |
| permlink | learn-ai-series-89-medical-and-scientific-imaging |
| rshares | 2061290219 |
| total vote weight | 1506349465341 |
| voter | newsrx |
| weight | 2061290219 |
| Transaction Info | Block #107028740/Trx b3e4f896d176ca125c0b1cdd7b232663ba5de64f |
View Raw JSON Data
{
"block": 107028740,
"op": [
"effective_comment_vote",
{
"author": "scipio",
"pending_payout": "0.112 HBD",
"permlink": "learn-ai-series-89-medical-and-scientific-imaging",
"rshares": 2061290219,
"total_vote_weight": 1506349465341,
"voter": "newsrx",
"weight": 2061290219
}
],
"op_in_trx": 1,
"timestamp": "2026-06-06T07:05:51",
"trx_id": "b3e4f896d176ca125c0b1cdd7b232663ba5de64f",
"trx_in_block": 5,
"virtual_op": true
}newsrxupvoted (100.00%) @scipio / learn-ai-series-89-medical-and-scientific-imaging2026/06/06 07:05:51
newsrxupvoted (100.00%) @scipio / learn-ai-series-89-medical-and-scientific-imaging
2026/06/06 07:05:51
| author | scipio |
| permlink | learn-ai-series-89-medical-and-scientific-imaging |
| voter | newsrx |
| weight | 10000 (100.00%) |
| Transaction Info | Block #107028740/Trx b3e4f896d176ca125c0b1cdd7b232663ba5de64f |
View Raw JSON Data
{
"block": 107028740,
"op": [
"vote",
{
"author": "scipio",
"permlink": "learn-ai-series-89-medical-and-scientific-imaging",
"voter": "newsrx",
"weight": 10000
}
],
"op_in_trx": 0,
"timestamp": "2026-06-06T07:05:51",
"trx_id": "b3e4f896d176ca125c0b1cdd7b232663ba5de64f",
"trx_in_block": 5,
"virtual_op": false
}blue-witnesseffective vote applied for @scipio / learn-ai-series-89-medical-and-scientific-imaging2026/06/06 07:05:45
blue-witnesseffective vote applied for @scipio / learn-ai-series-89-medical-and-scientific-imaging
2026/06/06 07:05:45
| author | scipio |
| pending payout | 0.112 HBD |
| permlink | learn-ai-series-89-medical-and-scientific-imaging |
| rshares | 4185108046 |
| total vote weight | 1504288175122 |
| voter | blue-witness |
| weight | 4185108046 |
| Transaction Info | Block #107028738/Trx 08c63b58be1bdc55385090b0843b7b090b1258d2 |
View Raw JSON Data
{
"block": 107028738,
"op": [
"effective_comment_vote",
{
"author": "scipio",
"pending_payout": "0.112 HBD",
"permlink": "learn-ai-series-89-medical-and-scientific-imaging",
"rshares": 4185108046,
"total_vote_weight": 1504288175122,
"voter": "blue-witness",
"weight": 4185108046
}
],
"op_in_trx": 1,
"timestamp": "2026-06-06T07:05:45",
"trx_id": "08c63b58be1bdc55385090b0843b7b090b1258d2",
"trx_in_block": 9,
"virtual_op": true
}blue-witnessupvoted (100.00%) @scipio / learn-ai-series-89-medical-and-scientific-imaging2026/06/06 07:05:45
blue-witnessupvoted (100.00%) @scipio / learn-ai-series-89-medical-and-scientific-imaging
2026/06/06 07:05:45
| author | scipio |
| permlink | learn-ai-series-89-medical-and-scientific-imaging |
| voter | blue-witness |
| weight | 10000 (100.00%) |
| Transaction Info | Block #107028738/Trx 08c63b58be1bdc55385090b0843b7b090b1258d2 |
View Raw JSON Data
{
"block": 107028738,
"op": [
"vote",
{
"author": "scipio",
"permlink": "learn-ai-series-89-medical-and-scientific-imaging",
"voter": "blue-witness",
"weight": 10000
}
],
"op_in_trx": 0,
"timestamp": "2026-06-06T07:05:45",
"trx_id": "08c63b58be1bdc55385090b0843b7b090b1258d2",
"trx_in_block": 9,
"virtual_op": false
}steem-uaeffective vote applied for @scipio / learn-ai-series-89-medical-and-scientific-imaging2026/06/06 07:05:21
steem-uaeffective vote applied for @scipio / learn-ai-series-89-medical-and-scientific-imaging
2026/06/06 07:05:21
| author | scipio |
| pending payout | 0.112 HBD |
| permlink | learn-ai-series-89-medical-and-scientific-imaging |
| rshares | 513489055127 |
| total vote weight | 1500103067076 |
| voter | steem-ua |
| weight | 513489055127 |
| Transaction Info | Block #107028730/Trx fe3cf49ef4cf5e897f681ef4bc4b2ed0c6c2334b |
View Raw JSON Data
{
"block": 107028730,
"op": [
"effective_comment_vote",
{
"author": "scipio",
"pending_payout": "0.112 HBD",
"permlink": "learn-ai-series-89-medical-and-scientific-imaging",
"rshares": 513489055127,
"total_vote_weight": 1500103067076,
"voter": "steem-ua",
"weight": 513489055127
}
],
"op_in_trx": 1,
"timestamp": "2026-06-06T07:05:21",
"trx_id": "fe3cf49ef4cf5e897f681ef4bc4b2ed0c6c2334b",
"trx_in_block": 2,
"virtual_op": true
}steem-uaupvoted (100.00%) @scipio / learn-ai-series-89-medical-and-scientific-imaging2026/06/06 07:05:21
steem-uaupvoted (100.00%) @scipio / learn-ai-series-89-medical-and-scientific-imaging
2026/06/06 07:05:21
| author | scipio |
| permlink | learn-ai-series-89-medical-and-scientific-imaging |
| voter | steem-ua |
| weight | 10000 (100.00%) |
| Transaction Info | Block #107028730/Trx fe3cf49ef4cf5e897f681ef4bc4b2ed0c6c2334b |
View Raw JSON Data
{
"block": 107028730,
"op": [
"vote",
{
"author": "scipio",
"permlink": "learn-ai-series-89-medical-and-scientific-imaging",
"voter": "steem-ua",
"weight": 10000
}
],
"op_in_trx": 0,
"timestamp": "2026-06-06T07:05:21",
"trx_id": "fe3cf49ef4cf5e897f681ef4bc4b2ed0c6c2334b",
"trx_in_block": 2,
"virtual_op": false
}isnochyseffective vote applied for @scipio / learn-ai-series-89-medical-and-scientific-imaging2026/06/06 07:05:18
isnochyseffective vote applied for @scipio / learn-ai-series-89-medical-and-scientific-imaging
2026/06/06 07:05:18
| author | scipio |
| pending payout | 0.073 HBD |
| permlink | learn-ai-series-89-medical-and-scientific-imaging |
| rshares | 15882476354 |
| total vote weight | 986614011949 |
| voter | isnochys |
| weight | 15882476354 |
| Transaction Info | Block #107028729/Trx 7c0a00c8e7ee72b7f3aa7acf885764d211626774 |
View Raw JSON Data
{
"block": 107028729,
"op": [
"effective_comment_vote",
{
"author": "scipio",
"pending_payout": "0.073 HBD",
"permlink": "learn-ai-series-89-medical-and-scientific-imaging",
"rshares": 15882476354,
"total_vote_weight": 986614011949,
"voter": "isnochys",
"weight": 15882476354
}
],
"op_in_trx": 1,
"timestamp": "2026-06-06T07:05:18",
"trx_id": "7c0a00c8e7ee72b7f3aa7acf885764d211626774",
"trx_in_block": 5,
"virtual_op": true
}isnochysupvoted (5.00%) @scipio / learn-ai-series-89-medical-and-scientific-imaging2026/06/06 07:05:18
isnochysupvoted (5.00%) @scipio / learn-ai-series-89-medical-and-scientific-imaging
2026/06/06 07:05:18
| author | scipio |
| permlink | learn-ai-series-89-medical-and-scientific-imaging |
| voter | isnochys |
| weight | 500 (5.00%) |
| Transaction Info | Block #107028729/Trx 7c0a00c8e7ee72b7f3aa7acf885764d211626774 |
View Raw JSON Data
{
"block": 107028729,
"op": [
"vote",
{
"author": "scipio",
"permlink": "learn-ai-series-89-medical-and-scientific-imaging",
"voter": "isnochys",
"weight": 500
}
],
"op_in_trx": 0,
"timestamp": "2026-06-06T07:05:18",
"trx_id": "7c0a00c8e7ee72b7f3aa7acf885764d211626774",
"trx_in_block": 5,
"virtual_op": false
}blueroboeffective vote applied for @scipio / learn-ai-series-89-medical-and-scientific-imaging2026/06/06 07:05:15
blueroboeffective vote applied for @scipio / learn-ai-series-89-medical-and-scientific-imaging
2026/06/06 07:05:15
| author | scipio |
| pending payout | 0.072 HBD |
| permlink | learn-ai-series-89-medical-and-scientific-imaging |
| rshares | 769779157987 |
| total vote weight | 970731535595 |
| voter | bluerobo |
| weight | 769779157987 |
| Transaction Info | Block #107028728/Trx fd2afeec1b74feee6ccbbc2d7141bd157c609de8 |
View Raw JSON Data
{
"block": 107028728,
"op": [
"effective_comment_vote",
{
"author": "scipio",
"pending_payout": "0.072 HBD",
"permlink": "learn-ai-series-89-medical-and-scientific-imaging",
"rshares": 769779157987,
"total_vote_weight": 970731535595,
"voter": "bluerobo",
"weight": 769779157987
}
],
"op_in_trx": 1,
"timestamp": "2026-06-06T07:05:15",
"trx_id": "fd2afeec1b74feee6ccbbc2d7141bd157c609de8",
"trx_in_block": 14,
"virtual_op": true
}blueroboupvoted (100.00%) @scipio / learn-ai-series-89-medical-and-scientific-imaging2026/06/06 07:05:15
blueroboupvoted (100.00%) @scipio / learn-ai-series-89-medical-and-scientific-imaging
2026/06/06 07:05:15
| author | scipio |
| permlink | learn-ai-series-89-medical-and-scientific-imaging |
| voter | bluerobo |
| weight | 10000 (100.00%) |
| Transaction Info | Block #107028728/Trx fd2afeec1b74feee6ccbbc2d7141bd157c609de8 |
View Raw JSON Data
{
"block": 107028728,
"op": [
"vote",
{
"author": "scipio",
"permlink": "learn-ai-series-89-medical-and-scientific-imaging",
"voter": "bluerobo",
"weight": 10000
}
],
"op_in_trx": 0,
"timestamp": "2026-06-06T07:05:15",
"trx_id": "fd2afeec1b74feee6ccbbc2d7141bd157c609de8",
"trx_in_block": 14,
"virtual_op": false
}scipioeffective vote applied for @scipio / learn-ai-series-89-medical-and-scientific-imaging2026/06/06 07:05:15
scipioeffective vote applied for @scipio / learn-ai-series-89-medical-and-scientific-imaging
2026/06/06 07:05:15
| author | scipio |
| pending payout | 0.015 HBD |
| permlink | learn-ai-series-89-medical-and-scientific-imaging |
| rshares | 200952377608 |
| total vote weight | 200952377608 |
| voter | scipio |
| weight | 200952377608 |
| Transaction Info | Block #107028728/Trx d91ad66d63031b0fcb004603ef7b51da10a069db |
View Raw JSON Data
{
"block": 107028728,
"op": [
"effective_comment_vote",
{
"author": "scipio",
"pending_payout": "0.015 HBD",
"permlink": "learn-ai-series-89-medical-and-scientific-imaging",
"rshares": 200952377608,
"total_vote_weight": 200952377608,
"voter": "scipio",
"weight": 200952377608
}
],
"op_in_trx": 1,
"timestamp": "2026-06-06T07:05:15",
"trx_id": "d91ad66d63031b0fcb004603ef7b51da10a069db",
"trx_in_block": 11,
"virtual_op": true
}scipioupvoted (100.00%) @scipio / learn-ai-series-89-medical-and-scientific-imaging2026/06/06 07:05:15
scipioupvoted (100.00%) @scipio / learn-ai-series-89-medical-and-scientific-imaging
2026/06/06 07:05:15
| author | scipio |
| permlink | learn-ai-series-89-medical-and-scientific-imaging |
| voter | scipio |
| weight | 10000 (100.00%) |
| Transaction Info | Block #107028728/Trx d91ad66d63031b0fcb004603ef7b51da10a069db |
View Raw JSON Data
{
"block": 107028728,
"op": [
"vote",
{
"author": "scipio",
"permlink": "learn-ai-series-89-medical-and-scientific-imaging",
"voter": "scipio",
"weight": 10000
}
],
"op_in_trx": 0,
"timestamp": "2026-06-06T07:05:15",
"trx_id": "d91ad66d63031b0fcb004603ef7b51da10a069db",
"trx_in_block": 11,
"virtual_op": false
}scipiopublished a new post: learn-ai-series-89-medical-and-scientific-imaging2026/06/06 07:05:09
scipiopublished a new post: learn-ai-series-89-medical-and-scientific-imaging
2026/06/06 07:05:09
| author | scipio |
| body | # Learn AI Series (#89) - Medical and Scientific Imaging  ### What will I learn - You will learn the unique challenges of medical imaging: small datasets, class imbalance, and regulatory requirements; - transfer learning from natural images to medical domains and why it works despite massive visual differences; - data augmentation strategies specific to medical images (and which standard augmentations will break your model); - handling class imbalance when rare conditions are what matter most; - explainability requirements in healthcare AI and how Grad-CAM reveals what your model actually learned; - regulatory considerations for deploying AI in clinical settings; - scientific imaging beyond healthcare: satellite data, microscopy, and astronomical image analysis. ### Requirements - A working modern computer running macOS, Windows or Ubuntu; - An installed Python 3(.11+) distribution; - The ambition to learn AI and machine learning. ### Difficulty - Beginner ### Curriculum (of the `Learn AI Series`): - [Learn AI Series (#1) - What Machine Learning Actually Is](https://hive.blog/hive-196387/@scipio/learn-ai-series-1-what-machine-learning-actually-is) - [Learn AI Series (#2) - Setting Up Your AI Workbench - Python and NumPy](https://hive.blog/hive-196387/@scipio/learn-ai-series-2-setting-up-your-ai-workbench-python-and-numpy) - [Learn AI Series (#3) - Your Data Is Just Numbers - How Machines See the World](https://hive.blog/hive-196387/@scipio/learn-ai-series-3-your-data-is-just-numbers-how-machines-see-the-world) - [Learn AI Series (#4) - Your First Prediction - No Math, Just Intuition](https://hive.blog/hive-196387/@scipio/learn-ai-series-4-your-first-prediction-no-math-just-intuition) - [Learn AI Series (#5) - Patterns in Data - What "Learning" Actually Looks Like](https://hive.blog/hive-196387/@scipio/learn-ai-series-5-patterns-in-data-what-learning-actually-looks-like) - [Learn AI Series (#6) - From Intuition to Math - Why We Need Formulas](https://hive.blog/hive-196387/@scipio/learn-ai-series-6-from-intuition-to-math-why-we-need-formulas) - [Learn AI Series (#7) - The Training Loop - See It Work Step by Step](https://hive.blog/hive-196387/@scipio/learn-ai-series-7-the-training-loop-see-it-work-step-by-step) - [Learn AI Series (#8) - The Math You Actually Need (Part 1) - Linear Algebra](https://hive.blog/hive-196387/@scipio/learn-ai-series-8-the-math-you-actually-need-part-1-linear-algebra) - [Learn AI Series (#9) - The Math You Actually Need (Part 2) - Calculus and Probability](https://hive.blog/hive-196387/@scipio/learn-ai-series-9-the-math-you-actually-need-part-2-calculus-and-probability) - [Learn AI Series (#10) - Your First ML Model - Linear Regression From Scratch](https://hive.blog/hive-196387/@scipio/learn-ai-series-10-your-first-ml-model-linear-regression-from-scratch) - [Learn AI Series (#11) - Making Linear Regression Real](https://hive.blog/hive-196387/@scipio/learn-ai-series-11-making-linear-regression-real) - [Learn AI Series (#12) - Classification - Logistic Regression From Scratch](https://hive.blog/hive-196387/@scipio/learn-ai-series-12-classification-logistic-regression-from-scratch) - [Learn AI Series (#13) - Evaluation - How to Know If Your Model Actually Works](https://hive.blog/hive-196387/@scipio/learn-ai-series-13-evaluation-how-to-know-if-your-model-actually-works) - [Learn AI Series (#14) - Data Preparation - The 80% Nobody Talks About](https://hive.blog/hive-196387/@scipio/learn-ai-series-14-data-preparation-the-80-nobody-talks-about) - [Learn AI Series (#15) - Feature Engineering and Selection](https://hive.blog/hive-196387/@scipio/learn-ai-series-15-feature-engineering-and-selection) - [Learn AI Series (#16) - Scikit-Learn - The Standard Library of ML](https://hive.blog/hive-196387/@scipio/learn-ai-series-16-scikit-learn-the-standard-library-of-ml) - [Learn AI Series (#17) - Decision Trees - How Machines Make Decisions](https://hive.blog/hive-196387/@scipio/learn-ai-series-17-decision-trees-how-machines-make-decisions) - [Learn AI Series (#18) - Random Forests - Wisdom of Crowds](https://hive.blog/hive-196387/@scipio/learn-ai-series-18-random-forests-wisdom-of-crowds) - [Learn AI Series (#19) - Gradient Boosting - The Kaggle Champion](https://hive.blog/hive-196387/@scipio/learn-ai-series-19-gradient-boosting-the-kaggle-champion) - [Learn AI Series (#20) - Support Vector Machines - Drawing the Perfect Boundary](https://hive.blog/hive-196387/@scipio/learn-ai-series-20-support-vector-machines-drawing-the-perfect-boundary) - [Learn AI Series (#21) - Mini Project - Predicting Crypto Market Regimes](https://hive.blog/hive-196387/@scipio/learn-ai-series-21-mini-project-predicting-crypto-market-regimes) - [Learn AI Series (#22) - K-Means Clustering - Finding Groups](https://hive.blog/hive-196387/@scipio/learn-ai-series-22-k-means-clustering-finding-groups) - [Learn AI Series (#23) - Advanced Clustering - Beyond K-Means](https://hive.blog/hive-196387/@scipio/learn-ai-series-23-advanced-clustering-beyond-k-means) - [Learn AI Series (#24) - Dimensionality Reduction - PCA](https://hive.blog/hive-196387/@scipio/learn-ai-series-24-dimensionality-reduction-pca) - [Learn AI Series (#25) - Advanced Dimensionality Reduction - t-SNE and UMAP](https://hive.blog/hive-196387/@scipio/learn-ai-series-25-advanced-dimensionality-reduction-t-sne-and-umap) - [Learn AI Series (#26) - Anomaly Detection - Finding What Doesn't Belong](https://hive.blog/hive-196387/@scipio/learn-ai-series-26-anomaly-detection-finding-what-doesnt-belong) - [Learn AI Series (#27) - Recommendation Systems - "Users Like You Also Liked..."](https://hive.blog/hive-196387/@scipio/learn-ai-series-27-recommendation-systems-users-like-you-also-liked) - [Learn AI Series (#28) - Time Series Fundamentals - When Order Matters](https://hive.blog/hive-196387/@scipio/learn-ai-series-28-time-series-fundamentals-when-order-matters) - [Learn AI Series (#29) - Time Series Forecasting - Predicting What Comes Next](https://hive.blog/hive-196387/@scipio/learn-ai-series-29-time-series-forecasting-predicting-what-comes-next) - [Learn AI Series (#30) - Natural Language Processing - Text as Data](https://hive.blog/hive-196387/@scipio/learn-ai-series-30-natural-language-processing-text-as-data) - [Learn AI Series (#31) - Word Embeddings - Meaning in Numbers](https://hive.blog/hive-196387/@scipio/learn-ai-series-31-word-embeddings-meaning-in-numbers) - [Learn AI Series (#32) - Bayesian Methods - Thinking in Probabilities](https://hive.blog/hive-196387/@scipio/learn-ai-series-32-bayesian-methods-thinking-in-probabilities) - [Learn AI Series (#33) - Ensemble Methods Deep Dive - Stacking and Blending](https://hive.blog/hive-196387/@scipio/learn-ai-series-33-ensemble-methods-deep-dive-stacking-and-blending) - [Learn AI Series (#34) - ML Engineering - From Notebook to Production](https://hive.blog/hive-196387/@scipio/learn-ai-series-34-ml-engineering-from-notebook-to-production) - [Learn AI Series (#35) - Data Ethics and Bias in ML](https://hive.blog/hive-196387/@scipio/learn-ai-series-35-data-ethics-and-bias-in-ml) - [Learn AI Series (#36) - Mini Project - Complete ML Pipeline](https://hive.blog/hive-196387/@scipio/learn-ai-series-36-mini-project-complete-ml-pipeline) - [Learn AI Series (#37) - The Perceptron - Where It All Started](https://hive.blog/hive-196387/@scipio/learn-ai-series-37-the-perceptron-where-it-all-started) - [Learn AI Series (#38) - Neural Networks From Scratch - Forward Pass](https://hive.blog/hive-196387/@scipio/learn-ai-series-38-neural-networks-from-scratch-forward-pass) - [Learn AI Series (#39) - Neural Networks From Scratch - Backpropagation](https://hive.blog/hive-196387/@scipio/learn-ai-series-39-neural-networks-from-scratch-backpropagation) - [Learn AI Series (#40) - Training Neural Networks - Practical Challenges](https://hive.blog/hive-196387/@scipio/learn-ai-series-40-training-neural-networks-practical-challenges) - [Learn AI Series (#41) - Optimization Algorithms - SGD, Momentum, Adam](https://hive.blog/hive-196387/@scipio/learn-ai-series-41-optimization-algorithms-sgd-momentum-adam) - [Learn AI Series (#42) - PyTorch Fundamentals - Tensors and Autograd](https://hive.blog/hive-196387/@scipio/learn-ai-series-42-pytorch-fundamentals-tensors-and-autograd) - [Learn AI Series (#43) - PyTorch Data and Training](https://hive.blog/hive-196387/@scipio/learn-ai-series-43-pytorch-data-and-training) - [Learn AI Series (#44) - PyTorch nn.Module - Building Real Networks](https://hive.blog/hive-196387/@scipio/learn-ai-series-44-pytorch-nnmodule-building-real-networks) - [Learn AI Series (#45) - Convolutional Neural Networks - Theory](https://hive.blog/hive-196387/@scipio/learn-ai-series-45-convolutional-neural-networks-theory) - [Learn AI Series (#46) - CNNs in Practice - Classic to Modern Architectures](https://hive.blog/hive-196387/@scipio/learn-ai-series-46-cnns-in-practice-classic-to-modern-architectures) - [Learn AI Series (#47) - CNN Applications - Detection, Segmentation, Style Transfer](https://hive.blog/hive-196387/@scipio/learn-ai-series-47-cnn-applications-detection-segmentation-style-transfer) - [Learn AI Series (#48) - Recurrent Neural Networks - Sequences](https://hive.blog/hive-196387/@scipio/learn-ai-series-48-recurrent-neural-networks-sequences) - [Learn AI Series (#49) - LSTM and GRU - Solving the Memory Problem](https://hive.blog/hive-196387/@scipio/learn-ai-series-49-lstm-and-gru-solving-the-memory-problem) - [Learn AI Series (#50) - Sequence-to-Sequence Models](https://hive.blog/hive-196387/@scipio/learn-ai-series-50-sequence-to-sequence-models) - [Learn AI Series (#51) - Attention Mechanisms](https://hive.blog/hive-196387/@scipio/learn-ai-series-51-attention-mechanisms) - [Learn AI Series (#52) - The Transformer Architecture (Part 1)](https://hive.blog/hive-196387/@scipio/learn-ai-series-52-the-transformer-architecture-part-1) - [Learn AI Series (#53) - The Transformer Architecture (Part 2)](https://hive.blog/hive-196387/@scipio/learn-ai-series-53-the-transformer-architecture-part-2) - [Learn AI Series (#54) - Vision Transformers](https://hive.blog/hive-196387/@scipio/learn-ai-series-54-vision-transformers) - [Learn AI Series (#55) - Generative Adversarial Networks](https://hive.blog/hive-196387/@scipio/learn-ai-series-55-generative-adversarial-networks) - [Learn AI Series (#56) - Mini Project - Building a Transformer From Scratch](https://hive.blog/hive-196387/@scipio/learn-ai-series-56-mini-project-building-a-transformer-from-scratch) - [Learn AI Series (#57) - Language Modeling - Predicting the Next Word](https://hive.blog/hive-196387/@scipio/learn-ai-series-57-language-modeling-predicting-the-next-word) - [Learn AI Series (#58) - GPT Architecture - Decoder-Only Transformers](https://hive.blog/hive-196387/@scipio/learn-ai-series-58-gpt-architecture-decoder-only-transformers) - [Learn AI Series (#59) - BERT and Encoder Models](https://hive.blog/hive-196387/@scipio/learn-ai-series-59-bert-and-encoder-models) - [Learn AI Series (#60) - Training Large Language Models](https://hive.blog/hive-196387/@scipio/learn-ai-series-60-training-large-language-models) - [Learn AI Series (#61) - Instruction Tuning and Alignment](https://hive.blog/hive-196387/@scipio/learn-ai-series-61-instruction-tuning-and-alignment) - [Learn AI Series (#62) - Prompt Engineering - Getting the Most from LLMs](https://hive.blog/hive-196387/@scipio/learn-ai-series-62-prompt-engineering-getting-the-most-from-llms) - [Learn AI Series (#63) - Embeddings and Vector Search](https://hive.blog/hive-196387/@scipio/learn-ai-series-63-embeddings-and-vector-search) - [Learn AI Series (#64) - Retrieval-Augmented Generation (RAG) - Basics](https://hive.blog/hive-196387/@scipio/learn-ai-series-64-retrieval-augmented-generation-rag-basics) - [Learn AI Series (#65) - RAG - Advanced Techniques](https://hive.blog/hive-196387/@scipio/learn-ai-series-65-rag-advanced-techniques) - [Learn AI Series (#66) - Working with LLM APIs](https://hive.blog/hive-196387/@scipio/learn-ai-series-66-working-with-llm-apis) - [Learn AI Series (#67) - Building AI Agents (Part 1) - Foundations](https://hive.blog/hive-196387/@scipio/learn-ai-series-67-building-ai-agents-part-1-foundations) - [Learn AI Series (#68) - Building AI Agents (Part 2) - Advanced Patterns](https://hive.blog/hive-196387/@scipio/learn-ai-series-68-building-ai-agents-part-2-advanced-patterns) - [Learn AI Series (#69) - Fine-Tuning Language Models](https://hive.blog/hive-196387/@scipio/learn-ai-series-69-fine-tuning-language-models) - [Learn AI Series (#70) - Running Local Models](https://hive.blog/hive-196387/@scipio/learn-ai-series-70-running-local-models) - [Learn AI Series (#71) - Text Generation Techniques](https://hive.blog/hive-196387/@scipio/learn-ai-series-71-text-generation-techniques) - [Learn AI Series (#72) - Tokenization Deep Dive](https://hive.blog/hive-196387/@scipio/learn-ai-series-72-tokenization-deep-dive) - [Learn AI Series (#73) - LLM Evaluation](https://hive.blog/hive-196387/@scipio/learn-ai-series-73-llm-evaluation) - [Learn AI Series (#74) - The Hugging Face Ecosystem](https://hive.blog/hive-196387/@scipio/learn-ai-series-74-the-hugging-face-ecosystem) - [Learn AI Series (#75) - Multimodal Models - Text Meets Vision](https://hive.blog/hive-196387/@scipio/learn-ai-series-75-multimodal-models-text-meets-vision) - [Learn AI Series (#76) - Mini Project - Your Own AI Assistant](https://hive.blog/hive-196387/@scipio/learn-ai-series-76-mini-project-your-own-ai-assistant) - [Learn AI Series (#77) - Image Processing Fundamentals](https://hive.blog/hive-196387/@scipio/learn-ai-series-77-image-processing-fundamentals) - [Learn AI Series (#78) - Object Detection (Part 1) - Foundations](https://hive.blog/hive-196387/@scipio/learn-ai-series-78-object-detection-part-1-foundations) - [Learn AI Series (#79) - Object Detection (Part 2) - Modern Approaches](https://hive.blog/hive-196387/@scipio/learn-ai-series-79-object-detection-part-2-modern-approaches) - [Learn AI Series (#80) - Image Segmentation](https://hive.blog/hive-196387/@scipio/learn-ai-series-80-image-segmentation) - [Learn AI Series (#81) - Pose Estimation and Tracking](https://hive.blog/hive-196387/@scipio/learn-ai-series-81-pose-estimation-and-tracking) - [Learn AI Series (#82) - Optical Character Recognition](https://hive.blog/hive-196387/@scipio/learn-ai-series-82-optical-character-recognition) - [Learn AI Series (#83) - Video Understanding](https://hive.blog/hive-196387/@scipio/learn-ai-series-83-video-understanding) - [Learn AI Series (#84) - Generative Images - Diffusion Models (Part 1)](https://hive.blog/hive-196387/@scipio/learn-ai-series-84-generative-images-diffusion-models-part-1) - [Learn AI Series (#85) - Generative Images - Diffusion Models (Part 2)](https://hive.blog/hive-196387/@scipio/learn-ai-series-85-generative-images-diffusion-models-part-2) - [Learn AI Series (#86) - Image-to-Image and Editing](https://hive.blog/hive-196387/@scipio/learn-ai-series-86-image-to-image-and-editing) - [Learn AI Series (#87) - 3D Vision](https://hive.blog/hive-196387/@scipio/learn-ai-series-87-3d-vision) - [Learn AI Series (#88) - Face Analysis](https://hive.blog/hive-196387/@scipio/learn-ai-series-88-face-analysis) - [Learn AI Series (#89) - Medical and Scientific Imaging](https://hive.blog/hive-196387/@scipio/learn-ai-series-89-medical-and-scientific-imaging) (this post) # Learn AI Series (#89) - Medical and Scientific Imaging ### Solutions to Episode #88 Exercises **Exercise 1:** Face embedding similarity analyzer. ```python import numpy as np class EmbeddingSimilarityAnalyzer: """Analyze face embedding similarity distributions for identity verification.""" def __init__(self, num_ids=5, photos_per=10, embed_dim=512, noise_sigma=0.15, seed=42): rng = np.random.RandomState(seed) self.num_ids = num_ids self.photos_per = photos_per self.labels = [] self.embeddings = [] for i in range(num_ids): centroid = rng.randn(embed_dim) centroid /= np.linalg.norm(centroid) for _ in range(photos_per): variant = centroid + rng.randn( embed_dim) * noise_sigma variant /= np.linalg.norm(variant) self.embeddings.append(variant) self.labels.append(i) self.embeddings = np.array(self.embeddings) self.labels = np.array(self.labels) def cosine_matrix(self): norms = np.linalg.norm( self.embeddings, axis=1, keepdims=True) normed = self.embeddings / norms return normed @ normed.T def analyze(self): sim = self.cosine_matrix() n = len(self.labels) same_sims = [] diff_sims = [] for i in range(n): for j in range(i + 1, n): if self.labels[i] == self.labels[j]: same_sims.append(sim[i, j]) else: diff_sims.append(sim[i, j]) same_sims = np.array(same_sims) diff_sims = np.array(diff_sims) print(f"Same-identity mean: " f"{same_sims.mean():.4f}") print(f"Diff-identity mean: " f"{diff_sims.mean():.4f}") print(f"Hardest positive: " f"{same_sims.min():.4f}") print(f"Hardest negative: " f"{diff_sims.max():.4f}") gap = same_sims.min() - diff_sims.max() print(f"Gap: {gap:.4f}") print(f"\n{'Thresh':>7} {'TPR':>6} " f"{'FPR':>6} {'Acc':>6}") print("-" * 28) best_acc, best_t = 0, 0 for t in [0.3, 0.4, 0.5, 0.6, 0.7, 0.8]: tp = (same_sims >= t).mean() fp = (diff_sims >= t).mean() acc = ((same_sims >= t).sum() + (diff_sims < t).sum()) / ( len(same_sims) + len(diff_sims)) if acc > best_acc: best_acc = acc best_t = t print(f"{t:>7.1f} {tp:>6.3f} " f"{fp:>6.3f} {acc:>6.3f}") print(f"\nBest: thresh={best_t}, " f"acc={best_acc:.3f}") return same_sims, diff_sims analyzer = EmbeddingSimilarityAnalyzer() analyzer.analyze() ``` With noise_sigma=0.15, the same-identity pairs cluster around cosine similarity 0.85-0.95 (high, because the noise is modest relative to the 512-dimensional centroid), while different-identity pairs settle near 0.0 (random unit vectors in 512D are nearly orthogonal). The gap between the hardest positive and hardest negative is comfortably positive, meaning a single threshold cleanly separates all identities. If you increase noise_sigma to 0.5 or higher, the same-identity distribution spreads out and starts overlapping with the different-identity distribution -- the gap shrinks toward zero and no threshold achieves perfect accuracy. This directly demonstrates why face recognition systems demand well-lit, frontal, aligned crops: reducing noise in the embedding space widens the gap. **Exercise 2:** Landmark-based face alignment tool. ```python import numpy as np class FaceAligner: """Align faces using 5-point landmarks via affine transformation.""" def __init__(self, target_eye_dist=70, target_center=(112, 112)): self.target_dist = target_eye_dist self.target_cx = target_center[0] self.target_cy = target_center[1] def compute_alignment(self, left_eye, right_eye): dx = right_eye[0] - left_eye[0] dy = right_eye[1] - left_eye[1] angle = np.degrees(np.arctan2(dy, dx)) dist = np.sqrt(dx ** 2 + dy ** 2) scale = self.target_dist / max( dist, 1e-8) cos_a = np.cos(np.radians(-angle)) sin_a = np.sin(np.radians(-angle)) R = np.array([[cos_a, -sin_a], [sin_a, cos_a]]) * scale mid_x = (left_eye[0] + right_eye[0]) / 2 mid_y = (left_eye[1] + right_eye[1]) / 2 tx = self.target_cx - ( R[0, 0] * mid_x + R[0, 1] * mid_y) ty = self.target_cy - ( R[1, 0] * mid_x + R[1, 1] * mid_y) M = np.zeros((2, 3)) M[:2, :2] = R M[0, 2] = tx M[1, 2] = ty return M, angle, dist def apply_affine(self, points, M): pts = np.array(points) ones = np.ones((len(pts), 1)) aug = np.hstack([pts, ones]) return (M @ aug.T).T def run(self): rng = np.random.RandomState(42) print(f"{'Case':>5} {'InAngle':>8} " f"{'OutAngle':>9} {'InDist':>7} " f"{'OutDist':>8} {'EyeCenter':>14}") print("-" * 56) for i in range(5): angle = rng.uniform(-30, 30) dist = rng.uniform(40, 120) cx = rng.uniform(80, 160) cy = rng.uniform(80, 160) rad = np.radians(angle) le = (cx - dist / 2 * np.cos(rad), cy - dist / 2 * np.sin(rad)) re = (cx + dist / 2 * np.cos(rad), cy + dist / 2 * np.sin(rad)) M, in_angle, in_dist = ( self.compute_alignment(le, re)) landmarks = [le, re, (cx, cy + 20), (cx - 15, cy + 40), (cx + 15, cy + 40)] aligned = self.apply_affine( landmarks, M) out_dx = aligned[1, 0] - aligned[0, 0] out_dy = aligned[1, 1] - aligned[0, 1] out_angle = np.degrees( np.arctan2(out_dy, out_dx)) out_dist = np.sqrt( out_dx ** 2 + out_dy ** 2) eye_cx = (aligned[0, 0] + aligned[1, 0]) / 2 eye_cy = (aligned[0, 1] + aligned[1, 1]) / 2 print(f"{i + 1:>5} {in_angle:>8.1f} " f"{out_angle:>9.4f} " f"{in_dist:>7.1f} " f"{out_dist:>8.1f} " f"({eye_cx:.0f}, {eye_cy:.0f})") aligner = FaceAligner() aligner.run() ``` Every test case produces an aligned eye angle of essentially 0 degrees (within floating point precision), an inter-eye distance of exactly 70 pixels, and eye centers at (112, 112) -- regardless of the input rotation, scale, or position. This consistency is exactly why alignment matters for face recognition: the downstream embedding network sees faces in a canonical pose every time, so it can focus all of its capacity on identity-discriminative features rather than wasting parameters on learning to handle rotations and scale variations. **Exercise 3:** Expression confusion matrix analyzer. ```python import numpy as np class ExpressionAnalyzer: """Analyze expression classification confusion patterns.""" EXPRESSIONS = [ "angry", "disgusted", "fearful", "happy", "neutral", "sad", "surprised" ] def __init__(self, samples_per=100, seed=42): self.n = samples_per self.seed = seed def generate_confusion(self, correct_probs= None, boost=None): rng = np.random.RandomState(self.seed) if correct_probs is None: correct_probs = { "angry": 0.65, "disgusted": 0.60, "fearful": 0.60, "happy": 0.80, "neutral": 0.70, "sad": 0.68, "surprised": 0.72} if boost: for cls, amt in boost.items(): correct_probs[cls] = min( correct_probs[cls] + amt, 0.95) confusion_pairs = { ("angry", "disgusted"): 0.12, ("disgusted", "angry"): 0.12, ("fearful", "surprised"): 0.15, ("surprised", "fearful"): 0.10, ("sad", "neutral"): 0.08, ("neutral", "sad"): 0.06} nc = len(self.EXPRESSIONS) cm = np.zeros((nc, nc), dtype=int) for i, expr in enumerate(self.EXPRESSIONS): p_correct = correct_probs[expr] remaining = 1.0 - p_correct probs = np.zeros(nc) probs[i] = p_correct used = 0.0 for (a, b), p in confusion_pairs.items(): if a == expr: j = self.EXPRESSIONS.index(b) probs[j] = min(p, remaining) used += probs[j] leftover = remaining - used for j in range(nc): if j != i and probs[j] == 0: probs[j] = leftover / max( nc - 1 - sum( 1 for k in range(nc) if k != i and probs[k] > 0), 1) probs /= probs.sum() cm[i] = rng.multinomial(self.n, probs) return cm def metrics(self, cm): nc = cm.shape[0] results = {} for i, expr in enumerate(self.EXPRESSIONS): tp = cm[i, i] fp = cm[:, i].sum() - tp fn = cm[i, :].sum() - tp prec = tp / max(tp + fp, 1) rec = tp / max(tp + fn, 1) f1 = (2 * prec * rec / max(prec + rec, 1e-8)) results[expr] = { "precision": prec, "recall": rec, "f1": f1} return results def run(self): cm = self.generate_confusion() # Print confusion matrix header = " " + " ".join( f"{e[:4]:>5}" for e in self.EXPRESSIONS) print(header) for i, expr in enumerate( self.EXPRESSIONS): row = f"{expr[:4]:>5} " + " ".join( f"{cm[i, j]:>5}" for j in range(len( self.EXPRESSIONS))) print(row) m = self.metrics(cm) print(f"\n{'Class':<12} {'Prec':>6} " f"{'Rec':>6} {'F1':>6}") print("-" * 32) for expr in self.EXPRESSIONS: d = m[expr] print(f"{expr:<12} " f"{d['precision']:>6.3f} " f"{d['recall']:>6.3f} " f"{d['f1']:>6.3f}") # Most confused pairs nc = len(self.EXPRESSIONS) pairs = [] for i in range(nc): for j in range(nc): if i != j: pairs.append( (cm[i, j], self.EXPRESSIONS[i], self.EXPRESSIONS[j])) pairs.sort(reverse=True) print(f"\nTop confused pairs:") for cnt, a, b in pairs[:4]: print(f" {a} -> {b}: {cnt}") ranked = sorted(m.items(), key=lambda x: x[1]["f1"]) print(f"\nDifficulty ranking (worst F1 " f"first):") for expr, d in ranked: print(f" {expr}: F1={d['f1']:.3f}") # Boost worst two worst = [ranked[0][0], ranked[1][0]] print(f"\nBoosting {worst} by +0.05:") cm2 = self.generate_confusion( boost={w: 0.05 for w in worst}) m2 = self.metrics(cm2) for w in worst: print(f" {w}: F1 {m[w]['f1']:.3f}" f" -> {m2[w]['f1']:.3f}") analyzer = ExpressionAnalyzer() analyzer.run() ``` The angry/disgusted and fearful/surprised pairs dominate the off-diagonal entries -- exactly as configured, and consistent with real expression recognition research. These pairs are genuinely hard because the underlying facial muscle movements overlap: anger and disgust both involve brow lowering and nose wrinkling, while fear and surprise both involve wide eyes and raised eyebrows. Happy achieves the highest F1 because smiling is the most visually distinctive expression (unique cheek raise + lip corner pull), while disgusted and fearful rank lowest due to their high mutual confusion rates. Adding 0.05 to the correct-class probability for the two worst performers improves their F1 scores modestly, demonstrating the diminishing returns of more data when the fundamental visual similarity between confusable classes remains. ### On to today's episode Here we go! We've spent the past thirteen episodes building a thorough understanding of computer vision: from basic image processing (#77) through object detection (#78-79), segmentation (#80), pose estimation (#81), OCR (#82), video (#83), diffusion models (#84-85), image editing (#86), 3D reconstruction (#87), and face analysis (#88). That entire arc dealt with **natural images** -- photographs of everyday scenes, objects, people, and places. But some of the highest-impact applications of computer vision don't involve natural photos at all. They involve **medical images**: chest X-rays, retinal scans, pathology slides, CT volumes. And beyond medicine, there's a whole universe of **scientific imaging**: satellite photos, microscopy, spectrograms, astronomical observations. These domains share a common property -- the images look nothing like ImageNet, the datasets are small and expensive to label, and getting the answer wrong can have serious consequences. This episode covers how to adapt everything we've learned to domains where the stakes are literally life-and-death, and where the standard "download a big dataset, train a deep model, deploy" pipeline falls apart completely ;-) ## Why medical imaging is fundamentally different If you've been following along since episode #13 (evaluation) and #14 (data preparation), you already know that data quality matters more than model architecture. Medical imaging takes every data challenge from those episodes and turns the dial to 11. **Small datasets**: a hospital might have 500 labeled chest X-rays showing a rare condition. Compare that to ImageNet's 14 million images. You can't just train a deeper model and hope it generalizes -- you'll overfit before the first epoch finishes. **Annotation cost**: labeling a single medical image often requires a board-certified specialist spending 10-30 minutes. Pixel-level segmentation masks (delineating tumor boundaries on a pathology slide) can take _hours_ per image. Some images need consensus from multiple experts because even specialists disagree on borderline cases. You can't crowdsource this on Mechanical Turk like you can with "is there a cat in this picture?" **Class imbalance**: in screening applications, the positive rate might be 1-5%. A model that always predicts "healthy" achieves 95%+ accuracy while being completely useless. Remember the precision-recall tradeoff from episode #13? This is where it becomes a matter of life and death. **High stakes**: a false negative (missed cancer) can kill. A false positive (unnecessary biopsy) causes harm, stress, and expense. The error _profile_ matters far more than the aggregate accuracy number. Telling a doctor "my model is 97% accurate" is meaningless without specifying sensitivity and specificity separately. **Domain shift**: a model trained on X-rays from Hospital A's GE scanner may fail on Hospital B's Siemens scanner. Different imaging protocols, sensor characteristics, patient demographics, and even the brand of contrast dye can shift the data distribution enough to break a model that worked perfectly in development. This is the same distribution shift problem from episode #14, but with much higher consequences. ```python import numpy as np class MedicalDatasetProfiler: """Profile a medical imaging dataset for common challenges: imbalance, size, and annotation cost estimation.""" def __init__(self, n_total, n_positive, annotation_minutes=15): self.n_total = n_total self.n_pos = n_positive self.n_neg = n_total - n_positive self.ann_min = annotation_minutes def profile(self): ratio = self.n_pos / self.n_total imbalance = self.n_neg / max( self.n_pos, 1) # Estimate annotation budget total_hours = ( self.n_total * self.ann_min / 60) cost_per_hour = 150 # specialist rate total_cost = total_hours * cost_per_hour # Effective training size accounting # for imbalance effective = min( self.n_pos, self.n_neg) * 2 print(f"Total samples: {self.n_total:,}") print(f"Positive: {self.n_pos:,} " f"({ratio * 100:.1f}%)") print(f"Negative: {self.n_neg:,} " f"({(1 - ratio) * 100:.1f}%)") print(f"Imbalance ratio: " f"{imbalance:.1f}:1") print(f"Effective samples: " f"{effective:,}") print(f"Annotation time: " f"{total_hours:,.0f} hours") print(f"Annotation cost: " f"${total_cost:,.0f}") if ratio < 0.05: print("WARNING: Severe imbalance -- " "focal loss recommended") if self.n_total < 1000: print("WARNING: Small dataset -- " "transfer learning essential") if effective < 200: print("WARNING: Very few effective " "samples -- consider few-shot") # Typical medical datasets print("=== Rare disease screening ===") MedicalDatasetProfiler(500, 15).profile() print() print("=== Chest X-ray (pneumonia) ===") MedicalDatasetProfiler(5000, 250).profile() print() print("=== Retinal scan (diabetic) ===") MedicalDatasetProfiler(10000, 1500).profile() ``` The numbers are sobering. A rare disease screening dataset with 500 images and 15 positives gives you an effective training set of just 30 samples after accounting for imbalance. And annotating those 500 images cost someone over $18,000 in specialist time. This is the reality of medical ML -- you don't get to complain about "only" having 50,000 training images like you might in a Kaggle competition. ## Transfer learning: standing on ImageNet's shoulders Despite the visual differences between ImageNet photos (dogs, cars, landscapes) and chest X-rays (greyscale, abstract anatomical structures), transfer learning works surprisingly well for medical images. The early layers of an ImageNet-pretrained CNN detect edges, textures, gradients, and simple shapes -- these are universal visual features that appear in every type of image. Only the later layers specialize for the target domain: ```python import torch import torch.nn as nn import torchvision.models as models class MedicalClassifier(nn.Module): """Medical image classifier built on top of an ImageNet-pretrained backbone.""" def __init__(self, num_classes=2, pretrained=True): super().__init__() self.backbone = models.resnet50( weights='DEFAULT' if pretrained else None) num_features = self.backbone.fc.in_features self.backbone.fc = nn.Sequential( nn.Dropout(0.5), nn.Linear(num_features, num_classes)) def forward(self, x): return self.backbone(x) def setup_gradual_unfreezing(model, lr_backbone=1e-5, lr_head=1e-3): """Different learning rates for pretrained backbone vs new classification head. Slow updates preserve learned features, fast updates adapt the new head.""" backbone_params = [] head_params = [] for name, param in model.named_parameters(): if 'fc' in name: head_params.append(param) else: backbone_params.append(param) return torch.optim.Adam([ {'params': backbone_params, 'lr': lr_backbone}, {'params': head_params, 'lr': lr_head}]) model = MedicalClassifier(num_classes=3) optimizer = setup_gradual_unfreezing(model) print(f"Backbone params: " f"{sum(p.numel() for p in model.backbone.parameters()):,}") print(f"Head params: " f"{sum(p.numel() for p in model.backbone.fc.parameters()):,}") ``` The strategy is: freeze the backbone initially, train only the classification head for a few epochs until it converges, then gradually unfreeze backbone layers from top to bottom with a much smaller learning rate. This prevents the pretrained features from being destroyed by agressive updates on a small medical dataset. The differential learning rate (100x difference between head and backbone) is critical -- the head needs to learn fast because it starts from random weights, while the backbone needs to adapt slowly because it already has useful features. **Medical foundation models** like MedCLIP and BiomedCLIP are now available -- pretrained on large collections of medical images and clinical reports. They serve as better starting points than ImageNet for medical tasks, similar to how domain-specific language models outperform general ones (as we discussed in episode #69). Having said that, ImageNet pretraining remains a surprisingly strong baseline even for medical domains, and many published results showing "medical foundation model beats ImageNet transfer" disappear when you control carefully for training procedure and hyperparameters. ## Data augmentation: what works and what breaks things Standard augmentation needs careful adaptation for medical images. You can't just copy-paste augmentation pipelines from a dog-vs-cat classifier and expect sensible results: ```python import torchvision.transforms as T import numpy as np from PIL import Image from scipy.ndimage import gaussian_filter from scipy.ndimage import map_coordinates class ElasticDeformation: """Elastic deformation: simulates tissue variation in medical images. The single most effective augmentation for soft-tissue segmentation tasks.""" def __init__(self, alpha=50, sigma=5): self.alpha = alpha self.sigma = sigma def __call__(self, image): img = np.array(image) shape = img.shape[:2] dx = gaussian_filter( np.random.randn(*shape), self.sigma) * self.alpha dy = gaussian_filter( np.random.randn(*shape), self.sigma) * self.alpha y, x = np.meshgrid( np.arange(shape[0]), np.arange(shape[1]), indexing='ij') indices = [ np.clip(y + dy, 0, shape[0] - 1), np.clip(x + dx, 0, shape[1] - 1)] if img.ndim == 3: result = np.stack([ map_coordinates(img[:, :, c], indices, order=1) for c in range(img.shape[2])], axis=-1) else: result = map_coordinates( img, indices, order=1) return Image.fromarray( result.astype(np.uint8)) # Safe augmentations for medical images medical_train_transforms = T.Compose([ T.RandomRotation(degrees=15), T.RandomAffine( degrees=0, translate=(0.05, 0.05), scale=(0.95, 1.05)), T.RandomResizedCrop( 224, scale=(0.85, 1.0)), T.RandomAdjustSharpness( sharpness_factor=2, p=0.3), T.GaussianBlur( kernel_size=3, sigma=(0.1, 1.0)), T.ColorJitter( brightness=0.2, contrast=0.2), T.ToTensor(), T.Normalize( mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]), ]) # Things you MUST NOT do to medical images: print("SAFE augmentations:") print(" Small rotations (< 15 degrees)") print(" Small translations") print(" Elastic deformation") print(" Brightness/contrast jitter") print(" Gaussian blur/noise") print() print("DANGEROUS augmentations:") print(" Horizontal flip (heart is on the LEFT)") print(" Vertical flip (anatomy has a top)") print(" Heavy color jitter (diagnostic info)") print(" Random erasing (may mask pathology)") ``` The horizontal flip warning is not theoretical. The human heart is on the left side of the chest. A horizontally flipped chest X-ray represents **situs inversus** -- a rare anatomical condition where all organs are mirrored. If you augment with horizontal flips, your model sees "normal" anatomy in a mirrored configuration and learns that the heart can be on either side. This is a subtle but real failure mode that has tripped up published research papers. Elastic deformation, on the other hand, is a goldmine for medical images. It simulates the natural biological variation in soft tissues -- muscles stretch and compress differently across patients, organs shift slightly with breathing, and pathological changes deform surrounding tissue. It's the single most effective domain-specific augmentation for segmentation tasks like tumor boundary delineation. ## Handling class imbalance When only 2% of your training images show the condition you're trying to detect, standard training produces a model that never predicts positive. Three complementary strategies: ```python import torch import torch.nn as nn from torch.utils.data import WeightedRandomSampler from torch.utils.data import DataLoader # Strategy 1: Weighted cross-entropy loss class_counts = [9800, 200] # healthy, diseased weights = torch.tensor( [1.0 / c for c in class_counts]) weights = weights / weights.sum() criterion = nn.CrossEntropyLoss(weight=weights) print(f"Class weights: {weights.tolist()}") # Strategy 2: Focal loss (Lin et al., 2017) class FocalLoss(nn.Module): """Down-weight easy examples, focus on hard ones. Elegant solution to class imbalance without manual weight tuning.""" def __init__(self, alpha=0.25, gamma=2.0): super().__init__() self.alpha = alpha self.gamma = gamma def forward(self, logits, targets): ce = nn.functional.cross_entropy( logits, targets, reduction='none') p = torch.exp(-ce) focal_weight = ( self.alpha * (1 - p) ** self.gamma) return (focal_weight * ce).mean() # Strategy 3: Oversampling with # WeightedRandomSampler def make_balanced_loader(dataset, labels, batch_size=32): """Each batch gets roughly equal representation of both classes.""" class_counts_per = np.bincount(labels) sample_weights = [ 1.0 / class_counts_per[l] for l in labels] sampler = WeightedRandomSampler( sample_weights, num_samples=len(dataset)) return DataLoader( dataset, batch_size=batch_size, sampler=sampler) # Compare focal loss behavior focal = FocalLoss(alpha=0.25, gamma=2.0) logits = torch.tensor([[2.0, -2.0], [0.5, 0.5], [-1.0, 1.0]]) targets = torch.tensor([0, 0, 1]) loss = focal(logits, targets) print(f"Focal loss: {loss.item():.4f}") ``` Focal loss is particularly elegant. When the model is already confident about an easy normal case (high `p`), the `(1-p)^gamma` term makes the gradient nearly zero -- the model doesn't waste time getting more confident about cases it already handles. When it's uncertain about a difficult positive case (low `p`), the gradient is large, so the model focuses its learning capacity where it matters. No manual tuning of class weights required -- the `gamma` parameter controls how aggressively easy examples are down-weighted, and gamma=2.0 works well across a wide range of imbalance ratios. ## Evaluation: the metrics that actually matter Standard accuracy is misleading for imbalanced medical data. The metrics clinicians care about: ```python from sklearn.metrics import roc_auc_score from sklearn.metrics import confusion_matrix import numpy as np def medical_evaluation(y_true, y_prob, threshold=0.5): """Comprehensive evaluation for medical binary classification. Prints the metrics that actually matter in clinical settings.""" y_pred = (y_prob >= threshold).astype(int) cm = confusion_matrix(y_true, y_pred) tn, fp, fn, tp = cm.ravel() sensitivity = tp / (tp + fn + 1e-10) specificity = tn / (tn + fp + 1e-10) ppv = tp / (tp + fp + 1e-10) npv = tn / (tn + fn + 1e-10) auc = roc_auc_score(y_true, y_prob) print(f"AUC: {auc:.4f}") print(f"Sensitivity: {sensitivity:.4f} " f"(missed {fn} of {tp + fn} cases)") print(f"Specificity: {specificity:.4f} " f"(false alarms: {fp})") print(f"PPV: {ppv:.4f}, NPV: {npv:.4f}") print(f"Confusion matrix:") print(f" TN={tn}, FP={fp}") print(f" FN={fn}, TP={tp}") # Find threshold for 95% sensitivity thresholds = np.linspace(0, 1, 1000) for t in thresholds: preds = (y_prob >= t).astype(int) sens = preds[y_true == 1].sum() / max( (y_true == 1).sum(), 1) if sens >= 0.95: spec = ( 1 - preds[y_true == 0]).sum() / max( (y_true == 0).sum(), 1) print(f"\nAt 95% sensitivity: " f"thresh={t:.3f}, " f"specificity={spec:.4f}") break # Simulated screening results rng = np.random.RandomState(42) y_true = np.array([0] * 950 + [1] * 50) y_prob = np.where( y_true == 1, rng.beta(8, 3, len(y_true)), rng.beta(2, 8, len(y_true))) medical_evaluation(y_true, y_prob) ``` The threshold choice is a **clinical decision**, not a technical one. For screening (catching every possible case -- think mammography), you want high sensitivity even at the cost of more false positives. For confirmatory diagnosis (being sure about a positive result before scheduling surgery), you want high specificity. The model doesn't change -- only the operating point on the ROC curve changes. This is why AUC is the standard metric for comparing models: it measures performance across _all_ possible thresholds, independent of the clinical use case. ## Explainability: showing the model's reasoning In healthcare, a black-box prediction is often unacceptable. Clinicians need to understand _why_ the model flagged an image. **Grad-CAM** (Gradient-weighted Class Activation Mapping) produces heatmaps showing which regions of the image contributed most to the prediction: ```python import torch import torch.nn.functional as F class GradCAM: """Grad-CAM: visual explanations for CNN predictions. Highlights which image regions drove the classification decision.""" def __init__(self, model, target_layer): self.model = model self.gradients = None self.activations = None target_layer.register_forward_hook( self._save_activation) target_layer.register_full_backward_hook( self._save_gradient) def _save_activation(self, module, input, output): self.activations = output.detach() def _save_gradient(self, module, grad_in, grad_out): self.gradients = grad_out[0].detach() def generate(self, input_image, target_class): self.model.eval() output = self.model(input_image) self.model.zero_grad() output[0, target_class].backward() # Weight each channel by its # average gradient weights = self.gradients.mean( dim=[2, 3], keepdim=True) cam = (weights * self.activations).sum( dim=1, keepdim=True) cam = F.relu(cam) cam = F.interpolate( cam, size=input_image.shape[2:], mode='bilinear', align_corners=False) cam = cam / (cam.max() + 1e-8) return cam.squeeze() # Demo with a ResNet50 backbone = models.resnet50(weights='DEFAULT') backbone.eval() target = backbone.layer4[-1] # last conv layer gradcam = GradCAM(backbone, target) fake_input = torch.randn(1, 3, 224, 224) heatmap = gradcam.generate(fake_input, target_class=0) print(f"Heatmap shape: {heatmap.shape}") print(f"Range: [{heatmap.min():.3f}, " f"{heatmap.max():.3f}]") ``` The resulting heatmap highlights the region the model focused on when making its prediction. If the model correctly predicts pneumonia and the heatmap highlights the lower right lung where the consolidation is visible, that builds clinical trust. If the heatmap highlights the patient's name label in the corner of the X-ray, that reveals a **data leakage** problem -- the model learned to associate certain patients (who appear in both training and test sets) with certain diagnoses, rather than learning actual radiological features. This data leakage scenario is not hypothetical. A 2018 study found that a pneumonia detection model achieved suspiciously high accuracy because it learned to recognize which hospital the X-ray came from (via scanner-specific markings and formatting), and different hospitals had different patient populations with different disease prevalence. The model was predicting hospital identity, not pathology. Grad-CAM caught it ;-) ## Beyond medicine: scientific imaging at large Medical imaging gets the most attention (and funding), but the same techniques apply across scientific disciplines. The common thread: specialized imaging modalities, expensive expert annotations, small datasets, and high-stakes decisions. **Satellite and remote sensing**: images have more channels than RGB (infrared, radar, multispectral bands with 10-200+ channels), much larger spatial extents (a single Sentinel-2 tile is 10,980 x 10,980 pixels at 10m resolution), and the objects of interest are often tiny relative to the image (a single building, a few hectares of deforestation, individual vehicles). The preprocessing pipeline alone -- atmospheric correction, cloud masking, radiometric calibration, georeferencing -- is a field of its own. **Microscopy**: fluorescence microscopy produces multichannel images where each channel corresponds to a different stain or fluorescent marker. The images are often very noisy (photon shot noise at low light levels), may be 3D (confocal z-stacks), and the relevant structures (cell nuclei, mitochondria, protein aggregates) range from a few pixels to thousands of pixels in size. Cell segmentation -- finding individual cell boundaries in densely packed tissue -- is one of the enduring computer vision challenges that gets harder as cells overlap and deform. **Astronomy**: images from telescopes deal with extreme dynamic range (a star might be millions of times brighter than the background), noise that follows Poisson statistics rather than Gaussian, and objects so faint they're barely distinguishable from noise artifacts. Galaxy morphology classification (spiral, elliptical, irregular) is one of the classic ML astronomy tasks, and the Galaxy Zoo citizen science project produced one of the first large-scale crowd-annotated astronomical datasets. ```python import numpy as np class MultiChannelNormalizer: """Normalize scientific images with arbitrary channel counts (satellite, microscopy, spectral). Each channel gets independent normalization.""" def __init__(self): self.means = None self.stds = None def fit(self, images): """Compute per-channel statistics from a batch of images. images: (N, C, H, W)""" self.means = images.mean( axis=(0, 2, 3)) self.stds = images.std( axis=(0, 2, 3)) self.stds[self.stds < 1e-8] = 1.0 return self def transform(self, image): """Normalize a single image. image: (C, H, W)""" normalized = np.zeros_like( image, dtype=np.float32) for c in range(image.shape[0]): normalized[c] = ( (image[c] - self.means[c]) / self.stds[c]) return normalized def percentile_clip(self, image, low=1, high=99): """Clip each channel to percentile range -- handles extreme values in satellite and astronomical data.""" clipped = np.zeros_like( image, dtype=np.float32) for c in range(image.shape[0]): lo = np.percentile(image[c], low) hi = np.percentile(image[c], high) clipped[c] = np.clip( image[c], lo, hi) rng = hi - lo if rng > 1e-8: clipped[c] = ( (clipped[c] - lo) / rng) return clipped # Simulate a 13-band satellite image rng = np.random.RandomState(42) # Different channels have very different # value ranges (realistic for Sentinel-2) sat_image = np.zeros((13, 256, 256)) for c in range(13): base = rng.uniform(100, 5000) scale = rng.uniform(50, 2000) sat_image[c] = rng.normal( base, scale, (256, 256)) norm = MultiChannelNormalizer() batch = sat_image[np.newaxis] norm.fit(batch) result = norm.transform(sat_image) clipped = norm.percentile_clip(sat_image) print(f"Channels: {sat_image.shape[0]}") print(f"\n{'Ch':>3} {'RawMean':>10} " f"{'RawStd':>10} {'NormMean':>10} " f"{'ClipRange':>12}") print("-" * 48) for c in range(sat_image.shape[0]): print(f"{c:>3} " f"{sat_image[c].mean():>10.1f} " f"{sat_image[c].std():>10.1f} " f"{result[c].mean():>10.4f} " f"[{clipped[c].min():.3f}, " f"{clipped[c].max():.3f}]") ``` The key insight for all scientific imaging: you can't use ImageNet normalization statistics (mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) when your images have 13 bands with value ranges spanning three orders of magnitude. Each domain needs its own preprocessing pipeline, its own normalization strategy, and often its own evaluation metrics. The model architectures we've been building throughout this series (CNNs, transformers, U-Nets) transfer well across domains -- it's the data pipeline that needs the most domain-specific engineering. ## The regulatory landscape Medical AI devices require regulatory approval before clinical deployment. This isn't optional -- it's the law, and it fundamentally shapes how medical AI is developed: **FDA (US)**: Software as a Medical Device (SaMD) classification. AI diagnostics typically fall under Class II (510(k) pathway, showing "substantial equivalence" to an existing cleared device) or Class III (premarket approval, for higher-risk applications). The FDA has cleared hundreds of AI medical devices as of 2025, mostly in radiology and cardiology. **CE marking (EU)**: Under the Medical Device Regulation (MDR), AI systems must demonstrate safety and performance through clinical evaluation. The EU's AI Act adds additional requirements for "high-risk" AI systems, which includes most medical diagnostic applications. Both require: documented training data provenance (where did every image come from, with what consent?), validated performance on representative populations (does it work for all demographics?), post-market surveillance plans (how will you catch failures after deployment?), and clear intended use statements (this model is for X, not for Y). You can't just train a model and put it in a hospital. The regulatory process also means that medical AI development follows a fundamentally different timesline than typical ML projects. From initial model development to regulatory clearance takes 1-3 years and costs hundreds of thousands to millions of dollars. This is why most medical AI research stays in academic papers rather than reaching patients -- the gap between "works on our test set" and "approved for clinical use" is enormous. ## Samengevat - Medical imaging has unique constraints that fundamentally alter the ML pipeline: small datasets (hundreds, not millions), expensive expert annotations ($150+/hour specialists), severe class imbalance (1-5% positive rates), and high-stakes errors where false negatives can be fatal; - **transfer learning from ImageNet works** for medical images despite massive visual differences; the early CNN layers learn universal edge/texture features that transfer across domains; medical foundation models (MedCLIP, BiomedCLIP) are emerging as better starting points but ImageNet remains a strong baseline; - augmentation must be **domain-aware**: elastic deformation is highly effective for simulating biological tissue variation, but horizontal flipping breaks anatomical assumptions (the heart is on the left); every augmentation choice must be validated against domain knowledge; - **focal loss** automatically focuses training on hard examples without manual class weight tuning; the `(1-p)^gamma` modulation makes the gradient near-zero for easy cases and large for difficult ones; - evaluation requires **sensitivity, specificity, and AUC** rather than accuracy; threshold selection is a clinical decision that trades off missed cases against false alarms, and different clinical use cases (screening vs confirmation) demand different operating points; - **Grad-CAM** provides visual explanations essential for clinical trust and debugging data leakage -- verifying that models focus on pathology rather than scanner artifacts or patient labels is non-negotiable; - **scientific imaging** beyond medicine (satellite, microscopy, astronomy) shares the same challenges of specialized modalities, small datasets, and domain-specific preprocessing but with additional complications like multi-channel inputs, extreme dynamic ranges, and non-Gaussian noise models; - regulatory approval (FDA, CE marking) is mandatory before clinical deployment and shapes the entire development process -- from data provenance documentation to post-market surveillance plans. We've now covered how computer vision applies to specialized domains where getting it right really matters. The vision arc of this series has been extensive -- from raw pixels all the way to generating images, reconstructing 3D scenes, analyzing faces, and now domain-specific scientific applications. There's one more foundational concept in vision that we haven't explored yet: how machines can learn powerful visual representations _without_ any human labels at all, using only the structure of the data itself. ### Exercises **Exercise 1:** Build a **medical dataset augmentation validator**. Create a class `AugmentationValidator` that: (a) generates a synthetic 64x64 "chest X-ray" as a numpy array with a circle in the left half (simulating the heart) and a smaller circle in the right half (simulating a nodule), both with different intensities against a noisy background, (b) implements `check_anatomical_consistency(original, augmented)` that verifies the heart-like circle is still in the left half after augmentation -- compute the column-wise intensity sum for each half and check that the brighter half hasn't switched sides, (c) applies 5 different augmentations to the synthetic image: (1) small rotation 10 degrees, (2) horizontal flip, (3) elastic deformation with alpha=30 sigma=4, (4) brightness shift +20%, (5) Gaussian noise sigma=0.05, (d) for each augmentation, prints whether anatomical consistency is preserved, (e) computes a "signal preservation score" for each augmentation: the correlation coefficient between the original and augmented images (values near 1.0 mean the content is well preserved, values near 0.0 mean significant distortion). Verify that horizontal flip fails the anatomical consistency check while all other augmentations pass, and that elastic deformation preserves anatomy while having lower correlation than simple brightness changes. **Exercise 2:** Build a **class imbalance strategy comparator**. Create a class `ImbalanceComparator` that: (a) generates a synthetic binary classification dataset with 1000 samples where only 30 are positive, with features drawn from overlapping Gaussians (positive: mean=1.0, std=1.5; negative: mean=0.0, std=1.0) in 10 dimensions, (b) trains 3 logistic regression classifiers (using sklearn): one with default settings, one with `class_weight='balanced'`, and one using SMOTE oversampling on the training set (use imblearn.over_sampling.SMOTE or implement a simple random oversampler if imblearn is not available), (c) evaluates each on a held-out test set (20% split) using sensitivity, specificity, AUC, and F1, (d) prints a comparison table showing all 4 metrics for all 3 strategies, (e) for each strategy, finds the threshold that achieves >= 90% sensitivity and reports the corresponding specificity. Verify that the default (unweighted) classifier has high specificity but poor sensitivity, while balanced weighting and oversampling improve sensitivity at the cost of some specificity. **Exercise 3:** Build a **multi-channel scientific image normalizer and analyzer**. Create a class `ScientificImageAnalyzer` that: (a) generates a synthetic 8-channel "satellite image" of size 128x128 where: channels 0-2 are visible RGB (values 0-255), channels 3-4 are near-infrared (values 500-3000), channel 5 is thermal (values 250-320, representing Kelvin), channels 6-7 are radar backscatter (values in dB, range -25 to 5), (b) implements `per_channel_normalize(image)` that z-score normalizes each channel independently, (c) implements `percentile_clip(image, low_pct=2, high_pct=98)` that clips each channel to its 2nd and 98th percentiles then rescales to [0, 1], (d) implements `compute_ndvi(image)` that computes the Normalized Difference Vegetation Index from the NIR and Red channels: NDVI = (NIR - Red) / (NIR + Red + 1e-8), (e) prints per-channel statistics (mean, std, min, max) before and after normalization, (f) computes and prints NDVI statistics (mean, std, fraction of pixels with NDVI > 0.3 indicating vegetation), (g) compares the correlation between channel pairs before and after normalization to verify that normalization preserves inter-channel relationships. Verify that per-channel normalization brings all channels to mean~0, std~1 regardless of their original value ranges, and that NDVI values are invariant to the normalization method used. ### Thanks for reading! @scipio |
| json metadata | {"tags": ["stem", "stemsocial", "steemstem", "python", "programming"], "app": "hiveblog/0.1", "format": "markdown", "image": ["https://images.hive.blog/DQmP6whkhWUYdkmpCQ1kNbtQDbkZS4gB7HojM4NRt86wph1/variant-c-12-green.png"]} |
| parent author | |
| parent permlink | hive-196387 |
| permlink | learn-ai-series-89-medical-and-scientific-imaging |
| title | Learn AI Series (#89) - Medical and Scientific Imaging |
| Transaction Info | Block #107028726/Trx bd048f75341b420b2bee01a07362384eac2af931 |
View Raw JSON Data
{
"block": 107028726,
"op": [
"comment",
{
"author": "scipio",
"body": "# Learn AI Series (#89) - Medical and Scientific Imaging\n\n\n\n### What will I learn\n- You will learn the unique challenges of medical imaging: small datasets, class imbalance, and regulatory requirements;\n- transfer learning from natural images to medical domains and why it works despite massive visual differences;\n- data augmentation strategies specific to medical images (and which standard augmentations will break your model);\n- handling class imbalance when rare conditions are what matter most;\n- explainability requirements in healthcare AI and how Grad-CAM reveals what your model actually learned;\n- regulatory considerations for deploying AI in clinical settings;\n- scientific imaging beyond healthcare: satellite data, microscopy, and astronomical image analysis.\n\n### Requirements\n- A working modern computer running macOS, Windows or Ubuntu;\n- An installed Python 3(.11+) distribution;\n- The ambition to learn AI and machine learning.\n\n### Difficulty\n- Beginner\n\n### Curriculum (of the `Learn AI Series`):\n- [Learn AI Series (#1) - What Machine Learning Actually Is](https://hive.blog/hive-196387/@scipio/learn-ai-series-1-what-machine-learning-actually-is)\n- [Learn AI Series (#2) - Setting Up Your AI Workbench - Python and NumPy](https://hive.blog/hive-196387/@scipio/learn-ai-series-2-setting-up-your-ai-workbench-python-and-numpy)\n- [Learn AI Series (#3) - Your Data Is Just Numbers - How Machines See the World](https://hive.blog/hive-196387/@scipio/learn-ai-series-3-your-data-is-just-numbers-how-machines-see-the-world)\n- [Learn AI Series (#4) - Your First Prediction - No Math, Just Intuition](https://hive.blog/hive-196387/@scipio/learn-ai-series-4-your-first-prediction-no-math-just-intuition)\n- [Learn AI Series (#5) - Patterns in Data - What \"Learning\" Actually Looks Like](https://hive.blog/hive-196387/@scipio/learn-ai-series-5-patterns-in-data-what-learning-actually-looks-like)\n- [Learn AI Series (#6) - From Intuition to Math - Why We Need Formulas](https://hive.blog/hive-196387/@scipio/learn-ai-series-6-from-intuition-to-math-why-we-need-formulas)\n- [Learn AI Series (#7) - The Training Loop - See It Work Step by Step](https://hive.blog/hive-196387/@scipio/learn-ai-series-7-the-training-loop-see-it-work-step-by-step)\n- [Learn AI Series (#8) - The Math You Actually Need (Part 1) - Linear Algebra](https://hive.blog/hive-196387/@scipio/learn-ai-series-8-the-math-you-actually-need-part-1-linear-algebra)\n- [Learn AI Series (#9) - The Math You Actually Need (Part 2) - Calculus and Probability](https://hive.blog/hive-196387/@scipio/learn-ai-series-9-the-math-you-actually-need-part-2-calculus-and-probability)\n- [Learn AI Series (#10) - Your First ML Model - Linear Regression From Scratch](https://hive.blog/hive-196387/@scipio/learn-ai-series-10-your-first-ml-model-linear-regression-from-scratch)\n- [Learn AI Series (#11) - Making Linear Regression Real](https://hive.blog/hive-196387/@scipio/learn-ai-series-11-making-linear-regression-real)\n- [Learn AI Series (#12) - Classification - Logistic Regression From Scratch](https://hive.blog/hive-196387/@scipio/learn-ai-series-12-classification-logistic-regression-from-scratch)\n- [Learn AI Series (#13) - Evaluation - How to Know If Your Model Actually Works](https://hive.blog/hive-196387/@scipio/learn-ai-series-13-evaluation-how-to-know-if-your-model-actually-works)\n- [Learn AI Series (#14) - Data Preparation - The 80% Nobody Talks About](https://hive.blog/hive-196387/@scipio/learn-ai-series-14-data-preparation-the-80-nobody-talks-about)\n- [Learn AI Series (#15) - Feature Engineering and Selection](https://hive.blog/hive-196387/@scipio/learn-ai-series-15-feature-engineering-and-selection)\n- [Learn AI Series (#16) - Scikit-Learn - The Standard Library of ML](https://hive.blog/hive-196387/@scipio/learn-ai-series-16-scikit-learn-the-standard-library-of-ml)\n- [Learn AI Series (#17) - Decision Trees - How Machines Make Decisions](https://hive.blog/hive-196387/@scipio/learn-ai-series-17-decision-trees-how-machines-make-decisions)\n- [Learn AI Series (#18) - Random Forests - Wisdom of Crowds](https://hive.blog/hive-196387/@scipio/learn-ai-series-18-random-forests-wisdom-of-crowds)\n- [Learn AI Series (#19) - Gradient Boosting - The Kaggle Champion](https://hive.blog/hive-196387/@scipio/learn-ai-series-19-gradient-boosting-the-kaggle-champion)\n- [Learn AI Series (#20) - Support Vector Machines - Drawing the Perfect Boundary](https://hive.blog/hive-196387/@scipio/learn-ai-series-20-support-vector-machines-drawing-the-perfect-boundary)\n- [Learn AI Series (#21) - Mini Project - Predicting Crypto Market Regimes](https://hive.blog/hive-196387/@scipio/learn-ai-series-21-mini-project-predicting-crypto-market-regimes)\n- [Learn AI Series (#22) - K-Means Clustering - Finding Groups](https://hive.blog/hive-196387/@scipio/learn-ai-series-22-k-means-clustering-finding-groups)\n- [Learn AI Series (#23) - Advanced Clustering - Beyond K-Means](https://hive.blog/hive-196387/@scipio/learn-ai-series-23-advanced-clustering-beyond-k-means)\n- [Learn AI Series (#24) - Dimensionality Reduction - PCA](https://hive.blog/hive-196387/@scipio/learn-ai-series-24-dimensionality-reduction-pca)\n- [Learn AI Series (#25) - Advanced Dimensionality Reduction - t-SNE and UMAP](https://hive.blog/hive-196387/@scipio/learn-ai-series-25-advanced-dimensionality-reduction-t-sne-and-umap)\n- [Learn AI Series (#26) - Anomaly Detection - Finding What Doesn't Belong](https://hive.blog/hive-196387/@scipio/learn-ai-series-26-anomaly-detection-finding-what-doesnt-belong)\n- [Learn AI Series (#27) - Recommendation Systems - \"Users Like You Also Liked...\"](https://hive.blog/hive-196387/@scipio/learn-ai-series-27-recommendation-systems-users-like-you-also-liked)\n- [Learn AI Series (#28) - Time Series Fundamentals - When Order Matters](https://hive.blog/hive-196387/@scipio/learn-ai-series-28-time-series-fundamentals-when-order-matters)\n- [Learn AI Series (#29) - Time Series Forecasting - Predicting What Comes Next](https://hive.blog/hive-196387/@scipio/learn-ai-series-29-time-series-forecasting-predicting-what-comes-next)\n- [Learn AI Series (#30) - Natural Language Processing - Text as Data](https://hive.blog/hive-196387/@scipio/learn-ai-series-30-natural-language-processing-text-as-data)\n- [Learn AI Series (#31) - Word Embeddings - Meaning in Numbers](https://hive.blog/hive-196387/@scipio/learn-ai-series-31-word-embeddings-meaning-in-numbers)\n- [Learn AI Series (#32) - Bayesian Methods - Thinking in Probabilities](https://hive.blog/hive-196387/@scipio/learn-ai-series-32-bayesian-methods-thinking-in-probabilities)\n- [Learn AI Series (#33) - Ensemble Methods Deep Dive - Stacking and Blending](https://hive.blog/hive-196387/@scipio/learn-ai-series-33-ensemble-methods-deep-dive-stacking-and-blending)\n- [Learn AI Series (#34) - ML Engineering - From Notebook to Production](https://hive.blog/hive-196387/@scipio/learn-ai-series-34-ml-engineering-from-notebook-to-production)\n- [Learn AI Series (#35) - Data Ethics and Bias in ML](https://hive.blog/hive-196387/@scipio/learn-ai-series-35-data-ethics-and-bias-in-ml)\n- [Learn AI Series (#36) - Mini Project - Complete ML Pipeline](https://hive.blog/hive-196387/@scipio/learn-ai-series-36-mini-project-complete-ml-pipeline)\n- [Learn AI Series (#37) - The Perceptron - Where It All Started](https://hive.blog/hive-196387/@scipio/learn-ai-series-37-the-perceptron-where-it-all-started)\n- [Learn AI Series (#38) - Neural Networks From Scratch - Forward Pass](https://hive.blog/hive-196387/@scipio/learn-ai-series-38-neural-networks-from-scratch-forward-pass)\n- [Learn AI Series (#39) - Neural Networks From Scratch - Backpropagation](https://hive.blog/hive-196387/@scipio/learn-ai-series-39-neural-networks-from-scratch-backpropagation)\n- [Learn AI Series (#40) - Training Neural Networks - Practical Challenges](https://hive.blog/hive-196387/@scipio/learn-ai-series-40-training-neural-networks-practical-challenges)\n- [Learn AI Series (#41) - Optimization Algorithms - SGD, Momentum, Adam](https://hive.blog/hive-196387/@scipio/learn-ai-series-41-optimization-algorithms-sgd-momentum-adam)\n- [Learn AI Series (#42) - PyTorch Fundamentals - Tensors and Autograd](https://hive.blog/hive-196387/@scipio/learn-ai-series-42-pytorch-fundamentals-tensors-and-autograd)\n- [Learn AI Series (#43) - PyTorch Data and Training](https://hive.blog/hive-196387/@scipio/learn-ai-series-43-pytorch-data-and-training)\n- [Learn AI Series (#44) - PyTorch nn.Module - Building Real Networks](https://hive.blog/hive-196387/@scipio/learn-ai-series-44-pytorch-nnmodule-building-real-networks)\n- [Learn AI Series (#45) - Convolutional Neural Networks - Theory](https://hive.blog/hive-196387/@scipio/learn-ai-series-45-convolutional-neural-networks-theory)\n- [Learn AI Series (#46) - CNNs in Practice - Classic to Modern Architectures](https://hive.blog/hive-196387/@scipio/learn-ai-series-46-cnns-in-practice-classic-to-modern-architectures)\n- [Learn AI Series (#47) - CNN Applications - Detection, Segmentation, Style Transfer](https://hive.blog/hive-196387/@scipio/learn-ai-series-47-cnn-applications-detection-segmentation-style-transfer)\n- [Learn AI Series (#48) - Recurrent Neural Networks - Sequences](https://hive.blog/hive-196387/@scipio/learn-ai-series-48-recurrent-neural-networks-sequences)\n- [Learn AI Series (#49) - LSTM and GRU - Solving the Memory Problem](https://hive.blog/hive-196387/@scipio/learn-ai-series-49-lstm-and-gru-solving-the-memory-problem)\n- [Learn AI Series (#50) - Sequence-to-Sequence Models](https://hive.blog/hive-196387/@scipio/learn-ai-series-50-sequence-to-sequence-models)\n- [Learn AI Series (#51) - Attention Mechanisms](https://hive.blog/hive-196387/@scipio/learn-ai-series-51-attention-mechanisms)\n- [Learn AI Series (#52) - The Transformer Architecture (Part 1)](https://hive.blog/hive-196387/@scipio/learn-ai-series-52-the-transformer-architecture-part-1)\n- [Learn AI Series (#53) - The Transformer Architecture (Part 2)](https://hive.blog/hive-196387/@scipio/learn-ai-series-53-the-transformer-architecture-part-2)\n- [Learn AI Series (#54) - Vision Transformers](https://hive.blog/hive-196387/@scipio/learn-ai-series-54-vision-transformers)\n- [Learn AI Series (#55) - Generative Adversarial Networks](https://hive.blog/hive-196387/@scipio/learn-ai-series-55-generative-adversarial-networks)\n- [Learn AI Series (#56) - Mini Project - Building a Transformer From Scratch](https://hive.blog/hive-196387/@scipio/learn-ai-series-56-mini-project-building-a-transformer-from-scratch)\n- [Learn AI Series (#57) - Language Modeling - Predicting the Next Word](https://hive.blog/hive-196387/@scipio/learn-ai-series-57-language-modeling-predicting-the-next-word)\n- [Learn AI Series (#58) - GPT Architecture - Decoder-Only Transformers](https://hive.blog/hive-196387/@scipio/learn-ai-series-58-gpt-architecture-decoder-only-transformers)\n- [Learn AI Series (#59) - BERT and Encoder Models](https://hive.blog/hive-196387/@scipio/learn-ai-series-59-bert-and-encoder-models)\n- [Learn AI Series (#60) - Training Large Language Models](https://hive.blog/hive-196387/@scipio/learn-ai-series-60-training-large-language-models)\n- [Learn AI Series (#61) - Instruction Tuning and Alignment](https://hive.blog/hive-196387/@scipio/learn-ai-series-61-instruction-tuning-and-alignment)\n- [Learn AI Series (#62) - Prompt Engineering - Getting the Most from LLMs](https://hive.blog/hive-196387/@scipio/learn-ai-series-62-prompt-engineering-getting-the-most-from-llms)\n- [Learn AI Series (#63) - Embeddings and Vector Search](https://hive.blog/hive-196387/@scipio/learn-ai-series-63-embeddings-and-vector-search)\n- [Learn AI Series (#64) - Retrieval-Augmented Generation (RAG) - Basics](https://hive.blog/hive-196387/@scipio/learn-ai-series-64-retrieval-augmented-generation-rag-basics)\n- [Learn AI Series (#65) - RAG - Advanced Techniques](https://hive.blog/hive-196387/@scipio/learn-ai-series-65-rag-advanced-techniques)\n- [Learn AI Series (#66) - Working with LLM APIs](https://hive.blog/hive-196387/@scipio/learn-ai-series-66-working-with-llm-apis)\n- [Learn AI Series (#67) - Building AI Agents (Part 1) - Foundations](https://hive.blog/hive-196387/@scipio/learn-ai-series-67-building-ai-agents-part-1-foundations)\n- [Learn AI Series (#68) - Building AI Agents (Part 2) - Advanced Patterns](https://hive.blog/hive-196387/@scipio/learn-ai-series-68-building-ai-agents-part-2-advanced-patterns)\n- [Learn AI Series (#69) - Fine-Tuning Language Models](https://hive.blog/hive-196387/@scipio/learn-ai-series-69-fine-tuning-language-models)\n- [Learn AI Series (#70) - Running Local Models](https://hive.blog/hive-196387/@scipio/learn-ai-series-70-running-local-models)\n- [Learn AI Series (#71) - Text Generation Techniques](https://hive.blog/hive-196387/@scipio/learn-ai-series-71-text-generation-techniques)\n- [Learn AI Series (#72) - Tokenization Deep Dive](https://hive.blog/hive-196387/@scipio/learn-ai-series-72-tokenization-deep-dive)\n- [Learn AI Series (#73) - LLM Evaluation](https://hive.blog/hive-196387/@scipio/learn-ai-series-73-llm-evaluation)\n- [Learn AI Series (#74) - The Hugging Face Ecosystem](https://hive.blog/hive-196387/@scipio/learn-ai-series-74-the-hugging-face-ecosystem)\n- [Learn AI Series (#75) - Multimodal Models - Text Meets Vision](https://hive.blog/hive-196387/@scipio/learn-ai-series-75-multimodal-models-text-meets-vision)\n- [Learn AI Series (#76) - Mini Project - Your Own AI Assistant](https://hive.blog/hive-196387/@scipio/learn-ai-series-76-mini-project-your-own-ai-assistant)\n- [Learn AI Series (#77) - Image Processing Fundamentals](https://hive.blog/hive-196387/@scipio/learn-ai-series-77-image-processing-fundamentals)\n- [Learn AI Series (#78) - Object Detection (Part 1) - Foundations](https://hive.blog/hive-196387/@scipio/learn-ai-series-78-object-detection-part-1-foundations)\n- [Learn AI Series (#79) - Object Detection (Part 2) - Modern Approaches](https://hive.blog/hive-196387/@scipio/learn-ai-series-79-object-detection-part-2-modern-approaches)\n- [Learn AI Series (#80) - Image Segmentation](https://hive.blog/hive-196387/@scipio/learn-ai-series-80-image-segmentation)\n- [Learn AI Series (#81) - Pose Estimation and Tracking](https://hive.blog/hive-196387/@scipio/learn-ai-series-81-pose-estimation-and-tracking)\n- [Learn AI Series (#82) - Optical Character Recognition](https://hive.blog/hive-196387/@scipio/learn-ai-series-82-optical-character-recognition)\n- [Learn AI Series (#83) - Video Understanding](https://hive.blog/hive-196387/@scipio/learn-ai-series-83-video-understanding)\n- [Learn AI Series (#84) - Generative Images - Diffusion Models (Part 1)](https://hive.blog/hive-196387/@scipio/learn-ai-series-84-generative-images-diffusion-models-part-1)\n- [Learn AI Series (#85) - Generative Images - Diffusion Models (Part 2)](https://hive.blog/hive-196387/@scipio/learn-ai-series-85-generative-images-diffusion-models-part-2)\n- [Learn AI Series (#86) - Image-to-Image and Editing](https://hive.blog/hive-196387/@scipio/learn-ai-series-86-image-to-image-and-editing)\n- [Learn AI Series (#87) - 3D Vision](https://hive.blog/hive-196387/@scipio/learn-ai-series-87-3d-vision)\n- [Learn AI Series (#88) - Face Analysis](https://hive.blog/hive-196387/@scipio/learn-ai-series-88-face-analysis)\n- [Learn AI Series (#89) - Medical and Scientific Imaging](https://hive.blog/hive-196387/@scipio/learn-ai-series-89-medical-and-scientific-imaging) (this post)\n\n# Learn AI Series (#89) - Medical and Scientific Imaging\n\n### Solutions to Episode #88 Exercises\n\n**Exercise 1:** Face embedding similarity analyzer.\n\n```python\nimport numpy as np\n\n\nclass EmbeddingSimilarityAnalyzer:\n \"\"\"Analyze face embedding similarity\n distributions for identity verification.\"\"\"\n\n def __init__(self, num_ids=5, photos_per=10,\n embed_dim=512, noise_sigma=0.15,\n seed=42):\n rng = np.random.RandomState(seed)\n self.num_ids = num_ids\n self.photos_per = photos_per\n self.labels = []\n self.embeddings = []\n\n for i in range(num_ids):\n centroid = rng.randn(embed_dim)\n centroid /= np.linalg.norm(centroid)\n for _ in range(photos_per):\n variant = centroid + rng.randn(\n embed_dim) * noise_sigma\n variant /= np.linalg.norm(variant)\n self.embeddings.append(variant)\n self.labels.append(i)\n\n self.embeddings = np.array(self.embeddings)\n self.labels = np.array(self.labels)\n\n def cosine_matrix(self):\n norms = np.linalg.norm(\n self.embeddings, axis=1, keepdims=True)\n normed = self.embeddings / norms\n return normed @ normed.T\n\n def analyze(self):\n sim = self.cosine_matrix()\n n = len(self.labels)\n same_sims = []\n diff_sims = []\n for i in range(n):\n for j in range(i + 1, n):\n if self.labels[i] == self.labels[j]:\n same_sims.append(sim[i, j])\n else:\n diff_sims.append(sim[i, j])\n same_sims = np.array(same_sims)\n diff_sims = np.array(diff_sims)\n\n print(f\"Same-identity mean: \"\n f\"{same_sims.mean():.4f}\")\n print(f\"Diff-identity mean: \"\n f\"{diff_sims.mean():.4f}\")\n print(f\"Hardest positive: \"\n f\"{same_sims.min():.4f}\")\n print(f\"Hardest negative: \"\n f\"{diff_sims.max():.4f}\")\n gap = same_sims.min() - diff_sims.max()\n print(f\"Gap: {gap:.4f}\")\n\n print(f\"\\n{'Thresh':>7} {'TPR':>6} \"\n f\"{'FPR':>6} {'Acc':>6}\")\n print(\"-\" * 28)\n best_acc, best_t = 0, 0\n for t in [0.3, 0.4, 0.5, 0.6, 0.7, 0.8]:\n tp = (same_sims >= t).mean()\n fp = (diff_sims >= t).mean()\n acc = ((same_sims >= t).sum()\n + (diff_sims < t).sum()) / (\n len(same_sims) + len(diff_sims))\n if acc > best_acc:\n best_acc = acc\n best_t = t\n print(f\"{t:>7.1f} {tp:>6.3f} \"\n f\"{fp:>6.3f} {acc:>6.3f}\")\n print(f\"\\nBest: thresh={best_t}, \"\n f\"acc={best_acc:.3f}\")\n return same_sims, diff_sims\n\n\nanalyzer = EmbeddingSimilarityAnalyzer()\nanalyzer.analyze()\n```\n\nWith noise_sigma=0.15, the same-identity pairs cluster around cosine similarity 0.85-0.95 (high, because the noise is modest relative to the 512-dimensional centroid), while different-identity pairs settle near 0.0 (random unit vectors in 512D are nearly orthogonal). The gap between the hardest positive and hardest negative is comfortably positive, meaning a single threshold cleanly separates all identities. If you increase noise_sigma to 0.5 or higher, the same-identity distribution spreads out and starts overlapping with the different-identity distribution -- the gap shrinks toward zero and no threshold achieves perfect accuracy. This directly demonstrates why face recognition systems demand well-lit, frontal, aligned crops: reducing noise in the embedding space widens the gap.\n\n**Exercise 2:** Landmark-based face alignment tool.\n\n```python\nimport numpy as np\n\n\nclass FaceAligner:\n \"\"\"Align faces using 5-point landmarks\n via affine transformation.\"\"\"\n\n def __init__(self, target_eye_dist=70,\n target_center=(112, 112)):\n self.target_dist = target_eye_dist\n self.target_cx = target_center[0]\n self.target_cy = target_center[1]\n\n def compute_alignment(self, left_eye,\n right_eye):\n dx = right_eye[0] - left_eye[0]\n dy = right_eye[1] - left_eye[1]\n angle = np.degrees(np.arctan2(dy, dx))\n dist = np.sqrt(dx ** 2 + dy ** 2)\n scale = self.target_dist / max(\n dist, 1e-8)\n\n cos_a = np.cos(np.radians(-angle))\n sin_a = np.sin(np.radians(-angle))\n R = np.array([[cos_a, -sin_a],\n [sin_a, cos_a]]) * scale\n\n mid_x = (left_eye[0] + right_eye[0]) / 2\n mid_y = (left_eye[1] + right_eye[1]) / 2\n tx = self.target_cx - (\n R[0, 0] * mid_x + R[0, 1] * mid_y)\n ty = self.target_cy - (\n R[1, 0] * mid_x + R[1, 1] * mid_y)\n\n M = np.zeros((2, 3))\n M[:2, :2] = R\n M[0, 2] = tx\n M[1, 2] = ty\n return M, angle, dist\n\n def apply_affine(self, points, M):\n pts = np.array(points)\n ones = np.ones((len(pts), 1))\n aug = np.hstack([pts, ones])\n return (M @ aug.T).T\n\n def run(self):\n rng = np.random.RandomState(42)\n print(f\"{'Case':>5} {'InAngle':>8} \"\n f\"{'OutAngle':>9} {'InDist':>7} \"\n f\"{'OutDist':>8} {'EyeCenter':>14}\")\n print(\"-\" * 56)\n\n for i in range(5):\n angle = rng.uniform(-30, 30)\n dist = rng.uniform(40, 120)\n cx = rng.uniform(80, 160)\n cy = rng.uniform(80, 160)\n rad = np.radians(angle)\n le = (cx - dist / 2 * np.cos(rad),\n cy - dist / 2 * np.sin(rad))\n re = (cx + dist / 2 * np.cos(rad),\n cy + dist / 2 * np.sin(rad))\n\n M, in_angle, in_dist = (\n self.compute_alignment(le, re))\n landmarks = [le, re,\n (cx, cy + 20),\n (cx - 15, cy + 40),\n (cx + 15, cy + 40)]\n aligned = self.apply_affine(\n landmarks, M)\n\n out_dx = aligned[1, 0] - aligned[0, 0]\n out_dy = aligned[1, 1] - aligned[0, 1]\n out_angle = np.degrees(\n np.arctan2(out_dy, out_dx))\n out_dist = np.sqrt(\n out_dx ** 2 + out_dy ** 2)\n eye_cx = (aligned[0, 0]\n + aligned[1, 0]) / 2\n eye_cy = (aligned[0, 1]\n + aligned[1, 1]) / 2\n\n print(f\"{i + 1:>5} {in_angle:>8.1f} \"\n f\"{out_angle:>9.4f} \"\n f\"{in_dist:>7.1f} \"\n f\"{out_dist:>8.1f} \"\n f\"({eye_cx:.0f}, {eye_cy:.0f})\")\n\n\naligner = FaceAligner()\naligner.run()\n```\n\nEvery test case produces an aligned eye angle of essentially 0 degrees (within floating point precision), an inter-eye distance of exactly 70 pixels, and eye centers at (112, 112) -- regardless of the input rotation, scale, or position. This consistency is exactly why alignment matters for face recognition: the downstream embedding network sees faces in a canonical pose every time, so it can focus all of its capacity on identity-discriminative features rather than wasting parameters on learning to handle rotations and scale variations.\n\n**Exercise 3:** Expression confusion matrix analyzer.\n\n```python\nimport numpy as np\n\n\nclass ExpressionAnalyzer:\n \"\"\"Analyze expression classification\n confusion patterns.\"\"\"\n\n EXPRESSIONS = [\n \"angry\", \"disgusted\", \"fearful\",\n \"happy\", \"neutral\", \"sad\", \"surprised\"\n ]\n\n def __init__(self, samples_per=100, seed=42):\n self.n = samples_per\n self.seed = seed\n\n def generate_confusion(self, correct_probs=\n None, boost=None):\n rng = np.random.RandomState(self.seed)\n if correct_probs is None:\n correct_probs = {\n \"angry\": 0.65, \"disgusted\": 0.60,\n \"fearful\": 0.60, \"happy\": 0.80,\n \"neutral\": 0.70, \"sad\": 0.68,\n \"surprised\": 0.72}\n if boost:\n for cls, amt in boost.items():\n correct_probs[cls] = min(\n correct_probs[cls] + amt, 0.95)\n\n confusion_pairs = {\n (\"angry\", \"disgusted\"): 0.12,\n (\"disgusted\", \"angry\"): 0.12,\n (\"fearful\", \"surprised\"): 0.15,\n (\"surprised\", \"fearful\"): 0.10,\n (\"sad\", \"neutral\"): 0.08,\n (\"neutral\", \"sad\"): 0.06}\n\n nc = len(self.EXPRESSIONS)\n cm = np.zeros((nc, nc), dtype=int)\n\n for i, expr in enumerate(self.EXPRESSIONS):\n p_correct = correct_probs[expr]\n remaining = 1.0 - p_correct\n probs = np.zeros(nc)\n probs[i] = p_correct\n used = 0.0\n for (a, b), p in confusion_pairs.items():\n if a == expr:\n j = self.EXPRESSIONS.index(b)\n probs[j] = min(p, remaining)\n used += probs[j]\n leftover = remaining - used\n for j in range(nc):\n if j != i and probs[j] == 0:\n probs[j] = leftover / max(\n nc - 1 - sum(\n 1 for k in range(nc)\n if k != i\n and probs[k] > 0), 1)\n probs /= probs.sum()\n cm[i] = rng.multinomial(self.n, probs)\n return cm\n\n def metrics(self, cm):\n nc = cm.shape[0]\n results = {}\n for i, expr in enumerate(self.EXPRESSIONS):\n tp = cm[i, i]\n fp = cm[:, i].sum() - tp\n fn = cm[i, :].sum() - tp\n prec = tp / max(tp + fp, 1)\n rec = tp / max(tp + fn, 1)\n f1 = (2 * prec * rec\n / max(prec + rec, 1e-8))\n results[expr] = {\n \"precision\": prec,\n \"recall\": rec, \"f1\": f1}\n return results\n\n def run(self):\n cm = self.generate_confusion()\n\n # Print confusion matrix\n header = \" \" + \" \".join(\n f\"{e[:4]:>5}\" for e in\n self.EXPRESSIONS)\n print(header)\n for i, expr in enumerate(\n self.EXPRESSIONS):\n row = f\"{expr[:4]:>5} \" + \" \".join(\n f\"{cm[i, j]:>5}\"\n for j in range(len(\n self.EXPRESSIONS)))\n print(row)\n\n m = self.metrics(cm)\n print(f\"\\n{'Class':<12} {'Prec':>6} \"\n f\"{'Rec':>6} {'F1':>6}\")\n print(\"-\" * 32)\n for expr in self.EXPRESSIONS:\n d = m[expr]\n print(f\"{expr:<12} \"\n f\"{d['precision']:>6.3f} \"\n f\"{d['recall']:>6.3f} \"\n f\"{d['f1']:>6.3f}\")\n\n # Most confused pairs\n nc = len(self.EXPRESSIONS)\n pairs = []\n for i in range(nc):\n for j in range(nc):\n if i != j:\n pairs.append(\n (cm[i, j], self.EXPRESSIONS[i],\n self.EXPRESSIONS[j]))\n pairs.sort(reverse=True)\n print(f\"\\nTop confused pairs:\")\n for cnt, a, b in pairs[:4]:\n print(f\" {a} -> {b}: {cnt}\")\n\n ranked = sorted(m.items(),\n key=lambda x: x[1][\"f1\"])\n print(f\"\\nDifficulty ranking (worst F1 \"\n f\"first):\")\n for expr, d in ranked:\n print(f\" {expr}: F1={d['f1']:.3f}\")\n\n # Boost worst two\n worst = [ranked[0][0], ranked[1][0]]\n print(f\"\\nBoosting {worst} by +0.05:\")\n cm2 = self.generate_confusion(\n boost={w: 0.05 for w in worst})\n m2 = self.metrics(cm2)\n for w in worst:\n print(f\" {w}: F1 {m[w]['f1']:.3f}\"\n f\" -> {m2[w]['f1']:.3f}\")\n\n\nanalyzer = ExpressionAnalyzer()\nanalyzer.run()\n```\n\nThe angry/disgusted and fearful/surprised pairs dominate the off-diagonal entries -- exactly as configured, and consistent with real expression recognition research. These pairs are genuinely hard because the underlying facial muscle movements overlap: anger and disgust both involve brow lowering and nose wrinkling, while fear and surprise both involve wide eyes and raised eyebrows. Happy achieves the highest F1 because smiling is the most visually distinctive expression (unique cheek raise + lip corner pull), while disgusted and fearful rank lowest due to their high mutual confusion rates. Adding 0.05 to the correct-class probability for the two worst performers improves their F1 scores modestly, demonstrating the diminishing returns of more data when the fundamental visual similarity between confusable classes remains.\n\n### On to today's episode\n\nHere we go! We've spent the past thirteen episodes building a thorough understanding of computer vision: from basic image processing (#77) through object detection (#78-79), segmentation (#80), pose estimation (#81), OCR (#82), video (#83), diffusion models (#84-85), image editing (#86), 3D reconstruction (#87), and face analysis (#88). That entire arc dealt with **natural images** -- photographs of everyday scenes, objects, people, and places.\n\nBut some of the highest-impact applications of computer vision don't involve natural photos at all. They involve **medical images**: chest X-rays, retinal scans, pathology slides, CT volumes. And beyond medicine, there's a whole universe of **scientific imaging**: satellite photos, microscopy, spectrograms, astronomical observations. These domains share a common property -- the images look nothing like ImageNet, the datasets are small and expensive to label, and getting the answer wrong can have serious consequences.\n\nThis episode covers how to adapt everything we've learned to domains where the stakes are literally life-and-death, and where the standard \"download a big dataset, train a deep model, deploy\" pipeline falls apart completely ;-)\n\n## Why medical imaging is fundamentally different\n\nIf you've been following along since episode #13 (evaluation) and #14 (data preparation), you already know that data quality matters more than model architecture. Medical imaging takes every data challenge from those episodes and turns the dial to 11.\n\n**Small datasets**: a hospital might have 500 labeled chest X-rays showing a rare condition. Compare that to ImageNet's 14 million images. You can't just train a deeper model and hope it generalizes -- you'll overfit before the first epoch finishes.\n\n**Annotation cost**: labeling a single medical image often requires a board-certified specialist spending 10-30 minutes. Pixel-level segmentation masks (delineating tumor boundaries on a pathology slide) can take _hours_ per image. Some images need consensus from multiple experts because even specialists disagree on borderline cases. You can't crowdsource this on Mechanical Turk like you can with \"is there a cat in this picture?\"\n\n**Class imbalance**: in screening applications, the positive rate might be 1-5%. A model that always predicts \"healthy\" achieves 95%+ accuracy while being completely useless. Remember the precision-recall tradeoff from episode #13? This is where it becomes a matter of life and death.\n\n**High stakes**: a false negative (missed cancer) can kill. A false positive (unnecessary biopsy) causes harm, stress, and expense. The error _profile_ matters far more than the aggregate accuracy number. Telling a doctor \"my model is 97% accurate\" is meaningless without specifying sensitivity and specificity separately.\n\n**Domain shift**: a model trained on X-rays from Hospital A's GE scanner may fail on Hospital B's Siemens scanner. Different imaging protocols, sensor characteristics, patient demographics, and even the brand of contrast dye can shift the data distribution enough to break a model that worked perfectly in development. This is the same distribution shift problem from episode #14, but with much higher consequences.\n\n```python\nimport numpy as np\n\n\nclass MedicalDatasetProfiler:\n \"\"\"Profile a medical imaging dataset for\n common challenges: imbalance, size, and\n annotation cost estimation.\"\"\"\n\n def __init__(self, n_total, n_positive,\n annotation_minutes=15):\n self.n_total = n_total\n self.n_pos = n_positive\n self.n_neg = n_total - n_positive\n self.ann_min = annotation_minutes\n\n def profile(self):\n ratio = self.n_pos / self.n_total\n imbalance = self.n_neg / max(\n self.n_pos, 1)\n\n # Estimate annotation budget\n total_hours = (\n self.n_total * self.ann_min / 60)\n cost_per_hour = 150 # specialist rate\n total_cost = total_hours * cost_per_hour\n\n # Effective training size accounting\n # for imbalance\n effective = min(\n self.n_pos, self.n_neg) * 2\n\n print(f\"Total samples: {self.n_total:,}\")\n print(f\"Positive: {self.n_pos:,} \"\n f\"({ratio * 100:.1f}%)\")\n print(f\"Negative: {self.n_neg:,} \"\n f\"({(1 - ratio) * 100:.1f}%)\")\n print(f\"Imbalance ratio: \"\n f\"{imbalance:.1f}:1\")\n print(f\"Effective samples: \"\n f\"{effective:,}\")\n print(f\"Annotation time: \"\n f\"{total_hours:,.0f} hours\")\n print(f\"Annotation cost: \"\n f\"${total_cost:,.0f}\")\n\n if ratio < 0.05:\n print(\"WARNING: Severe imbalance -- \"\n \"focal loss recommended\")\n if self.n_total < 1000:\n print(\"WARNING: Small dataset -- \"\n \"transfer learning essential\")\n if effective < 200:\n print(\"WARNING: Very few effective \"\n \"samples -- consider few-shot\")\n\n\n# Typical medical datasets\nprint(\"=== Rare disease screening ===\")\nMedicalDatasetProfiler(500, 15).profile()\nprint()\nprint(\"=== Chest X-ray (pneumonia) ===\")\nMedicalDatasetProfiler(5000, 250).profile()\nprint()\nprint(\"=== Retinal scan (diabetic) ===\")\nMedicalDatasetProfiler(10000, 1500).profile()\n```\n\nThe numbers are sobering. A rare disease screening dataset with 500 images and 15 positives gives you an effective training set of just 30 samples after accounting for imbalance. And annotating those 500 images cost someone over $18,000 in specialist time. This is the reality of medical ML -- you don't get to complain about \"only\" having 50,000 training images like you might in a Kaggle competition.\n\n## Transfer learning: standing on ImageNet's shoulders\n\nDespite the visual differences between ImageNet photos (dogs, cars, landscapes) and chest X-rays (greyscale, abstract anatomical structures), transfer learning works surprisingly well for medical images. The early layers of an ImageNet-pretrained CNN detect edges, textures, gradients, and simple shapes -- these are universal visual features that appear in every type of image. Only the later layers specialize for the target domain:\n\n```python\nimport torch\nimport torch.nn as nn\nimport torchvision.models as models\n\n\nclass MedicalClassifier(nn.Module):\n \"\"\"Medical image classifier built on top\n of an ImageNet-pretrained backbone.\"\"\"\n\n def __init__(self, num_classes=2,\n pretrained=True):\n super().__init__()\n self.backbone = models.resnet50(\n weights='DEFAULT' if pretrained\n else None)\n num_features = self.backbone.fc.in_features\n self.backbone.fc = nn.Sequential(\n nn.Dropout(0.5),\n nn.Linear(num_features, num_classes))\n\n def forward(self, x):\n return self.backbone(x)\n\n\ndef setup_gradual_unfreezing(model,\n lr_backbone=1e-5,\n lr_head=1e-3):\n \"\"\"Different learning rates for pretrained\n backbone vs new classification head.\n Slow updates preserve learned features,\n fast updates adapt the new head.\"\"\"\n backbone_params = []\n head_params = []\n for name, param in model.named_parameters():\n if 'fc' in name:\n head_params.append(param)\n else:\n backbone_params.append(param)\n\n return torch.optim.Adam([\n {'params': backbone_params,\n 'lr': lr_backbone},\n {'params': head_params,\n 'lr': lr_head}])\n\n\nmodel = MedicalClassifier(num_classes=3)\noptimizer = setup_gradual_unfreezing(model)\nprint(f\"Backbone params: \"\n f\"{sum(p.numel() for p in model.backbone.parameters()):,}\")\nprint(f\"Head params: \"\n f\"{sum(p.numel() for p in model.backbone.fc.parameters()):,}\")\n```\n\nThe strategy is: freeze the backbone initially, train only the classification head for a few epochs until it converges, then gradually unfreeze backbone layers from top to bottom with a much smaller learning rate. This prevents the pretrained features from being destroyed by agressive updates on a small medical dataset. The differential learning rate (100x difference between head and backbone) is critical -- the head needs to learn fast because it starts from random weights, while the backbone needs to adapt slowly because it already has useful features.\n\n**Medical foundation models** like MedCLIP and BiomedCLIP are now available -- pretrained on large collections of medical images and clinical reports. They serve as better starting points than ImageNet for medical tasks, similar to how domain-specific language models outperform general ones (as we discussed in episode #69). Having said that, ImageNet pretraining remains a surprisingly strong baseline even for medical domains, and many published results showing \"medical foundation model beats ImageNet transfer\" disappear when you control carefully for training procedure and hyperparameters.\n\n## Data augmentation: what works and what breaks things\n\nStandard augmentation needs careful adaptation for medical images. You can't just copy-paste augmentation pipelines from a dog-vs-cat classifier and expect sensible results:\n\n```python\nimport torchvision.transforms as T\nimport numpy as np\nfrom PIL import Image\nfrom scipy.ndimage import gaussian_filter\nfrom scipy.ndimage import map_coordinates\n\n\nclass ElasticDeformation:\n \"\"\"Elastic deformation: simulates tissue\n variation in medical images. The single\n most effective augmentation for soft-tissue\n segmentation tasks.\"\"\"\n\n def __init__(self, alpha=50, sigma=5):\n self.alpha = alpha\n self.sigma = sigma\n\n def __call__(self, image):\n img = np.array(image)\n shape = img.shape[:2]\n dx = gaussian_filter(\n np.random.randn(*shape),\n self.sigma) * self.alpha\n dy = gaussian_filter(\n np.random.randn(*shape),\n self.sigma) * self.alpha\n y, x = np.meshgrid(\n np.arange(shape[0]),\n np.arange(shape[1]),\n indexing='ij')\n indices = [\n np.clip(y + dy, 0, shape[0] - 1),\n np.clip(x + dx, 0, shape[1] - 1)]\n if img.ndim == 3:\n result = np.stack([\n map_coordinates(img[:, :, c],\n indices, order=1)\n for c in range(img.shape[2])],\n axis=-1)\n else:\n result = map_coordinates(\n img, indices, order=1)\n return Image.fromarray(\n result.astype(np.uint8))\n\n\n# Safe augmentations for medical images\nmedical_train_transforms = T.Compose([\n T.RandomRotation(degrees=15),\n T.RandomAffine(\n degrees=0,\n translate=(0.05, 0.05),\n scale=(0.95, 1.05)),\n T.RandomResizedCrop(\n 224, scale=(0.85, 1.0)),\n T.RandomAdjustSharpness(\n sharpness_factor=2, p=0.3),\n T.GaussianBlur(\n kernel_size=3, sigma=(0.1, 1.0)),\n T.ColorJitter(\n brightness=0.2, contrast=0.2),\n T.ToTensor(),\n T.Normalize(\n mean=[0.485, 0.456, 0.406],\n std=[0.229, 0.224, 0.225]),\n])\n\n# Things you MUST NOT do to medical images:\nprint(\"SAFE augmentations:\")\nprint(\" Small rotations (< 15 degrees)\")\nprint(\" Small translations\")\nprint(\" Elastic deformation\")\nprint(\" Brightness/contrast jitter\")\nprint(\" Gaussian blur/noise\")\nprint()\nprint(\"DANGEROUS augmentations:\")\nprint(\" Horizontal flip (heart is on the LEFT)\")\nprint(\" Vertical flip (anatomy has a top)\")\nprint(\" Heavy color jitter (diagnostic info)\")\nprint(\" Random erasing (may mask pathology)\")\n```\n\nThe horizontal flip warning is not theoretical. The human heart is on the left side of the chest. A horizontally flipped chest X-ray represents **situs inversus** -- a rare anatomical condition where all organs are mirrored. If you augment with horizontal flips, your model sees \"normal\" anatomy in a mirrored configuration and learns that the heart can be on either side. This is a subtle but real failure mode that has tripped up published research papers.\n\nElastic deformation, on the other hand, is a goldmine for medical images. It simulates the natural biological variation in soft tissues -- muscles stretch and compress differently across patients, organs shift slightly with breathing, and pathological changes deform surrounding tissue. It's the single most effective domain-specific augmentation for segmentation tasks like tumor boundary delineation.\n\n## Handling class imbalance\n\nWhen only 2% of your training images show the condition you're trying to detect, standard training produces a model that never predicts positive. Three complementary strategies:\n\n```python\nimport torch\nimport torch.nn as nn\nfrom torch.utils.data import WeightedRandomSampler\nfrom torch.utils.data import DataLoader\n\n\n# Strategy 1: Weighted cross-entropy loss\nclass_counts = [9800, 200] # healthy, diseased\nweights = torch.tensor(\n [1.0 / c for c in class_counts])\nweights = weights / weights.sum()\ncriterion = nn.CrossEntropyLoss(weight=weights)\nprint(f\"Class weights: {weights.tolist()}\")\n\n\n# Strategy 2: Focal loss (Lin et al., 2017)\nclass FocalLoss(nn.Module):\n \"\"\"Down-weight easy examples, focus on\n hard ones. Elegant solution to class\n imbalance without manual weight tuning.\"\"\"\n\n def __init__(self, alpha=0.25, gamma=2.0):\n super().__init__()\n self.alpha = alpha\n self.gamma = gamma\n\n def forward(self, logits, targets):\n ce = nn.functional.cross_entropy(\n logits, targets, reduction='none')\n p = torch.exp(-ce)\n focal_weight = (\n self.alpha * (1 - p) ** self.gamma)\n return (focal_weight * ce).mean()\n\n\n# Strategy 3: Oversampling with\n# WeightedRandomSampler\ndef make_balanced_loader(dataset, labels,\n batch_size=32):\n \"\"\"Each batch gets roughly equal\n representation of both classes.\"\"\"\n class_counts_per = np.bincount(labels)\n sample_weights = [\n 1.0 / class_counts_per[l]\n for l in labels]\n sampler = WeightedRandomSampler(\n sample_weights,\n num_samples=len(dataset))\n return DataLoader(\n dataset, batch_size=batch_size,\n sampler=sampler)\n\n\n# Compare focal loss behavior\nfocal = FocalLoss(alpha=0.25, gamma=2.0)\nlogits = torch.tensor([[2.0, -2.0],\n [0.5, 0.5],\n [-1.0, 1.0]])\ntargets = torch.tensor([0, 0, 1])\nloss = focal(logits, targets)\nprint(f\"Focal loss: {loss.item():.4f}\")\n```\n\nFocal loss is particularly elegant. When the model is already confident about an easy normal case (high `p`), the `(1-p)^gamma` term makes the gradient nearly zero -- the model doesn't waste time getting more confident about cases it already handles. When it's uncertain about a difficult positive case (low `p`), the gradient is large, so the model focuses its learning capacity where it matters. No manual tuning of class weights required -- the `gamma` parameter controls how aggressively easy examples are down-weighted, and gamma=2.0 works well across a wide range of imbalance ratios.\n\n## Evaluation: the metrics that actually matter\n\nStandard accuracy is misleading for imbalanced medical data. The metrics clinicians care about:\n\n```python\nfrom sklearn.metrics import roc_auc_score\nfrom sklearn.metrics import confusion_matrix\nimport numpy as np\n\n\ndef medical_evaluation(y_true, y_prob,\n threshold=0.5):\n \"\"\"Comprehensive evaluation for medical\n binary classification. Prints the metrics\n that actually matter in clinical settings.\"\"\"\n y_pred = (y_prob >= threshold).astype(int)\n cm = confusion_matrix(y_true, y_pred)\n tn, fp, fn, tp = cm.ravel()\n\n sensitivity = tp / (tp + fn + 1e-10)\n specificity = tn / (tn + fp + 1e-10)\n ppv = tp / (tp + fp + 1e-10)\n npv = tn / (tn + fn + 1e-10)\n auc = roc_auc_score(y_true, y_prob)\n\n print(f\"AUC: {auc:.4f}\")\n print(f\"Sensitivity: {sensitivity:.4f} \"\n f\"(missed {fn} of {tp + fn} cases)\")\n print(f\"Specificity: {specificity:.4f} \"\n f\"(false alarms: {fp})\")\n print(f\"PPV: {ppv:.4f}, NPV: {npv:.4f}\")\n print(f\"Confusion matrix:\")\n print(f\" TN={tn}, FP={fp}\")\n print(f\" FN={fn}, TP={tp}\")\n\n # Find threshold for 95% sensitivity\n thresholds = np.linspace(0, 1, 1000)\n for t in thresholds:\n preds = (y_prob >= t).astype(int)\n sens = preds[y_true == 1].sum() / max(\n (y_true == 1).sum(), 1)\n if sens >= 0.95:\n spec = (\n 1 - preds[y_true == 0]).sum() / max(\n (y_true == 0).sum(), 1)\n print(f\"\\nAt 95% sensitivity: \"\n f\"thresh={t:.3f}, \"\n f\"specificity={spec:.4f}\")\n break\n\n\n# Simulated screening results\nrng = np.random.RandomState(42)\ny_true = np.array([0] * 950 + [1] * 50)\ny_prob = np.where(\n y_true == 1,\n rng.beta(8, 3, len(y_true)),\n rng.beta(2, 8, len(y_true)))\nmedical_evaluation(y_true, y_prob)\n```\n\nThe threshold choice is a **clinical decision**, not a technical one. For screening (catching every possible case -- think mammography), you want high sensitivity even at the cost of more false positives. For confirmatory diagnosis (being sure about a positive result before scheduling surgery), you want high specificity. The model doesn't change -- only the operating point on the ROC curve changes. This is why AUC is the standard metric for comparing models: it measures performance across _all_ possible thresholds, independent of the clinical use case.\n\n## Explainability: showing the model's reasoning\n\nIn healthcare, a black-box prediction is often unacceptable. Clinicians need to understand _why_ the model flagged an image. **Grad-CAM** (Gradient-weighted Class Activation Mapping) produces heatmaps showing which regions of the image contributed most to the prediction:\n\n```python\nimport torch\nimport torch.nn.functional as F\n\n\nclass GradCAM:\n \"\"\"Grad-CAM: visual explanations for CNN\n predictions. Highlights which image regions\n drove the classification decision.\"\"\"\n\n def __init__(self, model, target_layer):\n self.model = model\n self.gradients = None\n self.activations = None\n target_layer.register_forward_hook(\n self._save_activation)\n target_layer.register_full_backward_hook(\n self._save_gradient)\n\n def _save_activation(self, module,\n input, output):\n self.activations = output.detach()\n\n def _save_gradient(self, module,\n grad_in, grad_out):\n self.gradients = grad_out[0].detach()\n\n def generate(self, input_image,\n target_class):\n self.model.eval()\n output = self.model(input_image)\n self.model.zero_grad()\n output[0, target_class].backward()\n\n # Weight each channel by its\n # average gradient\n weights = self.gradients.mean(\n dim=[2, 3], keepdim=True)\n cam = (weights * self.activations).sum(\n dim=1, keepdim=True)\n cam = F.relu(cam)\n cam = F.interpolate(\n cam, size=input_image.shape[2:],\n mode='bilinear',\n align_corners=False)\n cam = cam / (cam.max() + 1e-8)\n return cam.squeeze()\n\n\n# Demo with a ResNet50\nbackbone = models.resnet50(weights='DEFAULT')\nbackbone.eval()\ntarget = backbone.layer4[-1] # last conv layer\ngradcam = GradCAM(backbone, target)\n\nfake_input = torch.randn(1, 3, 224, 224)\nheatmap = gradcam.generate(fake_input,\n target_class=0)\nprint(f\"Heatmap shape: {heatmap.shape}\")\nprint(f\"Range: [{heatmap.min():.3f}, \"\n f\"{heatmap.max():.3f}]\")\n```\n\nThe resulting heatmap highlights the region the model focused on when making its prediction. If the model correctly predicts pneumonia and the heatmap highlights the lower right lung where the consolidation is visible, that builds clinical trust. If the heatmap highlights the patient's name label in the corner of the X-ray, that reveals a **data leakage** problem -- the model learned to associate certain patients (who appear in both training and test sets) with certain diagnoses, rather than learning actual radiological features.\n\nThis data leakage scenario is not hypothetical. A 2018 study found that a pneumonia detection model achieved suspiciously high accuracy because it learned to recognize which hospital the X-ray came from (via scanner-specific markings and formatting), and different hospitals had different patient populations with different disease prevalence. The model was predicting hospital identity, not pathology. Grad-CAM caught it ;-)\n\n## Beyond medicine: scientific imaging at large\n\nMedical imaging gets the most attention (and funding), but the same techniques apply across scientific disciplines. The common thread: specialized imaging modalities, expensive expert annotations, small datasets, and high-stakes decisions.\n\n**Satellite and remote sensing**: images have more channels than RGB (infrared, radar, multispectral bands with 10-200+ channels), much larger spatial extents (a single Sentinel-2 tile is 10,980 x 10,980 pixels at 10m resolution), and the objects of interest are often tiny relative to the image (a single building, a few hectares of deforestation, individual vehicles). The preprocessing pipeline alone -- atmospheric correction, cloud masking, radiometric calibration, georeferencing -- is a field of its own.\n\n**Microscopy**: fluorescence microscopy produces multichannel images where each channel corresponds to a different stain or fluorescent marker. The images are often very noisy (photon shot noise at low light levels), may be 3D (confocal z-stacks), and the relevant structures (cell nuclei, mitochondria, protein aggregates) range from a few pixels to thousands of pixels in size. Cell segmentation -- finding individual cell boundaries in densely packed tissue -- is one of the enduring computer vision challenges that gets harder as cells overlap and deform.\n\n**Astronomy**: images from telescopes deal with extreme dynamic range (a star might be millions of times brighter than the background), noise that follows Poisson statistics rather than Gaussian, and objects so faint they're barely distinguishable from noise artifacts. Galaxy morphology classification (spiral, elliptical, irregular) is one of the classic ML astronomy tasks, and the Galaxy Zoo citizen science project produced one of the first large-scale crowd-annotated astronomical datasets.\n\n```python\nimport numpy as np\n\n\nclass MultiChannelNormalizer:\n \"\"\"Normalize scientific images with\n arbitrary channel counts (satellite,\n microscopy, spectral). Each channel\n gets independent normalization.\"\"\"\n\n def __init__(self):\n self.means = None\n self.stds = None\n\n def fit(self, images):\n \"\"\"Compute per-channel statistics\n from a batch of images.\n images: (N, C, H, W)\"\"\"\n self.means = images.mean(\n axis=(0, 2, 3))\n self.stds = images.std(\n axis=(0, 2, 3))\n self.stds[self.stds < 1e-8] = 1.0\n return self\n\n def transform(self, image):\n \"\"\"Normalize a single image.\n image: (C, H, W)\"\"\"\n normalized = np.zeros_like(\n image, dtype=np.float32)\n for c in range(image.shape[0]):\n normalized[c] = (\n (image[c] - self.means[c])\n / self.stds[c])\n return normalized\n\n def percentile_clip(self, image,\n low=1, high=99):\n \"\"\"Clip each channel to percentile\n range -- handles extreme values in\n satellite and astronomical data.\"\"\"\n clipped = np.zeros_like(\n image, dtype=np.float32)\n for c in range(image.shape[0]):\n lo = np.percentile(image[c], low)\n hi = np.percentile(image[c], high)\n clipped[c] = np.clip(\n image[c], lo, hi)\n rng = hi - lo\n if rng > 1e-8:\n clipped[c] = (\n (clipped[c] - lo) / rng)\n return clipped\n\n\n# Simulate a 13-band satellite image\nrng = np.random.RandomState(42)\n# Different channels have very different\n# value ranges (realistic for Sentinel-2)\nsat_image = np.zeros((13, 256, 256))\nfor c in range(13):\n base = rng.uniform(100, 5000)\n scale = rng.uniform(50, 2000)\n sat_image[c] = rng.normal(\n base, scale, (256, 256))\n\nnorm = MultiChannelNormalizer()\nbatch = sat_image[np.newaxis]\nnorm.fit(batch)\nresult = norm.transform(sat_image)\nclipped = norm.percentile_clip(sat_image)\n\nprint(f\"Channels: {sat_image.shape[0]}\")\nprint(f\"\\n{'Ch':>3} {'RawMean':>10} \"\n f\"{'RawStd':>10} {'NormMean':>10} \"\n f\"{'ClipRange':>12}\")\nprint(\"-\" * 48)\nfor c in range(sat_image.shape[0]):\n print(f\"{c:>3} \"\n f\"{sat_image[c].mean():>10.1f} \"\n f\"{sat_image[c].std():>10.1f} \"\n f\"{result[c].mean():>10.4f} \"\n f\"[{clipped[c].min():.3f}, \"\n f\"{clipped[c].max():.3f}]\")\n```\n\nThe key insight for all scientific imaging: you can't use ImageNet normalization statistics (mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) when your images have 13 bands with value ranges spanning three orders of magnitude. Each domain needs its own preprocessing pipeline, its own normalization strategy, and often its own evaluation metrics. The model architectures we've been building throughout this series (CNNs, transformers, U-Nets) transfer well across domains -- it's the data pipeline that needs the most domain-specific engineering.\n\n## The regulatory landscape\n\nMedical AI devices require regulatory approval before clinical deployment. This isn't optional -- it's the law, and it fundamentally shapes how medical AI is developed:\n\n**FDA (US)**: Software as a Medical Device (SaMD) classification. AI diagnostics typically fall under Class II (510(k) pathway, showing \"substantial equivalence\" to an existing cleared device) or Class III (premarket approval, for higher-risk applications). The FDA has cleared hundreds of AI medical devices as of 2025, mostly in radiology and cardiology.\n\n**CE marking (EU)**: Under the Medical Device Regulation (MDR), AI systems must demonstrate safety and performance through clinical evaluation. The EU's AI Act adds additional requirements for \"high-risk\" AI systems, which includes most medical diagnostic applications.\n\nBoth require: documented training data provenance (where did every image come from, with what consent?), validated performance on representative populations (does it work for all demographics?), post-market surveillance plans (how will you catch failures after deployment?), and clear intended use statements (this model is for X, not for Y). You can't just train a model and put it in a hospital.\n\nThe regulatory process also means that medical AI development follows a fundamentally different timesline than typical ML projects. From initial model development to regulatory clearance takes 1-3 years and costs hundreds of thousands to millions of dollars. This is why most medical AI research stays in academic papers rather than reaching patients -- the gap between \"works on our test set\" and \"approved for clinical use\" is enormous.\n\n## Samengevat\n\n- Medical imaging has unique constraints that fundamentally alter the ML pipeline: small datasets (hundreds, not millions), expensive expert annotations ($150+/hour specialists), severe class imbalance (1-5% positive rates), and high-stakes errors where false negatives can be fatal;\n- **transfer learning from ImageNet works** for medical images despite massive visual differences; the early CNN layers learn universal edge/texture features that transfer across domains; medical foundation models (MedCLIP, BiomedCLIP) are emerging as better starting points but ImageNet remains a strong baseline;\n- augmentation must be **domain-aware**: elastic deformation is highly effective for simulating biological tissue variation, but horizontal flipping breaks anatomical assumptions (the heart is on the left); every augmentation choice must be validated against domain knowledge;\n- **focal loss** automatically focuses training on hard examples without manual class weight tuning; the `(1-p)^gamma` modulation makes the gradient near-zero for easy cases and large for difficult ones;\n- evaluation requires **sensitivity, specificity, and AUC** rather than accuracy; threshold selection is a clinical decision that trades off missed cases against false alarms, and different clinical use cases (screening vs confirmation) demand different operating points;\n- **Grad-CAM** provides visual explanations essential for clinical trust and debugging data leakage -- verifying that models focus on pathology rather than scanner artifacts or patient labels is non-negotiable;\n- **scientific imaging** beyond medicine (satellite, microscopy, astronomy) shares the same challenges of specialized modalities, small datasets, and domain-specific preprocessing but with additional complications like multi-channel inputs, extreme dynamic ranges, and non-Gaussian noise models;\n- regulatory approval (FDA, CE marking) is mandatory before clinical deployment and shapes the entire development process -- from data provenance documentation to post-market surveillance plans.\n\nWe've now covered how computer vision applies to specialized domains where getting it right really matters. The vision arc of this series has been extensive -- from raw pixels all the way to generating images, reconstructing 3D scenes, analyzing faces, and now domain-specific scientific applications. There's one more foundational concept in vision that we haven't explored yet: how machines can learn powerful visual representations _without_ any human labels at all, using only the structure of the data itself.\n\n### Exercises\n\n**Exercise 1:** Build a **medical dataset augmentation validator**. Create a class `AugmentationValidator` that: (a) generates a synthetic 64x64 \"chest X-ray\" as a numpy array with a circle in the left half (simulating the heart) and a smaller circle in the right half (simulating a nodule), both with different intensities against a noisy background, (b) implements `check_anatomical_consistency(original, augmented)` that verifies the heart-like circle is still in the left half after augmentation -- compute the column-wise intensity sum for each half and check that the brighter half hasn't switched sides, (c) applies 5 different augmentations to the synthetic image: (1) small rotation 10 degrees, (2) horizontal flip, (3) elastic deformation with alpha=30 sigma=4, (4) brightness shift +20%, (5) Gaussian noise sigma=0.05, (d) for each augmentation, prints whether anatomical consistency is preserved, (e) computes a \"signal preservation score\" for each augmentation: the correlation coefficient between the original and augmented images (values near 1.0 mean the content is well preserved, values near 0.0 mean significant distortion). Verify that horizontal flip fails the anatomical consistency check while all other augmentations pass, and that elastic deformation preserves anatomy while having lower correlation than simple brightness changes.\n\n**Exercise 2:** Build a **class imbalance strategy comparator**. Create a class `ImbalanceComparator` that: (a) generates a synthetic binary classification dataset with 1000 samples where only 30 are positive, with features drawn from overlapping Gaussians (positive: mean=1.0, std=1.5; negative: mean=0.0, std=1.0) in 10 dimensions, (b) trains 3 logistic regression classifiers (using sklearn): one with default settings, one with `class_weight='balanced'`, and one using SMOTE oversampling on the training set (use imblearn.over_sampling.SMOTE or implement a simple random oversampler if imblearn is not available), (c) evaluates each on a held-out test set (20% split) using sensitivity, specificity, AUC, and F1, (d) prints a comparison table showing all 4 metrics for all 3 strategies, (e) for each strategy, finds the threshold that achieves >= 90% sensitivity and reports the corresponding specificity. Verify that the default (unweighted) classifier has high specificity but poor sensitivity, while balanced weighting and oversampling improve sensitivity at the cost of some specificity.\n\n**Exercise 3:** Build a **multi-channel scientific image normalizer and analyzer**. Create a class `ScientificImageAnalyzer` that: (a) generates a synthetic 8-channel \"satellite image\" of size 128x128 where: channels 0-2 are visible RGB (values 0-255), channels 3-4 are near-infrared (values 500-3000), channel 5 is thermal (values 250-320, representing Kelvin), channels 6-7 are radar backscatter (values in dB, range -25 to 5), (b) implements `per_channel_normalize(image)` that z-score normalizes each channel independently, (c) implements `percentile_clip(image, low_pct=2, high_pct=98)` that clips each channel to its 2nd and 98th percentiles then rescales to [0, 1], (d) implements `compute_ndvi(image)` that computes the Normalized Difference Vegetation Index from the NIR and Red channels: NDVI = (NIR - Red) / (NIR + Red + 1e-8), (e) prints per-channel statistics (mean, std, min, max) before and after normalization, (f) computes and prints NDVI statistics (mean, std, fraction of pixels with NDVI > 0.3 indicating vegetation), (g) compares the correlation between channel pairs before and after normalization to verify that normalization preserves inter-channel relationships. Verify that per-channel normalization brings all channels to mean~0, std~1 regardless of their original value ranges, and that NDVI values are invariant to the normalization method used.\n\n### Thanks for reading!\n\n@scipio\n",
"json_metadata": "{\"tags\": [\"stem\", \"stemsocial\", \"steemstem\", \"python\", \"programming\"], \"app\": \"hiveblog/0.1\", \"format\": \"markdown\", \"image\": [\"https://images.hive.blog/DQmP6whkhWUYdkmpCQ1kNbtQDbkZS4gB7HojM4NRt86wph1/variant-c-12-green.png\"]}",
"parent_author": "",
"parent_permlink": "hive-196387",
"permlink": "learn-ai-series-89-medical-and-scientific-imaging",
"title": "Learn AI Series (#89) - Medical and Scientific Imaging"
}
],
"op_in_trx": 0,
"timestamp": "2026-06-06T07:05:09",
"trx_id": "bd048f75341b420b2bee01a07362384eac2af931",
"trx_in_block": 0,
"virtual_op": false
}2026/06/06 07:05:06
2026/06/06 07:05:06
| id | reblog |
| json | ["reblog", {"account": "scipio", "author": "scipio", "permlink": "learn-ai-series-89-medical-and-scientific-imaging"}] |
| required auths | [] |
| required posting auths | ["scipio"] |
| Transaction Info | Block #107028725/Trx 94739b9dd78749bb9ce9f1358f79de07024af767 |
View Raw JSON Data
{
"block": 107028725,
"op": [
"custom_json",
{
"id": "reblog",
"json": "[\"reblog\", {\"account\": \"scipio\", \"author\": \"scipio\", \"permlink\": \"learn-ai-series-89-medical-and-scientific-imaging\"}]",
"required_auths": [],
"required_posting_auths": [
"scipio"
]
}
],
"op_in_trx": 0,
"timestamp": "2026-06-06T07:05:06",
"trx_id": "94739b9dd78749bb9ce9f1358f79de07024af767",
"trx_in_block": 16,
"virtual_op": false
}scipioreceived 0.006 HP curation reward for @nanixxx / welcome-to-my-new-addiction2026/06/06 06:36:27
scipioreceived 0.006 HP curation reward for @nanixxx / welcome-to-my-new-addiction
2026/06/06 06:36:27
| author | nanixxx |
| curator | scipio |
| payout must be claimed | true |
| permlink | welcome-to-my-new-addiction |
| reward | 9.744267 VESTS |
| Transaction Info | Block #107028155/Virtual Operation 4294967295:148 |
View Raw JSON Data
{
"block": 107028155,
"op": [
"curation_reward",
{
"author": "nanixxx",
"curator": "scipio",
"payout_must_be_claimed": true,
"permlink": "welcome-to-my-new-addiction",
"reward": "9.744267 VESTS"
}
],
"op_in_trx": 148,
"timestamp": "2026-06-06T06:36:27",
"trx_id": "0000000000000000000000000000000000000000",
"trx_in_block": 4294967295,
"virtual_op": true
}scipioreceived 0.004 HP curation reward for @kellyane / a-glimpse-of-the-new-art-museum-in-ormoc-city2026/06/06 06:32:57
scipioreceived 0.004 HP curation reward for @kellyane / a-glimpse-of-the-new-art-museum-in-ormoc-city
2026/06/06 06:32:57
| author | kellyane |
| curator | scipio |
| payout must be claimed | true |
| permlink | a-glimpse-of-the-new-art-museum-in-ormoc-city |
| reward | 6.496179 VESTS |
| Transaction Info | Block #107028086/Virtual Operation 4294967295:260 |
View Raw JSON Data
{
"block": 107028086,
"op": [
"curation_reward",
{
"author": "kellyane",
"curator": "scipio",
"payout_must_be_claimed": true,
"permlink": "a-glimpse-of-the-new-art-museum-in-ormoc-city",
"reward": "6.496179 VESTS"
}
],
"op_in_trx": 260,
"timestamp": "2026-06-06T06:32:57",
"trx_id": "0000000000000000000000000000000000000000",
"trx_in_block": 4294967295,
"virtual_op": true
}scipioeffective vote applied for @meno / 20260606t061606348z2026/06/06 06:26:27
scipioeffective vote applied for @meno / 20260606t061606348z
2026/06/06 06:26:27
| author | meno |
| pending payout | 1.078 HBD |
| permlink | 20260606t061606348z |
| rshares | 200952377608 |
| total vote weight | 14395696961464 |
| voter | scipio |
| weight | 200952377608 |
| Transaction Info | Block #107027956/Trx 4ee92a3ff64a54c6a74ccecb8bca3ad2b4184d55 |
View Raw JSON Data
{
"block": 107027956,
"op": [
"effective_comment_vote",
{
"author": "meno",
"pending_payout": "1.078 HBD",
"permlink": "20260606t061606348z",
"rshares": 200952377608,
"total_vote_weight": 14395696961464,
"voter": "scipio",
"weight": 200952377608
}
],
"op_in_trx": 1,
"timestamp": "2026-06-06T06:26:27",
"trx_id": "4ee92a3ff64a54c6a74ccecb8bca3ad2b4184d55",
"trx_in_block": 35,
"virtual_op": true
}scipioupvoted (100.00%) @meno / 20260606t061606348z2026/06/06 06:26:27
scipioupvoted (100.00%) @meno / 20260606t061606348z
2026/06/06 06:26:27
| author | meno |
| permlink | 20260606t061606348z |
| voter | scipio |
| weight | 10000 (100.00%) |
| Transaction Info | Block #107027956/Trx 4ee92a3ff64a54c6a74ccecb8bca3ad2b4184d55 |
View Raw JSON Data
{
"block": 107027956,
"op": [
"vote",
{
"author": "meno",
"permlink": "20260606t061606348z",
"voter": "scipio",
"weight": 10000
}
],
"op_in_trx": 0,
"timestamp": "2026-06-06T06:26:27",
"trx_id": "4ee92a3ff64a54c6a74ccecb8bca3ad2b4184d55",
"trx_in_block": 35,
"virtual_op": false
}scipioupdated payout for learn-ai-series-82-optical-character-recognition2026/06/06 06:24:45
scipioupdated payout for learn-ai-series-82-optical-character-recognition
2026/06/06 06:24:45
| author | scipio |
| permlink | learn-ai-series-82-optical-character-recognition |
| Transaction Info | Block #107027923/Virtual Operation 4294967295:19 |
View Raw JSON Data
{
"block": 107027923,
"op": [
"comment_payout_update",
{
"author": "scipio",
"permlink": "learn-ai-series-82-optical-character-recognition"
}
],
"op_in_trx": 19,
"timestamp": "2026-06-06T06:24:45",
"trx_id": "0000000000000000000000000000000000000000",
"trx_in_block": 4294967295,
"virtual_op": true
}scipioreceived 1.155 HBD reward share for learn-ai-series-82-optical-character-recognition2026/06/06 06:24:45
scipioreceived 1.155 HBD reward share for learn-ai-series-82-optical-character-recognition
2026/06/06 06:24:45
| author | scipio |
| author rewards | 10140 |
| beneficiary payout value | 0.000 HBD |
| curator payout value | 0.577 HBD |
| payout | 1.155 HBD |
| permlink | learn-ai-series-82-optical-character-recognition |
| total payout value | 0.577 HBD |
| Transaction Info | Block #107027923/Virtual Operation 4294967295:18 |
View Raw JSON Data
{
"block": 107027923,
"op": [
"comment_reward",
{
"author": "scipio",
"author_rewards": 10140,
"beneficiary_payout_value": "0.000 HBD",
"curator_payout_value": "0.577 HBD",
"payout": "1.155 HBD",
"permlink": "learn-ai-series-82-optical-character-recognition",
"total_payout_value": "0.577 HBD"
}
],
"op_in_trx": 18,
"timestamp": "2026-06-06T06:24:45",
"trx_id": "0000000000000000000000000000000000000000",
"trx_in_block": 4294967295,
"virtual_op": true
}scipioreceived 5.070 HIVE, 5.072 HP author reward for @scipio / learn-ai-series-82-optical-character-recognition2026/06/06 06:24:45
scipioreceived 5.070 HIVE, 5.072 HP author reward for @scipio / learn-ai-series-82-optical-character-recognition
2026/06/06 06:24:45
| author | scipio |
| curators vesting payout | 16453.206520 VESTS |
| hbd payout | 0.000 HBD |
| hive payout | 5.070 HIVE |
| payout must be claimed | true |
| permlink | learn-ai-series-82-optical-character-recognition |
| vesting payout | 8233.911465 VESTS |
| Transaction Info | Block #107027923/Virtual Operation 4294967295:17 |
View Raw JSON Data
{
"block": 107027923,
"op": [
"author_reward",
{
"author": "scipio",
"curators_vesting_payout": "16453.206520 VESTS",
"hbd_payout": "0.000 HBD",
"hive_payout": "5.070 HIVE",
"payout_must_be_claimed": true,
"permlink": "learn-ai-series-82-optical-character-recognition",
"vesting_payout": "8233.911465 VESTS"
}
],
"op_in_trx": 17,
"timestamp": "2026-06-06T06:24:45",
"trx_id": "0000000000000000000000000000000000000000",
"trx_in_block": 4294967295,
"virtual_op": true
}scipioreceived 0.129 HP curation reward for @scipio / learn-ai-series-82-optical-character-recognition2026/06/06 06:24:45
scipioreceived 0.129 HP curation reward for @scipio / learn-ai-series-82-optical-character-recognition
2026/06/06 06:24:45
| author | scipio |
| curator | scipio |
| payout must be claimed | true |
| permlink | learn-ai-series-82-optical-character-recognition |
| reward | 209.501889 VESTS |
| Transaction Info | Block #107027923/Virtual Operation 4294967295:8 |
View Raw JSON Data
{
"block": 107027923,
"op": [
"curation_reward",
{
"author": "scipio",
"curator": "scipio",
"payout_must_be_claimed": true,
"permlink": "learn-ai-series-82-optical-character-recognition",
"reward": "209.501889 VESTS"
}
],
"op_in_trx": 8,
"timestamp": "2026-06-06T06:24:45",
"trx_id": "0000000000000000000000000000000000000000",
"trx_in_block": 4294967295,
"virtual_op": true
}Manabar
Voting Power78.88%
Downvote Power100.00%
Resource Credits100.00%
Reputation Progress0.00%
{
"voting_manabar": {
"current_mana": 7629133205792,
"last_update_time": 1780734084
},
"downvote_manabar": {
"current_mana": 2514750603386,
"last_update_time": 1780734084
},
"rc_account": {
"account": "scipio",
"delegated_rc": 0,
"max_rc": 10061023162519,
"max_rc_creation_adjustment": {
"amount": "2020748973",
"nai": "@@000000037",
"precision": 6
},
"rc_manabar": {
"current_mana": 10060935555543,
"last_update_time": 1780734084
},
"received_delegated_rc": 0
}
}Account Metadata
| POSTING JSON METADATA | |
| profile | {"name":"scipio","about":"Does it matter who's right, or who's left?","cover_image":"https://static.pexels.com/photos/290386/pexels-photo-290386.jpeg","profile_image":"https://cdn.steemitimages.com/DQmSHCeBbjgGEsnigAh9qJTdcFuKvm8vaxRYACdkvPi8WBg/scipio.jpg"} |
| JSON METADATA | |
| profile | {"name":"scipio","about":"Does it matter who's right, or who's left?","cover_image":"https://static.pexels.com/photos/290386/pexels-photo-290386.jpeg","profile_image":"https://cdn.steemitimages.com/DQmSHCeBbjgGEsnigAh9qJTdcFuKvm8vaxRYACdkvPi8WBg/scipio.jpg"} |
{
"posting_json_metadata": {
"profile": {
"name": "scipio",
"about": "Does it matter who's right, or who's left?",
"cover_image": "https://static.pexels.com/photos/290386/pexels-photo-290386.jpeg",
"profile_image": "https://cdn.steemitimages.com/DQmSHCeBbjgGEsnigAh9qJTdcFuKvm8vaxRYACdkvPi8WBg/scipio.jpg"
}
},
"json_metadata": {
"profile": {
"name": "scipio",
"about": "Does it matter who's right, or who's left?",
"cover_image": "https://static.pexels.com/photos/290386/pexels-photo-290386.jpeg",
"profile_image": "https://cdn.steemitimages.com/DQmSHCeBbjgGEsnigAh9qJTdcFuKvm8vaxRYACdkvPi8WBg/scipio.jpg"
}
}
}Auth Keys
Owner
Single Signature
Public Keys
STM54sX7GE6LRFVn9b3rRyZkkgrAJeXAHsaupuxTCfrjqAuHgf6Ej1/1
Active
Single Signature
Public Keys
STM8RLCJUVQyKPiGzkZQpXLmoy2w5bMTv8shWEAfBPPaP4FpUX7To1/1
Posting
Single Signature
Public Keys
STM8eHBTT5K4piEfvYAk99g3CxxuAGFQdNqA98BPtRp1SucFxgBqD1/1
Memo
STM8ZMNP7R48LekiaQu9Kz4UNVYcnVwFsY1fRhgY7MGDx2hxxukvS
{
"owner": {
"account_auths": [],
"key_auths": [
[
"STM54sX7GE6LRFVn9b3rRyZkkgrAJeXAHsaupuxTCfrjqAuHgf6Ej",
1
]
],
"weight_threshold": 1
},
"active": {
"account_auths": [],
"key_auths": [
[
"STM8RLCJUVQyKPiGzkZQpXLmoy2w5bMTv8shWEAfBPPaP4FpUX7To",
1
]
],
"weight_threshold": 1
},
"posting": {
"account_auths": [
[
"utopian.app",
1
],
[
"utopianpay",
1
]
],
"key_auths": [
[
"STM8eHBTT5K4piEfvYAk99g3CxxuAGFQdNqA98BPtRp1SucFxgBqD",
1
]
],
"weight_threshold": 1
},
"memo": "STM8ZMNP7R48LekiaQu9Kz4UNVYcnVwFsY1fRhgY7MGDx2hxxukvS"
}Witness Votes
21 / 30
01.arcange |
02.ausbitbank |
03.blocktrades |
04.blue-witness |
05.dalz |
06.deathwing |
07.drakos |
08.good-karma |
09.gtg |
11.mahdiyari |
12.neoxian |
13.nuthman |
14.pharesim |
15.rishi556 |
16.roelandp |
17.someguy123 |
18.steempeak |
19.stoodkev |
20.threespeak |
21.ura-soul |
[ "arcange", "ausbitbank", "blocktrades", "blue-witness", "dalz", "deathwing", "drakos", "good-karma", "gtg", "guiltyparties", "mahdiyari", "neoxian", "nuthman", "pharesim", "rishi556", "roelandp", "someguy123", "steempeak", "stoodkev", "threespeak", "ura-soul" ]
